@wolsok/thanos 17.0.38 → 18.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +73 -12
  2. package/esm2022/lib/canvas-particle-renderer.js +98 -0
  3. package/esm2022/lib/canvas-particle-renderer.js.map +1 -0
  4. package/esm2022/lib/capture-scale.js +13 -0
  5. package/esm2022/lib/capture-scale.js.map +1 -0
  6. package/esm2022/lib/choose-particle-renderer.js +78 -0
  7. package/esm2022/lib/choose-particle-renderer.js.map +1 -0
  8. package/esm2022/lib/create-ws-thanos-options.js +4 -1
  9. package/esm2022/lib/create-ws-thanos-options.js.map +1 -1
  10. package/esm2022/lib/crumble-shape.js +24 -0
  11. package/esm2022/lib/crumble-shape.js.map +1 -0
  12. package/esm2022/lib/particle-renderer.js +1 -0
  13. package/esm2022/lib/particle-renderer.js.map +1 -0
  14. package/esm2022/lib/particles.js +84 -0
  15. package/esm2022/lib/particles.js.map +1 -0
  16. package/esm2022/lib/snap-sound-envelopes.js +38 -0
  17. package/esm2022/lib/snap-sound-envelopes.js.map +1 -0
  18. package/esm2022/lib/snap-sound.service.js +287 -0
  19. package/esm2022/lib/snap-sound.service.js.map +1 -0
  20. package/esm2022/lib/vaporizing-front.js +48 -0
  21. package/esm2022/lib/vaporizing-front.js.map +1 -0
  22. package/esm2022/lib/webgl/simplex-noise.glsl.js +69 -0
  23. package/esm2022/lib/webgl/simplex-noise.glsl.js.map +1 -0
  24. package/esm2022/lib/webgl/webgl-particle-renderer.js +308 -0
  25. package/esm2022/lib/webgl/webgl-particle-renderer.js.map +1 -0
  26. package/esm2022/lib/webgl/webgl-shaders.js +323 -0
  27. package/esm2022/lib/webgl/webgl-shaders.js.map +1 -0
  28. package/esm2022/lib/ws-thanos.directive.js +8 -6
  29. package/esm2022/lib/ws-thanos.directive.js.map +1 -1
  30. package/esm2022/lib/ws-thanos.options.js.map +1 -1
  31. package/esm2022/lib/ws-thanos.service.js +103 -220
  32. package/esm2022/lib/ws-thanos.service.js.map +1 -1
  33. package/lib/canvas-particle-renderer.d.ts +29 -0
  34. package/lib/capture-scale.d.ts +14 -0
  35. package/lib/choose-particle-renderer.d.ts +31 -0
  36. package/lib/crumble-shape.d.ts +11 -0
  37. package/lib/particle-renderer.d.ts +26 -0
  38. package/lib/particles.d.ts +35 -0
  39. package/lib/snap-sound-envelopes.d.ts +15 -0
  40. package/lib/snap-sound.service.d.ts +27 -0
  41. package/lib/vaporizing-front.d.ts +34 -0
  42. package/lib/webgl/simplex-noise.glsl.d.ts +5 -0
  43. package/lib/webgl/webgl-particle-renderer.d.ts +46 -0
  44. package/lib/webgl/webgl-shaders.d.ts +27 -0
  45. package/lib/ws-thanos.directive.d.ts +4 -2
  46. package/lib/ws-thanos.options.d.ts +14 -0
  47. package/lib/ws-thanos.service.d.ts +11 -8
  48. package/package.json +1 -1
@@ -0,0 +1,308 @@
1
+ import { EFFECT_HEIGHT_SCALE, EFFECT_WIDTH_SCALE } from '../capture-scale';
2
+ import { crumbleShape } from '../crumble-shape';
3
+ import { PARTICLE_STATE_LENGTH, ParticleStateIndex, } from '../particles';
4
+ import { vaporizingFront } from '../vaporizing-front';
5
+ import { AttributeLocation, CRACK_FEEDBACK_VARYINGS, CRACK_LENGTH, CRACK_VERTEX_SHADER, CRUMBLE_MODE, DRAW_FRAGMENT_SHADER, DRAW_VERTEX_SHADER, FEEDBACK_FRAGMENT_SHADER, UPDATE_FEEDBACK_VARYINGS, UPDATE_VERTEX_SHADER, } from './webgl-shaders';
6
+ const FLOAT_BYTES = Float32Array.BYTES_PER_ELEMENT;
7
+ const STATE_STRIDE = PARTICLE_STATE_LENGTH * FLOAT_BYTES;
8
+ const CRACK_STRIDE = CRACK_LENGTH * FLOAT_BYTES;
9
+ const UPDATE_UNIFORMS = [
10
+ 'uSize',
11
+ 'uDeltaTSec',
12
+ 'uAnimationT',
13
+ 'uParticleAcceleration',
14
+ 'uSeed',
15
+ 'uCrumble',
16
+ 'uPixelScale',
17
+ 'uFrontTime',
18
+ 'uFrontRadiusPow',
19
+ 'uFade',
20
+ 'uCoarseCrack',
21
+ 'uFineCrack',
22
+ 'uShardGrow',
23
+ ];
24
+ const DRAW_UNIFORMS = ['uSize', 'uPixelScale', 'uShardGrow'];
25
+ /** smallest shard size in css pixels */
26
+ const MIN_CELL_SIZE = 28;
27
+ /**
28
+ * Simulates and draws the particles on the GPU.
29
+ * The particle state lives in two buffers: every frame the update program reads
30
+ * one and writes the other via transform feedback, then the draw program renders it.
31
+ * Once at the start the crack program computes the shard and crack distances of every particle.
32
+ */
33
+ export class WebGlParticleRenderer {
34
+ /** get a WebGL2 context for the canvas or null if the browser can't */
35
+ static createContext(canvas) {
36
+ try {
37
+ return canvas.getContext('webgl2', {
38
+ alpha: true,
39
+ premultipliedAlpha: true,
40
+ antialias: false,
41
+ depth: false,
42
+ stencil: false,
43
+ });
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ /** the largest canvas size the GPU can render into */
50
+ static maxCanvasSize(gl) {
51
+ const viewport = gl.getParameter(gl.MAX_VIEWPORT_DIMS) ?? [];
52
+ return Math.min(gl.getParameter(gl.MAX_RENDERBUFFER_SIZE), viewport[0] ?? Infinity, viewport[1] ?? Infinity);
53
+ }
54
+ constructor(canvas, gl,
55
+ // only the scalars are kept, the particle arrays live on the GPU
56
+ particles, params) {
57
+ this.canvas = canvas;
58
+ this.gl = gl;
59
+ this.params = params;
60
+ this.kind = 'webgl';
61
+ this.buffers = [];
62
+ this.current = 0;
63
+ this.crumble = params.crumble;
64
+ this.count = particles.count;
65
+ this.maxParticleX = particles.maxParticleX;
66
+ this.minParticleY = particles.minParticleY;
67
+ canvas.width = params.width;
68
+ canvas.height = params.height;
69
+ this.updateProgram = this.createProgram(UPDATE_VERTEX_SHADER, FEEDBACK_FRAGMENT_SHADER, UPDATE_FEEDBACK_VARYINGS);
70
+ this.drawProgram = this.createProgram(DRAW_VERTEX_SHADER, DRAW_FRAGMENT_SHADER);
71
+ // the second state buffer is written by the first frame, it needs no data
72
+ this.stateBuffers = [
73
+ this.createBuffer(particles.state, gl.DYNAMIC_COPY),
74
+ this.createBuffer(particles.state.byteLength, gl.DYNAMIC_COPY),
75
+ ];
76
+ const colorBuffer = this.createBuffer(particles.colors, gl.STATIC_DRAW);
77
+ // dust has no cracks, the shaders then read the default attribute values
78
+ const crackBuffer = params.crumble === 'dust' ? undefined : this.createCracks();
79
+ const crackAttributes = crackBuffer
80
+ ? [
81
+ {
82
+ buffer: crackBuffer,
83
+ location: AttributeLocation.CRACK,
84
+ size: 4,
85
+ stride: CRACK_STRIDE,
86
+ offset: 0,
87
+ },
88
+ {
89
+ buffer: crackBuffer,
90
+ location: AttributeLocation.CELL_RANDOM,
91
+ size: 1,
92
+ stride: CRACK_STRIDE,
93
+ offset: 4 * FLOAT_BYTES,
94
+ },
95
+ ]
96
+ : [];
97
+ this.updateVaos = this.stateBuffers.map((buffer) => this.createVao([
98
+ ...stateAttributes(buffer, [
99
+ [AttributeLocation.POSITION, 2, ParticleStateIndex.X],
100
+ [AttributeLocation.VELOCITY, 2, ParticleStateIndex.VX],
101
+ [AttributeLocation.ACCELERATION, 2, ParticleStateIndex.AX],
102
+ [AttributeLocation.ALPHA, 1, ParticleStateIndex.ALPHA],
103
+ [AttributeLocation.RELEASED_AT, 1, ParticleStateIndex.RELEASED_AT],
104
+ ]),
105
+ ...crackAttributes,
106
+ ]));
107
+ this.drawVaos = this.stateBuffers.map((buffer) => this.createVao([
108
+ ...stateAttributes(buffer, [
109
+ [AttributeLocation.POSITION, 2, ParticleStateIndex.X],
110
+ [AttributeLocation.ALPHA, 1, ParticleStateIndex.ALPHA],
111
+ [AttributeLocation.RELEASED_AT, 1, ParticleStateIndex.RELEASED_AT],
112
+ ]),
113
+ ...crackAttributes,
114
+ {
115
+ buffer: colorBuffer,
116
+ location: AttributeLocation.COLOR,
117
+ size: 4,
118
+ type: gl.UNSIGNED_BYTE,
119
+ normalized: true,
120
+ stride: 0,
121
+ offset: 0,
122
+ },
123
+ ]));
124
+ this.updateUniforms = this.uniformLocations(this.updateProgram, UPDATE_UNIFORMS);
125
+ this.drawUniforms = this.uniformLocations(this.drawProgram, DRAW_UNIFORMS);
126
+ // everything that stays the same for the whole animation is set once
127
+ gl.useProgram(this.updateProgram);
128
+ gl.uniform2f(this.updateUniforms.uSize, params.width, params.height);
129
+ gl.uniform1f(this.updateUniforms.uParticleAcceleration, params.particleAcceleration);
130
+ gl.uniform1f(this.updateUniforms.uSeed, params.seed);
131
+ gl.uniform1i(this.updateUniforms.uCrumble, CRUMBLE_MODE[params.crumble]);
132
+ gl.uniform1f(this.updateUniforms.uPixelScale, params.pixelScale);
133
+ gl.useProgram(this.drawProgram);
134
+ gl.uniform2f(this.drawUniforms.uSize, params.width, params.height);
135
+ gl.uniform1f(this.drawUniforms.uPixelScale, params.pixelScale);
136
+ gl.viewport(0, 0, params.width, params.height);
137
+ gl.enable(gl.BLEND);
138
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
139
+ gl.clearColor(0, 0, 0, 0);
140
+ }
141
+ render({ deltaTSec, animationT }) {
142
+ const { gl, params } = this;
143
+ if (gl.isContextLost()) {
144
+ return;
145
+ }
146
+ const next = 1 - this.current;
147
+ const front = vaporizingFront(animationT, this.maxParticleX, this.minParticleY, params.height);
148
+ const shape = crumbleShape(params.crumble, animationT);
149
+ // simulate: read the current state, write the next one
150
+ const uniforms = this.updateUniforms;
151
+ gl.useProgram(this.updateProgram);
152
+ gl.uniform1f(uniforms.uDeltaTSec, deltaTSec);
153
+ gl.uniform1f(uniforms.uAnimationT, animationT);
154
+ gl.uniform1f(uniforms.uFrontTime, front.time);
155
+ gl.uniform1f(uniforms.uFrontRadiusPow, front.radiusPow);
156
+ gl.uniform1f(uniforms.uFade, front.fade);
157
+ gl.uniform1f(uniforms.uCoarseCrack, shape.coarseCrack * params.pixelScale);
158
+ gl.uniform1f(uniforms.uFineCrack, shape.fineCrack * params.pixelScale);
159
+ gl.uniform1f(uniforms.uShardGrow, shape.shardGrow);
160
+ gl.bindVertexArray(this.updateVaos[this.current]);
161
+ this.runTransformFeedback(this.stateBuffers[next]);
162
+ // draw the next state
163
+ gl.clear(gl.COLOR_BUFFER_BIT);
164
+ gl.useProgram(this.drawProgram);
165
+ gl.uniform1f(this.drawUniforms.uShardGrow, shape.shardGrow);
166
+ gl.bindVertexArray(this.drawVaos[next]);
167
+ gl.drawArrays(gl.POINTS, 0, this.count);
168
+ gl.bindVertexArray(null);
169
+ this.current = next;
170
+ }
171
+ dispose() {
172
+ const { gl } = this;
173
+ [...this.updateVaos, ...this.drawVaos].forEach((vao) => gl.deleteVertexArray(vao));
174
+ this.buffers.forEach((buffer) => gl.deleteBuffer(buffer));
175
+ gl.deleteProgram(this.updateProgram);
176
+ gl.deleteProgram(this.drawProgram);
177
+ // browsers only allow a few live contexts, free this one right away
178
+ gl.getExtension('WEBGL_lose_context')?.loseContext();
179
+ this.canvas.remove();
180
+ }
181
+ /** run the bound vertex array through the bound program into the target buffer */
182
+ runTransformFeedback(target) {
183
+ const { gl } = this;
184
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, target);
185
+ gl.enable(gl.RASTERIZER_DISCARD);
186
+ gl.beginTransformFeedback(gl.POINTS);
187
+ gl.drawArrays(gl.POINTS, 0, this.count);
188
+ gl.endTransformFeedback();
189
+ gl.disable(gl.RASTERIZER_DISCARD);
190
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
191
+ }
192
+ /** run the crack program once: shard center and crack distances per particle */
193
+ createCracks() {
194
+ const { gl, params } = this;
195
+ const crackBuffer = this.createBuffer(this.count * CRACK_STRIDE, gl.STATIC_COPY);
196
+ const program = this.createProgram(CRACK_VERTEX_SHADER, FEEDBACK_FRAGMENT_SHADER, CRACK_FEEDBACK_VARYINGS);
197
+ const elementSize = Math.min(params.width / EFFECT_WIDTH_SCALE, params.height / EFFECT_HEIGHT_SCALE);
198
+ const cellSize = Math.max(MIN_CELL_SIZE * params.pixelScale, elementSize / 2.2);
199
+ const uniforms = this.uniformLocations(program, [
200
+ 'uCellSize',
201
+ 'uSeed',
202
+ ]);
203
+ // the first state buffer still holds the start positions
204
+ const vao = this.createVao(stateAttributes(this.stateBuffers[0], [
205
+ [AttributeLocation.POSITION, 2, ParticleStateIndex.X],
206
+ ]));
207
+ gl.useProgram(program);
208
+ gl.uniform1f(uniforms.uCellSize, cellSize);
209
+ gl.uniform1f(uniforms.uSeed, params.seed);
210
+ gl.bindVertexArray(vao);
211
+ this.runTransformFeedback(crackBuffer);
212
+ gl.bindVertexArray(null);
213
+ gl.deleteVertexArray(vao);
214
+ gl.deleteProgram(program);
215
+ return crackBuffer;
216
+ }
217
+ uniformLocations(program, names) {
218
+ const uniforms = {};
219
+ for (const name of names) {
220
+ uniforms[name] = this.gl.getUniformLocation(program, name);
221
+ }
222
+ return uniforms;
223
+ }
224
+ createProgram(vertexSource, fragmentSource, transformFeedbackVaryings) {
225
+ const { gl } = this;
226
+ const program = gl.createProgram();
227
+ if (program == null) {
228
+ throw new Error('Could not create WebGL program');
229
+ }
230
+ const vertexShader = this.compileShader(gl.VERTEX_SHADER, vertexSource);
231
+ const fragmentShader = this.compileShader(gl.FRAGMENT_SHADER, fragmentSource);
232
+ gl.attachShader(program, vertexShader);
233
+ gl.attachShader(program, fragmentShader);
234
+ if (transformFeedbackVaryings) {
235
+ gl.transformFeedbackVaryings(program, transformFeedbackVaryings, gl.INTERLEAVED_ATTRIBS);
236
+ }
237
+ gl.linkProgram(program);
238
+ gl.deleteShader(vertexShader);
239
+ gl.deleteShader(fragmentShader);
240
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
241
+ const log = gl.getProgramInfoLog(program);
242
+ gl.deleteProgram(program);
243
+ throw new Error(`Could not link WebGL program: ${log}`);
244
+ }
245
+ return program;
246
+ }
247
+ compileShader(type, source) {
248
+ const { gl } = this;
249
+ const shader = gl.createShader(type);
250
+ if (shader == null) {
251
+ throw new Error('Could not create WebGL shader');
252
+ }
253
+ gl.shaderSource(shader, source);
254
+ gl.compileShader(shader);
255
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
256
+ const log = gl.getShaderInfoLog(shader);
257
+ gl.deleteShader(shader);
258
+ throw new Error(`Could not compile WebGL shader: ${log}`);
259
+ }
260
+ return shader;
261
+ }
262
+ /** a buffer with the given data, or of the given byte size when it is filled on the GPU */
263
+ createBuffer(data, usage) {
264
+ const { gl } = this;
265
+ const buffer = gl.createBuffer();
266
+ if (buffer == null) {
267
+ throw new Error('Could not create WebGL buffer');
268
+ }
269
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
270
+ // bufferData has separate overloads for a byte size and for data
271
+ if (typeof data === 'number') {
272
+ gl.bufferData(gl.ARRAY_BUFFER, data, usage);
273
+ }
274
+ else {
275
+ gl.bufferData(gl.ARRAY_BUFFER, data, usage);
276
+ }
277
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
278
+ this.buffers.push(buffer);
279
+ return buffer;
280
+ }
281
+ createVao(bindings) {
282
+ const { gl } = this;
283
+ const vao = gl.createVertexArray();
284
+ if (vao == null) {
285
+ throw new Error('Could not create WebGL vertex array');
286
+ }
287
+ gl.bindVertexArray(vao);
288
+ for (const binding of bindings) {
289
+ gl.bindBuffer(gl.ARRAY_BUFFER, binding.buffer);
290
+ gl.enableVertexAttribArray(binding.location);
291
+ gl.vertexAttribPointer(binding.location, binding.size, binding.type ?? gl.FLOAT, binding.normalized ?? false, binding.stride, binding.offset);
292
+ }
293
+ gl.bindVertexArray(null);
294
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
295
+ return vao;
296
+ }
297
+ }
298
+ /** attributes read from the interleaved particle state */
299
+ function stateAttributes(buffer, attributes) {
300
+ return attributes.map(([location, size, index]) => ({
301
+ buffer,
302
+ location,
303
+ size,
304
+ stride: STATE_STRIDE,
305
+ offset: index * FLOAT_BYTES,
306
+ }));
307
+ }
308
+ //# sourceMappingURL=webgl-particle-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgl-particle-renderer.js","sourceRoot":"","sources":["../../../../../../../libs/public/ws-thanos/src/lib/webgl/webgl-particle-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { AnimationState } from '../animation.state';\nimport { EFFECT_HEIGHT_SCALE, EFFECT_WIDTH_SCALE } from '../capture-scale';\nimport { crumbleShape } from '../crumble-shape';\nimport { ParticleRenderer, ParticleRendererParams } from '../particle-renderer';\nimport {\n PARTICLE_STATE_LENGTH,\n ParticleStateIndex,\n Particles,\n} from '../particles';\nimport { vaporizingFront } from '../vaporizing-front';\nimport { WsThanosCrumble } from '../ws-thanos.options';\nimport {\n AttributeLocation,\n CRACK_FEEDBACK_VARYINGS,\n CRACK_LENGTH,\n CRACK_VERTEX_SHADER,\n CRUMBLE_MODE,\n DRAW_FRAGMENT_SHADER,\n DRAW_VERTEX_SHADER,\n FEEDBACK_FRAGMENT_SHADER,\n UPDATE_FEEDBACK_VARYINGS,\n UPDATE_VERTEX_SHADER,\n} from './webgl-shaders';\n\nconst FLOAT_BYTES = Float32Array.BYTES_PER_ELEMENT;\nconst STATE_STRIDE = PARTICLE_STATE_LENGTH * FLOAT_BYTES;\nconst CRACK_STRIDE = CRACK_LENGTH * FLOAT_BYTES;\n\nconst UPDATE_UNIFORMS = [\n 'uSize',\n 'uDeltaTSec',\n 'uAnimationT',\n 'uParticleAcceleration',\n 'uSeed',\n 'uCrumble',\n 'uPixelScale',\n 'uFrontTime',\n 'uFrontRadiusPow',\n 'uFade',\n 'uCoarseCrack',\n 'uFineCrack',\n 'uShardGrow',\n] as const;\n\nconst DRAW_UNIFORMS = ['uSize', 'uPixelScale', 'uShardGrow'] as const;\n\ntype Uniforms<T extends readonly string[]> = Record<\n T[number],\n WebGLUniformLocation | null\n>;\n\n/** one vertex attribute read from a buffer */\ninterface AttributeBinding {\n buffer: WebGLBuffer;\n location: number;\n size: number;\n type?: GLenum;\n normalized?: boolean;\n stride: number;\n offset: number;\n}\n\n/** smallest shard size in css pixels */\nconst MIN_CELL_SIZE = 28;\n\n/**\n * Simulates and draws the particles on the GPU.\n * The particle state lives in two buffers: every frame the update program reads\n * one and writes the other via transform feedback, then the draw program renders it.\n * Once at the start the crack program computes the shard and crack distances of every particle.\n */\nexport class WebGlParticleRenderer implements ParticleRenderer {\n public readonly kind = 'webgl';\n public readonly crumble: WsThanosCrumble;\n private readonly count: number;\n private readonly maxParticleX: number;\n private readonly minParticleY: number;\n private readonly updateProgram: WebGLProgram;\n private readonly drawProgram: WebGLProgram;\n private readonly buffers: WebGLBuffer[] = [];\n private readonly stateBuffers: WebGLBuffer[];\n private readonly updateVaos: WebGLVertexArrayObject[];\n private readonly drawVaos: WebGLVertexArrayObject[];\n private readonly updateUniforms: Uniforms<typeof UPDATE_UNIFORMS>;\n private readonly drawUniforms: Uniforms<typeof DRAW_UNIFORMS>;\n private current = 0;\n\n /** get a WebGL2 context for the canvas or null if the browser can't */\n public static createContext(\n canvas: HTMLCanvasElement\n ): WebGL2RenderingContext | null {\n try {\n return canvas.getContext('webgl2', {\n alpha: true,\n premultipliedAlpha: true,\n antialias: false,\n depth: false,\n stencil: false,\n });\n } catch {\n return null;\n }\n }\n\n /** the largest canvas size the GPU can render into */\n public static maxCanvasSize(gl: WebGL2RenderingContext): number {\n const viewport: ArrayLike<number> =\n gl.getParameter(gl.MAX_VIEWPORT_DIMS) ?? [];\n return Math.min(\n gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),\n viewport[0] ?? Infinity,\n viewport[1] ?? Infinity\n );\n }\n\n public constructor(\n public readonly canvas: HTMLCanvasElement,\n private readonly gl: WebGL2RenderingContext,\n // only the scalars are kept, the particle arrays live on the GPU\n particles: Particles,\n private readonly params: ParticleRendererParams\n ) {\n this.crumble = params.crumble;\n this.count = particles.count;\n this.maxParticleX = particles.maxParticleX;\n this.minParticleY = particles.minParticleY;\n canvas.width = params.width;\n canvas.height = params.height;\n\n this.updateProgram = this.createProgram(\n UPDATE_VERTEX_SHADER,\n FEEDBACK_FRAGMENT_SHADER,\n UPDATE_FEEDBACK_VARYINGS\n );\n this.drawProgram = this.createProgram(\n DRAW_VERTEX_SHADER,\n DRAW_FRAGMENT_SHADER\n );\n\n // the second state buffer is written by the first frame, it needs no data\n this.stateBuffers = [\n this.createBuffer(particles.state, gl.DYNAMIC_COPY),\n this.createBuffer(particles.state.byteLength, gl.DYNAMIC_COPY),\n ];\n const colorBuffer = this.createBuffer(particles.colors, gl.STATIC_DRAW);\n // dust has no cracks, the shaders then read the default attribute values\n const crackBuffer =\n params.crumble === 'dust' ? undefined : this.createCracks();\n\n const crackAttributes: AttributeBinding[] = crackBuffer\n ? [\n {\n buffer: crackBuffer,\n location: AttributeLocation.CRACK,\n size: 4,\n stride: CRACK_STRIDE,\n offset: 0,\n },\n {\n buffer: crackBuffer,\n location: AttributeLocation.CELL_RANDOM,\n size: 1,\n stride: CRACK_STRIDE,\n offset: 4 * FLOAT_BYTES,\n },\n ]\n : [];\n this.updateVaos = this.stateBuffers.map((buffer) =>\n this.createVao([\n ...stateAttributes(buffer, [\n [AttributeLocation.POSITION, 2, ParticleStateIndex.X],\n [AttributeLocation.VELOCITY, 2, ParticleStateIndex.VX],\n [AttributeLocation.ACCELERATION, 2, ParticleStateIndex.AX],\n [AttributeLocation.ALPHA, 1, ParticleStateIndex.ALPHA],\n [AttributeLocation.RELEASED_AT, 1, ParticleStateIndex.RELEASED_AT],\n ]),\n ...crackAttributes,\n ])\n );\n this.drawVaos = this.stateBuffers.map((buffer) =>\n this.createVao([\n ...stateAttributes(buffer, [\n [AttributeLocation.POSITION, 2, ParticleStateIndex.X],\n [AttributeLocation.ALPHA, 1, ParticleStateIndex.ALPHA],\n [AttributeLocation.RELEASED_AT, 1, ParticleStateIndex.RELEASED_AT],\n ]),\n ...crackAttributes,\n {\n buffer: colorBuffer,\n location: AttributeLocation.COLOR,\n size: 4,\n type: gl.UNSIGNED_BYTE,\n normalized: true,\n stride: 0,\n offset: 0,\n },\n ])\n );\n\n this.updateUniforms = this.uniformLocations(\n this.updateProgram,\n UPDATE_UNIFORMS\n );\n this.drawUniforms = this.uniformLocations(this.drawProgram, DRAW_UNIFORMS);\n\n // everything that stays the same for the whole animation is set once\n gl.useProgram(this.updateProgram);\n gl.uniform2f(this.updateUniforms.uSize, params.width, params.height);\n gl.uniform1f(\n this.updateUniforms.uParticleAcceleration,\n params.particleAcceleration\n );\n gl.uniform1f(this.updateUniforms.uSeed, params.seed);\n gl.uniform1i(this.updateUniforms.uCrumble, CRUMBLE_MODE[params.crumble]);\n gl.uniform1f(this.updateUniforms.uPixelScale, params.pixelScale);\n gl.useProgram(this.drawProgram);\n gl.uniform2f(this.drawUniforms.uSize, params.width, params.height);\n gl.uniform1f(this.drawUniforms.uPixelScale, params.pixelScale);\n\n gl.viewport(0, 0, params.width, params.height);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.clearColor(0, 0, 0, 0);\n }\n\n public render({ deltaTSec, animationT }: AnimationState): void {\n const { gl, params } = this;\n if (gl.isContextLost()) {\n return;\n }\n const next = 1 - this.current;\n const front = vaporizingFront(\n animationT,\n this.maxParticleX,\n this.minParticleY,\n params.height\n );\n const shape = crumbleShape(params.crumble, animationT);\n\n // simulate: read the current state, write the next one\n const uniforms = this.updateUniforms;\n gl.useProgram(this.updateProgram);\n gl.uniform1f(uniforms.uDeltaTSec, deltaTSec);\n gl.uniform1f(uniforms.uAnimationT, animationT);\n gl.uniform1f(uniforms.uFrontTime, front.time);\n gl.uniform1f(uniforms.uFrontRadiusPow, front.radiusPow);\n gl.uniform1f(uniforms.uFade, front.fade);\n gl.uniform1f(uniforms.uCoarseCrack, shape.coarseCrack * params.pixelScale);\n gl.uniform1f(uniforms.uFineCrack, shape.fineCrack * params.pixelScale);\n gl.uniform1f(uniforms.uShardGrow, shape.shardGrow);\n gl.bindVertexArray(this.updateVaos[this.current]);\n this.runTransformFeedback(this.stateBuffers[next]);\n\n // draw the next state\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(this.drawProgram);\n gl.uniform1f(this.drawUniforms.uShardGrow, shape.shardGrow);\n gl.bindVertexArray(this.drawVaos[next]);\n gl.drawArrays(gl.POINTS, 0, this.count);\n gl.bindVertexArray(null);\n\n this.current = next;\n }\n\n public dispose(): void {\n const { gl } = this;\n [...this.updateVaos, ...this.drawVaos].forEach((vao) =>\n gl.deleteVertexArray(vao)\n );\n this.buffers.forEach((buffer) => gl.deleteBuffer(buffer));\n gl.deleteProgram(this.updateProgram);\n gl.deleteProgram(this.drawProgram);\n // browsers only allow a few live contexts, free this one right away\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n this.canvas.remove();\n }\n\n /** run the bound vertex array through the bound program into the target buffer */\n private runTransformFeedback(target: WebGLBuffer): void {\n const { gl } = this;\n gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, target);\n gl.enable(gl.RASTERIZER_DISCARD);\n gl.beginTransformFeedback(gl.POINTS);\n gl.drawArrays(gl.POINTS, 0, this.count);\n gl.endTransformFeedback();\n gl.disable(gl.RASTERIZER_DISCARD);\n gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);\n }\n\n /** run the crack program once: shard center and crack distances per particle */\n private createCracks(): WebGLBuffer {\n const { gl, params } = this;\n const crackBuffer = this.createBuffer(\n this.count * CRACK_STRIDE,\n gl.STATIC_COPY\n );\n const program = this.createProgram(\n CRACK_VERTEX_SHADER,\n FEEDBACK_FRAGMENT_SHADER,\n CRACK_FEEDBACK_VARYINGS\n );\n const elementSize = Math.min(\n params.width / EFFECT_WIDTH_SCALE,\n params.height / EFFECT_HEIGHT_SCALE\n );\n const cellSize = Math.max(\n MIN_CELL_SIZE * params.pixelScale,\n elementSize / 2.2\n );\n const uniforms = this.uniformLocations(program, [\n 'uCellSize',\n 'uSeed',\n ] as const);\n\n // the first state buffer still holds the start positions\n const vao = this.createVao(\n stateAttributes(this.stateBuffers[0], [\n [AttributeLocation.POSITION, 2, ParticleStateIndex.X],\n ])\n );\n gl.useProgram(program);\n gl.uniform1f(uniforms.uCellSize, cellSize);\n gl.uniform1f(uniforms.uSeed, params.seed);\n gl.bindVertexArray(vao);\n this.runTransformFeedback(crackBuffer);\n gl.bindVertexArray(null);\n\n gl.deleteVertexArray(vao);\n gl.deleteProgram(program);\n return crackBuffer;\n }\n\n private uniformLocations<T extends readonly string[]>(\n program: WebGLProgram,\n names: T\n ): Uniforms<T> {\n const uniforms = {} as Uniforms<T>;\n for (const name of names) {\n uniforms[name as T[number]] = this.gl.getUniformLocation(program, name);\n }\n return uniforms;\n }\n\n private createProgram(\n vertexSource: string,\n fragmentSource: string,\n transformFeedbackVaryings?: string[]\n ): WebGLProgram {\n const { gl } = this;\n const program = gl.createProgram();\n if (program == null) {\n throw new Error('Could not create WebGL program');\n }\n const vertexShader = this.compileShader(gl.VERTEX_SHADER, vertexSource);\n const fragmentShader = this.compileShader(\n gl.FRAGMENT_SHADER,\n fragmentSource\n );\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n if (transformFeedbackVaryings) {\n gl.transformFeedbackVaryings(\n program,\n transformFeedbackVaryings,\n gl.INTERLEAVED_ATTRIBS\n );\n }\n gl.linkProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(program);\n gl.deleteProgram(program);\n throw new Error(`Could not link WebGL program: ${log}`);\n }\n return program;\n }\n\n private compileShader(type: GLenum, source: string): WebGLShader {\n const { gl } = this;\n const shader = gl.createShader(type);\n if (shader == null) {\n throw new Error('Could not create WebGL shader');\n }\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(shader);\n gl.deleteShader(shader);\n throw new Error(`Could not compile WebGL shader: ${log}`);\n }\n return shader;\n }\n\n /** a buffer with the given data, or of the given byte size when it is filled on the GPU */\n private createBuffer(\n data: Float32Array | Uint8Array | number,\n usage: GLenum\n ): WebGLBuffer {\n const { gl } = this;\n const buffer = gl.createBuffer();\n if (buffer == null) {\n throw new Error('Could not create WebGL buffer');\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n // bufferData has separate overloads for a byte size and for data\n if (typeof data === 'number') {\n gl.bufferData(gl.ARRAY_BUFFER, data, usage);\n } else {\n gl.bufferData(gl.ARRAY_BUFFER, data, usage);\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n this.buffers.push(buffer);\n return buffer;\n }\n\n private createVao(bindings: AttributeBinding[]): WebGLVertexArrayObject {\n const { gl } = this;\n const vao = gl.createVertexArray();\n if (vao == null) {\n throw new Error('Could not create WebGL vertex array');\n }\n gl.bindVertexArray(vao);\n for (const binding of bindings) {\n gl.bindBuffer(gl.ARRAY_BUFFER, binding.buffer);\n gl.enableVertexAttribArray(binding.location);\n gl.vertexAttribPointer(\n binding.location,\n binding.size,\n binding.type ?? gl.FLOAT,\n binding.normalized ?? false,\n binding.stride,\n binding.offset\n );\n }\n gl.bindVertexArray(null);\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n return vao;\n }\n}\n\n/** attributes read from the interleaved particle state */\nfunction stateAttributes(\n buffer: WebGLBuffer,\n attributes: [location: number, size: number, index: ParticleStateIndex][]\n): AttributeBinding[] {\n return attributes.map(([location, size, index]) => ({\n buffer,\n location,\n size,\n stride: STATE_STRIDE,\n offset: index * FLOAT_BYTES,\n }));\n}\n"]}