@navaramap/three 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/fontWorker-JfTCpd38.js +2 -0
  3. package/dist/assets/fontWorker-JfTCpd38.js.map +1 -0
  4. package/dist/assets/{index-TKhRxurv.js → index-B3U_DvcP.js} +2 -2
  5. package/dist/assets/index-B3U_DvcP.js.map +1 -0
  6. package/dist/assets/{navara_wasm_font_worker_bg-Kk06ArRB.wasm → navara_wasm_font_worker_bg-DympKYVX.wasm} +0 -0
  7. package/dist/assets/{navara_wasm_worker_bg-BZjLKf4i.wasm → navara_wasm_worker_bg-Bx_l45cW.wasm} +0 -0
  8. package/dist/index.d.ts +510 -374
  9. package/dist/index.js +6150 -5810
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.umd.cjs +1188 -793
  12. package/dist/index.umd.cjs.map +1 -1
  13. package/package.json +12 -12
  14. package/src/core/DescRegistry.ts +3 -2
  15. package/src/core/EffectDesc.ts +10 -0
  16. package/src/core/EffectDescRegistry.ts +14 -0
  17. package/src/core/MeshDesc.ts +39 -2
  18. package/src/core/MeshDescWithSelectiveEffect.ts +14 -2
  19. package/src/core/ViewContext.ts +116 -7
  20. package/src/device.ts +4 -2
  21. package/src/index.ts +123 -11
  22. package/src/layer.ts +7 -5
  23. package/src/layers/effect/MRTPassEffectDesc.ts +34 -3
  24. package/src/layers/effect/SelectiveEffectDesc.ts +10 -2
  25. package/src/layers/effect/TransparentPassEffectDesc.ts +5 -4
  26. package/src/layersManager.ts +32 -1
  27. package/src/material/enhancer/model/modelBaseEnhancer/shader.ts +3 -2
  28. package/src/material/enhancer/polygon/polygonBaseEnhancer/index.ts +0 -6
  29. package/src/material/enhancer/polygon/polygonBaseEnhancer/mutates.ts +0 -6
  30. package/src/material/enhancer/polygon/polygonBaseEnhancer/shader.ts +4 -10
  31. package/src/material/enhancer/polygon/polygonBaseEnhancer/state.ts +1 -4
  32. package/src/material/enhancer/polygon/polygonBaseEnhancer/types.ts +0 -9
  33. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/coverage.ts +31 -0
  34. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.test.ts +75 -0
  35. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/shader.ts +26 -0
  36. package/src/material/enhancer/sdfText/sdfTextBaseEnhancer/types.ts +1 -1
  37. package/src/material/gbufferLayout.test.ts +171 -0
  38. package/src/material/gbufferLayout.ts +298 -0
  39. package/src/material/index.ts +1 -0
  40. package/src/material/macro/tileShader.test.ts +35 -33
  41. package/src/material/macro/tileShader.ts +33 -10
  42. package/src/material/overrideMaterialsForMRT.ts +113 -71
  43. package/src/mesh/DrapedMesh.test.ts +57 -0
  44. package/src/mesh/DrapedMesh.ts +82 -0
  45. package/src/mesh/model.ts +3 -0
  46. package/src/mesh/polygon.ts +3 -2
  47. package/src/mesh/polyline.ts +3 -0
  48. package/src/mesh/sdfText/layout.ts +1 -3
  49. package/src/mesh/tile/index.ts +8 -4
  50. package/src/passes/CustomRenderPass.test.ts +143 -0
  51. package/src/passes/CustomRenderPass.ts +273 -30
  52. package/src/passes/RenderTargetCopyPass.ts +6 -0
  53. package/src/plugins/AttributionPlugin/index.ts +3 -2
  54. package/src/shaders/index.ts +16 -0
  55. package/src/type/index.ts +9 -51
  56. package/dist/assets/fontWorker-OwvKiQ8o.js +0 -2
  57. package/dist/assets/fontWorker-OwvKiQ8o.js.map +0 -1
  58. package/dist/assets/index-TKhRxurv.js.map +0 -1
@@ -1,13 +1,62 @@
1
1
  // Ref: https://github.com/takram-design-engineering/three-geospatial/blob/main/packages/effects/src/setupMaterialsForGeometryPass.ts
2
2
 
3
3
  import { packing } from "@takram/three-geospatial/shaders";
4
- import { ShaderLib, ShaderMaterial, type ShaderLibShader } from "three";
4
+ import {
5
+ ShaderLib,
6
+ ShaderMaterial,
7
+ type Material,
8
+ type ShaderLibShader,
9
+ } from "three";
5
10
  import { LineMaterial } from "three-stdlib";
6
11
 
7
12
  import { createReplacer } from "../utils";
8
13
 
14
+ import {
15
+ GBUFFER_EFFECT_WRITE_BUILTIN,
16
+ GBUFFER_EFFECT_WRITE_ID_ONLY,
17
+ GBUFFER_EFFECT_WRITE_SHADER_MATERIAL,
18
+ GBUFFER_EFFECT_WRITE_ZERO,
19
+ GBUFFER_NORMAL_WRITE_BASIC,
20
+ GBUFFER_NORMAL_WRITE_PHYSICAL,
21
+ GBUFFER_PARS_FRAGMENT,
22
+ } from "./gbufferLayout";
23
+
9
24
  const SETUP = Symbol("SETUP");
10
25
 
26
+ /** `lit: true` – wins over {@link NVR_UNLIT_SCENE_DEFINE}. */
27
+ export const NVR_LIT_DEFINE = "NVR_LIT";
28
+ /** `lit: false` – only the lighting equation is bypassed; the lit pipeline
29
+ * still runs, so normals and the shadow G-buffer keep being written. */
30
+ export const NVR_UNLIT_DEFINE = "NVR_UNLIT";
31
+ /** Scene-level default, stamped by `CustomRenderPass` from `view.lit`. */
32
+ export const NVR_UNLIT_SCENE_DEFINE = "NVR_UNLIT_SCENE";
33
+ /**
34
+ * Stamped from `material.transparent`. Under blending, `normalBuffer.a` is
35
+ * the attachment's blend factor, so a blended material must write 1.0 there
36
+ * instead of roughness or it keeps the normal of whatever lies behind.
37
+ */
38
+ export const NVR_BLENDED_DEFINE = "NVR_BLENDED";
39
+
40
+ /** Applies a material's three-state `lit` option. */
41
+ export function applyLitOption(
42
+ material: Material,
43
+ lit: boolean | undefined,
44
+ ): void {
45
+ // `false` is three's "absent" define value, avoiding a delete.
46
+ const forceLit = lit === true ? 1 : false;
47
+ const forceUnlit = lit === false ? 1 : false;
48
+ material.defines ??= {};
49
+ if (
50
+ (material.defines[NVR_LIT_DEFINE] ?? false) === forceLit &&
51
+ (material.defines[NVR_UNLIT_DEFINE] ?? false) === forceUnlit
52
+ ) {
53
+ return;
54
+ }
55
+ material.defines[NVR_LIT_DEFINE] = forceLit;
56
+ material.defines[NVR_UNLIT_DEFINE] = forceUnlit;
57
+ material.needsUpdate = true;
58
+ }
59
+
11
60
  declare module "three" {
12
61
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
13
62
  interface ShaderLibShader {
@@ -94,27 +143,20 @@ function injectGBuffer(
94
143
  const normalBufferWrite =
95
144
  type === "physical"
96
145
  ? /* glsl */ `
97
- normalBuffer = vec4(packNormalToVec2(normal), metalnessFactor, roughnessFactor)
146
+ ${GBUFFER_NORMAL_WRITE_PHYSICAL}
98
147
  `
99
148
  : /* glsl */ `
100
149
  #ifdef USE_ROUGHNESS
101
150
  float roughnessFactor = roughness;
102
151
  #else
103
- float roughnessFactor = 0.0;
152
+ // Not just a default: 0.0 here would leak the normal behind a
153
+ // blended surface (see NVR_BLENDED_DEFINE).
154
+ float roughnessFactor = 1.0;
104
155
  #endif
105
- normalBuffer = vec4(packNormalToVec2(normal), reflectivity, roughnessFactor);
156
+ ${GBUFFER_NORMAL_WRITE_BASIC}
106
157
  `;
107
158
  shader.fragmentShader = /* glsl */ `
108
- // MRT layout:
109
- // location 0: gl_FragColor (color)
110
- // location 1: normalBuffer (normal + material props)
111
- // location 2: effectIdBuffer (effectIds: R=bitmask) — SelectiveEffect only
112
- // location 3: emissiveBuffer (emissive: RGB=diffuseColor×intensity+emissive additive, A=unused) — SelectiveEffect only
113
- #ifndef USE_SHADOWMAP_DEPTH
114
- layout(location = 1) out vec4 normalBuffer;
115
- layout(location = 2) out vec4 effectIdBuffer;
116
- layout(location = 3) out vec4 emissiveBuffer;
117
- #endif
159
+ ${GBUFFER_PARS_FRAGMENT}
118
160
 
119
161
  #if !defined(USE_ENVMAP)
120
162
  uniform float reflectivity;
@@ -131,23 +173,35 @@ function injectGBuffer(
131
173
 
132
174
  ${packing}
133
175
  ${
134
- createReplacer(shader.fragmentShader).replace(
135
- /}\s*$/, // Assume the last curly brace is of main()
136
- /* glsl */ `
176
+ createReplacer(shader.fragmentShader)
177
+ .replace(
178
+ /* glsl */ `#include <opaque_fragment>`,
179
+ /* glsl */ `
180
+ #if !defined(${NVR_LIT_DEFINE}) && (defined(${NVR_UNLIT_DEFINE}) || defined(${NVR_UNLIT_SCENE_DEFINE}))
181
+ // Overwrite rather than skip the lighting: the G-buffer writes
182
+ // below still need the lit pipeline to have run.
183
+ outgoingLight = diffuseColor.rgb;
184
+ #endif
185
+ #include <opaque_fragment>
186
+ `,
187
+ )
188
+ .replace(
189
+ /}\s*$/, // Assume the last curly brace is of main()
190
+ /* glsl */ `
137
191
  #ifndef USE_SHADOWMAP_DEPTH
138
- ${normalBufferWrite};
192
+ ${normalBufferWrite}
139
193
 
140
194
  #ifdef USE_SELECTIVE_EFFECT
141
- effectIdBuffer = vec4(uEffectIdsMask, 0.0, 0.0, 1.0);
142
- emissiveBuffer = vec4(diffuseColor.rgb * uEmissiveIntensity + emissive, 1.0);
195
+ ${GBUFFER_EFFECT_WRITE_BUILTIN}
143
196
  #else
144
- effectIdBuffer = vec4(0.0);
145
- emissiveBuffer = vec4(0.0);
197
+ ${GBUFFER_EFFECT_WRITE_ZERO}
146
198
  #endif
199
+
200
+ GBUFFER_WRITE_SHADOW
147
201
  #endif
148
202
  }
149
203
  `,
150
- ).source
204
+ ).source
151
205
  }
152
206
  `;
153
207
  shader[SETUP] = true;
@@ -173,9 +227,7 @@ function injectGBufferToSpriteMaterial(shader: ShaderLibShader) {
173
227
  `;
174
228
 
175
229
  shader.fragmentShader = /* glsl */ `
176
- layout(location = 1) out vec4 normalBuffer;
177
- layout(location = 2) out vec4 effectIdBuffer;
178
- layout(location = 3) out vec4 emissiveBuffer;
230
+ ${GBUFFER_PARS_FRAGMENT}
179
231
 
180
232
  #ifdef USE_SELECTIVE_EFFECT
181
233
  uniform float uEffectIdsMask;
@@ -189,22 +241,21 @@ function injectGBufferToSpriteMaterial(shader: ShaderLibShader) {
189
241
  createReplacer(shader.fragmentShader).replace(
190
242
  /}\s*$/, // Assume the last curly brace is of main()
191
243
  /* glsl */ `
192
- // Flat shading
193
- vec3 fdx = dFdx( vViewPosition );
194
- vec3 fdy = dFdy( vViewPosition );
195
- vec3 normal = normalize( cross( fdx, fdy ) );
196
- normalBuffer = vec4(
197
- packNormalToVec2(normal),
198
- 0.0,
199
- 0.0
200
- );
201
-
202
- #ifdef USE_SELECTIVE_EFFECT
203
- effectIdBuffer = vec4(uEffectIdsMask, 0.0, 0.0, 1.0);
204
- emissiveBuffer = vec4(0.0);
205
- #else
206
- effectIdBuffer = vec4(0.0);
207
- emissiveBuffer = vec4(0.0);
244
+ #ifndef USE_SHADOWMAP_DEPTH
245
+ // Flat shading
246
+ vec3 fdx = dFdx( vViewPosition );
247
+ vec3 fdy = dFdy( vViewPosition );
248
+ vec3 normal = normalize( cross( fdx, fdy ) );
249
+ // A=1.0 replaces the normal behind under blending.
250
+ GBUFFER_WRITE_NORMAL(normal, 0.0, 1.0)
251
+
252
+ #ifdef USE_SELECTIVE_EFFECT
253
+ ${GBUFFER_EFFECT_WRITE_ID_ONLY}
254
+ #else
255
+ ${GBUFFER_EFFECT_WRITE_ZERO}
256
+ #endif
257
+
258
+ GBUFFER_WRITE_SHADOW_ZERO
208
259
  #endif
209
260
  }
210
261
  `,
@@ -269,18 +320,12 @@ function injectGBufferToShaderMaterial(
269
320
  const logdepthParsFrag = "#include <logdepthbuf_pars_fragment>";
270
321
  const logdepthFrag = "#include <logdepthbuf_fragment>";
271
322
  const normalBufferWrite = /* glsl */ `
272
- vec4(
273
- packNormalToVec2(${normalVariableName}),
274
- 0.0,
275
- 0.0
276
- );
323
+ GBUFFER_WRITE_NORMAL(${normalVariableName}, 0.0, 1.0)
277
324
  `;
278
325
  shader.fragmentShader = /* glsl */ `
279
- #ifndef USE_SHADOWMAP_DEPTH
280
- layout(location = 1) out vec4 normalBuffer;
281
- layout(location = 2) out vec4 effectIdBuffer;
282
- layout(location = 3) out vec4 emissiveBuffer;
326
+ ${GBUFFER_PARS_FRAGMENT}
283
327
 
328
+ #ifndef USE_SHADOWMAP_DEPTH
284
329
  #ifdef USE_SELECTIVE_EFFECT
285
330
  uniform float uEffectIdsMask;
286
331
  uniform vec3 uEmissiveColor;
@@ -299,15 +344,15 @@ function injectGBufferToShaderMaterial(
299
344
  ${shader.fragmentShader.includes(logdepthFrag) ? "" : logdepthFrag}
300
345
 
301
346
  #ifndef USE_SHADOWMAP_DEPTH
302
- normalBuffer = ${normalBufferWrite};
347
+ ${normalBufferWrite};
303
348
 
304
349
  #ifdef USE_SELECTIVE_EFFECT
305
- effectIdBuffer = vec4(uEffectIdsMask, 0.0, 0.0, 1.0);
306
- emissiveBuffer = vec4((gl_FragColor.rgb + uEmissiveColor) * uEmissiveIntensity, 1.0);
350
+ ${GBUFFER_EFFECT_WRITE_SHADER_MATERIAL}
307
351
  #else
308
- effectIdBuffer = vec4(0.0);
309
- emissiveBuffer = vec4(0.0);
352
+ ${GBUFFER_EFFECT_WRITE_ZERO}
310
353
  #endif
354
+
355
+ GBUFFER_WRITE_SHADOW_ZERO
311
356
  #endif
312
357
  }
313
358
  `,
@@ -350,9 +395,7 @@ function injectGBufferToLineMaterial(lineMaterial: LineMaterial) {
350
395
  // LineMaterial already has proper vertex shader setup, so we only modify fragment shader
351
396
 
352
397
  lineMaterial.fragmentShader = /* glsl */ `
353
- layout(location = 1) out vec4 normalBuffer;
354
- layout(location = 2) out vec4 effectIdBuffer;
355
- layout(location = 3) out vec4 emissiveBuffer;
398
+ ${GBUFFER_PARS_FRAGMENT}
356
399
 
357
400
  #ifdef USE_SELECTIVE_EFFECT
358
401
  uniform float uEffectIdsMask;
@@ -378,18 +421,17 @@ function injectGBufferToLineMaterial(lineMaterial: LineMaterial) {
378
421
  .replace(
379
422
  /}\s*$/, // Assume the last curly brace is of main()
380
423
  /* glsl */ `
381
- normalBuffer = vec4(
382
- packNormalToVec2(normal),
383
- 0.0,
384
- 0.0
385
- );
386
-
387
- #ifdef USE_SELECTIVE_EFFECT
388
- effectIdBuffer = vec4(uEffectIdsMask, 0.0, 0.0, 1.0);
389
- emissiveBuffer = vec4(0.0);
390
- #else
391
- effectIdBuffer = vec4(0.0);
392
- emissiveBuffer = vec4(0.0);
424
+ #ifndef USE_SHADOWMAP_DEPTH
425
+ // A=1.0 replaces the normal behind under blending.
426
+ GBUFFER_WRITE_NORMAL(normal, 0.0, 1.0)
427
+
428
+ #ifdef USE_SELECTIVE_EFFECT
429
+ ${GBUFFER_EFFECT_WRITE_ID_ONLY}
430
+ #else
431
+ ${GBUFFER_EFFECT_WRITE_ZERO}
432
+ #endif
433
+
434
+ GBUFFER_WRITE_SHADOW_ZERO
393
435
  #endif
394
436
  }
395
437
  `,
@@ -0,0 +1,57 @@
1
+ import { MeshLambertMaterial, ShaderLib, type WebGLRenderer } from "three";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ import { overrideMaterialsForMRT } from "../material";
5
+
6
+ import { setupMaterialForDrape } from "./DrapedMesh";
7
+
8
+ type ShaderStub = Parameters<MeshLambertMaterial["onBeforeCompile"]>[0];
9
+
10
+ const compile = (material: MeshLambertMaterial): ShaderStub => {
11
+ const shader = {
12
+ name: "",
13
+ uniforms: {},
14
+ vertexShader: ShaderLib.lambert.vertexShader,
15
+ fragmentShader: ShaderLib.lambert.fragmentShader,
16
+ defines: {},
17
+ } as unknown as ShaderStub;
18
+ material.onBeforeCompile(shader, null as unknown as WebGLRenderer);
19
+ return shader;
20
+ };
21
+
22
+ describe("setupMaterialForDrape", () => {
23
+ it("substitutes the globe normal ahead of the lighting", () => {
24
+ overrideMaterialsForMRT();
25
+ const material = new MeshLambertMaterial();
26
+ const globeNormal = { value: null };
27
+
28
+ setupMaterialForDrape(material, globeNormal);
29
+ const shader = compile(material);
30
+
31
+ expect(shader.fragmentShader).toContain("nvrDrapeUv");
32
+ expect(shader.uniforms.uGlobeNormal).toBe(globeNormal);
33
+ expect(shader.fragmentShader.indexOf("nvrDrapeUv")).toBeLessThan(
34
+ shader.fragmentShader.indexOf("#include <lights_fragment_begin>"),
35
+ );
36
+ });
37
+
38
+ it("injects even when an earlier handler consumes the anchor", () => {
39
+ // navara_three_csm swaps `#include <lights_fragment_begin>` for its
40
+ // cascaded-lights chunk. Delegating to it first left nothing to anchor to,
41
+ // so the substitution silently never happened.
42
+ overrideMaterialsForMRT();
43
+ const material = new MeshLambertMaterial();
44
+ material.onBeforeCompile = (shader) => {
45
+ shader.fragmentShader = shader.fragmentShader.replace(
46
+ "#include <lights_fragment_begin>",
47
+ "// cascaded lights",
48
+ );
49
+ };
50
+
51
+ setupMaterialForDrape(material, { value: null });
52
+ const shader = compile(material);
53
+
54
+ expect(shader.fragmentShader).toContain("nvrDrapeUv");
55
+ expect(shader.fragmentShader).toContain("// cascaded lights");
56
+ });
57
+ });
@@ -12,8 +12,69 @@ import {
12
12
  type Material,
13
13
  type NormalBufferAttributes,
14
14
  type Object3DEventMap,
15
+ type Texture,
15
16
  } from "three";
16
17
 
18
+ import { createReplacer } from "../utils";
19
+
20
+ const DRAPE_SETUP = Symbol("DRAPE_SETUP");
21
+
22
+ /**
23
+ * Shades a draped material with the terrain's normal instead of its own. The
24
+ * visible fragments are the back faces of a volume clipped to the ground by
25
+ * {@link DrapedMesh.process}, so the mesh's own normals point in an arbitrary
26
+ * direction while the ground's normal is what the lighting should use.
27
+ *
28
+ * The decoder (`unpackVec2ToNormal`) comes from the packing helpers that
29
+ * `overrideMaterialsForMRT` puts in every `ShaderLib` material, so a custom
30
+ * `ShaderMaterial` in the draped scene needs to bring its own. Idempotent.
31
+ *
32
+ * @param globeNormal - Uniform ref for the globe normal copy, kept alive by
33
+ * the caller (see `CustomRenderPass`).
34
+ */
35
+ export function setupMaterialForDrape(
36
+ material: Material,
37
+ globeNormal: { value: Texture | null },
38
+ ): void {
39
+ const target = material as Material & { [DRAPE_SETUP]?: boolean };
40
+ if (target[DRAPE_SETUP]) return;
41
+ target[DRAPE_SETUP] = true;
42
+
43
+ // Draped and undraped materials must not share a compiled program.
44
+ const previousCacheKey = material.customProgramCacheKey;
45
+ material.customProgramCacheKey = () =>
46
+ `${previousCacheKey ? previousCacheKey.call(material) : ""}_NvrDraped`;
47
+
48
+ const previousOnBeforeCompile = material.onBeforeCompile;
49
+ material.onBeforeCompile = (shader, renderer) => {
50
+ // Runs BEFORE the previous handler on purpose: navara_three_csm swaps
51
+ // `#include <lights_fragment_begin>` for its cascaded-lights chunk, so
52
+ // delegating first would leave nothing to anchor to. The include survives
53
+ // this replacement, so the CSM swap still finds it afterwards.
54
+ const hook = "#include <lights_fragment_begin>";
55
+ // Absent from unlit materials, where drape shading is moot.
56
+ if (shader.fragmentShader.includes(hook)) {
57
+ shader.uniforms.uGlobeNormal = globeNormal;
58
+ shader.fragmentShader = createReplacer(
59
+ `uniform sampler2D uGlobeNormal;\n${shader.fragmentShader}`,
60
+ ).replace(
61
+ hook,
62
+ `
63
+ {
64
+ vec2 nvrDrapeUv = gl_FragCoord.xy / vec2(textureSize(uGlobeNormal, 0));
65
+ normal = normalize(unpackVec2ToNormal(texture2D(uGlobeNormal, nvrDrapeUv).xy));
66
+ }
67
+ ${hook}
68
+ `,
69
+ ).source;
70
+ }
71
+
72
+ previousOnBeforeCompile?.call(material, shader, renderer);
73
+ };
74
+
75
+ material.needsUpdate = true;
76
+ }
77
+
17
78
  export class DrapedMesh<
18
79
  TGeometry extends BufferGeometry = BufferGeometry<NormalBufferAttributes>,
19
80
  TMaterial extends Material | Material[] = Material | Material[],
@@ -24,6 +85,20 @@ export class DrapedMesh<
24
85
  constructor(geometry?: TGeometry, material?: TMaterial, enable = true) {
25
86
  super(geometry, material);
26
87
  this.drapedEnable = enable;
88
+
89
+ // Shadows are forced off while draped: see process() for why the shading
90
+ // must not depend on world position. Installed as an own accessor because
91
+ // Object3D declares `receiveShadow` as a plain property, which TypeScript
92
+ // refuses to let a subclass override with a getter/setter (TS2611).
93
+ let receiveShadow = false;
94
+ Object.defineProperty(this, "receiveShadow", {
95
+ get: () => (this.drapedEnable ? false : receiveShadow),
96
+ set: (value: boolean) => {
97
+ receiveShadow = value;
98
+ },
99
+ configurable: true,
100
+ enumerable: true,
101
+ });
27
102
  }
28
103
 
29
104
  enabled() {
@@ -34,6 +109,13 @@ export class DrapedMesh<
34
109
  * Run the stencil-test draping passes for this mesh.
35
110
  * The caller supplies a `render` callback that performs the actual draw call
36
111
  * (e.g. `renderer.render(scene, camera)`).
112
+ *
113
+ * The final pass draws every back face with the depth test off, so a pixel
114
+ * can be covered several times where the volume folds over a peak. The
115
+ * shading must therefore not depend on world position, or the far face
116
+ * shows through the near one — hence the screen-space normal from
117
+ * {@link setupMaterialForDrape} and the forced-off `receiveShadow`. The
118
+ * terrain underneath keeps its own shadows; only the drape stays lit.
37
119
  */
38
120
  process(render: () => void): void {
39
121
  if (!this.enabled()) return;
package/src/mesh/model.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  import invariant from "tiny-invariant";
25
25
 
26
26
  import type { EventContext } from "../event/context";
27
+ import { applyLitOption } from "../material";
27
28
  import {
28
29
  createModelMaterialEnhancer,
29
30
  createPntsEnhancer,
@@ -306,6 +307,7 @@ export class ModelMesh
306
307
 
307
308
  mesh.castShadow = !!meshMaterial.castShadow;
308
309
  mesh.receiveShadow = !!meshMaterial.receiveShadow;
310
+ applyLitOption(mesh.material, meshMaterial.lit);
309
311
 
310
312
  mesh.material.depthTest = true;
311
313
  mesh.material.depthWrite = true;
@@ -369,6 +371,7 @@ export class ModelMesh
369
371
  enhancer.update(updateProps);
370
372
  mesh.castShadow = !!material.castShadow;
371
373
  mesh.receiveShadow = !!material.receiveShadow;
374
+ applyLitOption(mesh.material, material.lit);
372
375
  }
373
376
 
374
377
  const pntsProps: PntsProps = {
@@ -3,7 +3,6 @@ import {
3
3
  PolygonMesh as NavaraPolygonMesh,
4
4
  PolygonMaterial,
5
5
  } from "@navaramap/engine";
6
- import type { Texture } from "three";
7
6
  import {
8
7
  BufferAttribute,
9
8
  BufferGeometry,
@@ -19,6 +18,7 @@ import {
19
18
 
20
19
  import { PolygonOutlineMesh } from "..";
21
20
  import type { EventContext } from "../event/context";
21
+ import { applyLitOption } from "../material";
22
22
  import type { PolygonMaterialProps } from "../material/enhancer/polygon";
23
23
  import { createPolygonMaterialEnhancer } from "../material/enhancer/polygon/polygonMaterialEnhancer";
24
24
 
@@ -286,6 +286,7 @@ export class PolygonMesh extends BatchedFeatureMesh<
286
286
 
287
287
  this.castShadow = !!meshMaterial.castShadow;
288
288
  this.receiveShadow = !!meshMaterial.receiveShadow;
289
+ applyLitOption(this.material, meshMaterial.lit);
289
290
 
290
291
  // This mesh is texturized if it has a tile handle (terrain attachment).
291
292
  // `!= null`, not truthiness: the root vector tile's handle is 0.
@@ -322,7 +323,6 @@ export class PolygonMesh extends BatchedFeatureMesh<
322
323
  roughness: meshMaterial.roughness,
323
324
  emissiveColor: meshMaterial.emissiveColor,
324
325
  emissiveIntensity: meshMaterial.emissiveIntensity,
325
- globeNormalTexture: uniforms.tGlobeNormal as { value: Texture | null },
326
326
  useRTE,
327
327
  },
328
328
  water: {
@@ -411,6 +411,7 @@ export class PolygonMesh extends BatchedFeatureMesh<
411
411
  }
412
412
  this.castShadow = !!material.castShadow;
413
413
  this.receiveShadow = !!material.receiveShadow;
414
+ applyLitOption(this.material, material.lit);
414
415
 
415
416
  const { base } = enhancer.states();
416
417
 
@@ -12,6 +12,7 @@ import {
12
12
  } from "three";
13
13
 
14
14
  import type { EventContext } from "../event/context";
15
+ import { applyLitOption } from "../material";
15
16
  import { createPolylineMaterialEnhancer } from "../material/enhancer";
16
17
 
17
18
  import {
@@ -263,6 +264,7 @@ export class PolylineMesh extends BatchedFeatureMesh<
263
264
 
264
265
  this.castShadow = !!meshMaterial.castShadow;
265
266
  this.receiveShadow = !!meshMaterial.receiveShadow;
267
+ applyLitOption(this.material, meshMaterial.lit);
266
268
 
267
269
  const isTexturized = mesh.should_be_texturized;
268
270
 
@@ -422,6 +424,7 @@ export class PolylineMesh extends BatchedFeatureMesh<
422
424
  this.visible = enhancer.states().isTexturized ? shown : shown && active;
423
425
  this.castShadow = !!material.castShadow;
424
426
  this.receiveShadow = !!material.receiveShadow;
427
+ applyLitOption(this.material, material.lit);
425
428
 
426
429
  const base = enhancer.states();
427
430
 
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  COLOR_GLYPH_PX_SIZE,
3
3
  GlyphCharClass,
4
+ SDF_PX_SIZE,
4
5
  type GlyphMetrics,
5
6
  type ShapedGlyph,
6
7
  type ShapeTextResult,
@@ -14,9 +15,6 @@ import {
14
15
  * — the part with all the edge cases — unit-testable without a GL context.
15
16
  */
16
17
 
17
- /** Must match Rust `SDF_PX_SIZE` in `crates/navara_wasm_font_worker/src/atlas.rs`. */
18
- export const SDF_PX_SIZE = 64.0;
19
-
20
18
  /** Horizontal alignment of lines within a multi-line block, as the fraction
21
19
  * of leftover width placed before each line. */
22
20
  export const ALIGN_FACTORS: Record<string, number> = {
@@ -42,6 +42,8 @@ import {
42
42
  import { setTransform } from "../../event";
43
43
  import type { EventContext, TileHandler } from "../../event/context";
44
44
  import {
45
+ GBUFFER_EFFECT_WRITE_BUILTIN,
46
+ GBUFFER_NORMAL_WRITE_BASIC,
45
47
  generateTileCommonInjection,
46
48
  generateTileMapFragment,
47
49
  generateTileNormalFragmentMaps,
@@ -49,6 +51,7 @@ import {
49
51
  TILE_NORMAL_BUFFER_REPLACEMENT,
50
52
  TILE_PICK_FRAGMENT_OVERRIDE,
51
53
  TILE_VERTEX_INJECTIONS,
54
+ applyLitOption,
52
55
  } from "../../material";
53
56
  import { deriveCompositeFeatures } from "../../material/enhancer/tileComposite";
54
57
  import type { CustomObject3DEventMap } from "../../object3DEvent";
@@ -564,6 +567,7 @@ export class TileMesh
564
567
  this.castShadow = !!mat.castShadow;
565
568
  this.receiveShadow = !!mat.receiveShadow;
566
569
  }
570
+ applyLitOption(this.material, mat.lit);
567
571
 
568
572
  this.visible = false;
569
573
  this.renderOrder = mesh.render_order;
@@ -813,7 +817,7 @@ ${generateTileCommonInjection(maxTextures)}
813
817
  )
814
818
  .replace(
815
819
  "#include <map_fragment>",
816
- generateTileMapFragment(maxTextures),
820
+ generateTileMapFragment(maxTextures, features),
817
821
  )
818
822
  .replaceWithCondition(
819
823
  "#include <normal_fragment_maps>",
@@ -845,13 +849,12 @@ ${generateTileCommonInjection(maxTextures)}
845
849
  ${TILE_PICK_FRAGMENT_OVERRIDE}`,
846
850
  )
847
851
  .replaceWithCondition(
848
- "normalBuffer = vec4(packNormalToVec2(normal), reflectivity, roughnessFactor);",
852
+ GBUFFER_NORMAL_WRITE_BASIC,
849
853
  TILE_NORMAL_BUFFER_REPLACEMENT,
850
854
  useNormal,
851
855
  )
852
856
  .replace(
853
- `effectIdBuffer = vec4(uEffectIdsMask, 0.0, 0.0, 1.0);
854
- emissiveBuffer = vec4(diffuseColor.rgb * uEmissiveIntensity + emissive, 1.0);`,
857
+ GBUFFER_EFFECT_WRITE_BUILTIN,
855
858
  TILE_EMISSIVE_EFFECT_BUFFER_REPLACEMENT,
856
859
  ).source;
857
860
  };
@@ -923,6 +926,7 @@ ${generateTileCommonInjection(maxTextures)}
923
926
  if (this.receiveShadow !== changedMaterial.receiveShadow) {
924
927
  this.receiveShadow = !!changedMaterial.receiveShadow;
925
928
  }
929
+ applyLitOption(this.material, changedMaterial.lit);
926
930
  if (this.material.color.getHex() !== globe.color) {
927
931
  this.material.color.setHex(globe.color);
928
932
  }