@ifc-lite/renderer 1.49.0 → 1.50.0

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 (86) hide show
  1. package/dist/camera.d.ts +24 -0
  2. package/dist/camera.d.ts.map +1 -1
  3. package/dist/camera.js +72 -1
  4. package/dist/camera.js.map +1 -1
  5. package/dist/device.d.ts +11 -0
  6. package/dist/device.d.ts.map +1 -1
  7. package/dist/device.js +71 -5
  8. package/dist/device.js.map +1 -1
  9. package/dist/frame-timing-cpu.d.ts +31 -0
  10. package/dist/frame-timing-cpu.d.ts.map +1 -0
  11. package/dist/frame-timing-cpu.js +25 -0
  12. package/dist/frame-timing-cpu.js.map +1 -0
  13. package/dist/frame-timing-gpu.d.ts +144 -0
  14. package/dist/frame-timing-gpu.d.ts.map +1 -0
  15. package/dist/frame-timing-gpu.js +176 -0
  16. package/dist/frame-timing-gpu.js.map +1 -0
  17. package/dist/frame-timing-stats.d.ts +100 -0
  18. package/dist/frame-timing-stats.d.ts.map +1 -0
  19. package/dist/frame-timing-stats.js +116 -0
  20. package/dist/frame-timing-stats.js.map +1 -0
  21. package/dist/frame-timing.d.ts +102 -0
  22. package/dist/frame-timing.d.ts.map +1 -0
  23. package/dist/frame-timing.js +112 -0
  24. package/dist/frame-timing.js.map +1 -0
  25. package/dist/index.d.ts +35 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +229 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/lod-simplify.d.ts +0 -9
  30. package/dist/lod-simplify.d.ts.map +1 -1
  31. package/dist/lod-simplify.js +45 -7
  32. package/dist/lod-simplify.js.map +1 -1
  33. package/dist/pipeline.d.ts +16 -0
  34. package/dist/pipeline.d.ts.map +1 -1
  35. package/dist/pipeline.js +89 -3
  36. package/dist/pipeline.js.map +1 -1
  37. package/dist/pointcloud/point-cloud-spatial-index.d.ts +81 -9
  38. package/dist/pointcloud/point-cloud-spatial-index.d.ts.map +1 -1
  39. package/dist/pointcloud/point-cloud-spatial-index.js +140 -29
  40. package/dist/pointcloud/point-cloud-spatial-index.js.map +1 -1
  41. package/dist/raycast-engine.d.ts.map +1 -1
  42. package/dist/raycast-engine.js +10 -3
  43. package/dist/raycast-engine.js.map +1 -1
  44. package/dist/scene.d.ts.map +1 -1
  45. package/dist/scene.js +42 -5
  46. package/dist/scene.js.map +1 -1
  47. package/dist/section-2d-overlay.d.ts.map +1 -1
  48. package/dist/section-2d-overlay.js +6 -3
  49. package/dist/section-2d-overlay.js.map +1 -1
  50. package/dist/section-plane-basis.d.ts +17 -11
  51. package/dist/section-plane-basis.d.ts.map +1 -1
  52. package/dist/section-plane-basis.js +55 -16
  53. package/dist/section-plane-basis.js.map +1 -1
  54. package/dist/section-plane.d.ts +11 -0
  55. package/dist/section-plane.d.ts.map +1 -1
  56. package/dist/section-plane.js.map +1 -1
  57. package/dist/shaders/main.wgsl.d.ts +1 -1
  58. package/dist/shaders/main.wgsl.d.ts.map +1 -1
  59. package/dist/shaders/main.wgsl.js +87 -4
  60. package/dist/shaders/main.wgsl.js.map +1 -1
  61. package/dist/shaders/shadow.wgsl.d.ts +31 -0
  62. package/dist/shaders/shadow.wgsl.d.ts.map +1 -0
  63. package/dist/shaders/shadow.wgsl.js +141 -0
  64. package/dist/shaders/shadow.wgsl.js.map +1 -0
  65. package/dist/shadow-light-matrix.d.ts +106 -0
  66. package/dist/shadow-light-matrix.d.ts.map +1 -0
  67. package/dist/shadow-light-matrix.js +185 -0
  68. package/dist/shadow-light-matrix.js.map +1 -0
  69. package/dist/shadow-occluders.d.ts +106 -0
  70. package/dist/shadow-occluders.d.ts.map +1 -0
  71. package/dist/shadow-occluders.js +156 -0
  72. package/dist/shadow-occluders.js.map +1 -0
  73. package/dist/shadow-pass.d.ts +132 -0
  74. package/dist/shadow-pass.d.ts.map +1 -0
  75. package/dist/shadow-pass.js +342 -0
  76. package/dist/shadow-pass.js.map +1 -0
  77. package/dist/snap-detector.d.ts +10 -0
  78. package/dist/snap-detector.d.ts.map +1 -1
  79. package/dist/snap-detector.js.map +1 -1
  80. package/dist/symbolic-overlay-pipelines.d.ts +35 -0
  81. package/dist/symbolic-overlay-pipelines.d.ts.map +1 -1
  82. package/dist/symbolic-overlay-pipelines.js +31 -15
  83. package/dist/symbolic-overlay-pipelines.js.map +1 -1
  84. package/dist/types.d.ts +40 -0
  85. package/dist/types.d.ts.map +1 -1
  86. package/package.json +4 -4
@@ -43,6 +43,89 @@ export const mainShaderSource = `
43
43
  }
44
44
  @binding(0) @group(1) var<uniform> env: Environment;
45
45
 
46
+ // Sun shadow map (#2670, Phase 2b) at group(1). The depth map, a
47
+ // comparison sampler, and the light matrix + params. Bound on every
48
+ // main-family pipeline; sampling is gated by shadowU.params.y (enabled),
49
+ // so when shadows are off this reads the 1×1 dummy and returns 1.0.
50
+ @binding(1) @group(1) var shadowMap: texture_depth_2d;
51
+ @binding(2) @group(1) var shadowCmp: sampler_comparison;
52
+ struct Shadow {
53
+ lightViewProj: mat4x4<f32>,
54
+ // x = texelSize (1/resolution), y = enabled (0/1),
55
+ // z = normalBias (world units), w = pcfRadius (texels).
56
+ params: vec4<f32>,
57
+ // x = depthBias (reverse-Z clip units, nudges toward lit).
58
+ params2: vec4<f32>,
59
+ }
60
+ @binding(3) @group(1) var<uniform> shadowU: Shadow;
61
+
62
+ // Fraction of the sun reaching this surface point (1 = lit, 0 = fully
63
+ // shadowed). Normal-offset + slope-scaled bias defeats acne without
64
+ // peter-panning; the penumbra is sampled with a 12-tap Poisson disk
65
+ // ROTATED per pixel (interleaved gradient noise). A fixed grid kernel
66
+ // undersamples a wide penumbra and breaks into discrete bands (the
67
+ // "tripled shadow" at high softness); a rotated disk turns that banding
68
+ // into fine dither that reads as smooth at any softness.
69
+ // textureSampleCompareLevel is used (not ...Compare) so it is legal in
70
+ // this non-uniform control flow.
71
+ const SHADOW_POISSON = array<vec2<f32>, 12>(
72
+ vec2<f32>(-0.326, -0.406), vec2<f32>(-0.840, -0.074), vec2<f32>(-0.696, 0.457),
73
+ vec2<f32>(-0.203, 0.621), vec2<f32>( 0.962, -0.195), vec2<f32>( 0.473, -0.480),
74
+ vec2<f32>( 0.519, 0.767), vec2<f32>( 0.185, -0.893), vec2<f32>( 0.507, 0.064),
75
+ vec2<f32>( 0.896, 0.412), vec2<f32>(-0.322, -0.933), vec2<f32>(-0.792, -0.598),
76
+ );
77
+
78
+ fn sunShadowFactor(worldPos: vec3<f32>, N: vec3<f32>, fragCoord: vec2<f32>) -> f32 {
79
+ if (shadowU.params.y < 0.5) { return 1.0; }
80
+ // The diffuse sun term is TWO-SIDED (abs(dot(N, sun)) in the shading
81
+ // below), so a face whose stabilized normal points away from the sun is
82
+ // still lit. Orient the normal toward the sun before biasing: otherwise
83
+ // the normal-offset push (params.z) moves the sample AWAY from the light
84
+ // (deeper behind the surface), and the slope term below collapses to its
85
+ // max (NdotL→0), together biasing the compare toward "lit" — which leaks
86
+ // direct sun onto interior faces the roof occludes (#2670 review).
87
+ let L = normalize(env.sunDirection);
88
+ let Ns = N * select(-1.0, 1.0, dot(N, L) >= 0.0);
89
+ let biased = worldPos + Ns * shadowU.params.z;
90
+ let clip = shadowU.lightViewProj * vec4<f32>(biased, 1.0);
91
+ let ndc = clip.xyz / clip.w;
92
+ let uv = vec2<f32>(ndc.x * 0.5 + 0.5, ndc.y * -0.5 + 0.5);
93
+ let inBounds = uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0 && ndc.z > 0.0;
94
+ if (!inBounds) { return 1.0; }
95
+ // Slope-scaled depth bias: at a grazing sun the receiver's light-space
96
+ // depth changes fast across the kernel, so a constant bias can't clear
97
+ // the whole footprint and the surface rings with moiré. Grow the bias
98
+ // as the surface tilts away from the sun (NdotL → 0) and with the
99
+ // kernel width. The hardware slope bias in the depth pass covers the
100
+ // occluder side; this covers the receiver side.
101
+ let NdotL = max(dot(Ns, L), 0.0);
102
+ let slope = clamp(sqrt(max(1.0 - NdotL * NdotL, 0.0)) / max(NdotL, 0.1), 1.0, 12.0);
103
+ let refDepth = ndc.z + shadowU.params2.x * slope * (1.0 + shadowU.params.w);
104
+ let radius = shadowU.params.x * shadowU.params.w; // penumbra, uv units
105
+ // Per-pixel rotation (interleaved gradient noise) dithers the disk so
106
+ // the discrete taps never line up into bands.
107
+ let ign = fract(52.9829189 * fract(dot(fragCoord, vec2<f32>(0.06711056, 0.00583715))));
108
+ let ang = ign * 6.2831853;
109
+ let cr = cos(ang);
110
+ let sr = sin(ang);
111
+ var sum = 0.0;
112
+ for (var i = 0; i < 12; i = i + 1) {
113
+ let p = SHADOW_POISSON[i];
114
+ let off = vec2<f32>(p.x * cr - p.y * sr, p.x * sr + p.y * cr) * radius;
115
+ sum = sum + textureSampleCompareLevel(shadowMap, shadowCmp, uv + off, refDepth);
116
+ }
117
+ let pcf = sum / 12.0;
118
+ // Terminator fade. On a surface nearly PARALLEL to the sun rays
119
+ // (NdotL → 0, e.g. a vertical wall under a midday sun) the receiver
120
+ // straddles the shadow threshold, so the rotated-disk taps randomly
121
+ // pass/fail and the wall breaks into salt-and-pepper speckle. The
122
+ // direct sun term is near zero there anyway, so fade the cast shadow
123
+ // smoothly toward lit as the surface goes grazing: a clean gradient
124
+ // replaces the ripple (#2670).
125
+ let graze = smoothstep(0.0, 0.3, NdotL);
126
+ return mix(1.0, pcf, graze);
127
+ }
128
+
46
129
  struct VertexInput {
47
130
  @location(0) position: vec3<f32>,
48
131
  @location(1) normal: vec3<f32>,
@@ -388,10 +471,10 @@ export const mainShaderSource = `
388
471
  // Darken whites/grays more to reduce washed-out appearance
389
472
  baseColor = mix(baseColor, baseColor * 0.7, isWhiteish * 0.4);
390
473
 
391
- // Combine all lighting. Keep the lighting term separate so the
392
- // selection highlight can reuse it (re-light a blue albedo) without
393
- // the base material colour bleeding through.
394
- let lightTerm = ambient + env.sunColor * diffuseSun + vec3<f32>(diffuseFill + rim);
474
+ // Combine all lighting. Only the DIRECT sun term is occluded by cast
475
+ // shadows (#2670); ambient/fill/rim are indirect and stay unshadowed.
476
+ let sunShadow = sunShadowFactor(input.worldPos, N, input.position.xy);
477
+ let lightTerm = ambient + env.sunColor * (diffuseSun * sunShadow) + vec3<f32>(diffuseFill + rim);
395
478
  var color = baseColor * lightTerm;
396
479
 
397
480
  // flags.x is a bitfield:
@@ -1 +1 @@
1
- {"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkiBzB,CAAC"}
1
+ {"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqnBzB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Sun shadow-map depth pre-pass shaders (issue #2670, Phase 2).
3
+ *
4
+ * Depth-only: each entry point transforms a vertex to the sun's light clip
5
+ * space and lets the depth test record the closest occluder. There is ONE
6
+ * entry point per geometry path, and each MUST reproduce the world-space
7
+ * position its main-shader counterpart computes, or that path silently stops
8
+ * casting (the divergence class #2670's acceptance test guards):
9
+ *
10
+ * • vs_shadow_flat ← vs_main worldPos = model * position
11
+ * • vs_shadow_quantized ← vs_main_quantized worldPos = model * dequant(q)
12
+ * • vs_shadow_instanced ← vs_instanced worldPos = instMat * position
13
+ * • vs_shadow_textured ← textured vs_main worldPos = model * position
14
+ *
15
+ * `lightViewProj` replaces the camera `viewProj`; `model`/`quantParams` come
16
+ * per-draw via a dynamic-offset uniform (unused by the instanced path, whose
17
+ * per-occurrence matrix arrives on vertex slot 1). The anti-z-fighting depth
18
+ * nudge from the main shaders is deliberately omitted — it perturbs camera
19
+ * clip depth for coplanar-face separation and has no meaning in light space.
20
+ *
21
+ * CLIPPING. Every entry point also passes world position to `fs_shadow_clip`,
22
+ * which re-applies the section plane and clip box exactly as `fs_main` does,
23
+ * so geometry a section cut removed from view stops casting too (otherwise the
24
+ * cut-away roof keeps shadowing the floor it no longer covers). The fragment
25
+ * stage is only attached when a clip is actually active — with no clipping the
26
+ * pipelines stay fragment-less, keeping the double-speed depth-only path — and
27
+ * the unused `worldPos` output on those pipelines costs an interpolator, not a
28
+ * shader stage.
29
+ */
30
+ export declare const shadowShaderSource = "\n struct Light {\n lightViewProj: mat4x4<f32>,\n }\n @binding(0) @group(0) var<uniform> light: Light;\n\n struct Draw {\n model: mat4x4<f32>,\n // xyz = lattice-aligned quantMin (batch-origin-relative), w = step.\n // Read only by vs_shadow_quantized; zero for the other paths.\n quantParams: vec4<f32>,\n }\n @binding(1) @group(0) var<uniform> draw: Draw;\n\n struct Clip {\n // xyz = plane normal, w = plane distance (world space).\n sectionPlane: vec4<f32>,\n clipBoxMin: vec4<f32>,\n clipBoxMax: vec4<f32>,\n // x packs: bit 0 = section enabled, bit 1 = section flipped,\n // bit 2 = clip box enabled. Mirrors main.wgsl's flags.y.\n flags: vec4<u32>,\n }\n @binding(2) @group(0) var<uniform> clip: Clip;\n\n struct FlatIn {\n @location(0) position: vec3<f32>,\n }\n\n struct QuantIn {\n @location(0) q: vec4<u32>, // uint16x4: lattice x, y, z, packedOct\n }\n\n struct InstanceIn {\n @location(3) m0: vec4<f32>,\n @location(4) m1: vec4<f32>,\n @location(5) m2: vec4<f32>,\n @location(6) m3: vec4<f32>,\n // Per-occurrence flags lane (bit 1 = hidden), same instance buffer the\n // colour pass reads. Locations 7 (entityId) and 8 (rgba) are unused by\n // the depth pass and left unbound.\n @location(9) flags: u32,\n }\n\n struct ShadowOut {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n }\n\n fn emit(worldPos: vec4<f32>) -> ShadowOut {\n var out: ShadowOut;\n out.position = light.lightViewProj * worldPos;\n out.worldPos = worldPos.xyz;\n return out;\n }\n\n @vertex\n fn vs_shadow_flat(input: FlatIn) -> ShadowOut {\n return emit(draw.model * vec4<f32>(input.position, 1.0));\n }\n\n @vertex\n fn vs_shadow_quantized(input: QuantIn) -> ShadowOut {\n let p = draw.quantParams.xyz\n + vec3<f32>(f32(input.q.x), f32(input.q.y), f32(input.q.z)) * draw.quantParams.w;\n return emit(draw.model * vec4<f32>(p, 1.0));\n }\n\n @vertex\n fn vs_shadow_instanced(input: FlatIn, inst: InstanceIn) -> ShadowOut {\n // A hidden / isolation-excluded occurrence (flags bit 1) must not cast:\n // the colour pass discards it in fs_main, so collapse it to a zero-area\n // triangle here (every vertex to one clip point) \u2014 the rasterizer drops\n // it and it writes no depth. Mirrors main.wgsl's instSelected & 2u check.\n if ((inst.flags & 2u) != 0u) {\n var out: ShadowOut;\n out.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n out.worldPos = vec3<f32>(0.0, 0.0, 0.0);\n return out;\n }\n let instMat = mat4x4<f32>(inst.m0, inst.m1, inst.m2, inst.m3);\n return emit(instMat * vec4<f32>(input.position, 1.0));\n }\n\n @vertex\n fn vs_shadow_textured(input: FlatIn) -> ShadowOut {\n return emit(draw.model * vec4<f32>(input.position, 1.0));\n }\n\n // Clipped variant: discard before the depth write, so a clipped-away\n // occluder leaves the shadow map untouched. Kept byte-for-byte in step\n // with the fs_main section/clip-box branches.\n @fragment\n fn fs_shadow_clip(@location(0) worldPos: vec3<f32>) {\n if ((clip.flags.x & 1u) == 1u) {\n let side = select(1.0, -1.0, (clip.flags.x & 2u) == 2u);\n let distToPlane = (dot(worldPos, clip.sectionPlane.xyz) - clip.sectionPlane.w) * side;\n if (distToPlane > 0.0) {\n discard;\n }\n }\n if ((clip.flags.x & 4u) != 0u) {\n if (any(worldPos < clip.clipBoxMin.xyz) || any(worldPos > clip.clipBoxMax.xyz)) {\n discard;\n }\n }\n }\n";
31
+ //# sourceMappingURL=shadow.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,kBAAkB,4hIA2G9B,CAAC"}
@@ -0,0 +1,141 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Sun shadow-map depth pre-pass shaders (issue #2670, Phase 2).
6
+ *
7
+ * Depth-only: each entry point transforms a vertex to the sun's light clip
8
+ * space and lets the depth test record the closest occluder. There is ONE
9
+ * entry point per geometry path, and each MUST reproduce the world-space
10
+ * position its main-shader counterpart computes, or that path silently stops
11
+ * casting (the divergence class #2670's acceptance test guards):
12
+ *
13
+ * • vs_shadow_flat ← vs_main worldPos = model * position
14
+ * • vs_shadow_quantized ← vs_main_quantized worldPos = model * dequant(q)
15
+ * • vs_shadow_instanced ← vs_instanced worldPos = instMat * position
16
+ * • vs_shadow_textured ← textured vs_main worldPos = model * position
17
+ *
18
+ * `lightViewProj` replaces the camera `viewProj`; `model`/`quantParams` come
19
+ * per-draw via a dynamic-offset uniform (unused by the instanced path, whose
20
+ * per-occurrence matrix arrives on vertex slot 1). The anti-z-fighting depth
21
+ * nudge from the main shaders is deliberately omitted — it perturbs camera
22
+ * clip depth for coplanar-face separation and has no meaning in light space.
23
+ *
24
+ * CLIPPING. Every entry point also passes world position to `fs_shadow_clip`,
25
+ * which re-applies the section plane and clip box exactly as `fs_main` does,
26
+ * so geometry a section cut removed from view stops casting too (otherwise the
27
+ * cut-away roof keeps shadowing the floor it no longer covers). The fragment
28
+ * stage is only attached when a clip is actually active — with no clipping the
29
+ * pipelines stay fragment-less, keeping the double-speed depth-only path — and
30
+ * the unused `worldPos` output on those pipelines costs an interpolator, not a
31
+ * shader stage.
32
+ */
33
+ export const shadowShaderSource = `
34
+ struct Light {
35
+ lightViewProj: mat4x4<f32>,
36
+ }
37
+ @binding(0) @group(0) var<uniform> light: Light;
38
+
39
+ struct Draw {
40
+ model: mat4x4<f32>,
41
+ // xyz = lattice-aligned quantMin (batch-origin-relative), w = step.
42
+ // Read only by vs_shadow_quantized; zero for the other paths.
43
+ quantParams: vec4<f32>,
44
+ }
45
+ @binding(1) @group(0) var<uniform> draw: Draw;
46
+
47
+ struct Clip {
48
+ // xyz = plane normal, w = plane distance (world space).
49
+ sectionPlane: vec4<f32>,
50
+ clipBoxMin: vec4<f32>,
51
+ clipBoxMax: vec4<f32>,
52
+ // x packs: bit 0 = section enabled, bit 1 = section flipped,
53
+ // bit 2 = clip box enabled. Mirrors main.wgsl's flags.y.
54
+ flags: vec4<u32>,
55
+ }
56
+ @binding(2) @group(0) var<uniform> clip: Clip;
57
+
58
+ struct FlatIn {
59
+ @location(0) position: vec3<f32>,
60
+ }
61
+
62
+ struct QuantIn {
63
+ @location(0) q: vec4<u32>, // uint16x4: lattice x, y, z, packedOct
64
+ }
65
+
66
+ struct InstanceIn {
67
+ @location(3) m0: vec4<f32>,
68
+ @location(4) m1: vec4<f32>,
69
+ @location(5) m2: vec4<f32>,
70
+ @location(6) m3: vec4<f32>,
71
+ // Per-occurrence flags lane (bit 1 = hidden), same instance buffer the
72
+ // colour pass reads. Locations 7 (entityId) and 8 (rgba) are unused by
73
+ // the depth pass and left unbound.
74
+ @location(9) flags: u32,
75
+ }
76
+
77
+ struct ShadowOut {
78
+ @builtin(position) position: vec4<f32>,
79
+ @location(0) worldPos: vec3<f32>,
80
+ }
81
+
82
+ fn emit(worldPos: vec4<f32>) -> ShadowOut {
83
+ var out: ShadowOut;
84
+ out.position = light.lightViewProj * worldPos;
85
+ out.worldPos = worldPos.xyz;
86
+ return out;
87
+ }
88
+
89
+ @vertex
90
+ fn vs_shadow_flat(input: FlatIn) -> ShadowOut {
91
+ return emit(draw.model * vec4<f32>(input.position, 1.0));
92
+ }
93
+
94
+ @vertex
95
+ fn vs_shadow_quantized(input: QuantIn) -> ShadowOut {
96
+ let p = draw.quantParams.xyz
97
+ + vec3<f32>(f32(input.q.x), f32(input.q.y), f32(input.q.z)) * draw.quantParams.w;
98
+ return emit(draw.model * vec4<f32>(p, 1.0));
99
+ }
100
+
101
+ @vertex
102
+ fn vs_shadow_instanced(input: FlatIn, inst: InstanceIn) -> ShadowOut {
103
+ // A hidden / isolation-excluded occurrence (flags bit 1) must not cast:
104
+ // the colour pass discards it in fs_main, so collapse it to a zero-area
105
+ // triangle here (every vertex to one clip point) — the rasterizer drops
106
+ // it and it writes no depth. Mirrors main.wgsl's instSelected & 2u check.
107
+ if ((inst.flags & 2u) != 0u) {
108
+ var out: ShadowOut;
109
+ out.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
110
+ out.worldPos = vec3<f32>(0.0, 0.0, 0.0);
111
+ return out;
112
+ }
113
+ let instMat = mat4x4<f32>(inst.m0, inst.m1, inst.m2, inst.m3);
114
+ return emit(instMat * vec4<f32>(input.position, 1.0));
115
+ }
116
+
117
+ @vertex
118
+ fn vs_shadow_textured(input: FlatIn) -> ShadowOut {
119
+ return emit(draw.model * vec4<f32>(input.position, 1.0));
120
+ }
121
+
122
+ // Clipped variant: discard before the depth write, so a clipped-away
123
+ // occluder leaves the shadow map untouched. Kept byte-for-byte in step
124
+ // with the fs_main section/clip-box branches.
125
+ @fragment
126
+ fn fs_shadow_clip(@location(0) worldPos: vec3<f32>) {
127
+ if ((clip.flags.x & 1u) == 1u) {
128
+ let side = select(1.0, -1.0, (clip.flags.x & 2u) == 2u);
129
+ let distToPlane = (dot(worldPos, clip.sectionPlane.xyz) - clip.sectionPlane.w) * side;
130
+ if (distToPlane > 0.0) {
131
+ discard;
132
+ }
133
+ }
134
+ if ((clip.flags.x & 4u) != 0u) {
135
+ if (any(worldPos < clip.clipBoxMin.xyz) || any(worldPos > clip.clipBoxMax.xyz)) {
136
+ discard;
137
+ }
138
+ }
139
+ }
140
+ `;
141
+ //# sourceMappingURL=shadow.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.wgsl.js","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2GjC,CAAC"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Sun shadow-map light matrix (issue #2670, Phase 2).
3
+ *
4
+ * Builds the orthographic light-view-projection the sun shadow depth pass
5
+ * renders occluders through. A directional sun is an orthographic camera:
6
+ * the frustum is a box whose axis is the (negated) sun direction, and whose
7
+ * cross-section must cover everything that can cast into the view.
8
+ *
9
+ * Fitting policy: by default the lateral (x/y) extent is fitted to the whole
10
+ * model AABB — a single, well-conditioned cascade that is correct at
11
+ * building scale. A caller MAY instead pass `focusCorners` (e.g. the camera
12
+ * frustum clipped to the model, for a site-scale view-fitted map, maintainer
13
+ * constraint #1 on #2670) and the lateral fit tightens to those; that path is
14
+ * a documented follow-up and needs a proper cascade to be robust against a
15
+ * reverse-Z / infinite-far camera, so the live renderer uses the bounds fit.
16
+ *
17
+ * The DEPTH range is deliberately NOT fitted to the focus alone: an occluder
18
+ * BEHIND the visible region along the sun axis (a roof over the room you are
19
+ * looking at) still has to be in the map to cast into it, so the near plane is
20
+ * pushed back to enclose the whole model along the light direction while the
21
+ * lateral (x/y) extent stays fitted to the focus.
22
+ *
23
+ * Everything here is pure and framework-free so it can be unit-tested without
24
+ * a GPU; the depth pass (`shadow-pass.ts`) consumes {@link SunLightFit}.
25
+ */
26
+ import type { Mat4, Vec3 } from './types.js';
27
+ export interface SunLightFitParams {
28
+ /**
29
+ * Unit vector TOWARD the sun in viewer/world space (Y-up), matching the
30
+ * shading path's `env.sunDirection` convention. Normalized defensively; a
31
+ * non-finite or zero vector falls back to straight-down sun (Y-up).
32
+ */
33
+ sunDirection: readonly [number, number, number];
34
+ /** World-space model AABB min corner. */
35
+ boundsMin: readonly [number, number, number];
36
+ /** World-space model AABB max corner. */
37
+ boundsMax: readonly [number, number, number];
38
+ /**
39
+ * Eight corners of the camera frustum clipped to the model bounds, world
40
+ * space. When provided the lateral fit uses these (tight, view-fitted map);
41
+ * when omitted or shorter than 4 the model AABB corners are used instead.
42
+ */
43
+ focusCorners?: readonly Vec3[];
44
+ /**
45
+ * Fractional slack added around the fitted lateral extent so a PCF/normal
46
+ * -offset kernel near the map edge still has neighbours to sample. Default
47
+ * 0.05 (5%). Clamped to [0, 1].
48
+ */
49
+ lateralPadding?: number;
50
+ }
51
+ export interface SunLightFit {
52
+ /** Column-major light view-projection (`ortho * lightView`), reverse-Z. */
53
+ lightViewProj: Mat4;
54
+ /** Light view matrix on its own (world → light view space). */
55
+ lightView: Mat4;
56
+ /** Fitted ortho half-width in world units (light-space x). */
57
+ orthoHalfWidth: number;
58
+ /** Fitted ortho half-height in world units (light-space y). */
59
+ orthoHalfHeight: number;
60
+ /** Near→far depth span along the sun axis, world units. */
61
+ depthRange: number;
62
+ }
63
+ /**
64
+ * Fit the sun's orthographic shadow frustum. Pure; see the file header for the
65
+ * fitting policy. The returned matrix maps world space straight to the shadow
66
+ * map's clip space (reverse-Z, depth 1 at near → 0 at far), so the depth pass
67
+ * uses it exactly where the main pass uses the camera `viewProj`.
68
+ */
69
+ export declare function fitSunLightMatrix(params: SunLightFitParams): SunLightFit;
70
+ export interface CameraFrustumParams {
71
+ /** Camera eye (world). */
72
+ eye: Vec3;
73
+ /** Unit view direction (eye → target). */
74
+ forward: Vec3;
75
+ /** Unit screen-right. */
76
+ right: Vec3;
77
+ /** Unit screen-up. */
78
+ up: Vec3;
79
+ /** Vertical FOV in radians (perspective). */
80
+ fovY: number;
81
+ /** Viewport width / height. */
82
+ aspect: number;
83
+ /** Orthographic projection? Then `orthoHalfHeight` sizes the frustum. */
84
+ ortho: boolean;
85
+ /** Half-height of the ortho view volume, world units (ortho only). */
86
+ orthoHalfHeight: number;
87
+ boundsMin: readonly [number, number, number];
88
+ boundsMax: readonly [number, number, number];
89
+ /** Lateral expansion so a caster just off-screen still reaches the map. */
90
+ margin?: number;
91
+ }
92
+ /**
93
+ * Eight corners of the camera frustum, clipped to the model AABB — the region
94
+ * the camera can actually see (maintainer constraint #1 on #2670). Passing
95
+ * these to {@link fitSunLightMatrix} as `focusCorners` concentrates the shadow
96
+ * map on visible geometry instead of a site-scale model's whole footprint,
97
+ * which is what makes distant terrain steal all the resolution.
98
+ *
99
+ * Unlike unprojecting the clip cube, this builds the frustum from the camera
100
+ * BASIS at finite near/far distances derived from where the model sits along
101
+ * the view axis, so it never depends on a reverse-Z / infinite-far plane.
102
+ * Returns `null` when the model is entirely behind the camera (no usable
103
+ * focus — the caller falls back to a whole-bounds fit).
104
+ */
105
+ export declare function cameraFrustumFocusCorners(p: CameraFrustumParams): Vec3[] | null;
106
+ //# sourceMappingURL=shadow-light-matrix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow-light-matrix.d.ts","sourceRoot":"","sources":["../src/shadow-light-matrix.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,yCAAyC;IACzC,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,yCAAyC;IACzC,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IAC/B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,aAAa,EAAE,IAAI,CAAC;IACpB,+DAA+D;IAC/D,SAAS,EAAE,IAAI,CAAC;IAChB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACpB;AAwCD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CA0FxE;AAED,MAAM,WAAW,mBAAmB;IAClC,0BAA0B;IAC1B,GAAG,EAAE,IAAI,CAAC;IACV,0CAA0C;IAC1C,OAAO,EAAE,IAAI,CAAC;IACd,yBAAyB;IACzB,KAAK,EAAE,IAAI,CAAC;IACZ,sBAAsB;IACtB,EAAE,EAAE,IAAI,CAAC;IACT,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,KAAK,EAAE,OAAO,CAAC;IACf,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI,EAAE,GAAG,IAAI,CAoC/E"}
@@ -0,0 +1,185 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { MathUtils } from './math.js';
5
+ const DEFAULT_SUN = { x: 0, y: 1, z: 0 };
6
+ /** Unit sun direction, falling back to straight down-up for unusable input. */
7
+ function resolveSun(dir) {
8
+ const x = dir[0], y = dir[1], z = dir[2];
9
+ const lenSq = x * x + y * y + z * z;
10
+ // Finiteness matters as much as length (#2489): an Infinity component makes
11
+ // the length Infinity and the divide below NaN, so guard both ends.
12
+ if (!(lenSq > 1e-12 && lenSq < Infinity))
13
+ return DEFAULT_SUN;
14
+ const inv = 1 / Math.sqrt(lenSq);
15
+ return { x: x * inv, y: y * inv, z: z * inv };
16
+ }
17
+ /** The eight corners of an axis-aligned box. */
18
+ function aabbCorners(min, max) {
19
+ const corners = [];
20
+ for (let i = 0; i < 8; i++) {
21
+ corners.push({
22
+ x: (i & 1) ? max[0] : min[0],
23
+ y: (i & 2) ? max[1] : min[1],
24
+ z: (i & 4) ? max[2] : min[2],
25
+ });
26
+ }
27
+ return corners;
28
+ }
29
+ /** Transform a world point by a column-major Mat4 (w = 1), returning xyz. */
30
+ function transformPoint(m, p) {
31
+ return {
32
+ x: m[0] * p.x + m[4] * p.y + m[8] * p.z + m[12],
33
+ y: m[1] * p.x + m[5] * p.y + m[9] * p.z + m[13],
34
+ z: m[2] * p.x + m[6] * p.y + m[10] * p.z + m[14],
35
+ };
36
+ }
37
+ /**
38
+ * Fit the sun's orthographic shadow frustum. Pure; see the file header for the
39
+ * fitting policy. The returned matrix maps world space straight to the shadow
40
+ * map's clip space (reverse-Z, depth 1 at near → 0 at far), so the depth pass
41
+ * uses it exactly where the main pass uses the camera `viewProj`.
42
+ */
43
+ export function fitSunLightMatrix(params) {
44
+ const sun = resolveSun(params.sunDirection);
45
+ const modelCorners = aabbCorners(params.boundsMin, params.boundsMax);
46
+ // Lateral fit target: the view-clipped frustum corners when usable, else the
47
+ // whole model. Depth always encloses the whole model (see header).
48
+ const lateralPts = params.focusCorners && params.focusCorners.length >= 4
49
+ ? params.focusCorners
50
+ : modelCorners;
51
+ // Centre the light view on the lateral focus so the fitted x/y box is
52
+ // symmetric around the region of interest.
53
+ let cx = 0, cy = 0, cz = 0;
54
+ for (const p of lateralPts) {
55
+ cx += p.x;
56
+ cy += p.y;
57
+ cz += p.z;
58
+ }
59
+ const n = lateralPts.length;
60
+ const center = { x: cx / n, y: cy / n, z: cz / n };
61
+ // Light view: eye one unit up-sun of the centre, looking down the sun axis
62
+ // (forward = -sun). The eye offset only shifts light-space z uniformly; near
63
+ // /far below are derived from the transformed points, so its magnitude is
64
+ // immaterial. `up` comes from viewBasis's degeneracy handling for the
65
+ // straight-overhead sun where any fixed up would be parallel.
66
+ const eye = {
67
+ x: center.x + sun.x,
68
+ y: center.y + sun.y,
69
+ z: center.z + sun.z,
70
+ };
71
+ // A world-Y up hint, swapped to world-Z when the sun is near-vertical, keeps
72
+ // the basis well-conditioned; viewBasis substitutes its own hint if this is
73
+ // still parallel, so the result is always finite.
74
+ const upHint = Math.abs(sun.y) > 0.99 ? { x: 0, y: 0, z: 1 } : { x: 0, y: 1, z: 0 };
75
+ const lightView = MathUtils.lookAt(eye, center, upHint);
76
+ const lv = lightView.m;
77
+ // Lateral (x/y) extent from the focus points.
78
+ let minX = Infinity, maxX = -Infinity;
79
+ let minY = Infinity, maxY = -Infinity;
80
+ for (const p of lateralPts) {
81
+ const v = transformPoint(lv, p);
82
+ if (v.x < minX)
83
+ minX = v.x;
84
+ if (v.x > maxX)
85
+ maxX = v.x;
86
+ if (v.y < minY)
87
+ minY = v.y;
88
+ if (v.y > maxY)
89
+ maxY = v.y;
90
+ }
91
+ // Depth (z) extent from the WHOLE model, so occluders behind the focus still
92
+ // cast into it. In lookAt view space the camera looks down -Z, so scene
93
+ // points have negative z and their distance from the eye is -z.
94
+ let minZ = Infinity, maxZ = -Infinity;
95
+ for (const p of modelCorners) {
96
+ const vz = lv[2] * p.x + lv[6] * p.y + lv[10] * p.z + lv[14];
97
+ if (vz < minZ)
98
+ minZ = vz;
99
+ if (vz > maxZ)
100
+ maxZ = vz;
101
+ }
102
+ // Pad the lateral box so an edge texel's kernel still has neighbours.
103
+ const pad = Math.min(Math.max(params.lateralPadding ?? 0.05, 0), 1);
104
+ const halfW = Math.max((maxX - minX) * 0.5, 1e-4);
105
+ const halfH = Math.max((maxY - minY) * 0.5, 1e-4);
106
+ const midX = (minX + maxX) * 0.5;
107
+ const midY = (minY + maxY) * 0.5;
108
+ const padW = halfW * (1 + pad);
109
+ const padH = halfH * (1 + pad);
110
+ const left = midX - padW;
111
+ const right = midX + padW;
112
+ const bottom = midY - padH;
113
+ const top = midY + padH;
114
+ // Distances along the view direction. In lookAt view space scene points have
115
+ // negative z, so distance = -z: the nearest point (largest z) sets `near`,
116
+ // the farthest (smallest z) sets `far`. `near` is NOT clamped to 0 — the eye
117
+ // sits just up-sun of the FOCUS centre, so model geometry further up-sun is
118
+ // behind it (positive z ⇒ negative near), and clamping would push those
119
+ // occluders out of the depth range so they stopped casting. A small epsilon
120
+ // pads both ends against precision and a zero-thickness (flat) frustum.
121
+ const zEps = Math.max((maxZ - minZ) * 1e-3, 1e-3);
122
+ const near = -maxZ - zEps;
123
+ const far = -minZ + zEps;
124
+ const ortho = MathUtils.orthographicReverseZ(left, right, bottom, top, near, far);
125
+ const lightViewProj = MathUtils.multiply(ortho, lightView);
126
+ return {
127
+ lightViewProj,
128
+ lightView,
129
+ orthoHalfWidth: padW,
130
+ orthoHalfHeight: padH,
131
+ depthRange: far - near,
132
+ };
133
+ }
134
+ /**
135
+ * Eight corners of the camera frustum, clipped to the model AABB — the region
136
+ * the camera can actually see (maintainer constraint #1 on #2670). Passing
137
+ * these to {@link fitSunLightMatrix} as `focusCorners` concentrates the shadow
138
+ * map on visible geometry instead of a site-scale model's whole footprint,
139
+ * which is what makes distant terrain steal all the resolution.
140
+ *
141
+ * Unlike unprojecting the clip cube, this builds the frustum from the camera
142
+ * BASIS at finite near/far distances derived from where the model sits along
143
+ * the view axis, so it never depends on a reverse-Z / infinite-far plane.
144
+ * Returns `null` when the model is entirely behind the camera (no usable
145
+ * focus — the caller falls back to a whole-bounds fit).
146
+ */
147
+ export function cameraFrustumFocusCorners(p) {
148
+ const corners = aabbCorners(p.boundsMin, p.boundsMax);
149
+ // Depth span of the model along the view axis, relative to the eye.
150
+ let nearD = Infinity;
151
+ let farD = -Infinity;
152
+ for (const c of corners) {
153
+ const d = (c.x - p.eye.x) * p.forward.x + (c.y - p.eye.y) * p.forward.y + (c.z - p.eye.z) * p.forward.z;
154
+ if (d < nearD)
155
+ nearD = d;
156
+ if (d > farD)
157
+ farD = d;
158
+ }
159
+ if (!(farD > 1e-3))
160
+ return null; // model behind the camera
161
+ nearD = Math.max(nearD, 0.01);
162
+ const margin = Math.min(Math.max(p.margin ?? 0.15, 0), 2);
163
+ const half = (d) => {
164
+ const h = (p.ortho ? p.orthoHalfHeight : d * Math.tan(p.fovY / 2)) * (1 + margin);
165
+ return { w: h * p.aspect, h };
166
+ };
167
+ const out = [];
168
+ for (const d of [nearD, farD]) {
169
+ const { w, h } = half(d);
170
+ for (const sx of [-1, 1]) {
171
+ for (const sy of [-1, 1]) {
172
+ const x = p.eye.x + p.forward.x * d + p.right.x * (sx * w) + p.up.x * (sy * h);
173
+ const y = p.eye.y + p.forward.y * d + p.right.y * (sx * w) + p.up.y * (sy * h);
174
+ const z = p.eye.z + p.forward.z * d + p.right.z * (sx * w) + p.up.z * (sy * h);
175
+ out.push({
176
+ x: Math.min(Math.max(x, p.boundsMin[0]), p.boundsMax[0]),
177
+ y: Math.min(Math.max(y, p.boundsMin[1]), p.boundsMax[1]),
178
+ z: Math.min(Math.max(z, p.boundsMin[2]), p.boundsMax[2]),
179
+ });
180
+ }
181
+ }
182
+ }
183
+ return out;
184
+ }
185
+ //# sourceMappingURL=shadow-light-matrix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow-light-matrix.js","sourceRoot":"","sources":["../src/shadow-light-matrix.ts"],"names":[],"mappings":"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"}