@driftengine/core 4.8.1 → 4.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.json +86 -0
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.js +2 -0
  4. package/dist/input/actionMap.d.ts +6 -1
  5. package/dist/input/actionMap.js +20 -7
  6. package/dist/input/input.d.ts +28 -0
  7. package/dist/input/input.js +71 -9
  8. package/dist/input/inputHarness.d.ts +9 -0
  9. package/dist/input/inputHarness.js +34 -2
  10. package/dist/render/backend/webgl2/renderer.d.ts +52 -3
  11. package/dist/render/backend/webgl2/renderer.js +151 -5
  12. package/dist/render/backend/webgpu/buffers.d.ts +2 -0
  13. package/dist/render/backend/webgpu/buffers.js +2 -0
  14. package/dist/render/backend/webgpu/flatPass.js +16 -7
  15. package/dist/render/backend/webgpu/pipelineCache.d.ts +40 -8
  16. package/dist/render/backend/webgpu/pipelineCache.js +76 -9
  17. package/dist/render/backend/webgpu/renderer.d.ts +56 -1
  18. package/dist/render/backend/webgpu/renderer.js +151 -26
  19. package/dist/render/mapsWithoutUvs.d.ts +24 -0
  20. package/dist/render/mapsWithoutUvs.js +24 -0
  21. package/dist/render/mesh.d.ts +2 -0
  22. package/dist/render/mesh.js +3 -0
  23. package/dist/render/particlePool.d.ts +15 -0
  24. package/dist/render/particlePool.js +72 -37
  25. package/dist/render/pass.d.ts +12 -0
  26. package/dist/render/renderQuality.d.ts +7 -6
  27. package/dist/render/shaders/flat/glassTint.d.ts +28 -0
  28. package/dist/render/shaders/flat/glassTint.js +31 -0
  29. package/dist/render/shaders/flat/index.d.ts +12 -0
  30. package/dist/render/shaders/flat/index.js +7 -1
  31. package/dist/render/shaders/flat/main.d.ts +1 -1
  32. package/dist/render/shaders/flat/main.js +60 -25
  33. package/dist/render/shaders/flat/surfaceEffects.d.ts +1 -1
  34. package/dist/render/shaders/flat/surfaceEffects.js +6 -2
  35. package/dist/render/shaders/generated/flat.wgsl.d.ts +64 -0
  36. package/dist/render/shaders/generated/flat.wgsl.js +102 -38
  37. package/dist/render/surfaceTexture.d.ts +2 -0
  38. package/dist/render/surfaceTexture.js +4 -0
  39. package/dist/render/viewModel.d.ts +29 -0
  40. package/dist/render/viewModel.js +35 -0
  41. package/package.json +6 -6
  42. package/src/index.ts +9 -1
  43. package/src/input/actionMap.ts +23 -8
  44. package/src/input/input.ts +79 -9
  45. package/src/input/inputHarness.ts +27 -2
  46. package/src/render/backend/webgl2/renderer.ts +166 -13
  47. package/src/render/backend/webgpu/buffers.ts +5 -0
  48. package/src/render/backend/webgpu/flatPass.ts +41 -25
  49. package/src/render/backend/webgpu/pipelineCache.ts +82 -8
  50. package/src/render/backend/webgpu/renderer.ts +151 -26
  51. package/src/render/mapsWithoutUvs.ts +43 -0
  52. package/src/render/mesh.ts +3 -0
  53. package/src/render/particlePool.ts +101 -54
  54. package/src/render/pass.ts +12 -0
  55. package/src/render/renderQuality.ts +7 -6
  56. package/src/render/shaders/flat/glassTint.ts +48 -0
  57. package/src/render/shaders/flat/index.ts +19 -1
  58. package/src/render/shaders/flat/main.ts +60 -25
  59. package/src/render/shaders/flat/surfaceEffects.ts +6 -2
  60. package/src/render/shaders/generated/flat.wgsl.ts +102 -38
  61. package/src/render/surfaceTexture.ts +5 -0
  62. package/src/render/viewModel.ts +41 -0
@@ -13,8 +13,13 @@ export const MAIN_GLSL = `void main() {
13
13
  * the material moves together. The gradients are taken here, outside every branch, for the room
14
14
  * a window samples through textureGrad further down.
15
15
  */
16
- fxLoad(int(surfaceAt.z));
17
- surfaceAt = fxAnimate(surfaceAt);
16
+ /* Zero rows unless a material has carried a table: \`SURFACE_EFFECTS\` is off until one does, and
17
+ every effect below reads exactly nothing from zero rows, which the device folds away. */
18
+ fxClear();
19
+ if (SURFACE_EFFECTS) {
20
+ fxLoad(int(surfaceAt.z));
21
+ surfaceAt = fxAnimate(surfaceAt);
22
+ }
18
23
  vec2 fxDx = dFdx(surfaceAt.xy);
19
24
  vec2 fxDy = dFdy(surfaceAt.xy);
20
25
  if (uWriteMode.y != 0.0 && !ditherKeeps(uWriteMode.y)) discard;
@@ -907,7 +912,12 @@ export const MAIN_GLSL = `void main() {
907
912
  */
908
913
  vec3 lampOpen = vec3(0.0);
909
914
  vec3 lampShadowed = vec3(0.0);
910
- float driftShare = driftLightShare(vWorldPos);
915
+ /* \`DRIFT_LIGHT\` is off until a consumer gives the renderer a DriftLight volume, and the device
916
+ compiles the volume's reads away with it. */
917
+ float driftShare = 0.0;
918
+ if (DRIFT_LIGHT) {
919
+ driftShare = driftLightShare(vWorldPos);
920
+ }
911
921
 
912
922
  /*
913
923
  * Which froxel this fragment is in.
@@ -920,28 +930,53 @@ export const MAIN_GLSL = `void main() {
920
930
  * same order and with the same clamps, because a fragment reading a froxel the binner filled
921
931
  * differently is a fragment lit by somebody else's lights.
922
932
  */
923
- vec3 clusterView = (uView * vec4(vWorldPos, 1.0)).xyz;
924
- float clusterDepth = -clusterView.z;
925
- float clusterHalfH = max(clusterDepth, 1e-4) * uClusterFrustum.z;
926
- float clusterHalfW = clusterHalfH * uClusterFrustum.w;
927
- int clusterTileX = clamp(
928
- int(floor((clusterView.x / clusterHalfW + 1.0) * 0.5 * float(CLUSTER_X))), 0, CLUSTER_X - 1);
929
- int clusterTileY = clamp(
930
- int(floor((clusterView.y / clusterHalfH + 1.0) * 0.5 * float(CLUSTER_Y))), 0, CLUSTER_Y - 1);
931
- int clusterSlice = 0;
932
- if (clusterDepth > uClusterFrustum.x) {
933
- clusterSlice = clamp(
934
- int(floor(log(clusterDepth / uClusterFrustum.x)
935
- / log(uClusterFrustum.y / uClusterFrustum.x) * float(CLUSTER_Z))),
936
- 0, CLUSTER_Z - 1);
933
+ /*
934
+ * **Only in a build that clusters, and there only in a frame that does.** \`CLUSTERED_LIGHTS\` is
935
+ * the profile's \`clusteredLights\`, a pipeline constant the device compiles this whole arm away
936
+ * on: the view transform, two logarithms and the froxel fetch below ran on every lit pixel of
937
+ * every scene, clustered or not, and the arm's records held their registers through the loop.
938
+ */
939
+ int clusterBaseTexel = 0;
940
+ int clusterLights = 0;
941
+ bool clusteredArm = false;
942
+ if (CLUSTERED_LIGHTS) {
943
+ if (uClustered != 0) {
944
+ vec3 clusterView = (uView * vec4(vWorldPos, 1.0)).xyz;
945
+ float clusterDepth = -clusterView.z;
946
+ float clusterHalfH = max(clusterDepth, 1e-4) * uClusterFrustum.z;
947
+ float clusterHalfW = clusterHalfH * uClusterFrustum.w;
948
+ int clusterTileX = clamp(
949
+ int(floor((clusterView.x / clusterHalfW + 1.0) * 0.5 * float(CLUSTER_X))), 0, CLUSTER_X - 1);
950
+ int clusterTileY = clamp(
951
+ int(floor((clusterView.y / clusterHalfH + 1.0) * 0.5 * float(CLUSTER_Y))), 0, CLUSTER_Y - 1);
952
+ int clusterSlice = 0;
953
+ if (clusterDepth > uClusterFrustum.x) {
954
+ clusterSlice = clamp(
955
+ int(floor(log(clusterDepth / uClusterFrustum.x)
956
+ / log(uClusterFrustum.y / uClusterFrustum.x) * float(CLUSTER_Z))),
957
+ 0, CLUSTER_Z - 1);
958
+ }
959
+ clusterBaseTexel = LIGHT_REGION_TEXELS
960
+ + (clusterTileX + clusterTileY * CLUSTER_X + clusterSlice * CLUSTER_X * CLUSTER_Y)
961
+ * CLUSTER_TEXELS;
962
+ clusterLights = int(clusterTexel(clusterBaseTexel).x);
963
+ clusteredArm = true;
964
+ }
937
965
  }
938
- int clusterBaseTexel = LIGHT_REGION_TEXELS
939
- + (clusterTileX + clusterTileY * CLUSTER_X + clusterSlice * CLUSTER_X * CLUSTER_Y)
940
- * CLUSTER_TEXELS;
941
- int clusterLights = uClustered != 0 ? int(clusterTexel(clusterBaseTexel).x) : 0;
942
966
 
943
967
  /* The fixture atlas's size, asked once: its cookie band and its profile rows are found from it. */
944
968
  vec2 fixtureSize = vec2(textureSize(uFixtureAtlas, 0));
969
+ /*
970
+ * Whether any light can carry a measured profile or a cookie, decided once rather than per
971
+ * light. \`LIGHT_FIXTURES\` is off until a consumer loads one, and the device compiles the
972
+ * fixture lookups away with it, which they hold their registers for in every scene otherwise.
973
+ */
974
+ bool fixtureProfiles = false;
975
+ bool fixtureShapes = false;
976
+ if (LIGHT_FIXTURES) {
977
+ fixtureProfiles = uIesAtlasRows > 1.0;
978
+ fixtureShapes = uIesPlaneCount > 1.0 || uCookieTiles > 0.0;
979
+ }
945
980
 
946
981
  // Point lights. Inverse-square-ish falloff clipped to a finite radius, so a
947
982
  // light can be culled without a visible seam.
@@ -967,7 +1002,7 @@ export const MAIN_GLSL = `void main() {
967
1002
  float iesProfile;
968
1003
  vec3 iesAxis;
969
1004
  float cookie;
970
- if (uClustered != 0) {
1005
+ if (clusteredArm) {
971
1006
  if (slot >= clusterLights) break;
972
1007
  /*
973
1008
  * Four indices to a texel. Selected with compares rather than by indexing the vector,
@@ -1272,7 +1307,7 @@ export const MAIN_GLSL = `void main() {
1272
1307
  vec3 iesRay = -toLight / max(dist, 1e-4);
1273
1308
  /* The angle from the fixture's axis, as a fraction of the 0-to-180 arc every row spans. */
1274
1309
  float iesU = 0.0;
1275
- if (uIesAtlasRows > 1.0) {
1310
+ if (fixtureProfiles) {
1276
1311
  float iesAngle = acos(clamp(dot(iesRay, lightDir), -1.0, 1.0));
1277
1312
  /* Clamped to the row's first and last texel centres, which is what the edge clamp of a
1278
1313
  texture exactly one row wide gave; the atlas can be wider now, with cookies beside it. */
@@ -1296,7 +1331,7 @@ export const MAIN_GLSL = `void main() {
1296
1331
  * \`uLightIesAxis\` to answer it. Behind a branch on two uniforms, so a scene using neither
1297
1332
  * builds no frame at all.
1298
1333
  */
1299
- if (uIesPlaneCount > 1.0 || uCookieTiles > 0.0) {
1334
+ if (fixtureShapes) {
1300
1335
  vec3 iesRef = iesAxis - lightDir * dot(iesAxis, lightDir);
1301
1336
  float iesRefLen = length(iesRef);
1302
1337
  if (iesRefLen > 1e-5) {
@@ -1353,7 +1388,7 @@ export const MAIN_GLSL = `void main() {
1353
1388
  * bytes on \`core-only\`** across the sixteen fragment permutations.
1354
1389
  */
1355
1390
  float photometric = 1.0;
1356
- if (uIesAtlasRows > 1.0) {
1391
+ if (fixtureProfiles) {
1357
1392
  float iesBase = iesRow * (uIesPlaneCount + 1.0);
1358
1393
  /* The profiles are the atlas's bottom rows, below any cookies. */
1359
1394
  float iesTop = fixtureSize.y - uIesAtlasRows;
@@ -26,4 +26,4 @@
26
26
  * lit or dark; and a room's box assumes a window as wide as it is tall, so a very tall cell
27
27
  * stretches its room.
28
28
  */
29
- export declare const SURFACE_EFFECTS_GLSL = "\n/*\n * The layer's six rows, and what every effect reads beside them, filled once at the top of main.\n * Globals rather than arguments, because naga passes every argument through a pointer and a copy,\n * and in sixteen permutations that traffic was most of what the effects cost in bytes.\n */\nvec4 fx0;\nvec4 fx1;\nvec4 fx2;\nvec4 fx3;\nvec4 fx4;\nvec4 fx5;\nvec3 fxNormal;\nfloat fxDistance;\n\n/** Fill the rows for this layer, or leave them zero where the array carries no table. */\nvoid fxLoad(int layer) {\n fx0 = vec4(0.0);\n fx1 = vec4(0.0);\n fx2 = vec4(0.0);\n fx3 = vec4(0.0);\n fx4 = vec4(0.0);\n fx5 = vec4(0.0);\n ivec2 size = textureSize(uSurfaceEffects, 0);\n if (size.x < 6 || layer < 0 || layer >= size.y) return;\n fx0 = texelFetch(uSurfaceEffects, ivec2(0, layer), 0);\n fx1 = texelFetch(uSurfaceEffects, ivec2(1, layer), 0);\n fx2 = texelFetch(uSurfaceEffects, ivec2(2, layer), 0);\n fx3 = texelFetch(uSurfaceEffects, ivec2(3, layer), 0);\n fx4 = texelFetch(uSurfaceEffects, ivec2(4, layer), 0);\n fx5 = texelFetch(uSurfaceEffects, ivec2(5, layer), 0);\n}\n\n/** A cell and a seed to 0..1, the same on every device. */\nfloat fxHash(vec2 cell, float seed) {\n uvec2 q = uvec2(ivec2(cell)) + uvec2(uint(seed) * 7919u, uint(seed) * 104729u);\n uint h = (q.x * 1597334677u) ^ (q.y * 3812015801u);\n h = (h ^ (h >> 16)) * 2246822519u;\n h ^= h >> 13;\n return float(h & 16777215u) / 16777216.0;\n}\n\nfloat fxNoise(vec2 p) {\n vec2 i = floor(p);\n vec2 u = smoothstep(0.0, 1.0, fract(p));\n return mix(\n mix(fxHash(i, 0.0), fxHash(i + vec2(1.0, 0.0), 0.0), u.x),\n mix(fxHash(i + vec2(0.0, 1.0), 0.0), fxHash(i + vec2(1.0, 1.0), 0.0), u.x),\n u.y);\n}\n\n/** Scroll and flipbook, on the caller's clock. `fract` keeps a long clock from eating precision. */\nvec3 fxAnimate(vec3 at) {\n float time = uSurfaceScene.x;\n at.xy += fract(fx4.xy * time);\n if (fx5.y > 0.0) at.z += mod(floor(time * fx5.z), fx5.y);\n return at;\n}\n\n/** Pulse, flicker and fade with distance, as a factor on the emission. */\nfloat fxEmission(float layer) {\n float time = uSurfaceScene.x;\n float e = 1.0 - fx4.w * (0.5 + 0.5 * sin(6.2831853 * fract(fx4.z * time)));\n e *= 1.0 - fx5.x * step(0.7, fxHash(vec2(floor(time * 14.0), layer), 3.0));\n if (fx5.w > 0.0) e *= 1.0 - smoothstep(fx5.w * 0.7, fx5.w, fxDistance);\n return e;\n}\n\n/** Dust, grime and streaks, then the scene's rain: the colour in xyz, the roughness in w. */\nvec4 fxWeather(vec3 world, vec3 albedo, float roughness) {\n float fade = fx3.w > 0.0 ? 1.0 - smoothstep(fx3.w * 0.5, fx3.w, fxDistance) : 1.0;\n if (fx3.x + fx3.y + fx3.z > 0.0) {\n float blotch = fxNoise(world.xz * 0.35 + world.y * 0.2);\n float dust = fx3.x * smoothstep(0.35, 0.9, fxNormal.y) * (0.5 + 0.5 * blotch) * fade;\n float grime = fx3.y * (0.55 - 0.55 * smoothstep(0.0, 2.5, world.y) + 0.45 * blotch) * fade;\n float streak = fx3.z * (1.0 - abs(fxNormal.y))\n * fxNoise(vec2((world.x + world.z) * 2.5, world.y * 0.15)) * fade;\n albedo = mix(albedo, vec3(0.56, 0.53, 0.48), dust * 0.6) * (1.0 - 0.5 * grime - 0.35 * streak);\n roughness = mix(roughness, 1.0, clamp(0.5 * (dust + grime), 0.0, 1.0));\n }\n float wet = uSurfaceScene.y * (1.0 - fx2.w) * mix(0.35, 1.0, smoothstep(0.2, 0.8, fxNormal.y));\n return vec4(albedo * (1.0 - 0.45 * wet), mix(roughness, 0.08, wet));\n}\n\n/**\n * Windows and the rooms behind them: the glow a lit window adds to the emission in xyz, and in w\n * how much of the surface is glass, which the caller mixes toward the room's dim colour. The room\n * itself is returned through `fxRoom`. `mask` is the albedo's alpha, and a window is a hole in the\n * wall's picture: glass where it is 0. The other way round would lose the wall's colour, which does\n * not survive an upload at alpha 0.\n *\n * **A building's own light, where its vertices name one.** The table is per layer, and every\n * facade of one style shares a layer, so a light that differs from building to building \u2014 its\n * colour and its strength \u2014 cannot live there. A window layer's vertex emissive is otherwise unused\n * (its windows glow here, not through the emissive term), so the vertex's emissive colour, strength\n * folded in, is that light: the window's colour where there is no room, a tint on the room where\n * there is. An absent colour reads negative and leaves both exactly as the table says.\n */\nvec3 fxRoom;\nvec4 fxWindows(vec3 at, vec2 dx, vec2 dy, float mask) {\n float glass = fx0.z > 0.0 ? 1.0 - mask : 1.0;\n vec2 g = at.xy * fx0.xy;\n vec2 local = fract(g);\n float share = floor(max(uSurfaceScene.z, uSurfaceScene.w) * 10.0 + 0.5) / 10.0;\n float lit = fxHash(floor(g), fx0.w) < share * (fx1.x > 0.0 ? fx1.z : 1.0) ? 1.0 : 0.0;\n fxRoom = fx2.rgb;\n bool own = vEmissiveColor.r >= 0.0;\n vec3 glow = own ? vEmissiveColor : fx2.rgb;\n float tangentLength = length(vTangent.xyz);\n if (fx1.x > 0.0 && tangentLength > 1e-4) {\n vec3 t = vTangent.xyz / tangentLength;\n vec3 v = normalize(vWorldPos - uCameraPos);\n vec3 d = vec3(dot(v, t), dot(v, cross(fxNormal, t)) * sign(vTangent.w + 0.5),\n min(dot(v, fxNormal), -1e-3) / max(fx1.y, 0.05));\n vec2 across = (step(0.0, d.xy) - local) / (sign(d.xy + 1e-9) * max(abs(d.xy), 1e-5));\n float travel = min(min(across.x, across.y), -1.0 / d.z);\n float depth = min(-d.z * travel, 1.0);\n vec2 picture = 0.5 + (local + d.xy * travel - 0.5) / (1.0 + depth);\n fxRoom = textureGrad(uAlbedo, vec3(picture, fx1.x - 1.0), dx * fx0.xy * 0.5, dy * fx0.xy * 0.5).rgb;\n glow = own ? fxRoom * vEmissiveColor : fxRoom;\n }\n return vec4(glow * (lit * fx1.w * glass), glass);\n}\n";
29
+ export declare const SURFACE_EFFECTS_GLSL = "\n/*\n * The layer's six rows, and what every effect reads beside them, filled once at the top of main.\n * Globals rather than arguments, because naga passes every argument through a pointer and a copy,\n * and in sixteen permutations that traffic was most of what the effects cost in bytes.\n */\nvec4 fx0;\nvec4 fx1;\nvec4 fx2;\nvec4 fx3;\nvec4 fx4;\nvec4 fx5;\nvec3 fxNormal;\nfloat fxDistance;\n\n/** Every row zero: the layer carries no effect. */\nvoid fxClear() {\n fx0 = vec4(0.0);\n fx1 = vec4(0.0);\n fx2 = vec4(0.0);\n fx3 = vec4(0.0);\n fx4 = vec4(0.0);\n fx5 = vec4(0.0);\n}\n\n/** Fill the rows for this layer, or leave them zero where the array carries no table. */\nvoid fxLoad(int layer) {\n ivec2 size = textureSize(uSurfaceEffects, 0);\n if (size.x < 6 || layer < 0 || layer >= size.y) return;\n fx0 = texelFetch(uSurfaceEffects, ivec2(0, layer), 0);\n fx1 = texelFetch(uSurfaceEffects, ivec2(1, layer), 0);\n fx2 = texelFetch(uSurfaceEffects, ivec2(2, layer), 0);\n fx3 = texelFetch(uSurfaceEffects, ivec2(3, layer), 0);\n fx4 = texelFetch(uSurfaceEffects, ivec2(4, layer), 0);\n fx5 = texelFetch(uSurfaceEffects, ivec2(5, layer), 0);\n}\n\n/** A cell and a seed to 0..1, the same on every device. */\nfloat fxHash(vec2 cell, float seed) {\n uvec2 q = uvec2(ivec2(cell)) + uvec2(uint(seed) * 7919u, uint(seed) * 104729u);\n uint h = (q.x * 1597334677u) ^ (q.y * 3812015801u);\n h = (h ^ (h >> 16)) * 2246822519u;\n h ^= h >> 13;\n return float(h & 16777215u) / 16777216.0;\n}\n\nfloat fxNoise(vec2 p) {\n vec2 i = floor(p);\n vec2 u = smoothstep(0.0, 1.0, fract(p));\n return mix(\n mix(fxHash(i, 0.0), fxHash(i + vec2(1.0, 0.0), 0.0), u.x),\n mix(fxHash(i + vec2(0.0, 1.0), 0.0), fxHash(i + vec2(1.0, 1.0), 0.0), u.x),\n u.y);\n}\n\n/** Scroll and flipbook, on the caller's clock. `fract` keeps a long clock from eating precision. */\nvec3 fxAnimate(vec3 at) {\n float time = uSurfaceScene.x;\n at.xy += fract(fx4.xy * time);\n if (fx5.y > 0.0) at.z += mod(floor(time * fx5.z), fx5.y);\n return at;\n}\n\n/** Pulse, flicker and fade with distance, as a factor on the emission. */\nfloat fxEmission(float layer) {\n float time = uSurfaceScene.x;\n float e = 1.0 - fx4.w * (0.5 + 0.5 * sin(6.2831853 * fract(fx4.z * time)));\n e *= 1.0 - fx5.x * step(0.7, fxHash(vec2(floor(time * 14.0), layer), 3.0));\n if (fx5.w > 0.0) e *= 1.0 - smoothstep(fx5.w * 0.7, fx5.w, fxDistance);\n return e;\n}\n\n/** Dust, grime and streaks, then the scene's rain: the colour in xyz, the roughness in w. */\nvec4 fxWeather(vec3 world, vec3 albedo, float roughness) {\n float fade = fx3.w > 0.0 ? 1.0 - smoothstep(fx3.w * 0.5, fx3.w, fxDistance) : 1.0;\n if (fx3.x + fx3.y + fx3.z > 0.0) {\n float blotch = fxNoise(world.xz * 0.35 + world.y * 0.2);\n float dust = fx3.x * smoothstep(0.35, 0.9, fxNormal.y) * (0.5 + 0.5 * blotch) * fade;\n float grime = fx3.y * (0.55 - 0.55 * smoothstep(0.0, 2.5, world.y) + 0.45 * blotch) * fade;\n float streak = fx3.z * (1.0 - abs(fxNormal.y))\n * fxNoise(vec2((world.x + world.z) * 2.5, world.y * 0.15)) * fade;\n albedo = mix(albedo, vec3(0.56, 0.53, 0.48), dust * 0.6) * (1.0 - 0.5 * grime - 0.35 * streak);\n roughness = mix(roughness, 1.0, clamp(0.5 * (dust + grime), 0.0, 1.0));\n }\n float wet = uSurfaceScene.y * (1.0 - fx2.w) * mix(0.35, 1.0, smoothstep(0.2, 0.8, fxNormal.y));\n return vec4(albedo * (1.0 - 0.45 * wet), mix(roughness, 0.08, wet));\n}\n\n/**\n * Windows and the rooms behind them: the glow a lit window adds to the emission in xyz, and in w\n * how much of the surface is glass, which the caller mixes toward the room's dim colour. The room\n * itself is returned through `fxRoom`. `mask` is the albedo's alpha, and a window is a hole in the\n * wall's picture: glass where it is 0. The other way round would lose the wall's colour, which does\n * not survive an upload at alpha 0.\n *\n * **A building's own light, where its vertices name one.** The table is per layer, and every\n * facade of one style shares a layer, so a light that differs from building to building \u2014 its\n * colour and its strength \u2014 cannot live there. A window layer's vertex emissive is otherwise unused\n * (its windows glow here, not through the emissive term), so the vertex's emissive colour, strength\n * folded in, is that light: the window's colour where there is no room, a tint on the room where\n * there is. An absent colour reads negative and leaves both exactly as the table says.\n */\nvec3 fxRoom;\nvec4 fxWindows(vec3 at, vec2 dx, vec2 dy, float mask) {\n float glass = fx0.z > 0.0 ? 1.0 - mask : 1.0;\n vec2 g = at.xy * fx0.xy;\n vec2 local = fract(g);\n float share = floor(max(uSurfaceScene.z, uSurfaceScene.w) * 10.0 + 0.5) / 10.0;\n float lit = fxHash(floor(g), fx0.w) < share * (fx1.x > 0.0 ? fx1.z : 1.0) ? 1.0 : 0.0;\n fxRoom = fx2.rgb;\n bool own = vEmissiveColor.r >= 0.0;\n vec3 glow = own ? vEmissiveColor : fx2.rgb;\n float tangentLength = length(vTangent.xyz);\n if (fx1.x > 0.0 && tangentLength > 1e-4) {\n vec3 t = vTangent.xyz / tangentLength;\n vec3 v = normalize(vWorldPos - uCameraPos);\n vec3 d = vec3(dot(v, t), dot(v, cross(fxNormal, t)) * sign(vTangent.w + 0.5),\n min(dot(v, fxNormal), -1e-3) / max(fx1.y, 0.05));\n vec2 across = (step(0.0, d.xy) - local) / (sign(d.xy + 1e-9) * max(abs(d.xy), 1e-5));\n float travel = min(min(across.x, across.y), -1.0 / d.z);\n float depth = min(-d.z * travel, 1.0);\n vec2 picture = 0.5 + (local + d.xy * travel - 0.5) / (1.0 + depth);\n fxRoom = textureGrad(uAlbedo, vec3(picture, fx1.x - 1.0), dx * fx0.xy * 0.5, dy * fx0.xy * 0.5).rgb;\n glow = own ? fxRoom * vEmissiveColor : fxRoom;\n }\n return vec4(glow * (lit * fx1.w * glass), glass);\n}\n";
@@ -41,14 +41,18 @@ vec4 fx5;
41
41
  vec3 fxNormal;
42
42
  float fxDistance;
43
43
 
44
- /** Fill the rows for this layer, or leave them zero where the array carries no table. */
45
- void fxLoad(int layer) {
44
+ /** Every row zero: the layer carries no effect. */
45
+ void fxClear() {
46
46
  fx0 = vec4(0.0);
47
47
  fx1 = vec4(0.0);
48
48
  fx2 = vec4(0.0);
49
49
  fx3 = vec4(0.0);
50
50
  fx4 = vec4(0.0);
51
51
  fx5 = vec4(0.0);
52
+ }
53
+
54
+ /** Fill the rows for this layer, or leave them zero where the array carries no table. */
55
+ void fxLoad(int layer) {
52
56
  ivec2 size = textureSize(uSurfaceEffects, 0);
53
57
  if (size.x < 6 || layer < 0 || layer >= size.y) return;
54
58
  fx0 = texelFetch(uSurfaceEffects, ivec2(0, layer), 0);
@@ -472,6 +472,10 @@ export declare const FLAT_BINDINGS: {
472
472
  };
473
473
  readonly overrides: {
474
474
  readonly GLASS_SHADOWS: 0;
475
+ readonly CLUSTERED_LIGHTS: 1;
476
+ readonly LIGHT_FIXTURES: 2;
477
+ readonly SURFACE_EFFECTS: 3;
478
+ readonly DRIFT_LIGHT: 4;
475
479
  };
476
480
  };
477
481
  readonly pointShadows: {
@@ -1075,6 +1079,10 @@ export declare const FLAT_BINDINGS: {
1075
1079
  };
1076
1080
  readonly overrides: {
1077
1081
  readonly GLASS_SHADOWS: 0;
1082
+ readonly CLUSTERED_LIGHTS: 1;
1083
+ readonly LIGHT_FIXTURES: 2;
1084
+ readonly SURFACE_EFFECTS: 3;
1085
+ readonly DRIFT_LIGHT: 4;
1078
1086
  };
1079
1087
  };
1080
1088
  readonly directionalShadows: {
@@ -1582,6 +1590,10 @@ export declare const FLAT_BINDINGS: {
1582
1590
  };
1583
1591
  readonly overrides: {
1584
1592
  readonly GLASS_SHADOWS: 0;
1593
+ readonly CLUSTERED_LIGHTS: 1;
1594
+ readonly LIGHT_FIXTURES: 2;
1595
+ readonly SURFACE_EFFECTS: 3;
1596
+ readonly DRIFT_LIGHT: 4;
1585
1597
  };
1586
1598
  };
1587
1599
  readonly "directionalShadows+pointShadows": {
@@ -2227,6 +2239,10 @@ export declare const FLAT_BINDINGS: {
2227
2239
  };
2228
2240
  readonly overrides: {
2229
2241
  readonly GLASS_SHADOWS: 0;
2242
+ readonly CLUSTERED_LIGHTS: 1;
2243
+ readonly LIGHT_FIXTURES: 2;
2244
+ readonly SURFACE_EFFECTS: 3;
2245
+ readonly DRIFT_LIGHT: 4;
2230
2246
  };
2231
2247
  };
2232
2248
  readonly environmentProbe: {
@@ -2747,6 +2763,10 @@ export declare const FLAT_BINDINGS: {
2747
2763
  };
2748
2764
  readonly overrides: {
2749
2765
  readonly GLASS_SHADOWS: 0;
2766
+ readonly CLUSTERED_LIGHTS: 1;
2767
+ readonly LIGHT_FIXTURES: 2;
2768
+ readonly SURFACE_EFFECTS: 3;
2769
+ readonly DRIFT_LIGHT: 4;
2750
2770
  };
2751
2771
  };
2752
2772
  readonly "environmentProbe+pointShadows": {
@@ -3405,6 +3425,10 @@ export declare const FLAT_BINDINGS: {
3405
3425
  };
3406
3426
  readonly overrides: {
3407
3427
  readonly GLASS_SHADOWS: 0;
3428
+ readonly CLUSTERED_LIGHTS: 1;
3429
+ readonly LIGHT_FIXTURES: 2;
3430
+ readonly SURFACE_EFFECTS: 3;
3431
+ readonly DRIFT_LIGHT: 4;
3408
3432
  };
3409
3433
  };
3410
3434
  readonly "directionalShadows+environmentProbe": {
@@ -3967,6 +3991,10 @@ export declare const FLAT_BINDINGS: {
3967
3991
  };
3968
3992
  readonly overrides: {
3969
3993
  readonly GLASS_SHADOWS: 0;
3994
+ readonly CLUSTERED_LIGHTS: 1;
3995
+ readonly LIGHT_FIXTURES: 2;
3996
+ readonly SURFACE_EFFECTS: 3;
3997
+ readonly DRIFT_LIGHT: 4;
3970
3998
  };
3971
3999
  };
3972
4000
  readonly "directionalShadows+environmentProbe+pointShadows": {
@@ -4667,6 +4695,10 @@ export declare const FLAT_BINDINGS: {
4667
4695
  };
4668
4696
  readonly overrides: {
4669
4697
  readonly GLASS_SHADOWS: 0;
4698
+ readonly CLUSTERED_LIGHTS: 1;
4699
+ readonly LIGHT_FIXTURES: 2;
4700
+ readonly SURFACE_EFFECTS: 3;
4701
+ readonly DRIFT_LIGHT: 4;
4670
4702
  };
4671
4703
  };
4672
4704
  readonly nightEmissive: {
@@ -5137,6 +5169,10 @@ export declare const FLAT_BINDINGS: {
5137
5169
  };
5138
5170
  readonly overrides: {
5139
5171
  readonly GLASS_SHADOWS: 0;
5172
+ readonly CLUSTERED_LIGHTS: 1;
5173
+ readonly LIGHT_FIXTURES: 2;
5174
+ readonly SURFACE_EFFECTS: 3;
5175
+ readonly DRIFT_LIGHT: 4;
5140
5176
  };
5141
5177
  };
5142
5178
  readonly "nightEmissive+pointShadows": {
@@ -5745,6 +5781,10 @@ export declare const FLAT_BINDINGS: {
5745
5781
  };
5746
5782
  readonly overrides: {
5747
5783
  readonly GLASS_SHADOWS: 0;
5784
+ readonly CLUSTERED_LIGHTS: 1;
5785
+ readonly LIGHT_FIXTURES: 2;
5786
+ readonly SURFACE_EFFECTS: 3;
5787
+ readonly DRIFT_LIGHT: 4;
5748
5788
  };
5749
5789
  };
5750
5790
  readonly "directionalShadows+nightEmissive": {
@@ -6257,6 +6297,10 @@ export declare const FLAT_BINDINGS: {
6257
6297
  };
6258
6298
  readonly overrides: {
6259
6299
  readonly GLASS_SHADOWS: 0;
6300
+ readonly CLUSTERED_LIGHTS: 1;
6301
+ readonly LIGHT_FIXTURES: 2;
6302
+ readonly SURFACE_EFFECTS: 3;
6303
+ readonly DRIFT_LIGHT: 4;
6260
6304
  };
6261
6305
  };
6262
6306
  readonly "directionalShadows+nightEmissive+pointShadows": {
@@ -6907,6 +6951,10 @@ export declare const FLAT_BINDINGS: {
6907
6951
  };
6908
6952
  readonly overrides: {
6909
6953
  readonly GLASS_SHADOWS: 0;
6954
+ readonly CLUSTERED_LIGHTS: 1;
6955
+ readonly LIGHT_FIXTURES: 2;
6956
+ readonly SURFACE_EFFECTS: 3;
6957
+ readonly DRIFT_LIGHT: 4;
6910
6958
  };
6911
6959
  };
6912
6960
  readonly "environmentProbe+nightEmissive": {
@@ -7432,6 +7480,10 @@ export declare const FLAT_BINDINGS: {
7432
7480
  };
7433
7481
  readonly overrides: {
7434
7482
  readonly GLASS_SHADOWS: 0;
7483
+ readonly CLUSTERED_LIGHTS: 1;
7484
+ readonly LIGHT_FIXTURES: 2;
7485
+ readonly SURFACE_EFFECTS: 3;
7486
+ readonly DRIFT_LIGHT: 4;
7435
7487
  };
7436
7488
  };
7437
7489
  readonly "environmentProbe+nightEmissive+pointShadows": {
@@ -8095,6 +8147,10 @@ export declare const FLAT_BINDINGS: {
8095
8147
  };
8096
8148
  readonly overrides: {
8097
8149
  readonly GLASS_SHADOWS: 0;
8150
+ readonly CLUSTERED_LIGHTS: 1;
8151
+ readonly LIGHT_FIXTURES: 2;
8152
+ readonly SURFACE_EFFECTS: 3;
8153
+ readonly DRIFT_LIGHT: 4;
8098
8154
  };
8099
8155
  };
8100
8156
  readonly "directionalShadows+environmentProbe+nightEmissive": {
@@ -8662,6 +8718,10 @@ export declare const FLAT_BINDINGS: {
8662
8718
  };
8663
8719
  readonly overrides: {
8664
8720
  readonly GLASS_SHADOWS: 0;
8721
+ readonly CLUSTERED_LIGHTS: 1;
8722
+ readonly LIGHT_FIXTURES: 2;
8723
+ readonly SURFACE_EFFECTS: 3;
8724
+ readonly DRIFT_LIGHT: 4;
8665
8725
  };
8666
8726
  };
8667
8727
  readonly "directionalShadows+environmentProbe+nightEmissive+pointShadows": {
@@ -9367,6 +9427,10 @@ export declare const FLAT_BINDINGS: {
9367
9427
  };
9368
9428
  readonly overrides: {
9369
9429
  readonly GLASS_SHADOWS: 0;
9430
+ readonly CLUSTERED_LIGHTS: 1;
9431
+ readonly LIGHT_FIXTURES: 2;
9432
+ readonly SURFACE_EFFECTS: 3;
9433
+ readonly DRIFT_LIGHT: 4;
9370
9434
  };
9371
9435
  };
9372
9436
  };