vireuikit 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +2 -5
- package/README.md +4 -64
- package/dist/index.cjs +0 -882
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -81
- package/dist/index.d.ts +1 -81
- package/dist/index.js +1 -1
- package/package.json +2 -14
- package/dist/chunk-2AFVQ65S.js +0 -857
- package/dist/chunk-2AFVQ65S.js.map +0 -1
- package/dist/dynamics-7Bq3LLds.d.cts +0 -271
- package/dist/dynamics-7Bq3LLds.d.ts +0 -271
- package/dist/web/index.cjs +0 -1181
- package/dist/web/index.cjs.map +0 -1
- package/dist/web/index.d.cts +0 -91
- package/dist/web/index.d.ts +0 -91
- package/dist/web/index.js +0 -457
- package/dist/web/index.js.map +0 -1
package/dist/web/index.js
DELETED
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
import { MEDIUM_VAPOR_FORWARD_SHADER, MEDIUM_VAPOR_CORRECT_SHADER, MEDIUM_VAPOR_REACT_SHADER, MEDIUM_CONDENSATE_FORWARD_SHADER, MEDIUM_CONDENSATE_CORRECT_SHADER, MEDIUM_CONDENSATE_REACT_SHADER, MEDIUM_TRACK_ADVECT_SHADER, MEDIUM_COMPOSITE_SHADER, MEDIUM_SEED_SHADER, MEDIUM_EMIT_SHADER, MEDIUM_RESAMPLE_SHADER, MEDIUM_DEFAULTS, computeMediumSpatialPeriods, srgbToOklab, depthFarOffsetPx, MEDIUM_TIME_PERIOD } from '../chunk-2AFVQ65S.js';
|
|
2
|
-
import { toGLSL } from 'vireglass';
|
|
3
|
-
import { createProgram, locationCache, FULLSCREEN_TRIANGLE_VERTEX_SOURCE, bindTextureAt, drawFullscreenTriangle, setUniform, createTexture, createFramebuffer } from 'vireglass/web';
|
|
4
|
-
|
|
5
|
-
function pickDyeFormat(gl) {
|
|
6
|
-
const hasFloat = gl.getExtension("EXT_color_buffer_float") !== null;
|
|
7
|
-
return hasFloat ? { internalFormat: gl.RGBA16F, type: gl.HALF_FLOAT } : { internalFormat: gl.RGBA8, type: gl.UNSIGNED_BYTE };
|
|
8
|
-
}
|
|
9
|
-
function createMediumRuntime(gl, vertexSource) {
|
|
10
|
-
const format = pickDyeFormat(gl);
|
|
11
|
-
const vaporForwardProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_VAPOR_FORWARD_SHADER));
|
|
12
|
-
const vaporCorrectProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_VAPOR_CORRECT_SHADER));
|
|
13
|
-
const vaporReactProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_VAPOR_REACT_SHADER));
|
|
14
|
-
const condensateForwardProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_CONDENSATE_FORWARD_SHADER));
|
|
15
|
-
const condensateCorrectProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_CONDENSATE_CORRECT_SHADER));
|
|
16
|
-
const condensateReactProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_CONDENSATE_REACT_SHADER));
|
|
17
|
-
const trackAdvectProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_TRACK_ADVECT_SHADER));
|
|
18
|
-
const compositeProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_COMPOSITE_SHADER));
|
|
19
|
-
const seedProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_SEED_SHADER));
|
|
20
|
-
const emitProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_EMIT_SHADER));
|
|
21
|
-
const resampleProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_RESAMPLE_SHADER));
|
|
22
|
-
const vaporForwardLoc = locationCache(gl, vaporForwardProgram);
|
|
23
|
-
const vaporCorrectLoc = locationCache(gl, vaporCorrectProgram);
|
|
24
|
-
const vaporReactLoc = locationCache(gl, vaporReactProgram);
|
|
25
|
-
const condensateForwardLoc = locationCache(gl, condensateForwardProgram);
|
|
26
|
-
const condensateCorrectLoc = locationCache(gl, condensateCorrectProgram);
|
|
27
|
-
const condensateReactLoc = locationCache(gl, condensateReactProgram);
|
|
28
|
-
const trackAdvectLoc = locationCache(gl, trackAdvectProgram);
|
|
29
|
-
const compositeLoc = locationCache(gl, compositeProgram);
|
|
30
|
-
const seedLoc = locationCache(gl, seedProgram);
|
|
31
|
-
const emitLoc = locationCache(gl, emitProgram);
|
|
32
|
-
const resampleLoc = locationCache(gl, resampleProgram);
|
|
33
|
-
let gridW = 0;
|
|
34
|
-
let gridH = 0;
|
|
35
|
-
let vapor = null;
|
|
36
|
-
let condensate = null;
|
|
37
|
-
let track = null;
|
|
38
|
-
let vaporForward = null;
|
|
39
|
-
let vaporCorrected = null;
|
|
40
|
-
let condensateForward = null;
|
|
41
|
-
let condensateCorrected = null;
|
|
42
|
-
let front = 0;
|
|
43
|
-
function makeTarget(w, h) {
|
|
44
|
-
const texture = createTexture(gl, {
|
|
45
|
-
width: w,
|
|
46
|
-
height: h,
|
|
47
|
-
internalFormat: format.internalFormat,
|
|
48
|
-
type: format.type,
|
|
49
|
-
// REPEAT: a domain with no border — see TextureOptions.wrap. All buffers carry the same
|
|
50
|
-
// field, so the border needs to behave identically for each of them, or vapor would wrap
|
|
51
|
-
// while condensate or a track at the same coordinate cuts off. The MacCormack limiter also
|
|
52
|
-
// reads neighbors across this same border — it needs REPEAT exactly as much as transport does.
|
|
53
|
-
wrap: gl.REPEAT
|
|
54
|
-
});
|
|
55
|
-
const fbo = createFramebuffer(gl, texture);
|
|
56
|
-
return { texture, fbo };
|
|
57
|
-
}
|
|
58
|
-
function clearTarget(target, w, h) {
|
|
59
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);
|
|
60
|
-
gl.viewport(0, 0, w, h);
|
|
61
|
-
gl.clearColor(0, 0, 0, 1);
|
|
62
|
-
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
63
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
64
|
-
}
|
|
65
|
-
function destroyPair(pair) {
|
|
66
|
-
if (!pair) return;
|
|
67
|
-
for (const t of pair) {
|
|
68
|
-
gl.deleteTexture(t.texture);
|
|
69
|
-
gl.deleteFramebuffer(t.fbo);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function destroyTarget(target) {
|
|
73
|
-
if (!target) return;
|
|
74
|
-
gl.deleteTexture(target.texture);
|
|
75
|
-
gl.deleteFramebuffer(target.fbo);
|
|
76
|
-
}
|
|
77
|
-
function destroyTargets() {
|
|
78
|
-
destroyPair(vapor);
|
|
79
|
-
destroyPair(condensate);
|
|
80
|
-
destroyPair(track);
|
|
81
|
-
destroyTarget(vaporForward);
|
|
82
|
-
destroyTarget(vaporCorrected);
|
|
83
|
-
destroyTarget(condensateForward);
|
|
84
|
-
destroyTarget(condensateCorrected);
|
|
85
|
-
vapor = null;
|
|
86
|
-
condensate = null;
|
|
87
|
-
track = null;
|
|
88
|
-
vaporForward = null;
|
|
89
|
-
vaporCorrected = null;
|
|
90
|
-
condensateForward = null;
|
|
91
|
-
condensateCorrected = null;
|
|
92
|
-
}
|
|
93
|
-
function seed(w, h) {
|
|
94
|
-
if (!vapor || !condensate || !track) return;
|
|
95
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, vapor[front].fbo);
|
|
96
|
-
gl.viewport(0, 0, w, h);
|
|
97
|
-
gl.disable(gl.BLEND);
|
|
98
|
-
gl.useProgram(seedProgram);
|
|
99
|
-
setUniform(gl, seedLoc("u_resolution"), [w, h]);
|
|
100
|
-
setUniform(gl, seedLoc("u_spot0"), [w * 0.28, h * 0.64]);
|
|
101
|
-
setUniform(gl, seedLoc("u_spot1"), [w * 0.7, h * 0.32]);
|
|
102
|
-
setUniform(gl, seedLoc("u_spot2"), [w * 0.48, h * 0.84]);
|
|
103
|
-
setUniform(gl, seedLoc("u_spotRadius"), Math.max(w, h) * 0.18);
|
|
104
|
-
drawFullscreenTriangle(gl);
|
|
105
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
106
|
-
clearTarget(condensate[0], w, h);
|
|
107
|
-
clearTarget(condensate[1], w, h);
|
|
108
|
-
clearTarget(track[0], w, h);
|
|
109
|
-
clearTarget(track[1], w, h);
|
|
110
|
-
}
|
|
111
|
-
function resample(src, srcW, srcH, dst, dstW, dstH) {
|
|
112
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, dst.fbo);
|
|
113
|
-
gl.viewport(0, 0, dstW, dstH);
|
|
114
|
-
gl.disable(gl.BLEND);
|
|
115
|
-
gl.useProgram(resampleProgram);
|
|
116
|
-
bindTextureAt(gl, 0, src.texture, resampleProgram, "u_src");
|
|
117
|
-
setUniform(gl, resampleLoc("u_srcSize"), [srcW, srcH]);
|
|
118
|
-
setUniform(gl, resampleLoc("u_resolution"), [dstW, dstH]);
|
|
119
|
-
drawFullscreenTriangle(gl);
|
|
120
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
121
|
-
}
|
|
122
|
-
function resizeGrid(w, h) {
|
|
123
|
-
if (!vapor || !condensate || !track) return;
|
|
124
|
-
const oldVapor = vapor;
|
|
125
|
-
const oldCondensate = condensate;
|
|
126
|
-
const oldTrack = track;
|
|
127
|
-
const oldW = gridW;
|
|
128
|
-
const oldH = gridH;
|
|
129
|
-
const newVaporFront = makeTarget(w, h);
|
|
130
|
-
const newCondensateFront = makeTarget(w, h);
|
|
131
|
-
const newTrackFront = makeTarget(w, h);
|
|
132
|
-
resample(oldVapor[front], oldW, oldH, newVaporFront, w, h);
|
|
133
|
-
resample(oldCondensate[front], oldW, oldH, newCondensateFront, w, h);
|
|
134
|
-
resample(oldTrack[front], oldW, oldH, newTrackFront, w, h);
|
|
135
|
-
destroyTargets();
|
|
136
|
-
gridW = w;
|
|
137
|
-
gridH = h;
|
|
138
|
-
vapor = [newVaporFront, makeTarget(w, h)];
|
|
139
|
-
condensate = [newCondensateFront, makeTarget(w, h)];
|
|
140
|
-
track = [newTrackFront, makeTarget(w, h)];
|
|
141
|
-
vaporForward = makeTarget(w, h);
|
|
142
|
-
vaporCorrected = makeTarget(w, h);
|
|
143
|
-
condensateForward = makeTarget(w, h);
|
|
144
|
-
condensateCorrected = makeTarget(w, h);
|
|
145
|
-
front = 0;
|
|
146
|
-
const areaRatio = w * h / (oldW * oldH);
|
|
147
|
-
waterTargetTotal *= areaRatio;
|
|
148
|
-
}
|
|
149
|
-
function ensureGrid(gridWidth, gridHeight) {
|
|
150
|
-
const w = Math.max(2, Math.round(gridWidth));
|
|
151
|
-
const h = Math.max(2, Math.round(gridHeight));
|
|
152
|
-
if (vapor && condensate && track && w === gridW && h === gridH) return;
|
|
153
|
-
if (!vapor || !condensate || !track) {
|
|
154
|
-
gridW = w;
|
|
155
|
-
gridH = h;
|
|
156
|
-
vapor = [makeTarget(w, h), makeTarget(w, h)];
|
|
157
|
-
condensate = [makeTarget(w, h), makeTarget(w, h)];
|
|
158
|
-
track = [makeTarget(w, h), makeTarget(w, h)];
|
|
159
|
-
vaporForward = makeTarget(w, h);
|
|
160
|
-
vaporCorrected = makeTarget(w, h);
|
|
161
|
-
condensateForward = makeTarget(w, h);
|
|
162
|
-
condensateCorrected = makeTarget(w, h);
|
|
163
|
-
front = 0;
|
|
164
|
-
seed(w, h);
|
|
165
|
-
waterTargetTotal = readTarget(vapor[front]) + readTarget(condensate[front]);
|
|
166
|
-
waterScale = 1;
|
|
167
|
-
timeSinceRenorm = 0;
|
|
168
|
-
framesSinceRenorm = 0;
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
resizeGrid(w, h);
|
|
172
|
-
}
|
|
173
|
-
function bindVelocityUniforms(loc, dt, phase2, p, spatial) {
|
|
174
|
-
setUniform(gl, loc("u_dyeSize"), [gridW, gridH]);
|
|
175
|
-
setUniform(gl, loc("u_resolution"), [gridW, gridH]);
|
|
176
|
-
setUniform(gl, loc("u_dt"), dt);
|
|
177
|
-
setUniform(gl, loc("u_phase"), phase2);
|
|
178
|
-
setUniform(gl, loc("u_curlFreq"), p.curlFreq);
|
|
179
|
-
setUniform(gl, loc("u_advectSpeed"), p.advectSpeed);
|
|
180
|
-
setUniform(gl, loc("u_turbulence"), p.turbulence);
|
|
181
|
-
setUniform(gl, loc("u_vaporDrift"), p.gravityVaporDrift);
|
|
182
|
-
setUniform(gl, loc("u_spatial0"), [spatial[0].freqX, spatial[0].freqY, spatial[0].periodX, spatial[0].periodY]);
|
|
183
|
-
setUniform(gl, loc("u_spatial1"), [spatial[1].freqX, spatial[1].freqY, spatial[1].periodX, spatial[1].periodY]);
|
|
184
|
-
setUniform(gl, loc("u_spatial2"), [spatial[2].freqX, spatial[2].freqY, spatial[2].periodX, spatial[2].periodY]);
|
|
185
|
-
setUniform(gl, loc("u_spatial3"), [spatial[3].freqX, spatial[3].freqY, spatial[3].periodX, spatial[3].periodY]);
|
|
186
|
-
}
|
|
187
|
-
let phase = 0;
|
|
188
|
-
function wrapPhase(value) {
|
|
189
|
-
const wrapped = value % MEDIUM_TIME_PERIOD;
|
|
190
|
-
return wrapped < 0 ? wrapped + MEDIUM_TIME_PERIOD : wrapped;
|
|
191
|
-
}
|
|
192
|
-
const WATER_RENORM_INTERVAL_S = 1;
|
|
193
|
-
let waterTargetTotal = 0;
|
|
194
|
-
let waterScale = 1;
|
|
195
|
-
let timeSinceRenorm = 0;
|
|
196
|
-
let framesSinceRenorm = 0;
|
|
197
|
-
function step(dt, params = {}) {
|
|
198
|
-
if (!vapor || !condensate || !track || !vaporForward || !vaporCorrected || !condensateForward || !condensateCorrected) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
const p = { ...MEDIUM_DEFAULTS, ...params };
|
|
202
|
-
const spatial = computeMediumSpatialPeriods(gridW, gridH, p.curlFreq);
|
|
203
|
-
const clampedDt = Math.min(Math.max(dt, 0), 1 / 15);
|
|
204
|
-
phase = wrapPhase(phase + clampedDt * p.curlSpeed);
|
|
205
|
-
const back = front === 0 ? 1 : 0;
|
|
206
|
-
const gridSize = [gridW, gridH];
|
|
207
|
-
gl.viewport(0, 0, gridW, gridH);
|
|
208
|
-
gl.disable(gl.BLEND);
|
|
209
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, vaporForward.fbo);
|
|
210
|
-
gl.useProgram(vaporForwardProgram);
|
|
211
|
-
bindTextureAt(gl, 0, vapor[front].texture, vaporForwardProgram, "u_dye");
|
|
212
|
-
bindVelocityUniforms(vaporForwardLoc, clampedDt, phase, p, spatial);
|
|
213
|
-
drawFullscreenTriangle(gl);
|
|
214
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, condensateForward.fbo);
|
|
215
|
-
gl.useProgram(condensateForwardProgram);
|
|
216
|
-
bindTextureAt(gl, 0, condensate[front].texture, condensateForwardProgram, "u_dye");
|
|
217
|
-
bindVelocityUniforms(condensateForwardLoc, clampedDt, phase, p, spatial);
|
|
218
|
-
setUniform(gl, condensateForwardLoc("u_settleSpeed"), p.condensateSettleSpeed);
|
|
219
|
-
setUniform(gl, condensateForwardLoc("u_settleWiggle"), p.condensateSettleWiggle);
|
|
220
|
-
drawFullscreenTriangle(gl);
|
|
221
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, vaporCorrected.fbo);
|
|
222
|
-
gl.useProgram(vaporCorrectProgram);
|
|
223
|
-
bindTextureAt(gl, 0, vapor[front].texture, vaporCorrectProgram, "u_dye");
|
|
224
|
-
bindTextureAt(gl, 1, vaporForward.texture, vaporCorrectProgram, "u_forward");
|
|
225
|
-
setUniform(gl, vaporCorrectLoc("u_forwardSize"), gridSize);
|
|
226
|
-
bindVelocityUniforms(vaporCorrectLoc, clampedDt, phase, p, spatial);
|
|
227
|
-
drawFullscreenTriangle(gl);
|
|
228
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, condensateCorrected.fbo);
|
|
229
|
-
gl.useProgram(condensateCorrectProgram);
|
|
230
|
-
bindTextureAt(gl, 0, condensate[front].texture, condensateCorrectProgram, "u_dye");
|
|
231
|
-
bindTextureAt(gl, 1, condensateForward.texture, condensateCorrectProgram, "u_forward");
|
|
232
|
-
setUniform(gl, condensateCorrectLoc("u_forwardSize"), gridSize);
|
|
233
|
-
bindVelocityUniforms(condensateCorrectLoc, clampedDt, phase, p, spatial);
|
|
234
|
-
setUniform(gl, condensateCorrectLoc("u_settleSpeed"), p.condensateSettleSpeed);
|
|
235
|
-
setUniform(gl, condensateCorrectLoc("u_settleWiggle"), p.condensateSettleWiggle);
|
|
236
|
-
drawFullscreenTriangle(gl);
|
|
237
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, vapor[back].fbo);
|
|
238
|
-
gl.useProgram(vaporReactProgram);
|
|
239
|
-
bindTextureAt(gl, 0, vaporCorrected.texture, vaporReactProgram, "u_transported");
|
|
240
|
-
bindTextureAt(gl, 1, condensateCorrected.texture, vaporReactProgram, "u_condensateTransported");
|
|
241
|
-
setUniform(gl, vaporReactLoc("u_transportedSize"), gridSize);
|
|
242
|
-
setUniform(gl, vaporReactLoc("u_condensateTransportedSize"), gridSize);
|
|
243
|
-
setUniform(gl, vaporReactLoc("u_resolution"), gridSize);
|
|
244
|
-
setUniform(gl, vaporReactLoc("u_dt"), clampedDt);
|
|
245
|
-
setUniform(gl, vaporReactLoc("u_condensationRate"), p.condensationRate);
|
|
246
|
-
setUniform(gl, vaporReactLoc("u_condensationFloor"), p.condensationFloor);
|
|
247
|
-
setUniform(gl, vaporReactLoc("u_evaporationRate"), p.evaporationRate);
|
|
248
|
-
setUniform(gl, vaporReactLoc("u_totalScale"), waterScale);
|
|
249
|
-
drawFullscreenTriangle(gl);
|
|
250
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, condensate[back].fbo);
|
|
251
|
-
gl.useProgram(condensateReactProgram);
|
|
252
|
-
bindTextureAt(gl, 0, condensateCorrected.texture, condensateReactProgram, "u_transported");
|
|
253
|
-
bindTextureAt(gl, 1, vaporCorrected.texture, condensateReactProgram, "u_vaporTransported");
|
|
254
|
-
bindTextureAt(gl, 2, condensate[front].texture, condensateReactProgram, "u_dye");
|
|
255
|
-
setUniform(gl, condensateReactLoc("u_transportedSize"), gridSize);
|
|
256
|
-
setUniform(gl, condensateReactLoc("u_vaporTransportedSize"), gridSize);
|
|
257
|
-
setUniform(gl, condensateReactLoc("u_dyeSize"), gridSize);
|
|
258
|
-
setUniform(gl, condensateReactLoc("u_resolution"), gridSize);
|
|
259
|
-
setUniform(gl, condensateReactLoc("u_dt"), clampedDt);
|
|
260
|
-
setUniform(gl, condensateReactLoc("u_condensationRate"), p.condensationRate);
|
|
261
|
-
setUniform(gl, condensateReactLoc("u_condensationFloor"), p.condensationFloor);
|
|
262
|
-
setUniform(gl, condensateReactLoc("u_evaporationRate"), p.evaporationRate);
|
|
263
|
-
setUniform(gl, condensateReactLoc("u_spreadRate"), p.condensateSpreadRate);
|
|
264
|
-
setUniform(gl, condensateReactLoc("u_totalScale"), waterScale);
|
|
265
|
-
drawFullscreenTriangle(gl);
|
|
266
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, track[back].fbo);
|
|
267
|
-
gl.useProgram(trackAdvectProgram);
|
|
268
|
-
bindTextureAt(gl, 0, track[front].texture, trackAdvectProgram, "u_dye");
|
|
269
|
-
bindVelocityUniforms(trackAdvectLoc, clampedDt, phase, p, spatial);
|
|
270
|
-
setUniform(gl, trackAdvectLoc("u_decay"), p.decay);
|
|
271
|
-
drawFullscreenTriangle(gl);
|
|
272
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
273
|
-
front = back;
|
|
274
|
-
framesSinceRenorm += 1;
|
|
275
|
-
timeSinceRenorm += clampedDt;
|
|
276
|
-
if (waterTargetTotal > 0 && timeSinceRenorm >= WATER_RENORM_INTERVAL_S && framesSinceRenorm > 0) {
|
|
277
|
-
const current = readTarget(vapor[front]) + readTarget(condensate[front]);
|
|
278
|
-
if (current > 1e-6) {
|
|
279
|
-
const totalRatio = waterTargetTotal / current;
|
|
280
|
-
const perFrame = Math.pow(totalRatio, 1 / framesSinceRenorm);
|
|
281
|
-
waterScale = Math.min(1.05, Math.max(0.95, perFrame));
|
|
282
|
-
}
|
|
283
|
-
timeSinceRenorm = 0;
|
|
284
|
-
framesSinceRenorm = 0;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
function emit(emissions) {
|
|
288
|
-
if (!track || emissions.length === 0) return;
|
|
289
|
-
const minDim = Math.min(gridW, gridH);
|
|
290
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, track[front].fbo);
|
|
291
|
-
gl.viewport(0, 0, gridW, gridH);
|
|
292
|
-
gl.enable(gl.BLEND);
|
|
293
|
-
gl.blendFunc(gl.ONE, gl.ONE);
|
|
294
|
-
gl.useProgram(emitProgram);
|
|
295
|
-
setUniform(gl, emitLoc("u_resolution"), [gridW, gridH]);
|
|
296
|
-
for (const e of emissions) {
|
|
297
|
-
const source = [e.source[0] * gridW, e.source[1] * gridH];
|
|
298
|
-
const length = e.lengthFrac * minDim;
|
|
299
|
-
const head = [
|
|
300
|
-
source[0] + Math.cos(e.angle) * length,
|
|
301
|
-
source[1] + Math.sin(e.angle) * length
|
|
302
|
-
];
|
|
303
|
-
setUniform(gl, emitLoc("u_source"), source);
|
|
304
|
-
setUniform(gl, emitLoc("u_head"), head);
|
|
305
|
-
setUniform(gl, emitLoc("u_headWidth"), e.headWidthFrac * minDim);
|
|
306
|
-
setUniform(gl, emitLoc("u_tailWidth"), e.tailWidthFrac * minDim);
|
|
307
|
-
setUniform(gl, emitLoc("u_raggedAmp"), e.raggedFrac * minDim);
|
|
308
|
-
setUniform(gl, emitLoc("u_raggedFreq"), e.raggedFreq);
|
|
309
|
-
setUniform(gl, emitLoc("u_settle"), e.settleFrac * minDim);
|
|
310
|
-
setUniform(gl, emitLoc("u_tailDim"), e.tailDim);
|
|
311
|
-
setUniform(gl, emitLoc("u_intensity"), e.intensity);
|
|
312
|
-
setUniform(gl, emitLoc("u_seed"), e.seed);
|
|
313
|
-
setUniform(gl, emitLoc("u_channelMask"), e.channel);
|
|
314
|
-
drawFullscreenTriangle(gl);
|
|
315
|
-
}
|
|
316
|
-
gl.disable(gl.BLEND);
|
|
317
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
318
|
-
}
|
|
319
|
-
function composite(contentWidth, contentHeight, params = {}) {
|
|
320
|
-
if (!vapor || !condensate || !track) return;
|
|
321
|
-
const p = { ...MEDIUM_DEFAULTS, ...params };
|
|
322
|
-
gl.viewport(0, 0, contentWidth, contentHeight);
|
|
323
|
-
gl.disable(gl.BLEND);
|
|
324
|
-
gl.useProgram(compositeProgram);
|
|
325
|
-
bindTextureAt(gl, 0, vapor[front].texture, compositeProgram, "u_vapor");
|
|
326
|
-
bindTextureAt(gl, 1, condensate[front].texture, compositeProgram, "u_condensate");
|
|
327
|
-
bindTextureAt(gl, 2, track[front].texture, compositeProgram, "u_track");
|
|
328
|
-
setUniform(gl, compositeLoc("u_vaporSize"), [gridW, gridH]);
|
|
329
|
-
setUniform(gl, compositeLoc("u_condensateSize"), [gridW, gridH]);
|
|
330
|
-
setUniform(gl, compositeLoc("u_trackSize"), [gridW, gridH]);
|
|
331
|
-
setUniform(gl, compositeLoc("u_resolution"), [contentWidth, contentHeight]);
|
|
332
|
-
setUniform(gl, compositeLoc("u_dyeScale"), [gridW / contentWidth, gridH / contentHeight]);
|
|
333
|
-
setUniform(gl, compositeLoc("u_lab0"), srgbToOklab(p.channelColors[0]));
|
|
334
|
-
setUniform(gl, compositeLoc("u_lab1"), srgbToOklab(p.channelColors[1]));
|
|
335
|
-
setUniform(gl, compositeLoc("u_lab2"), srgbToOklab(p.channelColors[2]));
|
|
336
|
-
setUniform(gl, compositeLoc("u_labBg"), srgbToOklab(p.baseColor));
|
|
337
|
-
setUniform(gl, compositeLoc("u_labCondensate"), srgbToOklab(p.condensateColor));
|
|
338
|
-
setUniform(gl, compositeLoc("u_baseWeight"), p.baseWeight);
|
|
339
|
-
setUniform(gl, compositeLoc("u_condensateTint"), p.condensateTint);
|
|
340
|
-
setUniform(gl, compositeLoc("u_condensateGain"), p.condensateGain);
|
|
341
|
-
setUniform(gl, compositeLoc("u_farScale"), p.depthFarScale);
|
|
342
|
-
setUniform(gl, compositeLoc("u_farOffset"), depthFarOffsetPx(p.depthFarOffsetFrac, gridW, gridH));
|
|
343
|
-
setUniform(gl, compositeLoc("u_farBlurRadius"), p.depthFarBlurRadius);
|
|
344
|
-
setUniform(gl, compositeLoc("u_farWeight"), p.depthFarWeight);
|
|
345
|
-
setUniform(gl, compositeLoc("u_gravityBoost"), p.gravityBottomBoost);
|
|
346
|
-
setUniform(gl, compositeLoc("u_gravityBoostStart"), p.gravityBottomBoostStart);
|
|
347
|
-
drawFullscreenTriangle(gl);
|
|
348
|
-
}
|
|
349
|
-
function readTarget(target) {
|
|
350
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);
|
|
351
|
-
let sum = 0;
|
|
352
|
-
if (format.type === gl.HALF_FLOAT) {
|
|
353
|
-
const buf = new Float32Array(gridW * gridH * 4);
|
|
354
|
-
gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.FLOAT, buf);
|
|
355
|
-
for (let i = 0; i < buf.length; i += 4) sum += buf[i] + buf[i + 1] + buf[i + 2];
|
|
356
|
-
} else {
|
|
357
|
-
const buf = new Uint8Array(gridW * gridH * 4);
|
|
358
|
-
gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.UNSIGNED_BYTE, buf);
|
|
359
|
-
for (let i = 0; i < buf.length; i += 4) sum += (buf[i] + buf[i + 1] + buf[i + 2]) / 255;
|
|
360
|
-
}
|
|
361
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
362
|
-
return sum;
|
|
363
|
-
}
|
|
364
|
-
function readTotals() {
|
|
365
|
-
if (!vapor || !condensate || !track) return null;
|
|
366
|
-
return {
|
|
367
|
-
vapor: readTarget(vapor[front]),
|
|
368
|
-
condensate: readTarget(condensate[front]),
|
|
369
|
-
track: readTarget(track[front])
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
function readVaporGrid() {
|
|
373
|
-
if (!vapor) return null;
|
|
374
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, vapor[front].fbo);
|
|
375
|
-
const cells = gridW * gridH;
|
|
376
|
-
const data = new Array(cells * 3);
|
|
377
|
-
if (format.type === gl.HALF_FLOAT) {
|
|
378
|
-
const buf = new Float32Array(cells * 4);
|
|
379
|
-
gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.FLOAT, buf);
|
|
380
|
-
for (let i = 0; i < cells; i += 1) {
|
|
381
|
-
data[i * 3] = buf[i * 4];
|
|
382
|
-
data[i * 3 + 1] = buf[i * 4 + 1];
|
|
383
|
-
data[i * 3 + 2] = buf[i * 4 + 2];
|
|
384
|
-
}
|
|
385
|
-
} else {
|
|
386
|
-
const buf = new Uint8Array(cells * 4);
|
|
387
|
-
gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.UNSIGNED_BYTE, buf);
|
|
388
|
-
for (let i = 0; i < cells; i += 1) {
|
|
389
|
-
data[i * 3] = buf[i * 4] / 255;
|
|
390
|
-
data[i * 3 + 1] = buf[i * 4 + 1] / 255;
|
|
391
|
-
data[i * 3 + 2] = buf[i * 4 + 2] / 255;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
395
|
-
return { cols: gridW, rows: gridH, data };
|
|
396
|
-
}
|
|
397
|
-
function readCondensateGrid() {
|
|
398
|
-
if (!condensate) return null;
|
|
399
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, condensate[front].fbo);
|
|
400
|
-
const cells = gridW * gridH;
|
|
401
|
-
const data = new Array(cells);
|
|
402
|
-
if (format.type === gl.HALF_FLOAT) {
|
|
403
|
-
const buf = new Float32Array(cells * 4);
|
|
404
|
-
gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.FLOAT, buf);
|
|
405
|
-
for (let i = 0; i < cells; i += 1) data[i] = buf[i * 4];
|
|
406
|
-
} else {
|
|
407
|
-
const buf = new Uint8Array(cells * 4);
|
|
408
|
-
gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.UNSIGNED_BYTE, buf);
|
|
409
|
-
for (let i = 0; i < cells; i += 1) data[i] = buf[i * 4] / 255;
|
|
410
|
-
}
|
|
411
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
412
|
-
return { cols: gridW, rows: gridH, data };
|
|
413
|
-
}
|
|
414
|
-
function destroy() {
|
|
415
|
-
destroyTargets();
|
|
416
|
-
gl.deleteProgram(vaporForwardProgram);
|
|
417
|
-
gl.deleteProgram(vaporCorrectProgram);
|
|
418
|
-
gl.deleteProgram(vaporReactProgram);
|
|
419
|
-
gl.deleteProgram(condensateForwardProgram);
|
|
420
|
-
gl.deleteProgram(condensateCorrectProgram);
|
|
421
|
-
gl.deleteProgram(condensateReactProgram);
|
|
422
|
-
gl.deleteProgram(trackAdvectProgram);
|
|
423
|
-
gl.deleteProgram(compositeProgram);
|
|
424
|
-
gl.deleteProgram(seedProgram);
|
|
425
|
-
gl.deleteProgram(emitProgram);
|
|
426
|
-
gl.deleteProgram(resampleProgram);
|
|
427
|
-
}
|
|
428
|
-
return { ensureGrid, step, emit, composite, readTotals, readVaporGrid, readCondensateGrid, destroy };
|
|
429
|
-
}
|
|
430
|
-
function createMediumBackdrop() {
|
|
431
|
-
let runtime = null;
|
|
432
|
-
let owner = null;
|
|
433
|
-
return {
|
|
434
|
-
pass: (frame) => (gl, target) => {
|
|
435
|
-
if (owner && owner !== gl) throw new Error("vireuikit: the medium is already bound to another WebGL context");
|
|
436
|
-
owner = gl;
|
|
437
|
-
runtime ??= createMediumRuntime(gl, FULLSCREEN_TRIANGLE_VERTEX_SOURCE);
|
|
438
|
-
runtime.ensureGrid(frame.gridWidth, frame.gridHeight);
|
|
439
|
-
runtime.step(frame.dt, frame.params);
|
|
440
|
-
runtime.emit(frame.emissions ?? []);
|
|
441
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);
|
|
442
|
-
runtime.composite(target.width, target.height, frame.params);
|
|
443
|
-
},
|
|
444
|
-
readTotals: () => runtime?.readTotals() ?? null,
|
|
445
|
-
readVaporGrid: () => runtime?.readVaporGrid() ?? null,
|
|
446
|
-
readCondensateGrid: () => runtime?.readCondensateGrid() ?? null,
|
|
447
|
-
destroy: () => {
|
|
448
|
-
runtime?.destroy();
|
|
449
|
-
runtime = null;
|
|
450
|
-
owner = null;
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
export { createMediumBackdrop, createMediumRuntime };
|
|
456
|
-
//# sourceMappingURL=index.js.map
|
|
457
|
-
//# sourceMappingURL=index.js.map
|
package/dist/web/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/web/medium.ts","../../src/web/medium-backdrop.ts"],"names":["phase"],"mappings":"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","file":"index.js","sourcesContent":["// The medium's runtime layer: ping-pong FBOs for the simulation grid and its GPU passes. Shader\n// text and parameters live in the package's model (`../medium`) — this file is WebGL only.\n//\n// Three independent buffers, not one: vapor (a conserved color reserve), condensate (droplets —\n// they grow, settle, and exchange with vapor) and tracks (born from a stamp, fade through decay).\n// Vapor and condensate form ONE conserved sum — water; tracks aren't part of it, and their decay\n// and stamping haven't been folded into that accounting.\nimport { computeMediumSpatialPeriods, type MediumSpatialOctave } from '../medium/noise';\nimport {\n depthFarOffsetPx,\n MEDIUM_COMPOSITE_SHADER,\n MEDIUM_CONDENSATE_CORRECT_SHADER,\n MEDIUM_CONDENSATE_FORWARD_SHADER,\n MEDIUM_CONDENSATE_REACT_SHADER,\n MEDIUM_DEFAULTS,\n MEDIUM_EMIT_SHADER,\n MEDIUM_RESAMPLE_SHADER,\n MEDIUM_SEED_SHADER,\n MEDIUM_TIME_PERIOD,\n MEDIUM_TRACK_ADVECT_SHADER,\n MEDIUM_VAPOR_CORRECT_SHADER,\n MEDIUM_VAPOR_FORWARD_SHADER,\n MEDIUM_VAPOR_REACT_SHADER,\n srgbToOklab,\n type VireUIKitMediumEmission,\n type VireUIKitMediumParams,\n} from '../medium';\nimport { toGLSL } from 'vireglass';\nimport {\n bindTextureAt,\n createFramebuffer,\n createProgram,\n createTexture,\n drawFullscreenTriangle,\n locationCache,\n setUniform,\n} from 'vireglass/web';\n\nexport type MediumRuntime = {\n /** Recreates the grid on the first call (seeding it); on a later call with a different size,\n * RESAMPLES the old vapor/condensate/track state into the new size instead of reseeding — see\n * `resizeGrid` below for why. A no-op when the size hasn't changed. */\n ensureGrid(gridWidth: number, gridHeight: number): void;\n /** One transport step for all three buffers (vapor and condensate via MacCormack:\n * forward/correct/react, see `advect-shader.ts`; tracks via a single backtrace plus decay),\n * ping-ponging their own FBOs. The field's phase (curl-noise's time axis) is this runtime's own\n * state, accumulated from `dt` — see `u_phase` below — not derived from a caller-supplied clock. */\n step(dt: number, params?: Partial<VireUIKitMediumParams>): void;\n /**\n * Stamps tracks into the TRACK buffer just refreshed by advection — via additive blending,\n * rather than a separate pass or its own position list. From there the same field carries them\n * and their own decay extinguishes them; there is no lifetime timer here.\n */\n emit(emissions: readonly VireUIKitMediumEmission[]): void;\n /**\n * Composites the current density (vapor + condensate + tracks) into a buffer already bound by\n * the caller (the renderer's `contentFbo`) — this is exactly how the medium acts as the second\n * backdrop path: the result lands in the same `contentTexture` the lens samples, not a separate\n * pass drawn over the glass.\n */\n composite(contentWidth: number, contentHeight: number, params?: Partial<VireUIKitMediumParams>): void;\n /** The raw density sum across every cell — a measurement hook, not part of the frame. Balance\n * and structure gates read THIS, not a composited pixel: compositing muddies things with Oklab\n * nonlinearity, and the water (vapor+condensate) sum and contrast need to be measurable on\n * their own. */\n readTotals(): { vapor: number; condensate: number; track: number } | null;\n /** The raw VAPOR grid, at simulation-grid resolution, `data` as r/g/b triples (three species)\n * per cell, not a sum. Used by the structure gate (contrast BETWEEN species across the grid). */\n readVaporGrid(): { cols: number; rows: number; data: number[] } | null;\n /** The raw CONDENSATE grid, at simulation-grid resolution, `data` as one density value per\n * cell (condensate has no color — see `readVaporGrid`'s r/g/b for the contrast). Used by the\n * gravity gate to find the density-weighted row where the mass sits. */\n readCondensateGrid(): { cols: number; rows: number; data: number[] } | null;\n destroy(): void;\n};\n\ntype DyeTarget = { texture: WebGLTexture; fbo: WebGLFramebuffer };\ntype DyePair = readonly [DyeTarget, DyeTarget];\n\n/** RGBA16F keeps decay from banding on its long tail; without `EXT_color_buffer_float` (float\n * render targets need it) this falls back to RGBA8 — coarser, but works everywhere. */\nfunction pickDyeFormat(gl: WebGL2RenderingContext): { internalFormat: number; type: number } {\n const hasFloat = gl.getExtension('EXT_color_buffer_float') !== null;\n return hasFloat\n ? { internalFormat: gl.RGBA16F, type: gl.HALF_FLOAT }\n : { internalFormat: gl.RGBA8, type: gl.UNSIGNED_BYTE };\n}\n\nexport function createMediumRuntime(gl: WebGL2RenderingContext, vertexSource: string): MediumRuntime {\n const format = pickDyeFormat(gl);\n\n const vaporForwardProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_VAPOR_FORWARD_SHADER));\n const vaporCorrectProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_VAPOR_CORRECT_SHADER));\n const vaporReactProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_VAPOR_REACT_SHADER));\n const condensateForwardProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_CONDENSATE_FORWARD_SHADER));\n const condensateCorrectProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_CONDENSATE_CORRECT_SHADER));\n const condensateReactProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_CONDENSATE_REACT_SHADER));\n const trackAdvectProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_TRACK_ADVECT_SHADER));\n const compositeProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_COMPOSITE_SHADER));\n const seedProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_SEED_SHADER));\n const emitProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_EMIT_SHADER));\n const resampleProgram = createProgram(gl, vertexSource, toGLSL(MEDIUM_RESAMPLE_SHADER));\n\n const vaporForwardLoc = locationCache(gl, vaporForwardProgram);\n const vaporCorrectLoc = locationCache(gl, vaporCorrectProgram);\n const vaporReactLoc = locationCache(gl, vaporReactProgram);\n const condensateForwardLoc = locationCache(gl, condensateForwardProgram);\n const condensateCorrectLoc = locationCache(gl, condensateCorrectProgram);\n const condensateReactLoc = locationCache(gl, condensateReactProgram);\n const trackAdvectLoc = locationCache(gl, trackAdvectProgram);\n const compositeLoc = locationCache(gl, compositeProgram);\n const seedLoc = locationCache(gl, seedProgram);\n const emitLoc = locationCache(gl, emitProgram);\n const resampleLoc = locationCache(gl, resampleProgram);\n\n let gridW = 0;\n let gridH = 0;\n let vapor: DyePair | null = null;\n let condensate: DyePair | null = null;\n let track: DyePair | null = null;\n // MacCormack scratch buffers for a single frame, not ping-pong: each is fully overwritten by its\n // own pass and read again within the SAME step — no persistent state carried between frames.\n let vaporForward: DyeTarget | null = null;\n let vaporCorrected: DyeTarget | null = null;\n let condensateForward: DyeTarget | null = null;\n let condensateCorrected: DyeTarget | null = null;\n let front = 0;\n\n function makeTarget(w: number, h: number): DyeTarget {\n const texture = createTexture(gl, {\n width: w,\n height: h,\n internalFormat: format.internalFormat,\n type: format.type,\n // REPEAT: a domain with no border — see TextureOptions.wrap. All buffers carry the same\n // field, so the border needs to behave identically for each of them, or vapor would wrap\n // while condensate or a track at the same coordinate cuts off. The MacCormack limiter also\n // reads neighbors across this same border — it needs REPEAT exactly as much as transport does.\n wrap: gl.REPEAT,\n });\n const fbo = createFramebuffer(gl, texture);\n return { texture, fbo };\n }\n\n function clearTarget(target: DyeTarget, w: number, h: number): void {\n gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);\n gl.viewport(0, 0, w, h);\n gl.clearColor(0, 0, 0, 1);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n }\n\n function destroyPair(pair: DyePair | null): void {\n if (!pair) return;\n for (const t of pair) {\n gl.deleteTexture(t.texture);\n gl.deleteFramebuffer(t.fbo);\n }\n }\n\n function destroyTarget(target: DyeTarget | null): void {\n if (!target) return;\n gl.deleteTexture(target.texture);\n gl.deleteFramebuffer(target.fbo);\n }\n\n function destroyTargets(): void {\n destroyPair(vapor);\n destroyPair(condensate);\n destroyPair(track);\n destroyTarget(vaporForward);\n destroyTarget(vaporCorrected);\n destroyTarget(condensateForward);\n destroyTarget(condensateCorrected);\n vapor = null;\n condensate = null;\n track = null;\n vaporForward = null;\n vaporCorrected = null;\n condensateForward = null;\n condensateCorrected = null;\n }\n\n // Three spots at fixed, spread-out grid points — the \"initial spots\", and the entire water\n // supply: it has no other source (the sum of vapor and condensate is conserved). Condensate and\n // tracks seed empty: only background condensation stamps into condensate on the first step, and\n // only `emit` stamps into tracks.\n function seed(w: number, h: number): void {\n if (!vapor || !condensate || !track) return;\n gl.bindFramebuffer(gl.FRAMEBUFFER, vapor[front].fbo);\n gl.viewport(0, 0, w, h);\n gl.disable(gl.BLEND);\n gl.useProgram(seedProgram);\n setUniform(gl, seedLoc('u_resolution'), [w, h]);\n setUniform(gl, seedLoc('u_spot0'), [w * 0.28, h * 0.64]);\n setUniform(gl, seedLoc('u_spot1'), [w * 0.7, h * 0.32]);\n setUniform(gl, seedLoc('u_spot2'), [w * 0.48, h * 0.84]);\n setUniform(gl, seedLoc('u_spotRadius'), Math.max(w, h) * 0.18);\n drawFullscreenTriangle(gl);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n clearTarget(condensate[0], w, h);\n clearTarget(condensate[1], w, h);\n clearTarget(track[0], w, h);\n clearTarget(track[1], w, h);\n }\n\n /** Bilinear-resamples one buffer into a differently-sized target — see MEDIUM_RESAMPLE_SHADER. */\n function resample(src: DyeTarget, srcW: number, srcH: number, dst: DyeTarget, dstW: number, dstH: number): void {\n gl.bindFramebuffer(gl.FRAMEBUFFER, dst.fbo);\n gl.viewport(0, 0, dstW, dstH);\n gl.disable(gl.BLEND);\n gl.useProgram(resampleProgram);\n bindTextureAt(gl, 0, src.texture, resampleProgram, 'u_src');\n setUniform(gl, resampleLoc('u_srcSize'), [srcW, srcH]);\n setUniform(gl, resampleLoc('u_resolution'), [dstW, dstH]);\n drawFullscreenTriangle(gl);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n }\n\n // A resize used to destroy every target and reseed from scratch — a phone rotation or a window\n // resize wiped the vapor back to its three initial spots and blinked condensate/tracks out, the\n // flicker the design forbids. Instead: bilinear-resample the OLD front buffers (the only ones\n // that carry real state — see the comment on the MacCormack scratch buffers above) into NEW,\n // differently-sized targets, then delete the old ones. Seeding stays a first-call-only thing.\n function resizeGrid(w: number, h: number): void {\n if (!vapor || !condensate || !track) return; // narrows for TS; callers already checked\n const oldVapor = vapor;\n const oldCondensate = condensate;\n const oldTrack = track;\n const oldW = gridW;\n const oldH = gridH;\n\n const newVaporFront = makeTarget(w, h);\n const newCondensateFront = makeTarget(w, h);\n const newTrackFront = makeTarget(w, h);\n resample(oldVapor[front], oldW, oldH, newVaporFront, w, h);\n resample(oldCondensate[front], oldW, oldH, newCondensateFront, w, h);\n resample(oldTrack[front], oldW, oldH, newTrackFront, w, h);\n\n destroyTargets();\n gridW = w;\n gridH = h;\n vapor = [newVaporFront, makeTarget(w, h)];\n condensate = [newCondensateFront, makeTarget(w, h)];\n track = [newTrackFront, makeTarget(w, h)];\n vaporForward = makeTarget(w, h);\n vaporCorrected = makeTarget(w, h);\n condensateForward = makeTarget(w, h);\n condensateCorrected = makeTarget(w, h);\n front = 0;\n\n // Total water scales with cell count, not just density: a bilinear resample roughly preserves\n // the AVERAGE value per cell, so the SUM over every cell scales with how many cells there now\n // are. Rescaling the EXISTING target (rather than re-measuring the just-resampled buffers)\n // keeps the invariant exact even if the resample itself is slightly lossy at the edges — the\n // periodic renormalization below already self-corrects any residual, and it needs a real\n // target to correct TOWARD rather than one that already baked in this resize's own error.\n // `timeSinceRenorm`/`framesSinceRenorm` are left running: the interval they measure doesn't\n // care about grid size, only about how much correction accumulated frame to frame.\n const areaRatio = (w * h) / (oldW * oldH);\n waterTargetTotal *= areaRatio;\n }\n\n function ensureGrid(gridWidth: number, gridHeight: number): void {\n const w = Math.max(2, Math.round(gridWidth));\n const h = Math.max(2, Math.round(gridHeight));\n if (vapor && condensate && track && w === gridW && h === gridH) return;\n\n if (!vapor || !condensate || !track) {\n gridW = w;\n gridH = h;\n vapor = [makeTarget(w, h), makeTarget(w, h)];\n condensate = [makeTarget(w, h), makeTarget(w, h)];\n track = [makeTarget(w, h), makeTarget(w, h)];\n vaporForward = makeTarget(w, h);\n vaporCorrected = makeTarget(w, h);\n condensateForward = makeTarget(w, h);\n condensateCorrected = makeTarget(w, h);\n front = 0;\n seed(w, h);\n // The target water total — right after seeding, before the first step: this is \"the entire\n // supply\" (see the comment on `seed`), and every later renormalization checks against it.\n // Right after seeding condensate is empty, so the target equals the vapor sum — but it's\n // re-verified every time as vapor+condensate, not just vapor, in case the initial state ever\n // changes.\n waterTargetTotal = readTarget(vapor[front]) + readTarget(condensate[front]);\n waterScale = 1;\n timeSinceRenorm = 0;\n framesSinceRenorm = 0;\n return;\n }\n\n resizeGrid(w, h);\n }\n\n /** Shared velocity-field uniforms — one set for both phases' forward/correct passes and for\n * tracks (condensate adds its own gravity on top — see `u_settleSpeed` where it's declared).\n * The react passes don't mind them either: the extra `loc()` calls for curl uniforms that don't\n * exist there just return null, and `setUniform` silently skips them. */\n function bindVelocityUniforms(\n loc: (name: string) => WebGLUniformLocation | null,\n dt: number,\n phase: number,\n p: VireUIKitMediumParams,\n spatial: readonly MediumSpatialOctave[],\n ): void {\n setUniform(gl, loc('u_dyeSize'), [gridW, gridH]);\n setUniform(gl, loc('u_resolution'), [gridW, gridH]);\n setUniform(gl, loc('u_dt'), dt);\n setUniform(gl, loc('u_phase'), phase);\n setUniform(gl, loc('u_curlFreq'), p.curlFreq);\n setUniform(gl, loc('u_advectSpeed'), p.advectSpeed);\n setUniform(gl, loc('u_turbulence'), p.turbulence);\n // Only the vapor forward/correct programs declare u_vaporDrift; elsewhere loc() returns null\n // and setUniform silently skips it, same as the curl uniforms already do for the react passes.\n setUniform(gl, loc('u_vaporDrift'), p.gravityVaporDrift);\n setUniform(gl, loc('u_spatial0'), [spatial[0].freqX, spatial[0].freqY, spatial[0].periodX, spatial[0].periodY]);\n setUniform(gl, loc('u_spatial1'), [spatial[1].freqX, spatial[1].freqY, spatial[1].periodX, spatial[1].periodY]);\n setUniform(gl, loc('u_spatial2'), [spatial[2].freqX, spatial[2].freqY, spatial[2].periodX, spatial[2].periodY]);\n setUniform(gl, loc('u_spatial3'), [spatial[3].freqX, spatial[3].freqY, spatial[3].periodX, spatial[3].periodY]);\n }\n\n // The field's phase — curl-noise's time axis, accumulated here in float64 rather than derived\n // from an ever-growing caller-supplied clock, and wrapped at MEDIUM_TIME_PERIOD (noise.ts's\n // MEDIUM_TIME_OCTAVES makes that wrap exact, not approximate). Sending a raw, ever-growing\n // `time * curlSpeed` product to the GPU as a float32 uniform eventually loses the bits that\n // place a point within its own lattice cell — after hours of playback the field would visibly\n // jitter in place. Wrapping the ACCUMULATOR keeps its magnitude bounded, and because the rate is\n // baked in incrementally rather than multiplied in fresh every frame, changing `curlSpeed` at\n // runtime changes the slope going forward instead of rescaling everything already accumulated —\n // no jump.\n let phase = 0;\n\n function wrapPhase(value: number): number {\n const wrapped = value % MEDIUM_TIME_PERIOD;\n return wrapped < 0 ? wrapped + MEDIUM_TIME_PERIOD : wrapped;\n }\n\n // A semi-Lagrangian gather over a non-uniform velocity has no obligation to conserve the sum by\n // itself, and neither does the vapor<->condensate exchange at non-integer rounding exponents\n // (not a boundary or step-size issue — at zero advection and zero exchange there's no loss at\n // all); rather than change the scheme, once a second the WATER sum (vapor+condensate) is checked\n // against its target and the correction is spread as an EQUAL multiplier over the frames of the\n // next interval (an Nth root — otherwise an N-fold ratio would overshoot the target instead of\n // reaching it).\n const WATER_RENORM_INTERVAL_S = 1;\n let waterTargetTotal = 0;\n let waterScale = 1;\n let timeSinceRenorm = 0;\n let framesSinceRenorm = 0;\n\n function step(dt: number, params: Partial<VireUIKitMediumParams> = {}): void {\n if (\n !vapor ||\n !condensate ||\n !track ||\n !vaporForward ||\n !vaporCorrected ||\n !condensateForward ||\n !condensateCorrected\n ) {\n return;\n }\n const p = { ...MEDIUM_DEFAULTS, ...params };\n // Per step, not cached on resize: a caller may override curlFreq between frames.\n const spatial = computeMediumSpatialPeriods(gridW, gridH, p.curlFreq);\n // dt is clamped: a tab returning from the background would otherwise carry advection past the\n // grid's bounds in one jump, sweeping all the smoke to an edge in a single frame.\n const clampedDt = Math.min(Math.max(dt, 0), 1 / 15);\n // Advance and wrap the phase BEFORE using it this frame — see the comment on `phase` above.\n phase = wrapPhase(phase + clampedDt * p.curlSpeed);\n const back = front === 0 ? 1 : 0;\n const gridSize: readonly [number, number] = [gridW, gridH];\n\n // --- Transport: MacCormack splits each phase into forward/correct, both phases independently\n // (only the react pass below reads the partner, and only on already grid-aligned values). ---\n\n gl.viewport(0, 0, gridW, gridH);\n gl.disable(gl.BLEND);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, vaporForward.fbo);\n gl.useProgram(vaporForwardProgram);\n bindTextureAt(gl, 0, vapor[front].texture, vaporForwardProgram, 'u_dye');\n bindVelocityUniforms(vaporForwardLoc, clampedDt, phase, p, spatial);\n drawFullscreenTriangle(gl);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, condensateForward.fbo);\n gl.useProgram(condensateForwardProgram);\n bindTextureAt(gl, 0, condensate[front].texture, condensateForwardProgram, 'u_dye');\n bindVelocityUniforms(condensateForwardLoc, clampedDt, phase, p, spatial);\n setUniform(gl, condensateForwardLoc('u_settleSpeed'), p.condensateSettleSpeed);\n setUniform(gl, condensateForwardLoc('u_settleWiggle'), p.condensateSettleWiggle);\n drawFullscreenTriangle(gl);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, vaporCorrected.fbo);\n gl.useProgram(vaporCorrectProgram);\n bindTextureAt(gl, 0, vapor[front].texture, vaporCorrectProgram, 'u_dye');\n bindTextureAt(gl, 1, vaporForward.texture, vaporCorrectProgram, 'u_forward');\n // bindTextureAt only binds the sampler, not its auto uniform size (the transpiler generates a\n // <name>Size for every `uniform shader`) — the second (cross-) sampler needs its own size set\n // separately, or it silently stays (0,0) and `.eval()` divides the coordinate by zero.\n setUniform(gl, vaporCorrectLoc('u_forwardSize'), gridSize);\n bindVelocityUniforms(vaporCorrectLoc, clampedDt, phase, p, spatial);\n drawFullscreenTriangle(gl);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, condensateCorrected.fbo);\n gl.useProgram(condensateCorrectProgram);\n bindTextureAt(gl, 0, condensate[front].texture, condensateCorrectProgram, 'u_dye');\n bindTextureAt(gl, 1, condensateForward.texture, condensateCorrectProgram, 'u_forward');\n setUniform(gl, condensateCorrectLoc('u_forwardSize'), gridSize);\n bindVelocityUniforms(condensateCorrectLoc, clampedDt, phase, p, spatial);\n setUniform(gl, condensateCorrectLoc('u_settleSpeed'), p.condensateSettleSpeed);\n setUniform(gl, condensateCorrectLoc('u_settleWiggle'), p.condensateSettleWiggle);\n drawFullscreenTriangle(gl);\n\n // --- Reaction: vapor<->condensate exchange and droplet growth, on the already transported\n // (MacCormack) fields, with the partner read directly at its OWN point xy — both fields are\n // already grid-aligned. ---\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, vapor[back].fbo);\n gl.useProgram(vaporReactProgram);\n bindTextureAt(gl, 0, vaporCorrected.texture, vaporReactProgram, 'u_transported');\n bindTextureAt(gl, 1, condensateCorrected.texture, vaporReactProgram, 'u_condensateTransported');\n setUniform(gl, vaporReactLoc('u_transportedSize'), gridSize);\n setUniform(gl, vaporReactLoc('u_condensateTransportedSize'), gridSize);\n setUniform(gl, vaporReactLoc('u_resolution'), gridSize);\n setUniform(gl, vaporReactLoc('u_dt'), clampedDt);\n setUniform(gl, vaporReactLoc('u_condensationRate'), p.condensationRate);\n setUniform(gl, vaporReactLoc('u_condensationFloor'), p.condensationFloor);\n setUniform(gl, vaporReactLoc('u_evaporationRate'), p.evaporationRate);\n setUniform(gl, vaporReactLoc('u_totalScale'), waterScale);\n drawFullscreenTriangle(gl);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, condensate[back].fbo);\n gl.useProgram(condensateReactProgram);\n bindTextureAt(gl, 0, condensateCorrected.texture, condensateReactProgram, 'u_transported');\n bindTextureAt(gl, 1, vaporCorrected.texture, condensateReactProgram, 'u_vaporTransported');\n // Growth reads the neighbors of its OWN cell xy in the ORIGINAL (pre-transport-this-frame)\n // condensate — see the comment in advect-shader.ts on MEDIUM_CONDENSATE_REACT_SHADER — so the\n // third sampler is the old front buffer.\n bindTextureAt(gl, 2, condensate[front].texture, condensateReactProgram, 'u_dye');\n setUniform(gl, condensateReactLoc('u_transportedSize'), gridSize);\n setUniform(gl, condensateReactLoc('u_vaporTransportedSize'), gridSize);\n setUniform(gl, condensateReactLoc('u_dyeSize'), gridSize);\n setUniform(gl, condensateReactLoc('u_resolution'), gridSize);\n setUniform(gl, condensateReactLoc('u_dt'), clampedDt);\n setUniform(gl, condensateReactLoc('u_condensationRate'), p.condensationRate);\n setUniform(gl, condensateReactLoc('u_condensationFloor'), p.condensationFloor);\n setUniform(gl, condensateReactLoc('u_evaporationRate'), p.evaporationRate);\n setUniform(gl, condensateReactLoc('u_spreadRate'), p.condensateSpreadRate);\n setUniform(gl, condensateReactLoc('u_totalScale'), waterScale);\n drawFullscreenTriangle(gl);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, track[back].fbo);\n gl.useProgram(trackAdvectProgram);\n bindTextureAt(gl, 0, track[front].texture, trackAdvectProgram, 'u_dye');\n bindVelocityUniforms(trackAdvectLoc, clampedDt, phase, p, spatial);\n setUniform(gl, trackAdvectLoc('u_decay'), p.decay);\n drawFullscreenTriangle(gl);\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n front = back;\n framesSinceRenorm += 1;\n\n timeSinceRenorm += clampedDt;\n if (waterTargetTotal > 0 && timeSinceRenorm >= WATER_RENORM_INTERVAL_S && framesSinceRenorm > 0) {\n const current = readTarget(vapor[front]) + readTarget(condensate[front]);\n if (current > 1e-6) {\n // The Nth root: applied N times in a row (N frames of the past interval) has to PRODUCE\n // the ratio target/current, not apply that ratio in EACH frame — otherwise one second's\n // correction would fire N times and overshoot the target instead of reaching it.\n const totalRatio = waterTargetTotal / current;\n const perFrame = Math.pow(totalRatio, 1 / framesSinceRenorm);\n waterScale = Math.min(1.05, Math.max(0.95, perFrame));\n }\n timeSinceRenorm = 0;\n framesSinceRenorm = 0;\n }\n }\n\n // Stamping goes into the TRACK buffer (the one advection just wrote) via additive blending,\n // exactly as track stamping requires: not a separate pass, not its own buffer, not water.\n function emit(emissions: readonly VireUIKitMediumEmission[]): void {\n if (!track || emissions.length === 0) return;\n const minDim = Math.min(gridW, gridH);\n gl.bindFramebuffer(gl.FRAMEBUFFER, track[front].fbo);\n gl.viewport(0, 0, gridW, gridH);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE);\n gl.useProgram(emitProgram);\n // The flip-convention comment above doesn't apply here: the shader never reads its own\n // previous frame (no `.eval()`), so there's nothing to flip — but the transpiler always\n // requires u_resolution regardless (see `targets/glsl.ts`: the entry coordinate goes through\n // it unconditionally).\n setUniform(gl, emitLoc('u_resolution'), [gridW, gridH]);\n for (const e of emissions) {\n const source: [number, number] = [e.source[0] * gridW, e.source[1] * gridH];\n const length = e.lengthFrac * minDim;\n const head: [number, number] = [\n source[0] + Math.cos(e.angle) * length,\n source[1] + Math.sin(e.angle) * length,\n ];\n setUniform(gl, emitLoc('u_source'), source);\n setUniform(gl, emitLoc('u_head'), head);\n setUniform(gl, emitLoc('u_headWidth'), e.headWidthFrac * minDim);\n setUniform(gl, emitLoc('u_tailWidth'), e.tailWidthFrac * minDim);\n setUniform(gl, emitLoc('u_raggedAmp'), e.raggedFrac * minDim);\n setUniform(gl, emitLoc('u_raggedFreq'), e.raggedFreq);\n setUniform(gl, emitLoc('u_settle'), e.settleFrac * minDim);\n setUniform(gl, emitLoc('u_tailDim'), e.tailDim);\n setUniform(gl, emitLoc('u_intensity'), e.intensity);\n setUniform(gl, emitLoc('u_seed'), e.seed);\n setUniform(gl, emitLoc('u_channelMask'), e.channel);\n drawFullscreenTriangle(gl);\n }\n gl.disable(gl.BLEND);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n }\n\n function composite(\n contentWidth: number,\n contentHeight: number,\n params: Partial<VireUIKitMediumParams> = {},\n ): void {\n if (!vapor || !condensate || !track) return;\n const p = { ...MEDIUM_DEFAULTS, ...params };\n gl.viewport(0, 0, contentWidth, contentHeight);\n gl.disable(gl.BLEND);\n gl.useProgram(compositeProgram);\n bindTextureAt(gl, 0, vapor[front].texture, compositeProgram, 'u_vapor');\n bindTextureAt(gl, 1, condensate[front].texture, compositeProgram, 'u_condensate');\n bindTextureAt(gl, 2, track[front].texture, compositeProgram, 'u_track');\n setUniform(gl, compositeLoc('u_vaporSize'), [gridW, gridH]);\n setUniform(gl, compositeLoc('u_condensateSize'), [gridW, gridH]);\n setUniform(gl, compositeLoc('u_trackSize'), [gridW, gridH]);\n setUniform(gl, compositeLoc('u_resolution'), [contentWidth, contentHeight]);\n setUniform(gl, compositeLoc('u_dyeScale'), [gridW / contentWidth, gridH / contentHeight]);\n // The palette's forward direction (sRGB → Oklab) — here, once per frame (frame constants),\n // not in the shader per pixel (see the comment in composite-shader.ts).\n setUniform(gl, compositeLoc('u_lab0'), srgbToOklab(p.channelColors[0]));\n setUniform(gl, compositeLoc('u_lab1'), srgbToOklab(p.channelColors[1]));\n setUniform(gl, compositeLoc('u_lab2'), srgbToOklab(p.channelColors[2]));\n setUniform(gl, compositeLoc('u_labBg'), srgbToOklab(p.baseColor));\n setUniform(gl, compositeLoc('u_labCondensate'), srgbToOklab(p.condensateColor));\n setUniform(gl, compositeLoc('u_baseWeight'), p.baseWeight);\n setUniform(gl, compositeLoc('u_condensateTint'), p.condensateTint);\n setUniform(gl, compositeLoc('u_condensateGain'), p.condensateGain);\n setUniform(gl, compositeLoc('u_farScale'), p.depthFarScale);\n // Resolved against the GRID's own size (baseSrc is already grid-px), not content size — a\n // fixed fraction of a wrapping axis, not a fixed pixel count (see depthFarOffsetPx's own\n // comment for why a fixed grid-px value doesn't survive every grid size).\n setUniform(gl, compositeLoc('u_farOffset'), depthFarOffsetPx(p.depthFarOffsetFrac, gridW, gridH));\n setUniform(gl, compositeLoc('u_farBlurRadius'), p.depthFarBlurRadius);\n setUniform(gl, compositeLoc('u_farWeight'), p.depthFarWeight);\n setUniform(gl, compositeLoc('u_gravityBoost'), p.gravityBottomBoost);\n setUniform(gl, compositeLoc('u_gravityBoostStart'), p.gravityBottomBoostStart);\n drawFullscreenTriangle(gl);\n }\n\n function readTarget(target: DyeTarget): number {\n gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);\n let sum = 0;\n if (format.type === gl.HALF_FLOAT) {\n const buf = new Float32Array(gridW * gridH * 4);\n gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.FLOAT, buf);\n for (let i = 0; i < buf.length; i += 4) sum += buf[i] + buf[i + 1] + buf[i + 2];\n } else {\n const buf = new Uint8Array(gridW * gridH * 4);\n gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.UNSIGNED_BYTE, buf);\n for (let i = 0; i < buf.length; i += 4) sum += (buf[i] + buf[i + 1] + buf[i + 2]) / 255;\n }\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n return sum;\n }\n\n function readTotals(): { vapor: number; condensate: number; track: number } | null {\n if (!vapor || !condensate || !track) return null;\n return {\n vapor: readTarget(vapor[front]),\n condensate: readTarget(condensate[front]),\n track: readTarget(track[front]),\n };\n }\n\n // Three numbers per cell (r/g/b of the three vapor species), not a sum: the structure gate\n // (medium-structure-gate.mjs) measures contrast BETWEEN species — a summed density would lose it.\n function readVaporGrid(): { cols: number; rows: number; data: number[] } | null {\n if (!vapor) return null;\n gl.bindFramebuffer(gl.FRAMEBUFFER, vapor[front].fbo);\n const cells = gridW * gridH;\n const data = new Array(cells * 3);\n if (format.type === gl.HALF_FLOAT) {\n const buf = new Float32Array(cells * 4);\n gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.FLOAT, buf);\n for (let i = 0; i < cells; i += 1) {\n data[i * 3] = buf[i * 4];\n data[i * 3 + 1] = buf[i * 4 + 1];\n data[i * 3 + 2] = buf[i * 4 + 2];\n }\n } else {\n const buf = new Uint8Array(cells * 4);\n gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.UNSIGNED_BYTE, buf);\n for (let i = 0; i < cells; i += 1) {\n data[i * 3] = buf[i * 4] / 255;\n data[i * 3 + 1] = buf[i * 4 + 1] / 255;\n data[i * 3 + 2] = buf[i * 4 + 2] / 255;\n }\n }\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n return { cols: gridW, rows: gridH, data };\n }\n\n // One number per cell, not three: condensate has no color of its own (density lives in .r,\n // g/b unused — see MEDIUM_CONDENSATE_REACT_SHADER). A gravity gate reads THIS, row-weighting the\n // density to find where the mass sits, without decoding an RGB triple that's two-thirds padding.\n function readCondensateGrid(): { cols: number; rows: number; data: number[] } | null {\n if (!condensate) return null;\n gl.bindFramebuffer(gl.FRAMEBUFFER, condensate[front].fbo);\n const cells = gridW * gridH;\n const data = new Array(cells);\n if (format.type === gl.HALF_FLOAT) {\n const buf = new Float32Array(cells * 4);\n gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.FLOAT, buf);\n for (let i = 0; i < cells; i += 1) data[i] = buf[i * 4];\n } else {\n const buf = new Uint8Array(cells * 4);\n gl.readPixels(0, 0, gridW, gridH, gl.RGBA, gl.UNSIGNED_BYTE, buf);\n for (let i = 0; i < cells; i += 1) data[i] = buf[i * 4] / 255;\n }\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n return { cols: gridW, rows: gridH, data };\n }\n\n function destroy(): void {\n destroyTargets();\n gl.deleteProgram(vaporForwardProgram);\n gl.deleteProgram(vaporCorrectProgram);\n gl.deleteProgram(vaporReactProgram);\n gl.deleteProgram(condensateForwardProgram);\n gl.deleteProgram(condensateCorrectProgram);\n gl.deleteProgram(condensateReactProgram);\n gl.deleteProgram(trackAdvectProgram);\n gl.deleteProgram(compositeProgram);\n gl.deleteProgram(seedProgram);\n gl.deleteProgram(emitProgram);\n gl.deleteProgram(resampleProgram);\n }\n\n return { ensureGrid, step, emit, composite, readTotals, readVaporGrid, readCondensateGrid, destroy };\n}\n","import {\n FULLSCREEN_TRIANGLE_VERTEX_SOURCE,\n type VireGlassBackdropPass,\n} from 'vireglass/web';\nimport type { VireUIKitMediumEmission, VireUIKitMediumParams } from '../medium';\nimport { createMediumRuntime, type MediumRuntime } from './medium';\n\n/** The simulation grid — its own resolution, coarser than the frame. `dt` is the caller's clock:\n * pausing on `prefers-reduced-motion` and testability both stay on its side. There is no `time`\n * field — the field's phase is the runtime's own accumulated state (see `u_phase` in\n * `web/medium.ts`), not something the caller has to track across an arbitrarily long session. */\nexport type MediumFrame = {\n gridWidth: number;\n gridHeight: number;\n dt: number;\n params?: Partial<VireUIKitMediumParams>;\n /** Tracks stamped this frame; `createMediumDynamics` schedules them. */\n emissions?: readonly VireUIKitMediumEmission[];\n};\n\nexport type MediumBackdrop = {\n /** The VireGlass renderer's backdrop for this frame: steps the medium and composites it into\n * the lens's texture. */\n pass(frame: MediumFrame): VireGlassBackdropPass;\n /** Measurement hooks for balance and structure gates; `null` until the medium has rendered once. */\n readTotals(): { vapor: number; condensate: number; track: number } | null;\n readVaporGrid(): { cols: number; rows: number; data: number[] } | null;\n readCondensateGrid(): { cols: number; rows: number; data: number[] } | null;\n destroy(): void;\n};\n\nexport function createMediumBackdrop(): MediumBackdrop {\n let runtime: MediumRuntime | null = null;\n let owner: WebGL2RenderingContext | null = null;\n\n return {\n pass: (frame) => (gl, target) => {\n if (owner && owner !== gl) throw new Error('vireuikit: the medium is already bound to another WebGL context');\n owner = gl;\n runtime ??= createMediumRuntime(gl, FULLSCREEN_TRIANGLE_VERTEX_SOURCE);\n runtime.ensureGrid(frame.gridWidth, frame.gridHeight);\n runtime.step(frame.dt, frame.params);\n runtime.emit(frame.emissions ?? []);\n gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);\n runtime.composite(target.width, target.height, frame.params);\n },\n readTotals: () => runtime?.readTotals() ?? null,\n readVaporGrid: () => runtime?.readVaporGrid() ?? null,\n readCondensateGrid: () => runtime?.readCondensateGrid() ?? null,\n destroy: () => {\n runtime?.destroy();\n runtime = null;\n owner = null;\n },\n };\n}\n"]}
|