reze-engine 0.43.0 → 0.50.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.
Files changed (140) hide show
  1. package/README.md +53 -429
  2. package/dist/animation.d.ts +26 -0
  3. package/dist/animation.d.ts.map +1 -1
  4. package/dist/animation.js +42 -0
  5. package/dist/camera.d.ts +3 -0
  6. package/dist/camera.d.ts.map +1 -1
  7. package/dist/camera.js +33 -8
  8. package/dist/engine.d.ts +841 -53
  9. package/dist/engine.d.ts.map +1 -1
  10. package/dist/engine.js +3498 -451
  11. package/dist/graph/registry.d.ts +2 -2
  12. package/dist/graph/registry.d.ts.map +1 -1
  13. package/dist/graph/registry.js +1 -1
  14. package/dist/graph/slots.d.ts +0 -1
  15. package/dist/graph/slots.d.ts.map +1 -1
  16. package/dist/graph/slots.js +37 -9
  17. package/dist/hdr.d.ts +18 -0
  18. package/dist/hdr.d.ts.map +1 -0
  19. package/dist/hdr.js +162 -0
  20. package/dist/ibl.d.ts +19 -0
  21. package/dist/ibl.d.ts.map +1 -0
  22. package/dist/ibl.js +113 -0
  23. package/dist/ik-solver.d.ts +2 -1
  24. package/dist/ik-solver.d.ts.map +1 -1
  25. package/dist/index.d.ts +5 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +10 -0
  28. package/dist/math.d.ts +20 -1
  29. package/dist/math.d.ts.map +1 -1
  30. package/dist/math.js +23 -16
  31. package/dist/midi-loader.d.ts +10 -0
  32. package/dist/midi-loader.d.ts.map +1 -0
  33. package/dist/midi-loader.js +247 -0
  34. package/dist/model.d.ts +19 -14
  35. package/dist/model.d.ts.map +1 -1
  36. package/dist/model.js +31 -2
  37. package/dist/param-track.d.ts +48 -0
  38. package/dist/param-track.d.ts.map +1 -0
  39. package/dist/param-track.js +80 -0
  40. package/dist/physics/types.d.ts.map +1 -1
  41. package/dist/physics/types.js +3 -0
  42. package/dist/reflection.d.ts +27 -0
  43. package/dist/reflection.d.ts.map +1 -0
  44. package/dist/reflection.js +93 -0
  45. package/dist/shaders/anchor-table.d.ts +56 -0
  46. package/dist/shaders/anchor-table.d.ts.map +1 -0
  47. package/dist/shaders/anchor-table.js +128 -0
  48. package/dist/shaders/cast-api.d.ts +2 -0
  49. package/dist/shaders/cast-api.d.ts.map +1 -0
  50. package/dist/shaders/cast-api.js +121 -0
  51. package/dist/shaders/cast-layout.d.ts +21 -0
  52. package/dist/shaders/cast-layout.d.ts.map +1 -0
  53. package/dist/shaders/cast-layout.js +20 -0
  54. package/dist/shaders/lights.d.ts +79 -0
  55. package/dist/shaders/lights.d.ts.map +1 -0
  56. package/dist/shaders/lights.js +269 -0
  57. package/dist/shaders/lyrics-api.d.ts +39 -0
  58. package/dist/shaders/lyrics-api.d.ts.map +1 -0
  59. package/dist/shaders/lyrics-api.js +187 -0
  60. package/dist/shaders/materials/common.d.ts +2 -4
  61. package/dist/shaders/materials/common.d.ts.map +1 -1
  62. package/dist/shaders/materials/common.js +87 -35
  63. package/dist/shaders/midi-api.d.ts +10 -0
  64. package/dist/shaders/midi-api.d.ts.map +1 -0
  65. package/dist/shaders/midi-api.js +114 -0
  66. package/dist/shaders/passes/composite.d.ts +28 -22
  67. package/dist/shaders/passes/composite.d.ts.map +1 -1
  68. package/dist/shaders/passes/composite.js +165 -138
  69. package/dist/shaders/passes/cull.d.ts +2 -0
  70. package/dist/shaders/passes/cull.d.ts.map +1 -0
  71. package/dist/shaders/passes/cull.js +138 -0
  72. package/dist/shaders/passes/field-blit.d.ts +26 -0
  73. package/dist/shaders/passes/field-blit.d.ts.map +1 -0
  74. package/dist/shaders/passes/field-blit.js +65 -0
  75. package/dist/shaders/passes/grid.d.ts +31 -0
  76. package/dist/shaders/passes/grid.d.ts.map +1 -0
  77. package/dist/shaders/passes/grid.js +169 -0
  78. package/dist/shaders/passes/ground.d.ts +13 -1
  79. package/dist/shaders/passes/ground.d.ts.map +1 -1
  80. package/dist/shaders/passes/ground.js +170 -25
  81. package/dist/shaders/passes/hosted-api.d.ts +57 -0
  82. package/dist/shaders/passes/hosted-api.d.ts.map +1 -0
  83. package/dist/shaders/passes/hosted-api.js +166 -0
  84. package/dist/shaders/passes/id-debug.d.ts +28 -0
  85. package/dist/shaders/passes/id-debug.d.ts.map +1 -0
  86. package/dist/shaders/passes/id-debug.js +74 -0
  87. package/dist/shaders/passes/particles.d.ts +10 -2
  88. package/dist/shaders/passes/particles.d.ts.map +1 -1
  89. package/dist/shaders/passes/particles.js +37 -109
  90. package/dist/shaders/passes/scene-contract.d.ts +128 -0
  91. package/dist/shaders/passes/scene-contract.d.ts.map +1 -0
  92. package/dist/shaders/passes/scene-contract.js +207 -0
  93. package/dist/shaders/passes/sim.d.ts +34 -0
  94. package/dist/shaders/passes/sim.d.ts.map +1 -0
  95. package/dist/shaders/passes/sim.js +169 -0
  96. package/dist/shaders/passes/trails.d.ts +14 -2
  97. package/dist/shaders/passes/trails.d.ts.map +1 -1
  98. package/dist/shaders/passes/trails.js +55 -90
  99. package/dist/shaders/score-api.d.ts +10 -0
  100. package/dist/shaders/score-api.d.ts.map +1 -0
  101. package/dist/shaders/score-api.js +114 -0
  102. package/dist/shadow-cascades.d.ts +45 -0
  103. package/dist/shadow-cascades.d.ts.map +1 -0
  104. package/dist/shadow-cascades.js +70 -0
  105. package/dist/vmd-loader.d.ts +3 -2
  106. package/dist/vmd-loader.d.ts.map +1 -1
  107. package/package.json +1 -1
  108. package/src/animation.ts +41 -0
  109. package/src/camera.ts +31 -8
  110. package/src/engine.ts +4015 -556
  111. package/src/graph/registry.ts +2 -2
  112. package/src/graph/slots.ts +37 -9
  113. package/src/hdr.ts +156 -0
  114. package/src/ibl.ts +115 -0
  115. package/src/ik-solver.ts +1 -1
  116. package/src/index.ts +12 -0
  117. package/src/math.ts +23 -17
  118. package/src/midi-loader.ts +246 -0
  119. package/src/model.ts +34 -4
  120. package/src/param-track.ts +83 -0
  121. package/src/physics/types.ts +4 -1
  122. package/src/reflection.ts +94 -0
  123. package/src/shaders/anchor-table.ts +147 -0
  124. package/src/shaders/cast-api.ts +123 -0
  125. package/src/shaders/cast-layout.ts +20 -0
  126. package/src/shaders/lights.ts +280 -0
  127. package/src/shaders/lyrics-api.ts +202 -0
  128. package/src/shaders/materials/common.ts +89 -35
  129. package/src/shaders/midi-api.ts +116 -0
  130. package/src/shaders/passes/composite.ts +182 -139
  131. package/src/shaders/passes/cull.ts +139 -0
  132. package/src/shaders/passes/grid.ts +178 -0
  133. package/src/shaders/passes/ground.ts +172 -25
  134. package/src/shaders/passes/hosted-api.ts +171 -0
  135. package/src/shaders/passes/id-debug.ts +75 -0
  136. package/src/shaders/passes/particles.ts +54 -112
  137. package/src/shaders/passes/scene-contract.ts +266 -0
  138. package/src/shaders/passes/trails.ts +77 -93
  139. package/src/shadow-cascades.ts +97 -0
  140. package/src/vmd-loader.ts +2 -2
@@ -0,0 +1,166 @@
1
+ // The half of the effect API whose VALUES are module-local.
2
+ //
3
+ // An effect file is spliced WHOLE into every module it has a mount in. A file
4
+ // with a trail and a lightEmit compiles its trail code inside the light module,
5
+ // where it never runs but still has to resolve — so `rzTime()` has to exist
6
+ // there even though a compute pass that writes lamp positions has no obvious
7
+ // clock, and `rzFalloff()` has to exist there even though nothing calls it.
8
+ //
9
+ // EFFECT_SCENE_API (composite.ts) is the other half: names that mean the same
10
+ // thing everywhere, written once. These are the ones that cannot be, because
11
+ // each module keeps its clock and its viewport in a different uniform. So they
12
+ // are written once HERE and the expression is the parameter — which is the
13
+ // same trick the grid pass already used for one of them, generalised to all.
14
+ //
15
+ // The rule this file exists to make keepable: a hosting module includes every
16
+ // block below. Not the ones its own mount happens to need — every one, because
17
+ // what lands in it is the author's whole file.
18
+ //
19
+ // EXCEPT that EFFECT_SCENE_API already carries EFFECT_MATH_API and the Particle
20
+ // struct, so the three modules built on it take those two by inheritance and
21
+ // must not add them again. tests/hosted-api.test.mjs fails on a name declared
22
+ // twice for exactly this reason — it is the mistake sharing blocks invites.
23
+ /**
24
+ * The particle record, laid out by hand — declared in EVERY hosting module.
25
+ *
26
+ * Not only in the particle ones: `fn particleStep(p: Particle)` is part of the
27
+ * author's file, so the trail, field and lightEmit modules compile that
28
+ * signature as dead code and a missing struct is a compile error on a function
29
+ * the author was right to write. The trail module used to carry a hand-copied
30
+ * duplicate for exactly this reason.
31
+ *
32
+ * `age` and `life` sit in the padding that vec3f alignment would waste anyway
33
+ * (a vec3f occupies 12 bytes but aligns the next field to 16), so the struct is
34
+ * 48 bytes rather than the 64 a naive ordering costs. At 4096 particles that is
35
+ * 192KB instead of 256KB, and it is read every frame by both stages.
36
+ *
37
+ * `life <= 0` means "not alive" and is what the pool checks to recycle a slot,
38
+ * so a freshly zeroed buffer is entirely dead and every particle is born on the
39
+ * first step rather than needing a separate seeding pass.
40
+ *
41
+ * RzLight is the same kind of thing and lives in lights.ts, because it is that
42
+ * module's own type; every hosting module splices it for this same reason.
43
+ */
44
+ export const PARTICLE_STRUCT_WGSL = /* wgsl */ `
45
+ struct Particle {
46
+ pos: vec3f,
47
+ age: f32,
48
+ vel: vec3f,
49
+ life: f32,
50
+ size: f32,
51
+ rot: f32,
52
+ seed: f32,
53
+ // Aspect along the direction of travel. 1 or less is a square billboard; a
54
+ // raindrop is 10 or 20. Zero-initialised, so an effect that never sets it gets
55
+ // the square it expects.
56
+ stretch: f32,
57
+ }
58
+ `;
59
+ /**
60
+ * `rzTime`/`rzDt`, from whatever the module calls its clock.
61
+ *
62
+ * A module with no clock of its own passes a literal. Zero dt is honest there:
63
+ * nothing spliced into it is being stepped, so a kernel that integrates would
64
+ * integrate by nothing rather than by a plausible-looking wrong number.
65
+ */
66
+ export function clockApi(timeExpr, dtExpr) {
67
+ return /* wgsl */ `
68
+ fn rzTime() -> f32 { return ${timeExpr}; }
69
+ fn rzDt() -> f32 { return ${dtExpr}; }
70
+ `;
71
+ }
72
+ /** `rzViewportHeight` — the render target's height in pixels, which is what a
73
+ * point size in world units has to be divided by to become a pixel radius. */
74
+ export function viewportApi(heightExpr) {
75
+ return /* wgsl */ `
76
+ fn rzViewportHeight() -> f32 { return ${heightExpr}; }
77
+ `;
78
+ }
79
+ /**
80
+ * The one cast number that is PER EFFECT rather than per engine.
81
+ *
82
+ * Author-visible, and published effects loop over it, so its meaning is pinned:
83
+ * how many of THIS effect's anchors asked for a trail. Deliberately not the
84
+ * anchor address space — that is RZ_MAX_ANCHORS in CAST_API, and the two being
85
+ * one number was the old trail bug. Everything else about the cast's shape is a
86
+ * constant and lives there.
87
+ */
88
+ export function trailSlotsApi(trailCount) {
89
+ return /* wgsl */ `
90
+ const RZ_TRAIL_SLOTS: i32 = ${trailCount};
91
+ `;
92
+ }
93
+ /**
94
+ * Pure math — no bindings, no uniforms, identical in every module.
95
+ *
96
+ * These were duplicated in the particle and trail modules, byte for byte in the
97
+ * case of rzFalloff and to a `+ vec3f(0.0)` in the case of rzValueNoise. Two
98
+ * copies of a noise function is one bad merge away from an effect that looks
99
+ * different depending on which mount drew it, which is not a bug anyone would
100
+ * think to look for.
101
+ *
102
+ * EVERY hosting module includes this and none defines any of these itself, so
103
+ * "which module am I in" can never change what a helper returns.
104
+ */
105
+ export const EFFECT_MATH_API = /* wgsl */ `
106
+ fn rzHash11(x: f32) -> f32 {
107
+ var p = fract(x * 0.1031);
108
+ p = p * (p + 33.33);
109
+ return fract(p * (p + p));
110
+ }
111
+ fn rzHash21(p: vec2f) -> f32 {
112
+ var p3 = fract(vec3f(p.x, p.y, p.x) * 0.1031);
113
+ p3 = p3 + dot(p3, p3.yzx + 33.33);
114
+ return fract((p3.x + p3.y) * p3.z);
115
+ }
116
+ /** Three independent randoms from one seed — the usual need when spawning. */
117
+ fn rzHash13(x: f32) -> vec3f {
118
+ return vec3f(rzHash11(x), rzHash11(x + 17.13), rzHash11(x + 41.71));
119
+ }
120
+ /**
121
+ * Compact-support falloff: 1 at the centre, exactly 0 at r, smooth between.
122
+ *
123
+ * It reaches exactly zero rather than merely getting small, because a glow that
124
+ * never quite ends has to be culled somewhere, and culling it wherever it looks
125
+ * close enough is what put a visible hard edge on the first halo effect.
126
+ */
127
+ fn rzFalloff(d: f32, r: f32) -> f32 {
128
+ let x = clamp(d / max(r, 1e-6), 0.0, 1.0);
129
+ let f = 1.0 - x;
130
+ return f * f * f;
131
+ }
132
+ fn rzHash31(p: vec3f) -> f32 {
133
+ var p3 = fract(p * 0.1031);
134
+ p3 = p3 + dot(p3, p3.zyx + 31.32);
135
+ return fract((p3.x + p3.y) * p3.z);
136
+ }
137
+ fn rzValueNoise(p: vec3f) -> f32 {
138
+ let i = floor(p);
139
+ let f = fract(p);
140
+ let u = f * f * (3.0 - 2.0 * f);
141
+ let n000 = rzHash31(i);
142
+ let n100 = rzHash31(i + vec3f(1.0, 0.0, 0.0));
143
+ let n010 = rzHash31(i + vec3f(0.0, 1.0, 0.0));
144
+ let n110 = rzHash31(i + vec3f(1.0, 1.0, 0.0));
145
+ let n001 = rzHash31(i + vec3f(0.0, 0.0, 1.0));
146
+ let n101 = rzHash31(i + vec3f(1.0, 0.0, 1.0));
147
+ let n011 = rzHash31(i + vec3f(0.0, 1.0, 1.0));
148
+ let n111 = rzHash31(i + vec3f(1.0, 1.0, 1.0));
149
+ let x00 = mix(n000, n100, u.x);
150
+ let x10 = mix(n010, n110, u.x);
151
+ let x01 = mix(n001, n101, u.x);
152
+ let x11 = mix(n011, n111, u.x);
153
+ return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);
154
+ }
155
+ /** Divergence-free flow — the field a wisp of smoke follows without a solver. */
156
+ fn rzCurlNoise(p: vec3f) -> vec3f {
157
+ let e = 0.1;
158
+ let dx = vec3f(e, 0.0, 0.0);
159
+ let dy = vec3f(0.0, e, 0.0);
160
+ let dz = vec3f(0.0, 0.0, e);
161
+ let x0 = rzValueNoise(p - dx); let x1 = rzValueNoise(p + dx);
162
+ let y0 = rzValueNoise(p - dy); let y1 = rzValueNoise(p + dy);
163
+ let z0 = rzValueNoise(p - dz); let z1 = rzValueNoise(p + dz);
164
+ return normalize(vec3f((y1 - y0) - (z1 - z0), (z1 - z0) - (x1 - x0), (x1 - x0) - (y1 - y0)) + vec3f(1e-6));
165
+ }
166
+ `;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The id attachment, drawn so a person can look at it.
3
+ *
4
+ * The id buffer is correct or incorrect in ways nothing else in the frame can
5
+ * show: with no consumer, a perfect id buffer and a completely wrong one
6
+ * produce exactly the same picture. This pass exists so that "it works" is
7
+ * something seen rather than inferred.
8
+ *
9
+ * WHAT CORRECT LOOKS LIKE, and what each failure looks like instead:
10
+ *
11
+ * - Every material is ONE FLAT COLOUR, with hard edges. Ids are names, not
12
+ * quantities: a gradient anywhere, or fringing along an edge, means
13
+ * something is interpolating or resolving them — the two failures the
14
+ * no-resolve/no-blend rules exist to prevent.
15
+ * - Parts differ from each other. A whole model in one colour means the
16
+ * material index never made it into the uniform.
17
+ * - The FLOOR is a fixed light grey, the reserved id, and never shares a
18
+ * colour with a body part.
19
+ * - Where nothing was drawn is BLACK — id 0, the reserved nothing, which is
20
+ * also what the attachment clears to. Black *over geometry* means that draw
21
+ * wrote no id; colour where the sky should be means the clear is wrong.
22
+ *
23
+ * Colours come from a hash of the pair, so neighbouring materials land on
24
+ * unrelated hues rather than adjacent shades of one — the point is telling them
25
+ * apart, not ordering them.
26
+ */
27
+ export declare const ID_DEBUG_SHADER_WGSL = "\n// Multisampled and NEVER resolved, so it is read the way it is written: one\n// sample, by texel. Sample 0 is what every consumer of this attachment reads.\n@group(0) @binding(0) var idTex: texture_multisampled_2d<u32>;\n\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {\n let x = f32((vi & 1u) << 2u) - 1.0;\n let y = f32((vi & 2u) << 1u) - 1.0;\n return vec4f(x, y, 0.0, 1.0);\n}\n\n/** Integer hash \u2192 0..1. Distinct inputs land on unrelated outputs, which is the\n * whole requirement: adjacent ids must not read as adjacent colours. */\nfn hashId(x: u32) -> f32 {\n var h = x * 747796405u + 2891336453u;\n h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u;\n return f32((h >> 22u) ^ h) / 4294967295.0;\n}\n\n@fragment fn fs(@builtin(position) fragCoord: vec4f) -> @location(0) vec4f {\n let ids = textureLoad(idTex, vec2<i32>(fragCoord.xy), 0);\n let materialId = ids.x;\n let objectId = ids.y;\n\n // Nothing drawn here. Black, and it must cover exactly the empty space.\n if (materialId == 0u && objectId == 0u) {\n return vec4f(0.0, 0.0, 0.0, 1.0);\n }\n // The floor, at the top of the range. Flat light grey so it is unmistakable\n // and cannot be confused with a hashed colour.\n if (materialId == 65535u) {\n return vec4f(0.72, 0.72, 0.75, 1.0);\n }\n // Hue from the material, brightness from the object, so two models wearing\n // the same material index still read apart.\n let h = hashId(materialId * 1973u + 9277u);\n let shade = 0.55 + 0.45 * hashId(objectId * 6151u + 1u);\n // Cheap hue ramp: three offset cosines. Saturated on purpose \u2014 this is a\n // diagnostic, not a look.\n let rgb = vec3f(\n 0.5 + 0.5 * cos(6.28318 * (h + 0.00)),\n 0.5 + 0.5 * cos(6.28318 * (h + 0.33)),\n 0.5 + 0.5 * cos(6.28318 * (h + 0.67)),\n );\n return vec4f(rgb * shade, 1.0);\n}\n";
28
+ //# sourceMappingURL=id-debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-debug.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/id-debug.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,oBAAoB,20DA8ChC,CAAA"}
@@ -0,0 +1,74 @@
1
+ import { GROUND_MATERIAL_ID } from "./ground";
2
+ /**
3
+ * The id attachment, drawn so a person can look at it.
4
+ *
5
+ * The id buffer is correct or incorrect in ways nothing else in the frame can
6
+ * show: with no consumer, a perfect id buffer and a completely wrong one
7
+ * produce exactly the same picture. This pass exists so that "it works" is
8
+ * something seen rather than inferred.
9
+ *
10
+ * WHAT CORRECT LOOKS LIKE, and what each failure looks like instead:
11
+ *
12
+ * - Every material is ONE FLAT COLOUR, with hard edges. Ids are names, not
13
+ * quantities: a gradient anywhere, or fringing along an edge, means
14
+ * something is interpolating or resolving them — the two failures the
15
+ * no-resolve/no-blend rules exist to prevent.
16
+ * - Parts differ from each other. A whole model in one colour means the
17
+ * material index never made it into the uniform.
18
+ * - The FLOOR is a fixed light grey, the reserved id, and never shares a
19
+ * colour with a body part.
20
+ * - Where nothing was drawn is BLACK — id 0, the reserved nothing, which is
21
+ * also what the attachment clears to. Black *over geometry* means that draw
22
+ * wrote no id; colour where the sky should be means the clear is wrong.
23
+ *
24
+ * Colours come from a hash of the pair, so neighbouring materials land on
25
+ * unrelated hues rather than adjacent shades of one — the point is telling them
26
+ * apart, not ordering them.
27
+ */
28
+ export const ID_DEBUG_SHADER_WGSL = /* wgsl */ `
29
+ // Multisampled and NEVER resolved, so it is read the way it is written: one
30
+ // sample, by texel. Sample 0 is what every consumer of this attachment reads.
31
+ @group(0) @binding(0) var idTex: texture_multisampled_2d<u32>;
32
+
33
+ @vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
34
+ let x = f32((vi & 1u) << 2u) - 1.0;
35
+ let y = f32((vi & 2u) << 1u) - 1.0;
36
+ return vec4f(x, y, 0.0, 1.0);
37
+ }
38
+
39
+ /** Integer hash → 0..1. Distinct inputs land on unrelated outputs, which is the
40
+ * whole requirement: adjacent ids must not read as adjacent colours. */
41
+ fn hashId(x: u32) -> f32 {
42
+ var h = x * 747796405u + 2891336453u;
43
+ h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u;
44
+ return f32((h >> 22u) ^ h) / 4294967295.0;
45
+ }
46
+
47
+ @fragment fn fs(@builtin(position) fragCoord: vec4f) -> @location(0) vec4f {
48
+ let ids = textureLoad(idTex, vec2<i32>(fragCoord.xy), 0);
49
+ let materialId = ids.x;
50
+ let objectId = ids.y;
51
+
52
+ // Nothing drawn here. Black, and it must cover exactly the empty space.
53
+ if (materialId == 0u && objectId == 0u) {
54
+ return vec4f(0.0, 0.0, 0.0, 1.0);
55
+ }
56
+ // The floor, at the top of the range. Flat light grey so it is unmistakable
57
+ // and cannot be confused with a hashed colour.
58
+ if (materialId == ${GROUND_MATERIAL_ID}u) {
59
+ return vec4f(0.72, 0.72, 0.75, 1.0);
60
+ }
61
+ // Hue from the material, brightness from the object, so two models wearing
62
+ // the same material index still read apart.
63
+ let h = hashId(materialId * 1973u + 9277u);
64
+ let shade = 0.55 + 0.45 * hashId(objectId * 6151u + 1u);
65
+ // Cheap hue ramp: three offset cosines. Saturated on purpose — this is a
66
+ // diagnostic, not a look.
67
+ let rgb = vec3f(
68
+ 0.5 + 0.5 * cos(6.28318 * (h + 0.00)),
69
+ 0.5 + 0.5 * cos(6.28318 * (h + 0.33)),
70
+ 0.5 + 0.5 * cos(6.28318 * (h + 0.67)),
71
+ );
72
+ return vec4f(rgb * shade, 1.0);
73
+ }
74
+ `;
@@ -4,11 +4,18 @@ export type CastLayout = {
4
4
  samples: number;
5
5
  base: number;
6
6
  trailBase: number;
7
+ /** The scene's anchor CAP — the address space, not how many asked for trails. */
7
8
  slots: number;
9
+ /** How many of this effect's anchors asked for a trail. AUTHOR-VISIBLE as
10
+ * RZ_TRAIL_SLOTS: published effects loop over it, so its meaning is pinned
11
+ * even though the address space it used to share is now a separate number. */
12
+ trailCount: number;
13
+ /** This effect's local slot → scene slot. Identity while one effect exists. */
14
+ alias: number[];
8
15
  };
9
16
  /** How the author's quads combine with the scene. */
10
- export type ParticleBlend = "alpha" | "additive";
11
- export type ParticleSource = {
17
+ type ParticleBlend = "alpha" | "additive";
18
+ type ParticleSource = {
12
19
  /** The author's WGSL verbatim. */
13
20
  wgsl: string;
14
21
  /** Simultaneous particles. Fixed at install; the pool recycles rather than grows. */
@@ -55,4 +62,5 @@ export declare function buildParticleComputeShader(src: ParticleSource, cast: Ca
55
62
  * readback every frame to save vertices the rasteriser was going to reject anyway.
56
63
  */
57
64
  export declare function buildParticleRenderShader(src: ParticleSource, cast: CastLayout): string;
65
+ export {};
58
66
  //# sourceMappingURL=particles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"particles.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/particles.ts"],"names":[],"mappings":"AAkBA,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAmC9G,qDAAqD;AACrD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,aAAa,CAAA;IACpB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,KAAK,CAAA;AAiIjC,oDAAoD;AACpD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAE9D;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAMlG;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAwCxF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CA2GvF"}
1
+ {"version":3,"file":"particles.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/particles.ts"],"names":[],"mappings":"AAwBA,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb;;mFAE+E;IAC/E,UAAU,EAAE,MAAM,CAAA;IAClB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAiCD,qDAAqD;AACrD,KAAK,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEzC,KAAK,cAAc,GAAG;IACpB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,aAAa,CAAA;IACpB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,KAAK,CAAA;AAkDjC,oDAAoD;AACpD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAE9D;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAMlG;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CA0CxF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CA6GvF"}
@@ -1,4 +1,10 @@
1
+ import { RZ_LIGHT_STRUCT_WGSL } from "../lights";
1
2
  import { audioApi } from "../audio-api";
3
+ import { lyricsApi } from "../lyrics-api";
4
+ import { anchorAliasWgsl } from "../anchor-table";
5
+ import { midiApi } from "../midi-api";
6
+ import { CAST_API } from "../cast-api";
7
+ import { clockApi, EFFECT_MATH_API, PARTICLE_STRUCT_WGSL, trailSlotsApi, viewportApi } from "./hosted-api";
2
8
  /**
3
9
  * The trail accessors, in the PARTICLE module.
4
10
  *
@@ -7,11 +13,15 @@ import { audioApi } from "../audio-api";
7
13
  * particleInit reading the same recorded history the trail draws from. One
8
14
  * effect file, two mounts, one buffer.
9
15
  */
16
+ /** Shared with the grid pass, which reads the same buffer for the same reason:
17
+ * a kernel that displaces fog has to know where the dancer's feet are. */
10
18
  function castApi(cast) {
11
- return `
12
- const RZ_SUBJECTS: i32 = ${cast.subjects};
13
- const RZ_SAMPLES: i32 = ${cast.samples};
14
- const RZ_TRAIL_SLOTS: i32 = ${cast.slots};
19
+ return (
20
+ // rzSubjectCount FIRST, because CAST_API is written against it and this
21
+ // module has no view uniform to read the engine's count out of. Scanning
22
+ // for a subject whose bounding sphere has a radius is the same answer by a
23
+ // different route — the seam is named at the top of cast-api.ts.
24
+ `
15
25
  fn rzSubjectCount() -> i32 {
16
26
  var n = 0;
17
27
  for (var i = 0; i < RZ_SUBJECTS; i++) {
@@ -19,47 +29,13 @@ fn rzSubjectCount() -> i32 {
19
29
  }
20
30
  return n;
21
31
  }
22
- fn rzTrailCount(subject: i32, slot: i32) -> i32 {
23
- if (subject < 0 || subject >= RZ_SUBJECTS || slot < 0 || slot >= RZ_TRAIL_SLOTS) { return 0; }
24
- return i32(_rzCast[${cast.base} + (slot * RZ_SUBJECTS + subject) * 3 + 2].w);
25
- }
26
- /** Sample i of a path: xyz where it was, w how many seconds ago. i = 0 is now. */
27
- fn rzTrail(subject: i32, slot: i32, i: i32) -> vec4f {
28
- let n = rzTrailCount(subject, slot);
29
- if (i < 0 || i >= n) { return vec4f(0.0); }
30
- return _rzCast[${cast.trailBase} + (slot * RZ_SUBJECTS + subject) * RZ_SAMPLES + i];
31
- }
32
- `;
32
+ ` +
33
+ CAST_API +
34
+ trailSlotsApi(cast.trailCount) +
35
+ anchorAliasWgsl(cast.alias));
33
36
  }
34
37
  /** Bytes per particle. Explicitly padded — see the struct below. */
35
38
  export const PARTICLE_STRIDE = 48;
36
- /**
37
- * The particle record, laid out by hand.
38
- *
39
- * `age` and `life` sit in the padding that vec3f alignment would waste anyway
40
- * (a vec3f occupies 12 bytes but aligns the next field to 16), so the struct is
41
- * 48 bytes rather than the 64 a naive ordering costs. At 4096 particles that is
42
- * 192KB instead of 256KB, and it is read every frame by both stages.
43
- *
44
- * `life <= 0` means "not alive" and is what the pool checks to recycle a slot,
45
- * so a freshly zeroed buffer is entirely dead and every particle is born on the
46
- * first step rather than needing a separate seeding pass.
47
- */
48
- const PARTICLE_STRUCT = /* wgsl */ `
49
- struct Particle {
50
- pos: vec3f,
51
- age: f32,
52
- vel: vec3f,
53
- life: f32,
54
- size: f32,
55
- rot: f32,
56
- seed: f32,
57
- // Aspect along the direction of travel. 1 or less is a square billboard; a
58
- // raindrop is 10 or 20. Zero-initialised, so an effect that never sets it gets
59
- // the square it expects.
60
- stretch: f32,
61
- }
62
- `;
63
39
  const CAMERA_STRUCT = /* wgsl */ `
64
40
  struct CameraU {
65
41
  view: mat4x4f,
@@ -77,72 +53,21 @@ struct ParticleU {
77
53
  }
78
54
  `;
79
55
  /**
80
- * The shared prelude, everything `rz`-prefixed.
56
+ * This module's half of the prelude: the camera, from its own uniform.
81
57
  *
82
- * Not convenience — correctness. Every effect written against the old contract
83
- * re-derived its own hash and its own falloff, which is duplicated code and
84
- * duplicated bugs; `rzFalloff` in particular has COMPACT SUPPORT (it reaches
85
- * exactly zero at r), because an exponential glow that never quite reaches zero
86
- * has to be culled somewhere, and culling it wherever it "looks close enough"
87
- * is what put a visible hard edge on the first halo effect.
58
+ * The rest — hashes, noise, falloff, clock, viewport — is shared with every
59
+ * other module that hosts an effect's source, because an author's helper must
60
+ * mean the same thing in whichever one it lands in.
88
61
  */
89
- const PRELUDE = /* wgsl */ `
90
- fn rzHash11(x: f32) -> f32 {
91
- var p = fract(x * 0.1031);
92
- p = p * (p + 33.33);
93
- return fract(p * (p + p));
94
- }
95
- fn rzHash21(p: vec2f) -> f32 {
96
- var p3 = fract(vec3f(p.x, p.y, p.x) * 0.1031);
97
- p3 = p3 + dot(p3, p3.yzx + 33.33);
98
- return fract((p3.x + p3.y) * p3.z);
99
- }
100
- fn rzHash31(p: vec3f) -> f32 {
101
- var p3 = fract(p * 0.1031);
102
- p3 = p3 + dot(p3, p3.zyx + 31.32);
103
- return fract((p3.x + p3.y) * p3.z);
104
- }
105
- /** Three independent randoms from one seed — the usual need when spawning. */
106
- fn rzHash13(x: f32) -> vec3f {
107
- return vec3f(rzHash11(x), rzHash11(x + 17.13), rzHash11(x + 41.71));
108
- }
109
- fn rzValueNoise(p: vec3f) -> f32 {
110
- let i = floor(p);
111
- let f = fract(p);
112
- let u = f * f * (3.0 - 2.0 * f);
113
- let n000 = rzHash31(i + vec3f(0.0, 0.0, 0.0));
114
- let n100 = rzHash31(i + vec3f(1.0, 0.0, 0.0));
115
- let n010 = rzHash31(i + vec3f(0.0, 1.0, 0.0));
116
- let n110 = rzHash31(i + vec3f(1.0, 1.0, 0.0));
117
- let n001 = rzHash31(i + vec3f(0.0, 0.0, 1.0));
118
- let n101 = rzHash31(i + vec3f(1.0, 0.0, 1.0));
119
- let n011 = rzHash31(i + vec3f(0.0, 1.0, 1.0));
120
- let n111 = rzHash31(i + vec3f(1.0, 1.0, 1.0));
121
- let x00 = mix(n000, n100, u.x);
122
- let x10 = mix(n010, n110, u.x);
123
- let x01 = mix(n001, n101, u.x);
124
- let x11 = mix(n011, n111, u.x);
125
- return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);
126
- }
127
- /** Divergence-free flow — the standard drifting-air force. Snow and mist want this. */
128
- fn rzCurlNoise(p: vec3f) -> vec3f {
129
- let e = 0.1;
130
- let dx = vec3f(e, 0.0, 0.0);
131
- let dy = vec3f(0.0, e, 0.0);
132
- let dz = vec3f(0.0, 0.0, e);
133
- let x0 = rzValueNoise(p - dx); let x1 = rzValueNoise(p + dx);
134
- let y0 = rzValueNoise(p - dy); let y1 = rzValueNoise(p + dy);
135
- let z0 = rzValueNoise(p - dz); let z1 = rzValueNoise(p + dz);
136
- return normalize(vec3f((y1 - y0) - (z1 - z0), (z1 - z0) - (x1 - x0), (x1 - x0) - (y1 - y0)) + vec3f(1e-6));
137
- }
138
- /** Compact-support falloff: 1 at the centre, exactly 0 at r, smooth between. */
139
- fn rzFalloff(d: f32, r: f32) -> f32 {
140
- let x = clamp(d / max(r, 1e-6), 0.0, 1.0);
141
- let f = 1.0 - x;
142
- return f * f * f;
143
- }
144
- fn rzTime() -> f32 { return pu.time; }
145
- fn rzViewportHeight() -> f32 { return cam.targetHeight; }
62
+ const PRELUDE =
63
+ // The hashes, the noise and rzFalloff, identical in every module that hosts an
64
+ // effect's source; then the clock and the viewport, which cannot be, because
65
+ // this module keeps both in its own uniforms.
66
+ EFFECT_MATH_API +
67
+ clockApi("pu.time", "pu.dt") +
68
+ viewportApi("cam.targetHeight") +
69
+ /* wgsl */ `
70
+ ${RZ_LIGHT_STRUCT_WGSL}
146
71
  fn rzCameraPos() -> vec3f { return cam.camPos; }
147
72
  fn rzCameraRight() -> vec3f { return vec3f(cam.view[0][0], cam.view[1][0], cam.view[2][0]); }
148
73
  fn rzCameraUp() -> vec3f { return vec3f(cam.view[0][1], cam.view[1][1], cam.view[2][1]); }
@@ -153,7 +78,6 @@ fn rzProject(p: vec3f) -> vec3f {
153
78
  let w = max(clip.w, 1e-4);
154
79
  return vec3f(clip.xy / w * 0.5 + 0.5, clip.w);
155
80
  }
156
- fn rzDt() -> f32 { return pu.dt; }
157
81
  fn rzCamPos() -> vec3f { return cam.camPos; }
158
82
  `;
159
83
  /** `// @particles 4096` — how many live at once. */
@@ -192,7 +116,7 @@ export function particleEntryPoints(wgsl) {
192
116
  * snow is exactly right, and for a burst is what the age offset is for.
193
117
  */
194
118
  export function buildParticleComputeShader(src, cast) {
195
- return (PARTICLE_STRUCT +
119
+ return (PARTICLE_STRUCT_WGSL +
196
120
  CAMERA_STRUCT +
197
121
  PARTICLE_UNIFORMS +
198
122
  `
@@ -203,6 +127,8 @@ export function buildParticleComputeShader(src, cast) {
203
127
  ` +
204
128
  castApi(cast) +
205
129
  audioApi(0, 4) +
130
+ midiApi(0, 5) +
131
+ lyricsApi(0, 6) +
206
132
  PRELUDE +
207
133
  "\n// ── user effect ──\n" +
208
134
  src.wgsl +
@@ -246,7 +172,7 @@ fn main(@builtin(global_invocation_id) gid: vec3u) {
246
172
  export function buildParticleRenderShader(src, cast) {
247
173
  return (`override BLOOM: bool = ${src.bloom ? "true" : "false"};
248
174
  override ADDITIVE: bool = ${src.blend === "additive" ? "true" : "false"};\n` +
249
- PARTICLE_STRUCT +
175
+ PARTICLE_STRUCT_WGSL +
250
176
  CAMERA_STRUCT +
251
177
  PARTICLE_UNIFORMS +
252
178
  `
@@ -257,6 +183,8 @@ override ADDITIVE: bool = ${src.blend === "additive" ? "true" : "false"};\n` +
257
183
  ` +
258
184
  castApi(cast) +
259
185
  audioApi(0, 4) +
186
+ midiApi(0, 5) +
187
+ lyricsApi(0, 6) +
260
188
  PRELUDE +
261
189
  "\n// ── user effect ──\n" +
262
190
  src.wgsl +
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The scene pass's attachment contract, in one place.
3
+ *
4
+ * Everything drawn INSIDE the scene pass — models, ground, outline hulls,
5
+ * particles, ribbons, and the transparent depth prepass — shares one set of
6
+ * attachments, and therefore one set of formats, blends and write masks. That
7
+ * agreement used to be restated at every pipeline that joins the pass and in
8
+ * every shader that writes to it, which is why adding an attachment was
9
+ * dangerous rather than tedious: a class left behind does not fail loudly, it
10
+ * fails as a validation error naming a pipeline that was never edited.
11
+ *
12
+ * So the contract is DATA here, and the pipelines ask for it by render class.
13
+ * Adding the id attachment (MRT) becomes one edit in this file plus a per-class
14
+ * decision about whether that class writes it — which is the shape of the
15
+ * question, and now the shape of the code.
16
+ *
17
+ * WHAT IS NOT HERE. Two passes look like they belong and do not:
18
+ * - the FIELD pass has its own pair of rgba16float targets, its own blend and
19
+ * no MSAA. It is a different pass with a different contract.
20
+ * - the gizmo and selection-edge pipelines draw to the SWAPCHAIN in their own
21
+ * unmultisampled pass, so the presentation format is the whole of their
22
+ * contract. The plan listed gizmo as a scene render class; the code says
23
+ * otherwise, and the code is right.
24
+ */
25
+ /** The scene pass's attachments, as the engine has them at init. Passed in
26
+ * rather than imported: hdr is chosen per device (rg11b10ufloat where it is
27
+ * available and blendable, rgba16float otherwise). */
28
+ export type SceneFormats = {
29
+ /** The HDR colour attachment, @location(0). */
30
+ hdr: GPUTextureFormat;
31
+ /** The aux attachment, @location(1) — (bloom mask, coverage). */
32
+ aux: GPUTextureFormat;
33
+ };
34
+ /**
35
+ * The id attachment's format: (material index, object index), one u16 each.
36
+ *
37
+ * Two 16-bit channels rather than one 32-bit: 32-bit formats are not
38
+ * multisamplable, and this attachment is multisampled with the rest of the
39
+ * pass. Uint targets take no blend at all per spec, which is exactly right —
40
+ * an averaged id is not an id.
41
+ */
42
+ export declare const SCENE_ID_FORMAT: GPUTextureFormat;
43
+ /** Called by the engine at init, after probing the device. */
44
+ export declare function setMrtIds(on: boolean): void;
45
+ export declare function mrtIdsEnabled(): boolean;
46
+ /**
47
+ * What is being drawn, which is the only thing that varies.
48
+ *
49
+ * The classes differ ONLY in blend and write mask; formats are the pass's, not
50
+ * the draw's. A class here is a thing with a reason, not a pipeline name — two
51
+ * pipelines that blend the same way share one.
52
+ */
53
+ type SceneRenderClass =
54
+ /** Models, opaque and transparent. Straight alpha over. */
55
+ "material"
56
+ /** The shadow-catcher floor. Blends PREMULTIPLIED, not like a material: its
57
+ * coverage is a lit surface plus a colourless shadow layer, so it weights
58
+ * its own colour before the blend sees it. */
59
+ | "ground"
60
+ /** Backface-expanded hulls. Also a material blend — it is geometry. */
61
+ | "outline"
62
+ /** Particles and ribbons in their default, non-additive mode. */
63
+ | "particle"
64
+ /** Particles declaring `// @blend additive` — LIGHT rather than matter, so
65
+ * colour sums and alpha is left alone: a glow must not claim coverage it
66
+ * never occluded. The aux target sums with it, which is what lets an
67
+ * additive effect reach the bloom gate at all. */
68
+ | "particle-additive"
69
+ /** Ribbons. Additive colour like the above, but premultiplied by the
70
+ * fragment's own alpha on the way in (src-alpha, one) rather than added
71
+ * whole, and an ordinary alpha-over aux so a ribbon's mask does not
72
+ * saturate along every overlap. */
73
+ | "trail"
74
+ /** The transparent depth prepass: it exists to write DEPTH after the fabric's
75
+ * colour blended, so an outline drawn later is occluded behind it. It must
76
+ * therefore write no colour at all — the targets exist only to make the
77
+ * pipeline compatible with the pass it joins. */
78
+ | "depth-prepass";
79
+ /**
80
+ * The colour targets a scene-pass pipeline of this class declares, in
81
+ * attachment order.
82
+ *
83
+ * Fresh objects every call, deliberately: a caller that mutated a shared
84
+ * descriptor would change every pipeline built after it, and the ones built
85
+ * before would keep the old value — a difference that only shows up as one
86
+ * pipeline blending unlike its neighbours.
87
+ */
88
+ export declare function sceneTargets(cls: SceneRenderClass, formats: SceneFormats): GPUColorTargetState[];
89
+ /**
90
+ * The pass's colour attachments, in order — for the things that describe the
91
+ * pass itself rather than a draw within it.
92
+ *
93
+ * A render bundle declares the formats it will be replayed into and is rejected
94
+ * against a pass that does not match, so the bundle encoder has to move in
95
+ * lockstep with the targets above. It restated the list independently until
96
+ * this existed, which made it the one consumer an MRT change would have missed
97
+ * — a bundle is recorded once and replayed, so the failure would have arrived
98
+ * at replay, naming the bundle rather than the attachment that changed.
99
+ */
100
+ export declare function sceneColorFormats(formats: SceneFormats): GPUTextureFormat[];
101
+ /**
102
+ * The fragment-output struct a scene-pass shader returns.
103
+ *
104
+ * Emitted rather than written out per file so that the attachment list has one
105
+ * author. The struct and the targets above have to agree on count and order,
106
+ * and they now disagree in one file rather than in five.
107
+ *
108
+ * Only the shaders that will GAIN an output take this today — the materials
109
+ * (hand-written and graph-generated alike, through COMMON_FS_OUT_WGSL) and the
110
+ * ground. Outline, particles and ribbons keep their own declarations on
111
+ * purpose: they never write the id, so they would take an `id: false` argument
112
+ * forever, and their structs carry comments about their own blend that belong
113
+ * where they are.
114
+ */
115
+ export declare function sceneFsOutWgsl(opts?: {
116
+ name?: string;
117
+ aux?: string;
118
+ }): string;
119
+ /**
120
+ * The line a fragment shader assigns its id with, or nothing when ids are off.
121
+ *
122
+ * Emitted rather than written into each shader for the same reason as the
123
+ * struct: with ids off there must be no assignment either, and a shader cannot
124
+ * ask the device what it supports.
125
+ */
126
+ export declare function sceneIdWriteWgsl(out: string, material: string, object: string): string;
127
+ export {};
128
+ //# sourceMappingURL=scene-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-contract.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/scene-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;uDAEuD;AACvD,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,GAAG,EAAE,gBAAgB,CAAA;IACrB,iEAAiE;IACjE,GAAG,EAAE,gBAAgB,CAAA;CACtB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,gBAA6B,CAAA;AAkB3D,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAE3C;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;;;;GAMG;AACH,KAAK,gBAAgB;AACnB,2DAA2D;AACzD,UAAU;AACZ;;+CAE+C;GAC7C,QAAQ;AACV,uEAAuE;GACrE,SAAS;AACX,iEAAiE;GAC/D,UAAU;AACZ;;;mDAGmD;GACjD,mBAAmB;AACrB;;;oCAGoC;GAClC,OAAO;AACT;;;kDAGkD;GAChD,eAAe,CAAA;AAwEnB;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,YAAY,GAAG,mBAAmB,EAAE,CAqBhG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,gBAAgB,EAAE,CAE3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAmB7E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtF"}