@driftengine/core 4.5.0 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +62 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +13 -1
- package/dist/render/backend/api.d.ts +17 -3
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +122 -8
- package/dist/render/backend/webgl2/renderer.js +249 -128
- package/dist/render/backend/webgpu/buffers.d.ts +17 -0
- package/dist/render/backend/webgpu/buffers.js +34 -3
- package/dist/render/backend/webgpu/depthPass.js +5 -1
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
- package/dist/render/backend/webgpu/flatPass.js +38 -9
- package/dist/render/backend/webgpu/glassTintPass.js +2 -1
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +11 -1
- package/dist/render/backend/webgpu/instanced.js +21 -3
- package/dist/render/backend/webgpu/latePass.d.ts +6 -2
- package/dist/render/backend/webgpu/latePass.js +11 -4
- package/dist/render/backend/webgpu/motionPass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -10
- package/dist/render/backend/webgpu/renderer.js +478 -214
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +22 -33
- package/dist/render/lightBudget.js +24 -35
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +23 -24
- package/dist/render/mesh.js +56 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +2 -0
- package/dist/render/shaders/depth.js +12 -9
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/index.js +5 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +75 -23
- package/dist/render/shaders/flat/preamble.js +75 -20
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
- package/dist/render/shaders/generated/depth.wgsl.js +8 -8
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
- package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
- package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +51 -1
- package/src/render/backend/api.ts +21 -2
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +311 -150
- package/src/render/backend/webgpu/buffers.ts +49 -3
- package/src/render/backend/webgpu/depthPass.ts +5 -1
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -11
- package/src/render/backend/webgpu/glassTintPass.ts +2 -1
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +21 -3
- package/src/render/backend/webgpu/latePass.ts +12 -3
- package/src/render/backend/webgpu/motionPass.ts +2 -1
- package/src/render/backend/webgpu/renderer.ts +556 -239
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +25 -36
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +64 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +4 -0
- package/src/render/shaders/depth.ts +12 -9
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/index.ts +5 -3
- package/src/render/shaders/flat/main.ts +75 -23
- package/src/render/shaders/flat/preamble.ts +75 -20
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +8 -8
- package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
- package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -34,12 +34,16 @@
|
|
|
34
34
|
* fn reconMotion(x: i32, y: i32) -> vec4<f32> // uv, previous view depth, flag
|
|
35
35
|
* fn reconHistory(x: i32, y: i32) -> vec4<f32> // output size, colour and gathered weight
|
|
36
36
|
* fn reconHistoryBilinear(p: vec2<f32>) -> vec4<f32>
|
|
37
|
+
* fn reconFlicker(x: i32, y: i32) -> vec4<f32> // output size, last frame's flicker record
|
|
38
|
+
* fn reconSwing(x: i32, y: i32) -> f32 // output size, last frame's held depth swing
|
|
37
39
|
* ```
|
|
38
40
|
*
|
|
39
41
|
* and the uniform `recon: ReconParams`, whose struct this module declares.
|
|
40
42
|
*/
|
|
41
43
|
import { wgslSceneDepthToNdc } from '../../depthConvention.ts';
|
|
42
44
|
import { PLANAR_FROM, PLANAR_TO } from '../../recon/disocclusion.ts';
|
|
45
|
+
import { DEPTH_SWING_WIDTH } from '../../recon/resolve.ts';
|
|
46
|
+
import { FLICKER_BLEND, FLICKER_REPEAT, STILL_FROM, STILL_TO } from '../../temporalAa.ts';
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
49
|
* The parameters one resolve reads, as `ResolveFrame` minus its images.
|
|
@@ -66,11 +70,16 @@ struct ReconParams {
|
|
|
66
70
|
motionScale: f32,
|
|
67
71
|
normalFloor: f32,
|
|
68
72
|
normalCeiling: f32,
|
|
73
|
+
periodStart: u32,
|
|
74
|
+
antiFlicker: u32,
|
|
75
|
+
flickerMemory: f32,
|
|
76
|
+
flickerWidth: f32,
|
|
77
|
+
reprojection: mat4x4<f32>,
|
|
69
78
|
}
|
|
70
79
|
`;
|
|
71
80
|
|
|
72
81
|
/** Floats in `ReconParams`, which is what a caller sizes its uniform buffer by. */
|
|
73
|
-
export const RECON_PARAM_FLOATS =
|
|
82
|
+
export const RECON_PARAM_FLOATS = 92;
|
|
74
83
|
|
|
75
84
|
/** `SAMPLE_SPREAD` and `CONFIDENT_WEIGHT`, which the reference exports and this must not restate. */
|
|
76
85
|
export const RECON_SAMPLE_SPREAD = 0.47;
|
|
@@ -91,8 +100,8 @@ export const RECON_WORKGROUP = 8;
|
|
|
91
100
|
export const RECON_HISTORY_FORMAT: GPUTextureFormat = 'rgba16float';
|
|
92
101
|
|
|
93
102
|
/**
|
|
94
|
-
* The arithmetic, as text. Defines `reconResolvePixel(ox, oy) ->
|
|
95
|
-
* colour and the weight it has gathered — and `reconSharpenAt(ox, oy) -> vec3<f32>`, the picture
|
|
103
|
+
* The arithmetic, as text. Defines `reconResolvePixel(ox, oy) -> ReconResolved` — the next
|
|
104
|
+
* history's colour and the weight it has gathered, and the pixel's next flicker record — and `reconSharpenAt(ox, oy) -> vec3<f32>`, the picture
|
|
96
105
|
* shown, which reads the history the first has already written.
|
|
97
106
|
*/
|
|
98
107
|
export const RECON_RESOLVE_CORE_WGSL = /* wgsl */ `
|
|
@@ -100,6 +109,11 @@ const RECON_SAMPLE_SPREAD: f32 = ${RECON_SAMPLE_SPREAD};
|
|
|
100
109
|
const RECON_CONFIDENT_WEIGHT: f32 = ${RECON_CONFIDENT_WEIGHT}.0;
|
|
101
110
|
const RECON_PLANAR_FROM: f32 = ${PLANAR_FROM};
|
|
102
111
|
const RECON_PLANAR_TO: f32 = ${PLANAR_TO};
|
|
112
|
+
const RECON_FLICKER_BLEND: f32 = ${FLICKER_BLEND};
|
|
113
|
+
const RECON_FLICKER_REPEAT: f32 = ${FLICKER_REPEAT};
|
|
114
|
+
const RECON_STILL_FROM: f32 = ${STILL_FROM};
|
|
115
|
+
const RECON_STILL_TO: f32 = ${STILL_TO};
|
|
116
|
+
const RECON_DEPTH_SWING_WIDTH: f32 = ${DEPTH_SWING_WIDTH}.0;
|
|
103
117
|
|
|
104
118
|
fn reconClampTexel(value: i32, size: i32) -> i32 {
|
|
105
119
|
return clamp(value, 0, size - 1);
|
|
@@ -467,6 +481,10 @@ fn reconSurfaceAt(previous: bool, x: i32, y: i32, centre: f32) -> ReconSurface {
|
|
|
467
481
|
struct ReconHistory {
|
|
468
482
|
weight: f32,
|
|
469
483
|
colour: vec4<f32>,
|
|
484
|
+
/* How far the surface moved, in output pixels; read only where the weight is not zero. */
|
|
485
|
+
moved: f32,
|
|
486
|
+
/* How far, relative, last frame's depth missed the one expected there; -1 where none was held. */
|
|
487
|
+
missed: f32,
|
|
470
488
|
}
|
|
471
489
|
|
|
472
490
|
/*
|
|
@@ -474,10 +492,12 @@ struct ReconHistory {
|
|
|
474
492
|
* behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it
|
|
475
493
|
* is the same surface.
|
|
476
494
|
*/
|
|
477
|
-
fn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32) -> ReconHistory {
|
|
495
|
+
fn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32, swing: f32) -> ReconHistory {
|
|
478
496
|
var out: ReconHistory;
|
|
479
497
|
out.weight = 0.0;
|
|
480
498
|
out.colour = vec4<f32>(0.0);
|
|
499
|
+
out.moved = 0.0;
|
|
500
|
+
out.missed = -1.0;
|
|
481
501
|
if (recon.hasHistory == 0u) { return out; }
|
|
482
502
|
let rw = f32(recon.renderSize.x);
|
|
483
503
|
let rh = f32(recon.renderSize.y);
|
|
@@ -490,20 +510,24 @@ fn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32) -> ReconHistory {
|
|
|
490
510
|
mv = motion.y;
|
|
491
511
|
expected = motion.z;
|
|
492
512
|
} else {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
let
|
|
513
|
+
/* Through the one reprojection matrix the renderer multiplied: see resolve.ts. */
|
|
514
|
+
let du = (f32(dx) + 0.5 - recon.jitter.x) / rw;
|
|
515
|
+
let dv = (f32(dy) + 0.5 - recon.jitter.y) / rh;
|
|
516
|
+
let ndc = vec4<f32>(du * 2.0 - 1.0, dv * 2.0 - 1.0, reconDepth(dx, dy), 1.0);
|
|
517
|
+
let m = recon.inverseViewProj;
|
|
518
|
+
let hw = m[0][3] * ndc.x + m[1][3] * ndc.y + m[2][3] * ndc.z + m[3][3];
|
|
519
|
+
if (!(hw > 1e-12)) { return out; }
|
|
520
|
+
let clip = recon.reprojection * ndc;
|
|
496
521
|
/*
|
|
497
522
|
* A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no
|
|
498
523
|
* such depth, so it needs no refusal of its own here.
|
|
499
524
|
*/
|
|
500
|
-
expected = clip.w;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
mu = (clip.x / expected) * 0.5 + 0.5 - du;
|
|
504
|
-
mv = (clip.y / expected) * 0.5 + 0.5 - dv;
|
|
525
|
+
expected = clip.w / hw;
|
|
526
|
+
mu = (clip.x / clip.w) * 0.5 + 0.5 - du;
|
|
527
|
+
mv = (clip.y / clip.w) * 0.5 + 0.5 - dv;
|
|
505
528
|
}
|
|
506
529
|
|
|
530
|
+
out.moved = length(vec2<f32>(mu * f32(recon.outputSize.x), mv * f32(recon.outputSize.y)));
|
|
507
531
|
let pu = u + mu;
|
|
508
532
|
let pv = v + mv;
|
|
509
533
|
if (!(pu >= 0.0 && pu <= 1.0 && pv >= 0.0 && pv <= 1.0)) { return out; }
|
|
@@ -550,13 +574,41 @@ fn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32) -> ReconHistory {
|
|
|
550
574
|
normalDot = 1.0 - planar * (1.0 - dot(hereNormal.normal, thereNormal.normal));
|
|
551
575
|
}
|
|
552
576
|
}
|
|
553
|
-
|
|
577
|
+
if (held > 0.0 && expected > 0.0) { out.missed = abs(held - expected) / expected; }
|
|
578
|
+
/* A still pixel's own held depth swing: see resolve.ts. */
|
|
579
|
+
var swung = 0.0;
|
|
580
|
+
if (swing > 0.0) {
|
|
581
|
+
swung = RECON_DEPTH_SWING_WIDTH * swing *
|
|
582
|
+
(1.0 - reconSmoothstep(RECON_STILL_FROM, RECON_STILL_TO, out.moved));
|
|
583
|
+
}
|
|
584
|
+
let trust = reconDisocclusion(
|
|
585
|
+
expected,
|
|
586
|
+
held,
|
|
587
|
+
length(vec2<f32>(mu, mv)),
|
|
588
|
+
normalDot,
|
|
589
|
+
here.slope + swung,
|
|
590
|
+
);
|
|
554
591
|
out.weight = out.colour.w * trust;
|
|
555
592
|
return out;
|
|
556
593
|
}
|
|
557
594
|
|
|
558
|
-
/*
|
|
559
|
-
fn
|
|
595
|
+
/* temporalAa.ts's repeats: whether two periods' means are the same, one to zero. */
|
|
596
|
+
fn reconRepeats(mean: f32, previous: f32) -> f32 {
|
|
597
|
+
let apart = abs(mean - previous) / max(max(mean, previous), 1.0 / 255.0);
|
|
598
|
+
return 1.0 - reconSmoothstep(RECON_FLICKER_REPEAT, 2.0 * RECON_FLICKER_REPEAT, apart);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
struct ReconResolved {
|
|
602
|
+
/* The history's next colour, before sharpening, and the weight it has gathered. */
|
|
603
|
+
colour: vec4<f32>,
|
|
604
|
+
/* The pixel's next flicker record: spread, this period's sum, last period's mean, repeats. */
|
|
605
|
+
record: vec4<f32>,
|
|
606
|
+
/* And its next held depth swing. */
|
|
607
|
+
swing: f32,
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* One output pixel: the history's next colour and weight, and its next flicker record. */
|
|
611
|
+
fn reconResolvePixel(ox: i32, oy: i32) -> ReconResolved {
|
|
560
612
|
let rw = i32(recon.renderSize.x);
|
|
561
613
|
let rh = i32(recon.renderSize.y);
|
|
562
614
|
let ow = i32(recon.outputSize.x);
|
|
@@ -604,18 +656,68 @@ fn reconResolvePixel(ox: i32, oy: i32) -> vec4<f32> {
|
|
|
604
656
|
accum = accum / gathered;
|
|
605
657
|
|
|
606
658
|
/* The history, and the weight it still deserves; a tenth of each frame is always new. */
|
|
607
|
-
|
|
608
|
-
|
|
659
|
+
var swing = 0.0;
|
|
660
|
+
if (recon.antiFlicker != 0u) { swing = reconSwing(ox, oy); }
|
|
661
|
+
let history = reconHistoryWeight(u, v, dx, dy, swing);
|
|
662
|
+
var cap = (1.0 - recon.alpha) / recon.alpha;
|
|
663
|
+
var low = box.low;
|
|
664
|
+
var high = box.high;
|
|
665
|
+
var out: ReconResolved;
|
|
666
|
+
out.record = vec4<f32>(0.0);
|
|
667
|
+
out.swing = 0.0;
|
|
668
|
+
/* The anti-flicker, operation for operation resolve.ts's: see temporalAa.ts. */
|
|
669
|
+
if (recon.antiFlicker != 0u) {
|
|
670
|
+
let seen = dot(accum, vec3<f32>(0.2126, 0.7152, 0.0722));
|
|
671
|
+
let memory = recon.flickerMemory;
|
|
672
|
+
let width = recon.flickerWidth;
|
|
673
|
+
var held = vec4<f32>(0.0, seen * memory, seen, 0.0);
|
|
674
|
+
if (history.weight > 0.0) { held = reconFlicker(ox, oy); }
|
|
675
|
+
var still = 0.0;
|
|
676
|
+
if (history.weight > 0.0) {
|
|
677
|
+
still = 1.0 - reconSmoothstep(RECON_STILL_FROM, RECON_STILL_TO, history.moved);
|
|
678
|
+
}
|
|
679
|
+
let apart = abs(seen - held.z);
|
|
680
|
+
let within = apart <= width * held.x + 1.0 / 255.0;
|
|
681
|
+
let proven = held.w > 0.75 && within;
|
|
682
|
+
var widen = 0.0;
|
|
683
|
+
let base = 1.0 - recon.alpha;
|
|
684
|
+
var keep = base;
|
|
685
|
+
if (proven) {
|
|
686
|
+
widen = width * held.x * still;
|
|
687
|
+
keep = base + (RECON_FLICKER_BLEND - base) * still;
|
|
688
|
+
}
|
|
689
|
+
low = low - vec3<f32>(widen);
|
|
690
|
+
high = high + vec3<f32>(widen);
|
|
691
|
+
cap = keep / (1.0 - keep);
|
|
692
|
+
var sum = held.y;
|
|
693
|
+
var mean = held.z;
|
|
694
|
+
var repeated = 0.0;
|
|
695
|
+
if (within) { repeated = held.w; }
|
|
696
|
+
if (recon.periodStart != 0u) {
|
|
697
|
+
if (reconRepeats(sum, mean) > 0.5) { repeated = min(1.0, repeated + 0.5); } else { repeated = 0.0; }
|
|
698
|
+
mean = sum;
|
|
699
|
+
sum = 0.0;
|
|
700
|
+
}
|
|
701
|
+
out.record = vec4<f32>(
|
|
702
|
+
held.x + (apart - held.x) * memory,
|
|
703
|
+
sum + seen * memory,
|
|
704
|
+
mean,
|
|
705
|
+
repeated * still,
|
|
706
|
+
);
|
|
707
|
+
/* The peak carried on, halving over a period, whatever the test said. */
|
|
708
|
+
if (history.missed >= 0.0) { out.swing = max(history.missed, swing * pow(0.5, memory)); }
|
|
709
|
+
}
|
|
609
710
|
let kept = min(cap, history.weight);
|
|
610
711
|
|
|
611
712
|
let total = kept + gathered;
|
|
612
713
|
var mixed = accum;
|
|
613
714
|
if (kept > 0.0) {
|
|
614
|
-
let clipped = reconClipToBox(reconToYCoCg(history.colour.xyz),
|
|
715
|
+
let clipped = reconClipToBox(reconToYCoCg(history.colour.xyz), low, high);
|
|
615
716
|
mixed = reconAccumulate(accum, reconFromYCoCg(clipped), 1.0, gathered / total);
|
|
616
717
|
}
|
|
617
718
|
let known = min(1.0, total / RECON_CONFIDENT_WEIGHT);
|
|
618
|
-
|
|
719
|
+
out.colour = vec4<f32>(sample * (1.0 - known) + mixed * known, total);
|
|
720
|
+
return out;
|
|
619
721
|
}
|
|
620
722
|
|
|
621
723
|
/*
|
|
@@ -655,8 +757,9 @@ fn reconSharpenAt(ox: i32, oy: i32) -> vec3<f32> {
|
|
|
655
757
|
* doing its own bilinear so the comparison is arithmetic rather than a tolerance on the device's
|
|
656
758
|
* sampler.
|
|
657
759
|
*
|
|
658
|
-
* Two entry points over one set of bindings. `resolveMain` writes
|
|
659
|
-
* history — and `sharpenMain` reads that history back at binding 4
|
|
760
|
+
* Two entry points over one set of bindings. `resolveMain` writes nine floats a pixel — the next
|
|
761
|
+
* history, the next flicker record and the next depth swing — and `sharpenMain` reads that history back at binding 4
|
|
762
|
+
* and writes three.
|
|
660
763
|
*/
|
|
661
764
|
export function reconResolveParityWgsl(): string {
|
|
662
765
|
return /* wgsl */ `
|
|
@@ -668,6 +771,16 @@ export function reconResolveParityWgsl(): string {
|
|
|
668
771
|
${RECON_PARAMS_WGSL}
|
|
669
772
|
@group(0) @binding(5) var<uniform> recon: ReconParams;
|
|
670
773
|
@group(0) @binding(6) var<storage, read_write> output: array<f32>;
|
|
774
|
+
@group(0) @binding(7) var flickerTexture: texture_2d<f32>;
|
|
775
|
+
@group(0) @binding(8) var swingTexture: texture_2d<f32>;
|
|
776
|
+
|
|
777
|
+
fn reconFlicker(x: i32, y: i32) -> vec4<f32> {
|
|
778
|
+
return textureLoad(flickerTexture, vec2<i32>(x, y), 0);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
fn reconSwing(x: i32, y: i32) -> f32 {
|
|
782
|
+
return textureLoad(swingTexture, vec2<i32>(x, y), 0).x;
|
|
783
|
+
}
|
|
671
784
|
|
|
672
785
|
fn reconScene(x: i32, y: i32) -> vec3<f32> {
|
|
673
786
|
return textureLoad(sceneTexture, vec2<i32>(x, y), 0).xyz;
|
|
@@ -725,11 +838,16 @@ ${RECON_RESOLVE_CORE_WGSL}
|
|
|
725
838
|
fn resolveMain(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
726
839
|
if (id.x >= recon.outputSize.x || id.y >= recon.outputSize.y) { return; }
|
|
727
840
|
let value = reconResolvePixel(i32(id.x), i32(id.y));
|
|
728
|
-
let at = (id.y * recon.outputSize.x + id.x) *
|
|
729
|
-
output[at] = value.x;
|
|
730
|
-
output[at + 1u] = value.y;
|
|
731
|
-
output[at + 2u] = value.z;
|
|
732
|
-
output[at + 3u] = value.w;
|
|
841
|
+
let at = (id.y * recon.outputSize.x + id.x) * 9u;
|
|
842
|
+
output[at] = value.colour.x;
|
|
843
|
+
output[at + 1u] = value.colour.y;
|
|
844
|
+
output[at + 2u] = value.colour.z;
|
|
845
|
+
output[at + 3u] = value.colour.w;
|
|
846
|
+
output[at + 4u] = value.record.x;
|
|
847
|
+
output[at + 5u] = value.record.y;
|
|
848
|
+
output[at + 6u] = value.record.z;
|
|
849
|
+
output[at + 7u] = value.record.w;
|
|
850
|
+
output[at + 8u] = value.swing;
|
|
733
851
|
}
|
|
734
852
|
|
|
735
853
|
@compute @workgroup_size(${RECON_WORKGROUP}, ${RECON_WORKGROUP})
|
|
@@ -780,6 +898,12 @@ ${RECON_PARAMS_WGSL}
|
|
|
780
898
|
@group(0) @binding(6) var<uniform> recon: ReconParams;
|
|
781
899
|
@group(0) @binding(7) var resolved: texture_storage_2d<rgba16float, write>;
|
|
782
900
|
/* Not "target": it is a reserved keyword in WGSL, as "meta" was in the cluster cull. */
|
|
901
|
+
/* Last frame's flicker records, and where this frame's go: a pair beside the histories. */
|
|
902
|
+
@group(0) @binding(8) var flickerTexture: texture_2d<f32>;
|
|
903
|
+
@group(0) @binding(9) var flickerOut: texture_storage_2d<rgba16float, write>;
|
|
904
|
+
/* And each pixel's held depth swing, a pair of its own. */
|
|
905
|
+
@group(0) @binding(10) var swingTexture: texture_2d<f32>;
|
|
906
|
+
@group(0) @binding(11) var swingOut: texture_storage_2d<r32float, write>;
|
|
783
907
|
|
|
784
908
|
/* Rows flip here: the reference's row 0 is the bottom one and a texture's is the top. */
|
|
785
909
|
fn reconRenderRow(y: i32) -> i32 {
|
|
@@ -812,6 +936,14 @@ fn reconHistory(x: i32, y: i32) -> vec4<f32> {
|
|
|
812
936
|
return textureLoad(historyTexture, vec2<i32>(x, reconOutputRow(y)), 0);
|
|
813
937
|
}
|
|
814
938
|
|
|
939
|
+
fn reconFlicker(x: i32, y: i32) -> vec4<f32> {
|
|
940
|
+
return textureLoad(flickerTexture, vec2<i32>(x, reconOutputRow(y)), 0);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
fn reconSwing(x: i32, y: i32) -> f32 {
|
|
944
|
+
return textureLoad(swingTexture, vec2<i32>(x, reconOutputRow(y)), 0).x;
|
|
945
|
+
}
|
|
946
|
+
|
|
815
947
|
/* A texel coordinate becomes the uv of its centre, with the row taken from the other end. */
|
|
816
948
|
fn reconSceneBilinear(p: vec2<f32>) -> vec3<f32> {
|
|
817
949
|
let size = vec2<f32>(f32(recon.renderSize.x), f32(recon.renderSize.y));
|
|
@@ -831,7 +963,9 @@ fn resolveMain(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
831
963
|
if (id.x >= recon.outputSize.x || id.y >= recon.outputSize.y) { return; }
|
|
832
964
|
let y = i32(recon.outputSize.y) - 1 - i32(id.y);
|
|
833
965
|
let value = reconResolvePixel(i32(id.x), y);
|
|
834
|
-
textureStore(resolved, vec2<i32>(i32(id.x), i32(id.y)), value);
|
|
966
|
+
textureStore(resolved, vec2<i32>(i32(id.x), i32(id.y)), value.colour);
|
|
967
|
+
textureStore(flickerOut, vec2<i32>(i32(id.x), i32(id.y)), value.record);
|
|
968
|
+
textureStore(swingOut, vec2<i32>(i32(id.x), i32(id.y)), vec4<f32>(value.swing, 0.0, 0.0, 0.0));
|
|
835
969
|
}
|
|
836
970
|
|
|
837
971
|
@compute @workgroup_size(${RECON_WORKGROUP}, ${RECON_WORKGROUP})
|
|
@@ -104,6 +104,16 @@ uniform float uMotionMax;
|
|
|
104
104
|
*/
|
|
105
105
|
uniform sampler2D uAo;
|
|
106
106
|
uniform float uAoStrength;
|
|
107
|
+
/**
|
|
108
|
+
* Where this frame's jitter put the picture the occlusion was measured from, in this pass's uv.
|
|
109
|
+
*
|
|
110
|
+
* The occlusion is measured from the frame's own depth, which a temporal resolve or a
|
|
111
|
+
* reconstruction jittered, and it is applied here to a picture those have already taken the jitter
|
|
112
|
+
* out of — so read at the pixel's own uv it moved by the jitter every frame, and every crevice
|
|
113
|
+
* shimmered by up to half a render texel. Read where the jitter put it, it holds still. Zero for an
|
|
114
|
+
* unjittered frame.
|
|
115
|
+
*/
|
|
116
|
+
uniform vec2 uAoOffset;
|
|
107
117
|
|
|
108
118
|
/**
|
|
109
119
|
* The bloom chain's top level, already thresholded, blurred and summed by its own pass.
|
|
@@ -341,7 +351,7 @@ void main() {
|
|
|
341
351
|
* strength, so the parameter reads as "how much of this occlusion", and 0 is exactly the
|
|
342
352
|
* frame that existed before the effect did.
|
|
343
353
|
*/
|
|
344
|
-
float ao = mix(1.0, textureLod(uAo, vUv, 0.0).r, uAoStrength);
|
|
354
|
+
float ao = mix(1.0, textureLod(uAo, vUv + uAoOffset, 0.0).r, uAoStrength);
|
|
345
355
|
|
|
346
356
|
/*
|
|
347
357
|
* Distance from the centre, corrected so the falloff is a circle on screen rather than
|
|
@@ -30,7 +30,8 @@ precision highp float;
|
|
|
30
30
|
|
|
31
31
|
in vec2 vUv;
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
/* An array of one: every surface texture is an array, and an atlas is read at layer 0. */
|
|
34
|
+
uniform mediump sampler2DArray uAtlas;
|
|
34
35
|
uniform vec3 uColor;
|
|
35
36
|
uniform float uOpacity;
|
|
36
37
|
/* Texels the field spans, from the atlas metrics. Scales the antialiasing band. */
|
|
@@ -52,7 +53,7 @@ float median(vec3 rgb) {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
void main() {
|
|
55
|
-
vec3 sampled = textureLod(uAtlas, vUv, 0.0).rgb;
|
|
56
|
+
vec3 sampled = textureLod(uAtlas, vec3(vUv, 0.0), 0.0).rgb;
|
|
56
57
|
float distance = median(sampled) - 0.5;
|
|
57
58
|
|
|
58
59
|
/*
|
|
@@ -27,7 +27,7 @@ uniform vec2 uViewport;
|
|
|
27
27
|
/** Where the string's baseline-left sits, in pixels from the top left. */
|
|
28
28
|
uniform vec2 uOrigin;
|
|
29
29
|
uniform float uCellSize;
|
|
30
|
-
/**
|
|
30
|
+
/** How far each cell is seen from, head-on; larger flattens the perspective. */
|
|
31
31
|
uniform float uDepth;
|
|
32
32
|
|
|
33
33
|
/** 0 before a character has arrived, 1 once it has settled. */
|
|
@@ -105,14 +105,23 @@ void main() {
|
|
|
105
105
|
vec2 cell = aCell * uCellSize;
|
|
106
106
|
float bob = sin(uTime * 3.1 + aCharIndex * 0.7) * uBob;
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
vec2
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
// The cell's centre in pixels, y down from the top-left corner of the viewport.
|
|
109
|
+
vec2 centre = uOrigin + vec2(cell.x, -cell.y) + vec2(0.0, rise - bob);
|
|
110
|
+
|
|
111
|
+
// A hand-built perspective divide about the cell's own centre, so every cube
|
|
112
|
+
// is seen head-on: its front face at depth zero is exactly its cell, and at
|
|
113
|
+
// rest its sides are behind it wherever on the screen it is drawn.
|
|
114
|
+
//
|
|
115
|
+
// It divided about the viewport's top-left corner until 2026-09-30, which
|
|
116
|
+
// shifted a front face by its distance from that corner times z / uDepth and
|
|
117
|
+
// showed the side it uncovered. Near the corner that was nothing; across the
|
|
118
|
+
// screen it widened every stroke by up to a pixel either side and closed the
|
|
119
|
+
// one-cell gaps a 5x7 face reads by. What this gives up is the extruded look
|
|
120
|
+
// text far from the corner had at rest; what would make it wrong is a caller
|
|
121
|
+
// wanting that look, which is then one offset for every cell, not a position.
|
|
122
|
+
float invDepth = uDepth / max(0.001, uDepth - local3.z);
|
|
123
|
+
vec2 screen = centre + local3.xy * invDepth;
|
|
124
|
+
vec2 ndc = (screen / uViewport) * 2.0 - 1.0;
|
|
116
125
|
// Depth from the cube's own extrusion, mapped into a narrow slice of the
|
|
117
126
|
// range so a message sorts against itself without ever reaching the scene.
|
|
118
127
|
float depth = clamp(-local3.z / max(1.0, uCellSize * 2.0), -1.0, 1.0) * 0.4;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each layer of a surface array does beyond its picture: rooms behind its windows, windows that
|
|
3
|
+
* light by night, wear, animation, and whether rain wets it.
|
|
4
|
+
*
|
|
5
|
+
* **Per layer, because a city region is one mesh.** Its facades are told apart by the layer each
|
|
6
|
+
* vertex carries (`MeshData.layers`), so an effect's parameters cannot be a per-draw uniform — two facades of one
|
|
7
|
+
* draw would share them. They are a table beside the array, six texels a layer, read at the vertex's
|
|
8
|
+
* layer with `texelFetch`, and they bind whenever the array does.
|
|
9
|
+
*
|
|
10
|
+
* **Everything off is all zeros**, so a layer a scene names no effect for — and every layer of an
|
|
11
|
+
* array given no table — costs the shader one size query and nothing else.
|
|
12
|
+
*
|
|
13
|
+
* Layout, six RGBA texels a layer, half floats on the GPU (every whole number here is under 2,048,
|
|
14
|
+
* which a half holds exactly):
|
|
15
|
+
* - 0: window cells across and up, glass mask (1: glass where the albedo's alpha is 0), seed;
|
|
16
|
+
* - 1: room layer (0: none; the layer plus one), room depth (0 to 1 of a cell), share of rooms lit,
|
|
17
|
+
* window glow;
|
|
18
|
+
* - 2: window colour, then 1 where rain leaves the surface dry;
|
|
19
|
+
* - 3: dust, grime, streaks, the metres over which wear fades out with distance (0: never);
|
|
20
|
+
* - 4: scroll along u and v in repeats a second, pulse rate in hertz, pulse depth;
|
|
21
|
+
* - 5: flicker, flipbook frames, flipbook frames a second, the metres past which emission fades
|
|
22
|
+
* (0: never).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Texels a layer takes in the table. */
|
|
26
|
+
export const SURFACE_EFFECT_TEXELS = 6;
|
|
27
|
+
|
|
28
|
+
export interface SurfaceLayerEffect {
|
|
29
|
+
/**
|
|
30
|
+
* Windows across and up one repeat of the layer, for everything that works window by window:
|
|
31
|
+
* rooms, lit windows. With `glass`, a window is a hole in the albedo — glass where its alpha is 0,
|
|
32
|
+
* wall where it is 1 — which is how a facade is drawn, and the other way round would lose the
|
|
33
|
+
* wall's colour, which does not survive an upload at alpha 0.
|
|
34
|
+
*/
|
|
35
|
+
readonly windows?: {
|
|
36
|
+
readonly cells: readonly [number, number];
|
|
37
|
+
readonly glass?: boolean;
|
|
38
|
+
/** Varies which windows light and which rooms show, between facades wearing one layer. */
|
|
39
|
+
readonly seed?: number;
|
|
40
|
+
/** How bright a lit window glows, 0 for none. */
|
|
41
|
+
readonly glow?: number;
|
|
42
|
+
/**
|
|
43
|
+
* The light a window shows where there is no room behind it. **A vertex that names an emissive
|
|
44
|
+
* colour overrides it** — the window's colour, or a tint on the room where there is one — which
|
|
45
|
+
* is how buildings sharing a layer each keep their own light: a window layer's vertex emissive
|
|
46
|
+
* is otherwise unused, so the colour there, strength folded in, is free to say it.
|
|
47
|
+
*/
|
|
48
|
+
readonly colour?: readonly [number, number, number];
|
|
49
|
+
};
|
|
50
|
+
/** A room seen in parallax behind each window, drawn from another layer of the same array. */
|
|
51
|
+
readonly interior?: {
|
|
52
|
+
readonly roomLayer: number;
|
|
53
|
+
/** How deep a room is, as a share of a window's width. 0.5 unless stated. */
|
|
54
|
+
readonly depth?: number;
|
|
55
|
+
/** The share of rooms lit, 0 to 1. 1 unless stated. */
|
|
56
|
+
readonly lit?: number;
|
|
57
|
+
};
|
|
58
|
+
/** Procedural wear, each 0 to 1, fading out past `fade` metres (never, unless stated). */
|
|
59
|
+
readonly wear?: {
|
|
60
|
+
readonly dust?: number;
|
|
61
|
+
readonly grime?: number;
|
|
62
|
+
readonly streaks?: number;
|
|
63
|
+
readonly fade?: number;
|
|
64
|
+
};
|
|
65
|
+
/** Motion on the caller's clock (`Environment.surfaceTime`), so a held clock holds it. */
|
|
66
|
+
readonly animation?: {
|
|
67
|
+
/** Repeats a second along u and v. */
|
|
68
|
+
readonly scroll?: readonly [number, number];
|
|
69
|
+
/** Emission rising and falling: hertz, and how far it falls, 0 to 1. */
|
|
70
|
+
readonly pulse?: readonly [number, number];
|
|
71
|
+
/** Emission stuttering, 0 to 1. */
|
|
72
|
+
readonly flicker?: number;
|
|
73
|
+
/** Successive layers as frames: how many from this one, and how fast. */
|
|
74
|
+
readonly flipbook?: readonly [number, number];
|
|
75
|
+
/** Metres past which emission fades out, 0 for never. */
|
|
76
|
+
readonly fade?: number;
|
|
77
|
+
};
|
|
78
|
+
/** Rain leaves it dry: a covered walkway, an interior floor. */
|
|
79
|
+
readonly dry?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The table for `effects`, one entry a layer (`undefined` for a layer with none), as floats: six
|
|
84
|
+
* RGBA texels a layer, a row a layer.
|
|
85
|
+
*/
|
|
86
|
+
export function packSurfaceEffects(
|
|
87
|
+
effects: readonly (SurfaceLayerEffect | undefined)[],
|
|
88
|
+
): Float32Array {
|
|
89
|
+
const out = new Float32Array(effects.length * SURFACE_EFFECT_TEXELS * 4);
|
|
90
|
+
effects.forEach((effect, layer) => {
|
|
91
|
+
if (effect === undefined) return;
|
|
92
|
+
const at = layer * SURFACE_EFFECT_TEXELS * 4;
|
|
93
|
+
const windows = effect.windows;
|
|
94
|
+
if (windows !== undefined) {
|
|
95
|
+
out[at] = windows.cells[0];
|
|
96
|
+
out[at + 1] = windows.cells[1];
|
|
97
|
+
out[at + 2] = windows.glass === true ? 1 : 0;
|
|
98
|
+
out[at + 3] = windows.seed ?? 0;
|
|
99
|
+
out[at + 7] = windows.glow ?? 0;
|
|
100
|
+
const colour = windows.colour ?? [1, 0.82, 0.55];
|
|
101
|
+
out[at + 8] = colour[0];
|
|
102
|
+
out[at + 9] = colour[1];
|
|
103
|
+
out[at + 10] = colour[2];
|
|
104
|
+
}
|
|
105
|
+
const interior = effect.interior;
|
|
106
|
+
if (interior !== undefined) {
|
|
107
|
+
out[at + 4] = interior.roomLayer + 1;
|
|
108
|
+
out[at + 5] = interior.depth ?? 0.5;
|
|
109
|
+
out[at + 6] = interior.lit ?? 1;
|
|
110
|
+
}
|
|
111
|
+
out[at + 11] = effect.dry === true ? 1 : 0;
|
|
112
|
+
const wear = effect.wear;
|
|
113
|
+
if (wear !== undefined) {
|
|
114
|
+
out[at + 12] = wear.dust ?? 0;
|
|
115
|
+
out[at + 13] = wear.grime ?? 0;
|
|
116
|
+
out[at + 14] = wear.streaks ?? 0;
|
|
117
|
+
out[at + 15] = wear.fade ?? 0;
|
|
118
|
+
}
|
|
119
|
+
const animation = effect.animation;
|
|
120
|
+
if (animation !== undefined) {
|
|
121
|
+
out[at + 16] = animation.scroll?.[0] ?? 0;
|
|
122
|
+
out[at + 17] = animation.scroll?.[1] ?? 0;
|
|
123
|
+
out[at + 18] = animation.pulse?.[0] ?? 0;
|
|
124
|
+
out[at + 19] = animation.pulse?.[1] ?? 0;
|
|
125
|
+
out[at + 20] = animation.flicker ?? 0;
|
|
126
|
+
out[at + 21] = animation.flipbook?.[0] ?? 0;
|
|
127
|
+
out[at + 22] = animation.flipbook?.[1] ?? 0;
|
|
128
|
+
out[at + 23] = animation.fade ?? 0;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scene's surface clock and weather, decided once for both backends: the four numbers the lit
|
|
3
|
+
* stage reads as `uSurfaceScene`. Each backend only uploads them, so the two cannot disagree about
|
|
4
|
+
* what a wetness of 1.4 means — it means 1, as a share of lit windows past one means all of them.
|
|
5
|
+
*/
|
|
6
|
+
import type { Environment } from './backend/webgl2/renderer.ts';
|
|
7
|
+
|
|
8
|
+
const unit = (value: number | undefined): number => Math.min(1, Math.max(0, value ?? 0));
|
|
9
|
+
|
|
10
|
+
/** Fill `out`: seconds on the caller's clock, wetness, the lit share, the late share. */
|
|
11
|
+
export function resolveSurfaceScene(env: Environment, out: Float32Array): void {
|
|
12
|
+
out[0] = env.surfaceTime ?? 0;
|
|
13
|
+
out[1] = unit(env.wetness);
|
|
14
|
+
out[2] = unit(env.litWindows);
|
|
15
|
+
out[3] = unit(env.lateWindows);
|
|
16
|
+
}
|