@rosalana/sandbox 0.0.5 → 0.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.
- package/README.md +153 -46
- package/dist/errors/base.d.ts +6 -0
- package/dist/errors/context.d.ts +7 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/module.d.ts +42 -0
- package/dist/errors/program.d.ts +5 -0
- package/dist/errors/shader.d.ts +34 -0
- package/dist/errors/unknown.d.ts +7 -0
- package/dist/globals.d.ts +17 -0
- package/dist/index.cjs.js +834 -8
- package/dist/index.d.ts +43 -1
- package/dist/index.es.js +2292 -387
- package/dist/tools/compilable.d.ts +79 -0
- package/dist/tools/module.d.ts +46 -0
- package/dist/tools/module_registry.d.ts +63 -0
- package/dist/tools/parser.d.ts +32 -0
- package/dist/tools/program.d.ts +1 -12
- package/dist/tools/shader.d.ts +4 -0
- package/dist/tools/uniforms.d.ts +0 -2
- package/dist/tools/web_gl.d.ts +2 -1
- package/dist/types.d.ts +90 -1
- package/package.json +8 -5
- package/dist/errors.d.ts +0 -32
package/dist/index.es.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
class
|
|
5
|
-
constructor(
|
|
6
|
-
this.target =
|
|
1
|
+
var D = Object.defineProperty;
|
|
2
|
+
var N = (u, e, n) => e in u ? D(u, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : u[e] = n;
|
|
3
|
+
var a = (u, e, n) => N(u, typeof e != "symbol" ? e + "" : e, n);
|
|
4
|
+
class x {
|
|
5
|
+
constructor(e, n, t, i) {
|
|
6
|
+
this.target = e, this.type = n, this.listener = t, this.options = i, this.target.addEventListener(
|
|
7
7
|
this.type,
|
|
8
8
|
this.listener,
|
|
9
9
|
this.options
|
|
@@ -16,99 +16,211 @@ class l {
|
|
|
16
16
|
this.options
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
|
-
static on(
|
|
20
|
-
return
|
|
19
|
+
static on(e, n, t, i) {
|
|
20
|
+
return e.addEventListener(n, t, i), () => e.removeEventListener(n, t, i);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class h extends Error {
|
|
24
|
+
constructor(n, t) {
|
|
25
|
+
super(n);
|
|
26
|
+
a(this, "name", "SandboxError");
|
|
27
|
+
this.code = t;
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
|
-
class
|
|
24
|
-
constructor(
|
|
25
|
-
super(
|
|
30
|
+
class z extends h {
|
|
31
|
+
constructor() {
|
|
32
|
+
super("WebGL is not supported in this browser.", "CONTEXT_ERROR");
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
|
-
class
|
|
29
|
-
constructor(
|
|
30
|
-
const e = t === "not_supported" ? "WebGL is not supported in this browser." : "Failed to create WebGL context. The GPU may be unavailable.";
|
|
35
|
+
class de extends h {
|
|
36
|
+
constructor() {
|
|
31
37
|
super(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
)
|
|
38
|
+
"Failed to create WebGL context. The GPU may be unavailable.",
|
|
39
|
+
"CONTEXT_ERROR"
|
|
40
|
+
);
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
|
-
class
|
|
38
|
-
constructor(
|
|
43
|
+
class G extends h {
|
|
44
|
+
constructor(e, n) {
|
|
39
45
|
super(
|
|
40
|
-
`Vertex and fragment shader WebGL versions do not match (${
|
|
41
|
-
"
|
|
42
|
-
), this.vertexVersion =
|
|
46
|
+
`Vertex and fragment shader WebGL versions do not match (${e} vs ${n})`,
|
|
47
|
+
"VALIDATION_ERROR"
|
|
48
|
+
), this.vertexVersion = e, this.fragmentVersion = n;
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
|
-
class
|
|
46
|
-
constructor(
|
|
47
|
-
const
|
|
51
|
+
class _ extends h {
|
|
52
|
+
constructor(n, t, i) {
|
|
53
|
+
const r = _.parseErrorLines(i), s = r.length > 0 ? ` at line(s): ${r.join(", ")}` : "";
|
|
48
54
|
super(
|
|
49
|
-
`${
|
|
55
|
+
`${n} shader compilation failed${s}
|
|
50
56
|
|
|
51
|
-
${
|
|
52
|
-
"
|
|
57
|
+
${i}`,
|
|
58
|
+
"SHADER_ERROR"
|
|
53
59
|
);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.shaderType = e, this.source = i, this.infoLog = s, this.name = "SandboxShaderCompilationError", this.lines = n;
|
|
60
|
+
a(this, "lines");
|
|
61
|
+
this.shaderType = n, this.source = t, this.infoLog = i, this.lines = r;
|
|
57
62
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const i = [
|
|
63
|
+
static parseErrorLines(n) {
|
|
64
|
+
const t = [
|
|
61
65
|
/ERROR:\s*\d*:(\d+)/g,
|
|
62
|
-
// Chrome/ANGLE: ERROR: 0:15
|
|
63
66
|
/(\d+):(\d+)\(\d+\):/g,
|
|
64
|
-
// Mesa: 0:15(0):
|
|
65
67
|
/^(\d+):/gm
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
f > 0 && s.add(f);
|
|
68
|
+
], i = /* @__PURE__ */ new Set();
|
|
69
|
+
for (const r of t) {
|
|
70
|
+
let s;
|
|
71
|
+
for (; (s = r.exec(n)) !== null; ) {
|
|
72
|
+
const o = parseInt(s[1], 10);
|
|
73
|
+
o > 0 && i.add(o);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
|
-
return [...
|
|
76
|
+
return [...i].sort((r, s) => r - s);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class C extends h {
|
|
80
|
+
constructor(e, n, t, i) {
|
|
81
|
+
super(
|
|
82
|
+
`The shader ${e} "${n}" has type "${i}" but expected "${t}"`,
|
|
83
|
+
"SHADER_ERROR"
|
|
84
|
+
), this.requirement = e, this.name = n, this.expectedType = t, this.actualType = i;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
class W extends h {
|
|
88
|
+
constructor() {
|
|
89
|
+
super("Shader source does not contain any function.", "SHADER_ERROR");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
class q extends h {
|
|
93
|
+
constructor(e, n) {
|
|
94
|
+
super(
|
|
95
|
+
`Syntax error in shader import statement at line ${e}: ${n}`,
|
|
96
|
+
"SHADER_ERROR"
|
|
97
|
+
), this.line = e, this.details = n;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
class H extends h {
|
|
101
|
+
constructor(e, n) {
|
|
102
|
+
super(
|
|
103
|
+
`Duplicate import name "${e}" found at line ${n}. Each import must have a unique name.`,
|
|
104
|
+
"SHADER_ERROR"
|
|
105
|
+
), this.name = e, this.line = n;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
class j extends h {
|
|
109
|
+
constructor(e) {
|
|
110
|
+
super(
|
|
111
|
+
`Can not find module '${e}'. Check if it is defined before usage or if the name is correct.`,
|
|
112
|
+
"MODULE_ERROR"
|
|
113
|
+
), this.moduleName = e;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
class Y extends h {
|
|
117
|
+
constructor(e, n) {
|
|
118
|
+
super(
|
|
119
|
+
`Method '${n}' not found in shader module '${e}'. Check if the method is defined in the module source code or if the name is correct.`,
|
|
120
|
+
"MODULE_ERROR"
|
|
121
|
+
), this.moduleName = e, this.methodName = n;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
class K extends h {
|
|
125
|
+
constructor(e) {
|
|
126
|
+
super(
|
|
127
|
+
`Importing 'main' function from module '${e}' is forbidden.`,
|
|
128
|
+
"MODULE_ERROR"
|
|
129
|
+
), this.moduleName = e;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
class X extends h {
|
|
133
|
+
constructor(e) {
|
|
134
|
+
super(
|
|
135
|
+
`Name 'default' is reserved and cannot be used as a function name in module '${e}'.`,
|
|
136
|
+
"MODULE_ERROR"
|
|
137
|
+
), this.moduleName = e;
|
|
76
138
|
}
|
|
77
139
|
}
|
|
78
|
-
class
|
|
79
|
-
constructor(
|
|
140
|
+
class Q extends h {
|
|
141
|
+
constructor(e) {
|
|
142
|
+
super(
|
|
143
|
+
`Module name '${e}' is not allowed. Module names cannot be 'sandbox' or start with 'sandbox/'.`,
|
|
144
|
+
"MODULE_ERROR"
|
|
145
|
+
), this.moduleName = e;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
class J extends h {
|
|
149
|
+
constructor(e) {
|
|
150
|
+
super(
|
|
151
|
+
`Module '${e}' is already defined. Overwriting existing modules is not allowed.`,
|
|
152
|
+
"MODULE_ERROR"
|
|
153
|
+
), this.moduleName = e;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
class Z extends h {
|
|
157
|
+
constructor(e, n, t) {
|
|
158
|
+
super(
|
|
159
|
+
`Uniform '${t}' mentioned for function '${n}' of module '${e}' was not found among the module's declared uniforms. Check if the uniform is declared in the module source code or if the name is correct.`,
|
|
160
|
+
"MODULE_ERROR"
|
|
161
|
+
), this.moduleName = e, this.functionName = n, this.uniformName = t;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
class ee extends h {
|
|
165
|
+
constructor(e, n) {
|
|
166
|
+
super(
|
|
167
|
+
`Uniform '${n}' mentioned for function '${e}' was not imported from any module. Check if the function is imported from the correct module and if the uniform is declared in that module's source code with the correct name.`,
|
|
168
|
+
"MODULE_ERROR"
|
|
169
|
+
), this.functionName = e, this.uniformName = n;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
class ne extends h {
|
|
173
|
+
constructor(e, n) {
|
|
174
|
+
super(
|
|
175
|
+
`Mention '${e}' called in function '${n}' could not be replaced with the corresponding uniform reference. There might be an issue with the compilation process because the referenced uniform was not found among the shader requirements. Try use a different name for uniforms you want to mention in functions or check if the uniform is properly declared and mentioned in the module source code.`,
|
|
176
|
+
"MODULE_ERROR"
|
|
177
|
+
), this.mentionName = e, this.calledInFunction = n;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
class R extends h {
|
|
181
|
+
constructor(e) {
|
|
80
182
|
super(`Shader program linking failed
|
|
81
183
|
|
|
82
|
-
${
|
|
184
|
+
${e}`, "PROGRAM_ERROR"), this.infoLog = e;
|
|
83
185
|
}
|
|
84
186
|
}
|
|
85
|
-
class
|
|
187
|
+
class te extends h {
|
|
188
|
+
constructor(e) {
|
|
189
|
+
super(`Error in onLoad callback: ${e}`, "UNKNOWN_ERROR");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
class ie extends h {
|
|
193
|
+
constructor(e, n) {
|
|
194
|
+
super(`Error in onBefore/onAfter hook callback with ID ${e}: ${n}`, "UNKNOWN_ERROR");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
class re {
|
|
86
198
|
constructor() {
|
|
87
199
|
/** Total elapsed time in seconds */
|
|
88
|
-
|
|
200
|
+
a(this, "time", 0);
|
|
89
201
|
/** Delta time since last frame in seconds */
|
|
90
|
-
|
|
202
|
+
a(this, "delta", 0);
|
|
91
203
|
/** Frame counter */
|
|
92
|
-
|
|
204
|
+
a(this, "frame", 0);
|
|
93
205
|
/** Is clock running */
|
|
94
|
-
|
|
206
|
+
a(this, "running", !1);
|
|
95
207
|
/** Smoothed frames per second */
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
208
|
+
a(this, "fps", 0);
|
|
209
|
+
a(this, "startTime", 0);
|
|
210
|
+
a(this, "lastTime", 0);
|
|
211
|
+
a(this, "rafId", null);
|
|
212
|
+
a(this, "callback", null);
|
|
213
|
+
a(this, "maxFps", 0);
|
|
102
214
|
this.loop = this.loop.bind(this);
|
|
103
215
|
}
|
|
104
216
|
/**
|
|
105
217
|
* Start the animation loop with a render callback.
|
|
106
218
|
*/
|
|
107
|
-
start(
|
|
219
|
+
start(e) {
|
|
108
220
|
if (this.running) return this;
|
|
109
|
-
this.callback =
|
|
110
|
-
const
|
|
111
|
-
return this.frame === 0 ? this.startTime =
|
|
221
|
+
this.callback = e, this.running = !0;
|
|
222
|
+
const n = performance.now();
|
|
223
|
+
return this.frame === 0 ? this.startTime = n : this.startTime = n - this.time * 1e3, this.lastTime = n, this.rafId = requestAnimationFrame(this.loop), this;
|
|
112
224
|
}
|
|
113
225
|
/**
|
|
114
226
|
* Stop the animation loop.
|
|
@@ -139,14 +251,14 @@ class R {
|
|
|
139
251
|
* Advance clock by one tick (for single-shot rendering).
|
|
140
252
|
* Useful when autoplay is disabled.
|
|
141
253
|
*/
|
|
142
|
-
tick(
|
|
143
|
-
return this.delta =
|
|
254
|
+
tick(e = 0) {
|
|
255
|
+
return this.delta = e, this.time += e, this.frame++, this.callback && this.callback(this.getState()), this;
|
|
144
256
|
}
|
|
145
257
|
/**
|
|
146
258
|
* Set time directly (for deterministic rendering).
|
|
147
259
|
*/
|
|
148
|
-
setTime(
|
|
149
|
-
return this.time =
|
|
260
|
+
setTime(e) {
|
|
261
|
+
return this.time = e, this;
|
|
150
262
|
}
|
|
151
263
|
/**
|
|
152
264
|
* Cleanup.
|
|
@@ -157,46 +269,46 @@ class R {
|
|
|
157
269
|
/**
|
|
158
270
|
* Set maximum frames per second.
|
|
159
271
|
*/
|
|
160
|
-
setMaxFps(
|
|
161
|
-
return this.maxFps =
|
|
272
|
+
setMaxFps(e) {
|
|
273
|
+
return this.maxFps = e, this;
|
|
162
274
|
}
|
|
163
275
|
/**
|
|
164
276
|
* Internal animation frame handler.
|
|
165
277
|
*/
|
|
166
|
-
loop(
|
|
278
|
+
loop(e) {
|
|
167
279
|
if (!this.running) return;
|
|
168
280
|
if (this.maxFps > 0) {
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
281
|
+
const t = 1e3 / this.maxFps;
|
|
282
|
+
if (e - this.lastTime < t) {
|
|
171
283
|
this.rafId = requestAnimationFrame(this.loop);
|
|
172
284
|
return;
|
|
173
285
|
}
|
|
174
286
|
}
|
|
175
|
-
this.delta = (
|
|
176
|
-
const
|
|
177
|
-
this.fps = this.fps * 0.95 +
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
class
|
|
181
|
-
constructor(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
287
|
+
this.delta = (e - this.lastTime) / 1e3, this.lastTime = e;
|
|
288
|
+
const n = this.delta > 0 ? 1 / this.delta : 0;
|
|
289
|
+
this.fps = this.fps * 0.95 + n * 0.05, this.time = (e - this.startTime) / 1e3, this.frame++, this.callback && this.callback(this.getState()), this.rafId = requestAnimationFrame(this.loop);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
class A {
|
|
293
|
+
constructor(e) {
|
|
294
|
+
a(this, "gl");
|
|
295
|
+
a(this, "vao", null);
|
|
296
|
+
a(this, "vbo", null);
|
|
297
|
+
a(this, "ibo", null);
|
|
298
|
+
a(this, "vertexCount", 0);
|
|
299
|
+
a(this, "indexCount", 0);
|
|
300
|
+
a(this, "useIndices", !1);
|
|
189
301
|
// WebGL1 VAO extension (if available)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.gl =
|
|
302
|
+
a(this, "vaoExt", null);
|
|
303
|
+
a(this, "isWebGL2");
|
|
304
|
+
this.gl = e, this.isWebGL2 = e instanceof WebGL2RenderingContext, this.isWebGL2 || (this.vaoExt = e.getExtension("OES_vertex_array_object"));
|
|
193
305
|
}
|
|
194
306
|
/**
|
|
195
307
|
* Create a fullscreen quad geometry.
|
|
196
308
|
* This is the most common use case for shader effects.
|
|
197
309
|
*/
|
|
198
|
-
static fullscreenQuad(
|
|
199
|
-
const
|
|
310
|
+
static fullscreenQuad(e) {
|
|
311
|
+
const n = new A(e), t = new Float32Array([
|
|
200
312
|
// position texcoord
|
|
201
313
|
-1,
|
|
202
314
|
-1,
|
|
@@ -218,7 +330,7 @@ class p {
|
|
|
218
330
|
1,
|
|
219
331
|
1
|
|
220
332
|
// top-right
|
|
221
|
-
]),
|
|
333
|
+
]), i = new Uint16Array([
|
|
222
334
|
0,
|
|
223
335
|
1,
|
|
224
336
|
2,
|
|
@@ -228,33 +340,33 @@ class p {
|
|
|
228
340
|
3
|
|
229
341
|
// second triangle
|
|
230
342
|
]);
|
|
231
|
-
return
|
|
343
|
+
return n.setup(t, i), n;
|
|
232
344
|
}
|
|
233
345
|
/**
|
|
234
346
|
* Setup geometry from vertex and index data.
|
|
235
347
|
*/
|
|
236
|
-
setup(
|
|
237
|
-
const
|
|
238
|
-
return this.createVAO(), this.bindVAO(), this.vbo =
|
|
348
|
+
setup(e, n) {
|
|
349
|
+
const t = this.gl;
|
|
350
|
+
return this.createVAO(), this.bindVAO(), this.vbo = t.createBuffer(), t.bindBuffer(t.ARRAY_BUFFER, this.vbo), t.bufferData(t.ARRAY_BUFFER, e, t.STATIC_DRAW), this.vertexCount = e.length / 4, n && (this.ibo = t.createBuffer(), t.bindBuffer(t.ELEMENT_ARRAY_BUFFER, this.ibo), t.bufferData(t.ELEMENT_ARRAY_BUFFER, n, t.STATIC_DRAW), this.indexCount = n.length, this.useIndices = !0), this.unbindVAO(), this;
|
|
239
351
|
}
|
|
240
352
|
/**
|
|
241
353
|
* Link vertex attributes to shader program.
|
|
242
354
|
* Call this after compiling shaders.
|
|
243
355
|
*/
|
|
244
|
-
linkAttributes(
|
|
245
|
-
const
|
|
246
|
-
this.bindVAO(),
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
return
|
|
251
|
-
|
|
356
|
+
linkAttributes(e) {
|
|
357
|
+
const n = this.gl;
|
|
358
|
+
this.bindVAO(), n.bindBuffer(n.ARRAY_BUFFER, this.vbo);
|
|
359
|
+
const t = 4 * Float32Array.BYTES_PER_ELEMENT, i = this.getPositionLocation(e);
|
|
360
|
+
i >= 0 && (n.enableVertexAttribArray(i), n.vertexAttribPointer(i, 2, n.FLOAT, !1, t, 0));
|
|
361
|
+
const r = this.getTexcoordLocation(e);
|
|
362
|
+
return r >= 0 && (n.enableVertexAttribArray(r), n.vertexAttribPointer(
|
|
363
|
+
r,
|
|
252
364
|
2,
|
|
253
|
-
|
|
365
|
+
n.FLOAT,
|
|
254
366
|
!1,
|
|
255
|
-
|
|
367
|
+
t,
|
|
256
368
|
2 * Float32Array.BYTES_PER_ELEMENT
|
|
257
|
-
)), this.useIndices &&
|
|
369
|
+
)), this.useIndices && n.bindBuffer(n.ELEMENT_ARRAY_BUFFER, this.ibo), this.unbindVAO(), this;
|
|
258
370
|
}
|
|
259
371
|
/**
|
|
260
372
|
* Bind geometry for rendering.
|
|
@@ -272,31 +384,31 @@ class p {
|
|
|
272
384
|
* Draw the geometry.
|
|
273
385
|
*/
|
|
274
386
|
draw() {
|
|
275
|
-
const
|
|
276
|
-
return this.bindVAO(), this.useIndices ?
|
|
387
|
+
const e = this.gl;
|
|
388
|
+
return this.bindVAO(), this.useIndices ? e.drawElements(e.TRIANGLES, this.indexCount, e.UNSIGNED_SHORT, 0) : e.drawArrays(e.TRIANGLE_STRIP, 0, this.vertexCount), this;
|
|
277
389
|
}
|
|
278
390
|
/**
|
|
279
391
|
* Cleanup all GPU resources.
|
|
280
392
|
*/
|
|
281
393
|
destroy() {
|
|
282
|
-
const
|
|
283
|
-
this.deleteVAO(), this.vbo && (
|
|
394
|
+
const e = this.gl;
|
|
395
|
+
this.deleteVAO(), this.vbo && (e.deleteBuffer(this.vbo), this.vbo = null), this.ibo && (e.deleteBuffer(this.ibo), this.ibo = null);
|
|
284
396
|
}
|
|
285
397
|
/**
|
|
286
398
|
* Get position attribute location.
|
|
287
399
|
* Tries common naming conventions.
|
|
288
400
|
*/
|
|
289
|
-
getPositionLocation(
|
|
290
|
-
let
|
|
291
|
-
return
|
|
401
|
+
getPositionLocation(e) {
|
|
402
|
+
let n = e.getAttribLocation("a_position");
|
|
403
|
+
return n >= 0 || (n = e.getAttribLocation("aPosition"), n >= 0) || (n = e.getAttribLocation("position"), n >= 0) ? n : -1;
|
|
292
404
|
}
|
|
293
405
|
/**
|
|
294
406
|
* Get texcoord attribute location.
|
|
295
407
|
* Tries common naming conventions.
|
|
296
408
|
*/
|
|
297
|
-
getTexcoordLocation(
|
|
298
|
-
let
|
|
299
|
-
return
|
|
409
|
+
getTexcoordLocation(e) {
|
|
410
|
+
let n = e.getAttribLocation("a_texcoord");
|
|
411
|
+
return n >= 0 || (n = e.getAttribLocation("aTexCoord"), n >= 0) || (n = e.getAttribLocation("texcoord"), n >= 0) || (n = e.getAttribLocation("a_uv"), n >= 0) ? n : -1;
|
|
300
412
|
}
|
|
301
413
|
// ============================================================================
|
|
302
414
|
// VAO helpers (handle WebGL1 vs WebGL2 differences)
|
|
@@ -314,33 +426,21 @@ class p {
|
|
|
314
426
|
this.vao && (this.isWebGL2 ? this.gl.deleteVertexArray(this.vao) : this.vaoExt && this.vaoExt.deleteVertexArrayOES(this.vao), this.vao = null);
|
|
315
427
|
}
|
|
316
428
|
}
|
|
317
|
-
class
|
|
318
|
-
constructor(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
this.gl = t;
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Detect WebGL version from shader source.
|
|
328
|
-
* Looks for "#version 300 es" directive.
|
|
329
|
-
*/
|
|
330
|
-
static detectVersion(t) {
|
|
331
|
-
return /^\s*#version\s+300\s+es/m.test(t) ? 2 : 1;
|
|
429
|
+
class se {
|
|
430
|
+
constructor(e) {
|
|
431
|
+
a(this, "gl");
|
|
432
|
+
a(this, "program", null);
|
|
433
|
+
a(this, "vertexShader", null);
|
|
434
|
+
a(this, "fragmentShader", null);
|
|
435
|
+
this.gl = e;
|
|
332
436
|
}
|
|
333
437
|
/**
|
|
334
438
|
* Compile shaders and link program.
|
|
335
439
|
* @throws ShaderCompilationError if compilation fails
|
|
336
440
|
* @throws ProgramLinkError if linking fails
|
|
337
441
|
*/
|
|
338
|
-
compile(
|
|
339
|
-
this.destroy();
|
|
340
|
-
const i = a.detectVersion(t), s = a.detectVersion(e);
|
|
341
|
-
if (i != s)
|
|
342
|
-
throw new k(i, s);
|
|
343
|
-
return this.version = Math.max(i, s), this.vertexShader = this.compileShader("vertex", t), this.fragmentShader = this.compileShader("fragment", e), this.linkProgram(), this;
|
|
442
|
+
compile(e, n) {
|
|
443
|
+
return this.destroy(), this.vertexShader = this.compileShader("vertex", e), this.fragmentShader = this.compileShader("fragment", n), this.linkProgram(), this;
|
|
344
444
|
}
|
|
345
445
|
/**
|
|
346
446
|
* Bind this program for rendering.
|
|
@@ -354,93 +454,87 @@ class a {
|
|
|
354
454
|
getProgram() {
|
|
355
455
|
return this.program;
|
|
356
456
|
}
|
|
357
|
-
/**
|
|
358
|
-
* Get detected WebGL version.
|
|
359
|
-
*/
|
|
360
|
-
getVersion() {
|
|
361
|
-
return this.version;
|
|
362
|
-
}
|
|
363
457
|
/**
|
|
364
458
|
* Get attribute location.
|
|
365
459
|
*/
|
|
366
|
-
getAttribLocation(
|
|
367
|
-
return this.program ? this.gl.getAttribLocation(this.program,
|
|
460
|
+
getAttribLocation(e) {
|
|
461
|
+
return this.program ? this.gl.getAttribLocation(this.program, e) : -1;
|
|
368
462
|
}
|
|
369
463
|
/**
|
|
370
464
|
* Get uniform location.
|
|
371
465
|
*/
|
|
372
|
-
getUniformLocation(
|
|
373
|
-
return this.program ? this.gl.getUniformLocation(this.program,
|
|
466
|
+
getUniformLocation(e) {
|
|
467
|
+
return this.program ? this.gl.getUniformLocation(this.program, e) : null;
|
|
374
468
|
}
|
|
375
469
|
/**
|
|
376
470
|
* Cleanup all GPU resources.
|
|
377
471
|
*/
|
|
378
472
|
destroy() {
|
|
379
|
-
const
|
|
380
|
-
this.program && (this.vertexShader &&
|
|
473
|
+
const e = this.gl;
|
|
474
|
+
this.program && (this.vertexShader && e.detachShader(this.program, this.vertexShader), this.fragmentShader && e.detachShader(this.program, this.fragmentShader), e.deleteProgram(this.program), this.program = null), this.vertexShader && (e.deleteShader(this.vertexShader), this.vertexShader = null), this.fragmentShader && (e.deleteShader(this.fragmentShader), this.fragmentShader = null);
|
|
381
475
|
}
|
|
382
476
|
/**
|
|
383
477
|
* Compile a single shader.
|
|
384
478
|
* @throws ShaderCompilationError if compilation fails
|
|
385
479
|
*/
|
|
386
|
-
compileShader(
|
|
387
|
-
const
|
|
388
|
-
if (!
|
|
389
|
-
throw new
|
|
390
|
-
t,
|
|
480
|
+
compileShader(e, n) {
|
|
481
|
+
const t = this.gl, i = e === "vertex" ? t.VERTEX_SHADER : t.FRAGMENT_SHADER, r = t.createShader(i);
|
|
482
|
+
if (!r)
|
|
483
|
+
throw new _(
|
|
391
484
|
e,
|
|
485
|
+
n,
|
|
392
486
|
"Failed to create shader object"
|
|
393
487
|
);
|
|
394
|
-
if (
|
|
395
|
-
const
|
|
396
|
-
throw
|
|
488
|
+
if (t.shaderSource(r, n), t.compileShader(r), !t.getShaderParameter(r, t.COMPILE_STATUS)) {
|
|
489
|
+
const o = t.getShaderInfoLog(r) || "Unknown error";
|
|
490
|
+
throw t.deleteShader(r), new _(e, n, o);
|
|
397
491
|
}
|
|
398
|
-
return
|
|
492
|
+
return r;
|
|
399
493
|
}
|
|
400
494
|
/**
|
|
401
495
|
* Link vertex and fragment shaders into a program.
|
|
402
496
|
* @throws ProgramLinkError if linking fails
|
|
403
497
|
*/
|
|
404
498
|
linkProgram() {
|
|
405
|
-
const
|
|
499
|
+
const e = this.gl;
|
|
406
500
|
if (!this.vertexShader || !this.fragmentShader)
|
|
407
|
-
throw new
|
|
408
|
-
const
|
|
409
|
-
if (!
|
|
410
|
-
throw new
|
|
411
|
-
if (
|
|
412
|
-
const
|
|
413
|
-
throw
|
|
501
|
+
throw new R("Shaders not compiled");
|
|
502
|
+
const n = e.createProgram();
|
|
503
|
+
if (!n)
|
|
504
|
+
throw new R("Failed to create program object");
|
|
505
|
+
if (e.attachShader(n, this.vertexShader), e.attachShader(n, this.fragmentShader), e.linkProgram(n), !e.getProgramParameter(n, e.LINK_STATUS)) {
|
|
506
|
+
const i = e.getProgramInfoLog(n) || "Unknown error";
|
|
507
|
+
throw e.deleteProgram(n), new R(i);
|
|
414
508
|
}
|
|
415
|
-
this.program =
|
|
509
|
+
this.program = n;
|
|
416
510
|
}
|
|
417
511
|
}
|
|
418
|
-
class
|
|
419
|
-
constructor(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
this.name =
|
|
428
|
-
const
|
|
429
|
-
this.method =
|
|
512
|
+
class k {
|
|
513
|
+
constructor(e, n) {
|
|
514
|
+
a(this, "name");
|
|
515
|
+
a(this, "method");
|
|
516
|
+
a(this, "isArray");
|
|
517
|
+
a(this, "isMatrix");
|
|
518
|
+
a(this, "location", null);
|
|
519
|
+
a(this, "locationResolved", !1);
|
|
520
|
+
a(this, "value");
|
|
521
|
+
this.name = e, this.value = n;
|
|
522
|
+
const t = k.inferMethodInfo(n);
|
|
523
|
+
this.method = t.method, this.isArray = t.isArray, this.isMatrix = t.isMatrix;
|
|
430
524
|
}
|
|
431
525
|
/**
|
|
432
526
|
* Infer WebGL method and metadata from value type.
|
|
433
527
|
*/
|
|
434
|
-
static inferMethodInfo(
|
|
435
|
-
if (typeof
|
|
528
|
+
static inferMethodInfo(e) {
|
|
529
|
+
if (typeof e == "boolean")
|
|
436
530
|
return { method: "uniform1i", isArray: !1, isMatrix: !1 };
|
|
437
|
-
if (typeof
|
|
531
|
+
if (typeof e == "number")
|
|
438
532
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
439
|
-
if (!Array.isArray(
|
|
533
|
+
if (!Array.isArray(e))
|
|
440
534
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
441
|
-
const
|
|
442
|
-
if (Array.isArray(
|
|
443
|
-
switch (
|
|
535
|
+
const n = e.length, t = e[0];
|
|
536
|
+
if (Array.isArray(t))
|
|
537
|
+
switch (t.length) {
|
|
444
538
|
case 2:
|
|
445
539
|
return { method: "uniform2fv", isArray: !0, isMatrix: !1 };
|
|
446
540
|
case 3:
|
|
@@ -450,7 +544,7 @@ class x {
|
|
|
450
544
|
default:
|
|
451
545
|
return { method: "uniform1fv", isArray: !0, isMatrix: !1 };
|
|
452
546
|
}
|
|
453
|
-
switch (
|
|
547
|
+
switch (n) {
|
|
454
548
|
case 2:
|
|
455
549
|
return { method: "uniform2fv", isArray: !1, isMatrix: !1 };
|
|
456
550
|
case 3:
|
|
@@ -469,8 +563,8 @@ class x {
|
|
|
469
563
|
* Resolve and cache uniform location from program.
|
|
470
564
|
* Returns null if uniform doesn't exist (optimized out by compiler, etc.)
|
|
471
565
|
*/
|
|
472
|
-
resolveLocation(
|
|
473
|
-
return this.locationResolved || (this.location =
|
|
566
|
+
resolveLocation(e, n) {
|
|
567
|
+
return this.locationResolved || (this.location = e.getUniformLocation(n, this.name), this.locationResolved = !0), this.location;
|
|
474
568
|
}
|
|
475
569
|
/**
|
|
476
570
|
* Invalidate cached location (call when program changes).
|
|
@@ -481,8 +575,8 @@ class x {
|
|
|
481
575
|
/**
|
|
482
576
|
* Update value (doesn't upload to GPU until upload() is called).
|
|
483
577
|
*/
|
|
484
|
-
setValue(
|
|
485
|
-
this.value =
|
|
578
|
+
setValue(e) {
|
|
579
|
+
this.value = e;
|
|
486
580
|
}
|
|
487
581
|
/**
|
|
488
582
|
* Get current value.
|
|
@@ -495,96 +589,1782 @@ class x {
|
|
|
495
589
|
* @param gl - WebGL context
|
|
496
590
|
* @param program - Current WebGL program (for location resolution)
|
|
497
591
|
*/
|
|
498
|
-
upload(
|
|
499
|
-
const
|
|
500
|
-
if (
|
|
592
|
+
upload(e, n) {
|
|
593
|
+
const t = this.resolveLocation(e, n);
|
|
594
|
+
if (t === null)
|
|
501
595
|
return;
|
|
502
|
-
const
|
|
503
|
-
let
|
|
504
|
-
switch (typeof
|
|
505
|
-
|
|
506
|
-
) :
|
|
596
|
+
const i = this.value;
|
|
597
|
+
let r;
|
|
598
|
+
switch (typeof i == "boolean" ? r = i ? 1 : 0 : typeof i == "number" ? r = i : this.isArray && Array.isArray(i[0]) ? r = new Float32Array(
|
|
599
|
+
i.flat()
|
|
600
|
+
) : r = new Float32Array(i), this.method) {
|
|
507
601
|
case "uniform1f":
|
|
508
|
-
|
|
602
|
+
e.uniform1f(t, r);
|
|
509
603
|
break;
|
|
510
604
|
case "uniform1i":
|
|
511
|
-
|
|
605
|
+
e.uniform1i(t, r);
|
|
512
606
|
break;
|
|
513
607
|
case "uniform1fv":
|
|
514
|
-
|
|
608
|
+
e.uniform1fv(t, r);
|
|
515
609
|
break;
|
|
516
610
|
case "uniform2fv":
|
|
517
|
-
|
|
611
|
+
e.uniform2fv(t, r);
|
|
518
612
|
break;
|
|
519
613
|
case "uniform3fv":
|
|
520
|
-
|
|
614
|
+
e.uniform3fv(t, r);
|
|
521
615
|
break;
|
|
522
616
|
case "uniform4fv":
|
|
523
|
-
|
|
617
|
+
e.uniform4fv(t, r);
|
|
524
618
|
break;
|
|
525
619
|
case "uniformMatrix2fv":
|
|
526
|
-
|
|
620
|
+
e.uniformMatrix2fv(t, !1, r);
|
|
527
621
|
break;
|
|
528
622
|
case "uniformMatrix3fv":
|
|
529
|
-
|
|
623
|
+
e.uniformMatrix3fv(t, !1, r);
|
|
530
624
|
break;
|
|
531
625
|
case "uniformMatrix4fv":
|
|
532
|
-
|
|
626
|
+
e.uniformMatrix4fv(t, !1, r);
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
class w {
|
|
632
|
+
constructor(e) {
|
|
633
|
+
a(this, "parsed", null);
|
|
634
|
+
this.source = e;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Parse the shader source to extract imports, uniforms, functions, and version.
|
|
638
|
+
*/
|
|
639
|
+
parse() {
|
|
640
|
+
if (this.parsed) return this.parsed;
|
|
641
|
+
const e = this.detectVersion(), n = this.detectImports(), t = this.detectUniforms(), i = this.detectFunctions(t);
|
|
642
|
+
return this.parsed = {
|
|
643
|
+
version: e,
|
|
644
|
+
imports: n,
|
|
645
|
+
uniforms: t,
|
|
646
|
+
functions: i
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Check if the shader source has already been parsed
|
|
651
|
+
*/
|
|
652
|
+
isParsed() {
|
|
653
|
+
return this.parsed !== null;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Change the shader source and reset the parsed result
|
|
657
|
+
*/
|
|
658
|
+
setSource(e) {
|
|
659
|
+
this.source = e, this.parsed = null;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Get the detected GLSL version from the shader source without parsing the entire shader
|
|
663
|
+
*/
|
|
664
|
+
version() {
|
|
665
|
+
return this.detectVersion();
|
|
666
|
+
}
|
|
667
|
+
detectVersion() {
|
|
668
|
+
return /^\s*#version\s+300\s+es/m.test(this.source) ? 2 : 1;
|
|
669
|
+
}
|
|
670
|
+
detectImports() {
|
|
671
|
+
const e = /^[ \t]*#import\s+(\w+)(?:\s+as\s+(\w+))?\s+from\s+["'](.+)["']/gm, n = /^[ \t]*[^\w\s]?import\b/gm, t = [], i = /* @__PURE__ */ new Set();
|
|
672
|
+
let r, s = 1, o = 0;
|
|
673
|
+
for (; (r = e.exec(this.source)) !== null; ) {
|
|
674
|
+
s += (this.source.substring(o, r.index).match(/\n/g) || []).length, o = r.index, i.add(s);
|
|
675
|
+
const f = r[1], c = r[2] || r[1], m = r[3];
|
|
676
|
+
if (t.some((p) => p.alias === c))
|
|
677
|
+
throw new H(c, s);
|
|
678
|
+
t.push({ name: f, alias: c, module: m, line: s });
|
|
679
|
+
}
|
|
680
|
+
let l;
|
|
681
|
+
for (; (l = n.exec(this.source)) !== null; ) {
|
|
682
|
+
const f = (this.source.substring(0, l.index).match(/\n/g) || []).length + 1;
|
|
683
|
+
if (i.has(f)) continue;
|
|
684
|
+
const c = this.source.split(`
|
|
685
|
+
`)[f - 1].trim();
|
|
686
|
+
throw new q(
|
|
687
|
+
f,
|
|
688
|
+
this.diagnoseImport(c)
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
return t;
|
|
692
|
+
}
|
|
693
|
+
diagnoseImport(e) {
|
|
694
|
+
const n = e.match(/^([^\w\s])import\b/);
|
|
695
|
+
if (n && n[1] !== "#")
|
|
696
|
+
return `Invalid prefix '${n[1]}'. Expected: #import <function> from '<module>'`;
|
|
697
|
+
if (/^import\b/.test(e))
|
|
698
|
+
return "Missing '#' prefix. Expected: #import <function> from '<module>'";
|
|
699
|
+
if (/^#import\s+from\b/.test(e))
|
|
700
|
+
return "Missing function name. Expected: #import <function> from '<module>'";
|
|
701
|
+
if (/^#import\s+\w+\s*$/.test(e))
|
|
702
|
+
return `Missing 'from' clause. Expected: #import ${e.split(/\s+/)[1]} from '<module>'`;
|
|
703
|
+
if (/^#import\s+\w+\s+as\s*$/.test(e) || /^#import\s+\w+\s+as\s+from\b/.test(e))
|
|
704
|
+
return `Missing alias name after 'as'. Expected: #import ${e.split(/\s+/)[1]} as <alias> from '<module>'`;
|
|
705
|
+
if (/^#import\s+\w+\s+as\s+\w+\s*$/.test(e)) {
|
|
706
|
+
const t = e.split(/\s+/);
|
|
707
|
+
return `Missing 'from' clause. Expected: #import ${t[1]} as ${t[3]} from '<module>'`;
|
|
708
|
+
}
|
|
709
|
+
if (/^#import\s+\w+(?:\s+as\s+\w+)?\s+from\s+\w+/.test(e)) {
|
|
710
|
+
const t = e.match(/from\s+(\S+)/);
|
|
711
|
+
return `Module name must be quoted. Expected: from '${t == null ? void 0 : t[1]}'`;
|
|
712
|
+
}
|
|
713
|
+
return "Invalid syntax. Expected: #import <function> from '<module>'";
|
|
714
|
+
}
|
|
715
|
+
detectUniforms() {
|
|
716
|
+
const e = /^[ \t]*uniform\s+(?:(?:highp|mediump|lowp)\s+)?(\w+)\s+(\w+)(?:\[(\d+)\])?\s*;/gm, n = [];
|
|
717
|
+
let t, i = 1, r = 0;
|
|
718
|
+
for (; (t = e.exec(this.source)) !== null; ) {
|
|
719
|
+
i += (this.source.substring(r, t.index).match(/\n/g) || []).length, r = t.index;
|
|
720
|
+
const s = t[1], o = t[2], l = t[3] ? parseInt(t[3], 10) : void 0;
|
|
721
|
+
n.push({ name: o, type: s, line: i, arrayNum: l });
|
|
722
|
+
}
|
|
723
|
+
return n;
|
|
724
|
+
}
|
|
725
|
+
detectFunctions(e) {
|
|
726
|
+
const n = [], t = "void|float|int|uint|bool|vec[234]|ivec[234]|uvec[234]|bvec[234]|mat[234](?:x[234])?|sampler2D|samplerCube|sampler3D|sampler2DArray", i = new RegExp(
|
|
727
|
+
`^[ \\t]*(${t})\\s+(\\w+)\\s*\\(([^)]*)\\)\\s*\\{`,
|
|
728
|
+
"gm"
|
|
729
|
+
);
|
|
730
|
+
let r;
|
|
731
|
+
for (; (r = i.exec(this.source)) !== null; ) {
|
|
732
|
+
const s = r[1], o = r[2], l = r[3].trim(), f = r.index, c = (this.source.substring(0, f).match(/\n/g) || []).length + 1, m = this.source.indexOf("{", f), p = this.findClosingBrace(this.source, m);
|
|
733
|
+
if (p === -1) continue;
|
|
734
|
+
const g = this.source.slice(m, p + 1), T = this.parseParams(l), I = this.findFunctionCalls(g), P = this.findUniformCalls(g, e), B = this.findMentionCalls(g);
|
|
735
|
+
n.push({
|
|
736
|
+
name: o,
|
|
737
|
+
type: s,
|
|
738
|
+
params: T,
|
|
739
|
+
body: g,
|
|
740
|
+
dependencies: [...I, ...P, ...B],
|
|
741
|
+
line: c
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
return n;
|
|
745
|
+
}
|
|
746
|
+
parseParams(e) {
|
|
747
|
+
if (!e.trim()) return [];
|
|
748
|
+
const n = [], t = e.split(",");
|
|
749
|
+
for (const i of t) {
|
|
750
|
+
const r = i.trim();
|
|
751
|
+
if (!r) continue;
|
|
752
|
+
const o = r.replace(/\b(in|out|inout|const|highp|mediump|lowp)\b\s*/g, "").trim().match(/^(\w+)\s+(\w+)(?:\[\d*\])?$/);
|
|
753
|
+
o && n.push({
|
|
754
|
+
type: o[1],
|
|
755
|
+
name: o[2]
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
return n;
|
|
759
|
+
}
|
|
760
|
+
findClosingBrace(e, n) {
|
|
761
|
+
let t = 0, i = !1, r = !1, s = !1, o = !1;
|
|
762
|
+
for (let l = n; l < e.length; l++) {
|
|
763
|
+
const f = e[l], c = e[l + 1], m = e[l - 1];
|
|
764
|
+
if (!r && !o && f === "/" && c === "/") {
|
|
765
|
+
s = !0;
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (s && f === `
|
|
769
|
+
`) {
|
|
770
|
+
s = !1;
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
if (!r && !s && f === "/" && c === "*") {
|
|
774
|
+
o = !0, l++;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (o && f === "*" && c === "/") {
|
|
778
|
+
o = !1, l++;
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (!(s || o)) {
|
|
782
|
+
if (f === '"' && m !== "\\") {
|
|
783
|
+
r = !r;
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
if (!r) {
|
|
787
|
+
if (f === "{")
|
|
788
|
+
t++, i = !0;
|
|
789
|
+
else if (f === "}" && (t--, i && t === 0))
|
|
790
|
+
return l;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return -1;
|
|
795
|
+
}
|
|
796
|
+
findFunctionCalls(e) {
|
|
797
|
+
const n = [], t = /* @__PURE__ */ new Set([
|
|
798
|
+
"if",
|
|
799
|
+
"else",
|
|
800
|
+
"for",
|
|
801
|
+
"while",
|
|
802
|
+
"do",
|
|
803
|
+
"switch",
|
|
804
|
+
"case",
|
|
805
|
+
"return",
|
|
806
|
+
"break",
|
|
807
|
+
"continue",
|
|
808
|
+
"discard"
|
|
809
|
+
]), i = /\b([a-zA-Z_]\w*)\s*\(/g;
|
|
810
|
+
let r;
|
|
811
|
+
for (; (r = i.exec(e)) !== null; ) {
|
|
812
|
+
const s = r[1];
|
|
813
|
+
t.has(s) || n.push({
|
|
814
|
+
name: s,
|
|
815
|
+
type: "function",
|
|
816
|
+
index: r.index
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
return n;
|
|
820
|
+
}
|
|
821
|
+
findUniformCalls(e, n) {
|
|
822
|
+
const t = [];
|
|
823
|
+
for (const i of n) {
|
|
824
|
+
const r = new RegExp(`\\b${i.name}\\b`, "g");
|
|
825
|
+
let s;
|
|
826
|
+
for (; (s = r.exec(e)) !== null; )
|
|
827
|
+
t.push({
|
|
828
|
+
name: i.name,
|
|
829
|
+
type: "uniform",
|
|
830
|
+
index: s.index
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
return t;
|
|
834
|
+
}
|
|
835
|
+
findMentionCalls(e) {
|
|
836
|
+
const n = [], t = /@(\w+)\.([a-zA-Z_]\w*)/g;
|
|
837
|
+
let i;
|
|
838
|
+
for (; (i = t.exec(e)) !== null; ) {
|
|
839
|
+
const r = i[1], s = i[2];
|
|
840
|
+
n.push({
|
|
841
|
+
name: `${r}.${s}`,
|
|
842
|
+
type: "mention",
|
|
843
|
+
index: i.index
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
return n;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
class F {
|
|
850
|
+
constructor(e) {
|
|
851
|
+
/** Flag to track if the shader has been compiled */
|
|
852
|
+
a(this, "isCompiled", !1);
|
|
853
|
+
/** Original and compiled shader parsers */
|
|
854
|
+
a(this, "original");
|
|
855
|
+
/** Compiled parser will be updated with rewritten source after processing imports */
|
|
856
|
+
a(this, "compiled");
|
|
857
|
+
/** Collected requirements from imports */
|
|
858
|
+
a(this, "requirements", {
|
|
859
|
+
uniforms: /* @__PURE__ */ new Map(),
|
|
860
|
+
functions: /* @__PURE__ */ new Map()
|
|
861
|
+
});
|
|
862
|
+
this.original = new w(e), this.compiled = new w(e);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Detect WebGL version from shader source
|
|
866
|
+
*/
|
|
867
|
+
version() {
|
|
868
|
+
return this.original.version();
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Get the original source code of the shader
|
|
872
|
+
*/
|
|
873
|
+
source() {
|
|
874
|
+
return this.original.source;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Force recompilation of the shader, reprocessing all imports and rewrites
|
|
878
|
+
* It's not necessary to call this manually because the state gets lost whenever the shader is switched out and back in.
|
|
879
|
+
*/
|
|
880
|
+
recompile() {
|
|
881
|
+
return this.isCompiled = !1, this.compile();
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Compile the shader source, resolving all imports
|
|
885
|
+
*/
|
|
886
|
+
compile() {
|
|
887
|
+
return this.isCompiled ? this.compiled.source : (this.original.parse().imports.length > 0 && this.processImports(), this.compiled.setSource(this.build()), this.isCompiled = !0, this.compiled.source);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Process all #import directives
|
|
891
|
+
*/
|
|
892
|
+
processImports() {
|
|
893
|
+
const e = this.original.parse(), n = e.functions.flatMap((t) => t.dependencies.filter((i) => i.type === "mention").map((i) => ({
|
|
894
|
+
name: i.name.split(".")[0],
|
|
895
|
+
uniform: i.name.split(".")[1]
|
|
896
|
+
})));
|
|
897
|
+
for (const t of e.imports) {
|
|
898
|
+
const i = b.resolve(t.module), r = i.extract(t.name);
|
|
899
|
+
let s = n.filter(
|
|
900
|
+
(l) => l.name === r.function.name
|
|
901
|
+
);
|
|
902
|
+
if (s.length > 0) {
|
|
903
|
+
const l = i.getDefinition().uniforms;
|
|
904
|
+
if (s = s.filter((f) => {
|
|
905
|
+
const c = l.find(
|
|
906
|
+
(m) => m.name === `u_${f.uniform}` || m.name === f.uniform
|
|
907
|
+
);
|
|
908
|
+
if (c) {
|
|
909
|
+
r.dependencies.uniforms.some(
|
|
910
|
+
(p) => p.name === c.name
|
|
911
|
+
) || r.dependencies.uniforms.push(c);
|
|
912
|
+
const m = n.indexOf(f);
|
|
913
|
+
return m > -1 && n.splice(m, 1), !1;
|
|
914
|
+
}
|
|
915
|
+
return !0;
|
|
916
|
+
}), s.length > 0)
|
|
917
|
+
throw new Z(
|
|
918
|
+
t.module,
|
|
919
|
+
r.function.name,
|
|
920
|
+
s[0].uniform
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
const o = i.copy();
|
|
924
|
+
this.processExtraction(r, t.alias, o.options), y.merge(t.module, o);
|
|
925
|
+
}
|
|
926
|
+
if (n.length > 0)
|
|
927
|
+
throw new ee(
|
|
928
|
+
n[0].name,
|
|
929
|
+
n[0].uniform
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Process an extraction: rewrite names and collect as requirements
|
|
934
|
+
*/
|
|
935
|
+
processExtraction(e, n, t = {}) {
|
|
936
|
+
const i = e.function, r = Math.random().toString(36).substring(2, 8), s = `${n}_${r}`;
|
|
937
|
+
for (let l = e.dependencies.functions.length - 1; l >= 0; l--) {
|
|
938
|
+
const f = e.dependencies.functions[l], c = this.rewriteFunction(f, n, {
|
|
939
|
+
uniforms: e.dependencies.uniforms,
|
|
940
|
+
functions: e.dependencies.functions,
|
|
941
|
+
unique: s
|
|
942
|
+
});
|
|
943
|
+
this.requirements.functions.set(c.name, c);
|
|
944
|
+
}
|
|
945
|
+
const o = this.rewriteFunction(i, n, {
|
|
946
|
+
uniforms: e.dependencies.uniforms,
|
|
947
|
+
functions: e.dependencies.functions,
|
|
948
|
+
rename: !0,
|
|
949
|
+
unique: s
|
|
950
|
+
});
|
|
951
|
+
this.requirements.functions.set(o.name, o);
|
|
952
|
+
for (const l of e.dependencies.uniforms) {
|
|
953
|
+
if (E.has(l.name)) continue;
|
|
954
|
+
const f = {
|
|
955
|
+
...l,
|
|
956
|
+
name: `${s}_${l.name}${l.arrayNum ? `[${l.arrayNum}]` : ""}`
|
|
957
|
+
};
|
|
958
|
+
if (t[i.name]) {
|
|
959
|
+
const c = Object.entries(t[i.name]).find(
|
|
960
|
+
([m, p]) => p.uniform === l.name
|
|
961
|
+
);
|
|
962
|
+
c && (c[1].uniform = `${s}_${l.name}`);
|
|
963
|
+
}
|
|
964
|
+
this.requirements.uniforms.set(f.name, f);
|
|
965
|
+
}
|
|
966
|
+
t[i.name] && n !== i.name && (t[n] = t[i.name], delete t[i.name]);
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Rewrite a function: namespace all uniform and helper function references
|
|
970
|
+
*/
|
|
971
|
+
rewriteFunction(e, n, t = { rename: !1, uniforms: [], functions: [], unique: "" }) {
|
|
972
|
+
const i = new Set(t.uniforms.map((c) => c.name)), r = new Set(t.functions.map((c) => c.name)), s = [], o = t.unique ? t.unique : n;
|
|
973
|
+
for (const c of e.dependencies)
|
|
974
|
+
if (c.index !== void 0)
|
|
975
|
+
if (c.type === "uniform" && i.has(c.name)) {
|
|
976
|
+
if (E.has(c.name)) continue;
|
|
977
|
+
s.push({
|
|
978
|
+
index: c.index,
|
|
979
|
+
oldText: c.name,
|
|
980
|
+
newText: `${o}_${c.name}`
|
|
981
|
+
});
|
|
982
|
+
} else c.type === "function" && r.has(c.name) && s.push({
|
|
983
|
+
index: c.index,
|
|
984
|
+
oldText: c.name,
|
|
985
|
+
newText: `${o}_${c.name}`
|
|
986
|
+
});
|
|
987
|
+
const l = this.applyRewrites(e.body, s), f = t.rename ? n : `${o}_${e.name}`;
|
|
988
|
+
return {
|
|
989
|
+
...e,
|
|
990
|
+
name: f,
|
|
991
|
+
body: l
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Apply rewrite operations to a string, processing from end to start
|
|
996
|
+
*/
|
|
997
|
+
applyRewrites(e, n) {
|
|
998
|
+
const t = [...n].sort((r, s) => s.index - r.index);
|
|
999
|
+
let i = e;
|
|
1000
|
+
for (const r of t)
|
|
1001
|
+
i = i.slice(0, r.index) + r.newText + i.slice(r.index + r.oldText.length);
|
|
1002
|
+
return i;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Build the final compiled shader
|
|
1006
|
+
*/
|
|
1007
|
+
build() {
|
|
1008
|
+
const e = this.original.parse();
|
|
1009
|
+
let n = this.original.source;
|
|
1010
|
+
n = this.removeImportLines(n, e);
|
|
1011
|
+
const t = this.findInsertionPointForUniforms(n), i = this.generateUniformsCode();
|
|
1012
|
+
i && (n = n.slice(0, t) + i + `
|
|
1013
|
+
` + n.slice(t));
|
|
1014
|
+
const r = this.findInsertionPointForFunctions(n), s = this.generateFunctionCode();
|
|
1015
|
+
return s && (n = n.slice(0, r) + s + n.slice(r)), n = n.replace(/\n{3,}/g, `
|
|
1016
|
+
|
|
1017
|
+
`), n = this.replaceMentions(n, e), n;
|
|
1018
|
+
}
|
|
1019
|
+
replaceMentions(e, n) {
|
|
1020
|
+
let t = e;
|
|
1021
|
+
const i = n.functions.filter(
|
|
1022
|
+
(r) => r.dependencies.some((s) => s.type === "mention")
|
|
1023
|
+
);
|
|
1024
|
+
for (const r of i) {
|
|
1025
|
+
const s = r.dependencies.filter((o) => o.type === "mention");
|
|
1026
|
+
for (const o of s) {
|
|
1027
|
+
const l = o.name.split("."), f = new RegExp(`\\b${l[0]}_(\\w+)_${l[1]}\\b`, "g"), c = this.requirements.uniforms.keys().find((p) => {
|
|
1028
|
+
var g;
|
|
1029
|
+
return ((g = p.match(f)) == null ? void 0 : g[0]) === p;
|
|
1030
|
+
});
|
|
1031
|
+
if (!c)
|
|
1032
|
+
throw new ne(
|
|
1033
|
+
o.name,
|
|
1034
|
+
r.name
|
|
1035
|
+
);
|
|
1036
|
+
const m = new RegExp(`@\\b${o.name}\\b`, "g");
|
|
1037
|
+
t = t.replace(m, c);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return t;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Remove #import lines from shader source
|
|
1044
|
+
*/
|
|
1045
|
+
removeImportLines(e, n) {
|
|
1046
|
+
const t = e.split(`
|
|
1047
|
+
`), i = new Set(n.imports.map((r) => r.line));
|
|
1048
|
+
return t.filter((r, s) => {
|
|
1049
|
+
const o = i.has(s + 1);
|
|
1050
|
+
if (!o && r.trim() === "" && s > 0) {
|
|
1051
|
+
const l = s - 1;
|
|
1052
|
+
if (i.has(l + 1))
|
|
1053
|
+
return !1;
|
|
1054
|
+
}
|
|
1055
|
+
return !o;
|
|
1056
|
+
}).join(`
|
|
1057
|
+
`);
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Find insertion point for uniforms (after existing uniforms)
|
|
1061
|
+
*/
|
|
1062
|
+
findInsertionPointForUniforms(e) {
|
|
1063
|
+
const n = new w(e).parse(), t = n.uniforms.find(
|
|
1064
|
+
(o) => o.line === Math.max(...n.uniforms.map((l) => l.line ?? 0))
|
|
1065
|
+
), i = e.split(`
|
|
1066
|
+
`);
|
|
1067
|
+
let r = 0;
|
|
1068
|
+
if (t && t.line)
|
|
1069
|
+
r = t.line;
|
|
1070
|
+
else
|
|
1071
|
+
for (let o = 0; o < i.length; o++) {
|
|
1072
|
+
const l = i[o].trim();
|
|
1073
|
+
if (l.startsWith("#version")) {
|
|
1074
|
+
r = o + 1;
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
if (l.startsWith("precision ")) {
|
|
1078
|
+
r = o + 1;
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
if (l === "" || l.startsWith("//")) {
|
|
1082
|
+
r === o && (r = o + 1);
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
533
1085
|
break;
|
|
1086
|
+
}
|
|
1087
|
+
let s = 0;
|
|
1088
|
+
for (let o = 0; o < r; o++)
|
|
1089
|
+
s += i[o].length + 1;
|
|
1090
|
+
return s;
|
|
1091
|
+
}
|
|
1092
|
+
findInsertionPointForFunctions(e) {
|
|
1093
|
+
const n = new w(e).parse(), t = n.functions.find(
|
|
1094
|
+
(o) => o.line === Math.min(...n.functions.map((l) => l.line ?? 1 / 0))
|
|
1095
|
+
), i = e.split(`
|
|
1096
|
+
`);
|
|
1097
|
+
let r = 0;
|
|
1098
|
+
if (t && t.line)
|
|
1099
|
+
r = t.line - 2;
|
|
1100
|
+
else
|
|
1101
|
+
throw new W();
|
|
1102
|
+
let s = 0;
|
|
1103
|
+
for (let o = 0; o < r; o++)
|
|
1104
|
+
s += i[o].length + 1;
|
|
1105
|
+
return s;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Generate GLSL code for uniforms
|
|
1109
|
+
*/
|
|
1110
|
+
generateUniformsCode() {
|
|
1111
|
+
const e = [];
|
|
1112
|
+
if (this.requirements.uniforms.size > 0)
|
|
1113
|
+
for (const n of this.checkUniformsPresence())
|
|
1114
|
+
e.push(`uniform ${n.type} ${n.name};`);
|
|
1115
|
+
return e.length === 0 ? "" : e.join(`
|
|
1116
|
+
`) + `
|
|
1117
|
+
`;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Generate GLSL code for functions
|
|
1121
|
+
*/
|
|
1122
|
+
generateFunctionCode() {
|
|
1123
|
+
const e = [];
|
|
1124
|
+
if (this.requirements.functions.size > 0)
|
|
1125
|
+
for (const n of this.checkFunctionsPresence()) {
|
|
1126
|
+
const t = n.params.map((i) => `${i.type} ${i.name}`).join(", ");
|
|
1127
|
+
e.push(`
|
|
1128
|
+
${n.type} ${n.name}(${t}) ${n.body}`);
|
|
1129
|
+
}
|
|
1130
|
+
return e.length === 0 ? "" : e.join(`
|
|
1131
|
+
`) + `
|
|
1132
|
+
`;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Check which required uniforms are missing from the original shader
|
|
1136
|
+
*/
|
|
1137
|
+
checkUniformsPresence() {
|
|
1138
|
+
const e = this.original.parse(), n = [], t = this.requirements.uniforms;
|
|
1139
|
+
for (const [i, r] of t) {
|
|
1140
|
+
const s = e.uniforms.find((o) => o.name === i);
|
|
1141
|
+
if (!s) {
|
|
1142
|
+
n.push(r);
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (s.type !== r.type)
|
|
1146
|
+
throw new C(
|
|
1147
|
+
"uniform",
|
|
1148
|
+
i,
|
|
1149
|
+
r.type,
|
|
1150
|
+
s.type
|
|
1151
|
+
);
|
|
534
1152
|
}
|
|
1153
|
+
return n;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Check which required functions are missing from the original shader
|
|
1157
|
+
*/
|
|
1158
|
+
checkFunctionsPresence() {
|
|
1159
|
+
const e = this.original.parse(), n = [], t = this.requirements.functions;
|
|
1160
|
+
for (const [i, r] of t) {
|
|
1161
|
+
const s = e.functions.find((o) => o.name === i);
|
|
1162
|
+
if (!s) {
|
|
1163
|
+
n.push(r);
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
if (s.type !== r.type)
|
|
1167
|
+
throw new C(
|
|
1168
|
+
"function",
|
|
1169
|
+
i,
|
|
1170
|
+
r.type,
|
|
1171
|
+
s.type
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
return n;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
class v extends F {
|
|
1178
|
+
constructor(n, t, i = {}) {
|
|
1179
|
+
super(t);
|
|
1180
|
+
a(this, "name");
|
|
1181
|
+
a(this, "options", {});
|
|
1182
|
+
this.name = n, this.options = this.resolveOptions(i);
|
|
1183
|
+
}
|
|
1184
|
+
resolveOptions(n) {
|
|
1185
|
+
if (!(n != null && n.default)) return n || {};
|
|
1186
|
+
const t = this.original.parse(), i = n.default;
|
|
1187
|
+
for (const r of t.functions)
|
|
1188
|
+
if (!(r.name === "main" || r.name === "default"))
|
|
1189
|
+
if (n[r.name]) {
|
|
1190
|
+
const s = n[r.name];
|
|
1191
|
+
for (const o in i)
|
|
1192
|
+
o in s || (s[o] = i[o]);
|
|
1193
|
+
} else
|
|
1194
|
+
n[r.name] = { ...i };
|
|
1195
|
+
return delete n.default, n || {};
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Define a module with given name and source, and register it
|
|
1199
|
+
*/
|
|
1200
|
+
static define(n) {
|
|
1201
|
+
const { name: t, source: i, options: r } = n;
|
|
1202
|
+
if (t === "sandbox" || t.startsWith("sandbox/"))
|
|
1203
|
+
throw new Q(t);
|
|
1204
|
+
const s = new v(t, i, r);
|
|
1205
|
+
if (b.has(t))
|
|
1206
|
+
throw new J(t);
|
|
1207
|
+
return b.register(t, s), s;
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Resolve a module by name from the registry, throwing an error if not found
|
|
1211
|
+
*/
|
|
1212
|
+
static resolve(n) {
|
|
1213
|
+
return b.resolve(n);
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Create a copy of the module. To unplug references to the original object.
|
|
1217
|
+
* Used when copying module to the runtime registry to allow independent runtime changes to options without affecting the original module definition.
|
|
1218
|
+
*/
|
|
1219
|
+
copy(n = "original") {
|
|
1220
|
+
return new v(
|
|
1221
|
+
this.name,
|
|
1222
|
+
this[n].source,
|
|
1223
|
+
JSON.parse(JSON.stringify(this.options))
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Merge options from another module into this one, without affecting the original module definition.
|
|
1228
|
+
* Used when merging imported modules into the runtime registry.
|
|
1229
|
+
*/
|
|
1230
|
+
merge(n) {
|
|
1231
|
+
this.options = this.options || {};
|
|
1232
|
+
const t = this.getDefinition().uniforms.map((i) => i.name);
|
|
1233
|
+
for (const [i, r] of Object.entries(n.options ?? {}))
|
|
1234
|
+
if (!this.options[i]) this.options[i] = r;
|
|
1235
|
+
else
|
|
1236
|
+
for (const [s, o] of Object.entries(r))
|
|
1237
|
+
t.includes(o.uniform) || (this.options[i][s] = o);
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Get the module definition
|
|
1241
|
+
*/
|
|
1242
|
+
getDefinition() {
|
|
1243
|
+
return this.compile(), {
|
|
1244
|
+
name: this.name,
|
|
1245
|
+
methods: this.compiled.parse().functions.map((n) => n.name).filter((n) => n !== "main" && n !== "default"),
|
|
1246
|
+
uniforms: this.compiled.parse().uniforms.map((n) => ({ name: n.name, type: n.type })),
|
|
1247
|
+
options: this.options
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Extract a method with all its dependencies
|
|
1252
|
+
*/
|
|
1253
|
+
extract(n) {
|
|
1254
|
+
if (this.compile(), n === "main")
|
|
1255
|
+
throw new K(this.name);
|
|
1256
|
+
if (n === "default")
|
|
1257
|
+
throw new X(this.name);
|
|
1258
|
+
const t = this.compiled.parse(), i = t.functions.find((o) => o.name === n);
|
|
1259
|
+
if (!i)
|
|
1260
|
+
throw new Y(this.name, n);
|
|
1261
|
+
const r = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map();
|
|
1262
|
+
return this.collectDependencies({
|
|
1263
|
+
current: i,
|
|
1264
|
+
scope: {
|
|
1265
|
+
functions: t.functions,
|
|
1266
|
+
uniforms: t.uniforms
|
|
1267
|
+
},
|
|
1268
|
+
collected: {
|
|
1269
|
+
functions: r,
|
|
1270
|
+
uniforms: s
|
|
1271
|
+
},
|
|
1272
|
+
visited: /* @__PURE__ */ new Set([n])
|
|
1273
|
+
}), {
|
|
1274
|
+
function: i,
|
|
1275
|
+
dependencies: {
|
|
1276
|
+
functions: Array.from(r.values()),
|
|
1277
|
+
uniforms: Array.from(s.values())
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Recursively collect all function and uniform dependencies
|
|
1283
|
+
*/
|
|
1284
|
+
collectDependencies(n) {
|
|
1285
|
+
for (const t of n.current.dependencies)
|
|
1286
|
+
if (t.type === "function") {
|
|
1287
|
+
if (n.visited.has(t.name)) continue;
|
|
1288
|
+
const i = n.scope.functions.find((r) => r.name === t.name);
|
|
1289
|
+
i && (n.visited.add(t.name), n.collected.functions.set(t.name, i), this.collectDependencies({
|
|
1290
|
+
current: i,
|
|
1291
|
+
scope: {
|
|
1292
|
+
functions: n.scope.functions,
|
|
1293
|
+
uniforms: n.scope.uniforms
|
|
1294
|
+
},
|
|
1295
|
+
collected: {
|
|
1296
|
+
functions: n.collected.functions,
|
|
1297
|
+
uniforms: n.collected.uniforms
|
|
1298
|
+
},
|
|
1299
|
+
visited: n.visited
|
|
1300
|
+
}));
|
|
1301
|
+
} else if (t.type === "uniform") {
|
|
1302
|
+
const i = n.scope.uniforms.find(
|
|
1303
|
+
(r) => r.name === t.name
|
|
1304
|
+
);
|
|
1305
|
+
i && !n.collected.uniforms.has(t.name) && n.collected.uniforms.set(t.name, i);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
class V {
|
|
1310
|
+
constructor(e = []) {
|
|
1311
|
+
a(this, "modules", /* @__PURE__ */ new Map());
|
|
1312
|
+
e.forEach((n) => {
|
|
1313
|
+
this.register(n.name, n);
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Compile all registered modules.
|
|
1318
|
+
*/
|
|
1319
|
+
compile() {
|
|
1320
|
+
this.modules.forEach((e) => {
|
|
1321
|
+
e.compile();
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Get the list of available shader modules.
|
|
1326
|
+
*/
|
|
1327
|
+
available() {
|
|
1328
|
+
return Array.from(this.modules.values()).map(
|
|
1329
|
+
(e) => e.getDefinition()
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Get the list of uniforms required by the currently registered modules.
|
|
1334
|
+
* This is used to automatically set up the uniforms in the shader based on the modules in use.
|
|
1335
|
+
*/
|
|
1336
|
+
defaults() {
|
|
1337
|
+
const e = {};
|
|
1338
|
+
return this.modules.forEach((n) => {
|
|
1339
|
+
const t = n.getDefinition();
|
|
1340
|
+
if (t.options)
|
|
1341
|
+
for (const i in t.options) {
|
|
1342
|
+
const r = t.options[i];
|
|
1343
|
+
for (const s in r) {
|
|
1344
|
+
const o = r[s];
|
|
1345
|
+
o.default !== void 0 && !t.uniforms.map((l) => l.name).includes(o.uniform) && (e[o.uniform] = o.default);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}), e;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Resolve the options from the module definitions for a given function name.
|
|
1352
|
+
*/
|
|
1353
|
+
resolveOptions(e) {
|
|
1354
|
+
for (const n of this.modules.values())
|
|
1355
|
+
if (n.options && n.options[e])
|
|
1356
|
+
return n.options[e];
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Register a new module in the registry.
|
|
1361
|
+
*/
|
|
1362
|
+
register(e, n) {
|
|
1363
|
+
this.modules.set(e, n);
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Merge a module into the registry. If a module with the same name already exists, options will be merged together.
|
|
1367
|
+
*/
|
|
1368
|
+
merge(e, n) {
|
|
1369
|
+
if (!this.modules.has(e)) return this.register(e, n);
|
|
1370
|
+
const t = this.modules.get(e);
|
|
1371
|
+
t.merge(n), this.modules.set(e, t);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Resolve a module by name. Throws an error if the module is not found.
|
|
1375
|
+
*/
|
|
1376
|
+
resolve(e) {
|
|
1377
|
+
const n = this.modules.get(e);
|
|
1378
|
+
if (!n)
|
|
1379
|
+
throw new j(e);
|
|
1380
|
+
return n;
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Check if a module exists in the registry by name.
|
|
1384
|
+
*/
|
|
1385
|
+
has(e) {
|
|
1386
|
+
return this.modules.has(e);
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Check if the registry is empty (no modules registered).
|
|
1390
|
+
*/
|
|
1391
|
+
isEmpty() {
|
|
1392
|
+
return this.modules.size === 0;
|
|
535
1393
|
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Remove a module from the registry by name.
|
|
1396
|
+
*/
|
|
1397
|
+
remove(e) {
|
|
1398
|
+
this.modules.delete(e);
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Load multiple modules into the registry at once.
|
|
1402
|
+
*/
|
|
1403
|
+
load(e) {
|
|
1404
|
+
e.forEach((n) => {
|
|
1405
|
+
this.register(n.name, n);
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Clear all registered modules from the registry.
|
|
1410
|
+
*/
|
|
1411
|
+
clear() {
|
|
1412
|
+
this.modules.clear();
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
const oe = `// ─── Constants ──────────────────────────────────────────────
|
|
1416
|
+
|
|
1417
|
+
const float PI = 3.14159265359;
|
|
1418
|
+
const float TAU = 6.28318530718;
|
|
1419
|
+
|
|
1420
|
+
// ─── UV Transforms ──────────────────────────────────────────
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* Center UV coordinates — remap to range where (0,0) is canvas center.
|
|
1424
|
+
* Aspect-ratio corrected using resolution length.
|
|
1425
|
+
* @uv-modifier
|
|
1426
|
+
*/
|
|
1427
|
+
vec2 center(vec2 uv) {
|
|
1428
|
+
return (uv - 0.5 * u_resolution) / length(u_resolution);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* Translate UV coordinates by offset.
|
|
1433
|
+
* @uv-modifier
|
|
1434
|
+
*/
|
|
1435
|
+
vec2 translate(vec2 uv, vec2 offset) {
|
|
1436
|
+
return uv - offset;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Scale UV coordinates by factor around origin.
|
|
1441
|
+
* @uv-modifier
|
|
1442
|
+
*/
|
|
1443
|
+
vec2 scale(vec2 uv, float factor) {
|
|
1444
|
+
return uv * factor;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Zoom — scale UV with aspect ratio correction.
|
|
1449
|
+
* Expects centered UV (use center() first).
|
|
1450
|
+
* Pushes UV outward/inward from origin.
|
|
1451
|
+
* factor = zoom factor (>1 = zoom in, <1 = zoom out)
|
|
1452
|
+
* @uv-modifier
|
|
1453
|
+
*/
|
|
1454
|
+
vec2 zoom(vec2 uv, float factor) {
|
|
1455
|
+
return uv / max(factor, 0.001);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Normalize UV to range -1 to 1 with aspect ratio correction.
|
|
1460
|
+
* @uv-modifier
|
|
1461
|
+
*/
|
|
1462
|
+
vec2 norm(vec2 uv) {
|
|
1463
|
+
uv *= min(u_resolution.x, u_resolution.y) / length(u_resolution);
|
|
1464
|
+
return uv;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Rotate UV coordinates around origin by angle (radians).
|
|
1469
|
+
* @uv-modifier
|
|
1470
|
+
*/
|
|
1471
|
+
vec2 rotate(vec2 uv, float angle) {
|
|
1472
|
+
float c = cos(angle);
|
|
1473
|
+
float s = sin(angle);
|
|
1474
|
+
return vec2(uv.x * c - uv.y * s, uv.x * s + uv.y * c);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Tile — repeat coordinates in a grid of given size.
|
|
1479
|
+
* @uv-modifier
|
|
1480
|
+
*/
|
|
1481
|
+
vec2 tile(vec2 uv, float size) {
|
|
1482
|
+
return fract(uv / size) * size;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Cartesian to polar coordinates.
|
|
1487
|
+
* Returns vec2(angle, radius) where angle is -PI to PI.
|
|
1488
|
+
*/
|
|
1489
|
+
vec2 polar(vec2 uv) {
|
|
1490
|
+
return vec2(atan(uv.y, uv.x), length(uv));
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* Aspect ratio correction — makes UV square regardless of canvas shape.
|
|
1495
|
+
* @uv-modifier
|
|
1496
|
+
*/
|
|
1497
|
+
vec2 aspect(vec2 uv) {
|
|
1498
|
+
return uv * vec2(u_resolution.x / u_resolution.y, 1.0);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// ─── Math Utilities ─────────────────────────────────────────
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Remap a value from one range to another.
|
|
1505
|
+
* map(0.5, 0.0, 1.0, -1.0, 1.0) → 0.0
|
|
1506
|
+
*/
|
|
1507
|
+
float map(float value, float inMin, float inMax, float outMin, float outMax) {
|
|
1508
|
+
return outMin + (value - inMin) * (outMax - outMin) / (inMax - inMin);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// ─── Noise & Random ─────────────────────────────────────────
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Pseudo-random hash from 2D coordinates.
|
|
1515
|
+
* Returns float in 0–1 range.
|
|
1516
|
+
*/
|
|
1517
|
+
float hash(vec2 p) {
|
|
1518
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/**
|
|
1522
|
+
* 2D pseudo-random hash.
|
|
1523
|
+
* Returns vec2 in 0–1 range — used for point scattering (worley etc).
|
|
1524
|
+
*/
|
|
1525
|
+
vec2 hash2(vec2 p) {
|
|
1526
|
+
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
|
|
1527
|
+
return fract(sin(p) * 43758.5453123);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* Smooth value noise — interpolated hash grid.
|
|
1532
|
+
*/
|
|
1533
|
+
float noise(vec2 p) {
|
|
1534
|
+
vec2 i = floor(p);
|
|
1535
|
+
vec2 f = fract(p);
|
|
1536
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
1537
|
+
|
|
1538
|
+
return mix(
|
|
1539
|
+
mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),
|
|
1540
|
+
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
|
|
1541
|
+
u.y
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* Fractal Brownian Motion — 6 octaves of layered noise.
|
|
1547
|
+
* Returns ~0–1 range. Great for clouds, terrain, organic textures.
|
|
1548
|
+
*/
|
|
1549
|
+
float fbm(vec2 p) {
|
|
1550
|
+
float value = 0.0;
|
|
1551
|
+
float amplitude = 0.5;
|
|
1552
|
+
|
|
1553
|
+
for (int i = 0; i < 6; i++) {
|
|
1554
|
+
value += amplitude * noise(p);
|
|
1555
|
+
p *= 2.0;
|
|
1556
|
+
amplitude *= 0.5;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
return value;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
/**
|
|
1563
|
+
* Worley (cellular / voronoi) noise.
|
|
1564
|
+
* Returns distance to nearest random cell point.
|
|
1565
|
+
* Great for cells, cracks, organic patterns.
|
|
1566
|
+
*/
|
|
1567
|
+
float worley(vec2 p) {
|
|
1568
|
+
vec2 i = floor(p);
|
|
1569
|
+
vec2 f = fract(p);
|
|
1570
|
+
float minDist = 1.0;
|
|
1571
|
+
|
|
1572
|
+
for (int y = -1; y <= 1; y++) {
|
|
1573
|
+
for (int x = -1; x <= 1; x++) {
|
|
1574
|
+
vec2 neighbor = vec2(float(x), float(y));
|
|
1575
|
+
vec2 point = hash2(i + neighbor);
|
|
1576
|
+
float dist = length(neighbor + point - f);
|
|
1577
|
+
minDist = min(minDist, dist);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
return minDist;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* Waves — layered sine wave interference pattern.
|
|
1586
|
+
* Creates clean, regular wave lines like fabric or moiré.
|
|
1587
|
+
* frequency = wave density (5.0 = fabric, 10.0 = fine lines)
|
|
1588
|
+
* Returns 0–1 range.
|
|
1589
|
+
*/
|
|
1590
|
+
float waves(vec2 p, float frequency) {
|
|
1591
|
+
return 0.6 + 0.4 * sin(
|
|
1592
|
+
frequency * (p.x + p.y + cos(3.0 * p.x + 5.0 * p.y)) +
|
|
1593
|
+
sin(frequency * 4.0 * (p.x + p.y))
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Voronoi cell lookup — returns the position of the nearest cell point.
|
|
1599
|
+
* Same algorithm as worley, but returns the point instead of distance.
|
|
1600
|
+
* Great for UV snapping, cell-based effects.
|
|
1601
|
+
*/
|
|
1602
|
+
vec2 voronoi(vec2 p) {
|
|
1603
|
+
vec2 i = floor(p);
|
|
1604
|
+
vec2 f = fract(p);
|
|
1605
|
+
float minDist = 10.0;
|
|
1606
|
+
vec2 nearest = vec2(0.0);
|
|
1607
|
+
|
|
1608
|
+
for (int y = -1; y <= 1; y++) {
|
|
1609
|
+
for (int x = -1; x <= 1; x++) {
|
|
1610
|
+
vec2 neighbor = vec2(float(x), float(y));
|
|
1611
|
+
vec2 point = hash2(i + neighbor);
|
|
1612
|
+
float dist = length(neighbor + point - f);
|
|
1613
|
+
if (dist < minDist) {
|
|
1614
|
+
minDist = dist;
|
|
1615
|
+
nearest = i + neighbor + point;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
return nearest;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
void main() {}
|
|
1624
|
+
`, ae = `/**
|
|
1625
|
+
* Hex integer to RGB.
|
|
1626
|
+
* Usage: hex(0xFF6600) → orange
|
|
1627
|
+
*/
|
|
1628
|
+
vec3 hex(int value) {
|
|
1629
|
+
float v = float(value);
|
|
1630
|
+
float r = floor(v / 65536.0);
|
|
1631
|
+
float g = floor((v - r * 65536.0) / 256.0);
|
|
1632
|
+
float b = v - r * 65536.0 - g * 256.0;
|
|
1633
|
+
return vec3(r, g, b) / 255.0;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* RGB 0–255 to normalized RGB 0–1.
|
|
1638
|
+
* Usage: rgb255(255.0, 128.0, 0.0) → orange
|
|
1639
|
+
*/
|
|
1640
|
+
vec3 rgb255(float r, float g, float b) {
|
|
1641
|
+
return vec3(r, g, b) / 255.0;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* HSV to RGB.
|
|
1646
|
+
* Input: vec3(hue 0–1, saturation 0–1, value 0–1)
|
|
1647
|
+
*/
|
|
1648
|
+
vec3 hsv(vec3 c) {
|
|
1649
|
+
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
1650
|
+
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
1651
|
+
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
/**
|
|
1655
|
+
* HSL to RGB.
|
|
1656
|
+
* Input: vec3(hue 0–1, saturation 0–1, lightness 0–1)
|
|
1657
|
+
*/
|
|
1658
|
+
vec3 hsl(vec3 c) {
|
|
1659
|
+
vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
|
|
1660
|
+
return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/**
|
|
1664
|
+
* Linear gradient between two colors.
|
|
1665
|
+
* t is clamped to 0–1.
|
|
1666
|
+
*/
|
|
1667
|
+
vec3 gradient(vec3 a, vec3 b, float t) {
|
|
1668
|
+
return mix(a, b, clamp(t, 0.0, 1.0));
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* 3-stop gradient.
|
|
1673
|
+
* t=0 → a, t=0.5 → b, t=1 → c
|
|
1674
|
+
*/
|
|
1675
|
+
vec3 gradient3(vec3 a, vec3 b, vec3 c, float t) {
|
|
1676
|
+
t = clamp(t, 0.0, 1.0);
|
|
1677
|
+
return t < 0.5
|
|
1678
|
+
? mix(a, b, t * 2.0)
|
|
1679
|
+
: mix(b, c, (t - 0.5) * 2.0);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
/**
|
|
1683
|
+
* Cosine palette — Inigo Quilez formula.
|
|
1684
|
+
* color = a + b * cos(2π(c·t + d))
|
|
1685
|
+
* Generates infinite smooth color ramps from 4 vec3 params.
|
|
1686
|
+
*/
|
|
1687
|
+
vec3 palette(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
|
|
1688
|
+
return a + b * cos(6.28318 * (c * t + d));
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* Banded gradient — 3-zone color mapping with sharp transitions.
|
|
1693
|
+
* Uses tent functions instead of linear interpolation.
|
|
1694
|
+
* t=0 → b (center), t=1 → a (middle), t=2 → c (outer).
|
|
1695
|
+
* sharpness = transition width (2.0 = sharp, 1.0 = soft).
|
|
1696
|
+
*/
|
|
1697
|
+
vec3 bands(vec3 a, vec3 b, vec3 c, float t, float sharpness) {
|
|
1698
|
+
float w1 = max(0.0, 1.0 - sharpness * abs(1.0 - t));
|
|
1699
|
+
float w2 = max(0.0, 1.0 - sharpness * abs(t));
|
|
1700
|
+
float w3 = 1.0 - min(1.0, w1 + w2);
|
|
1701
|
+
return a * w1 + b * w2 + c * w3;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* Iridescent — iterative interference color pattern.
|
|
1706
|
+
* Generates rainbow-like colors from UV through trigonometric iteration.
|
|
1707
|
+
* Creates shimmering, oil-slick-like color fields.
|
|
1708
|
+
* time = animation time (pass u_time), speed = animation speed
|
|
1709
|
+
*/
|
|
1710
|
+
vec3 iridescent(vec2 uv, float time, float speed) {
|
|
1711
|
+
float d = -time * 0.5 * speed;
|
|
1712
|
+
float a = 0.0;
|
|
1713
|
+
for (int i = 0; i < 8; i++) {
|
|
1714
|
+
a += cos(float(i) - d - a * uv.x);
|
|
1715
|
+
d += sin(uv.y * float(i) + a);
|
|
1716
|
+
}
|
|
1717
|
+
d += time * 0.5 * speed;
|
|
1718
|
+
vec3 col = vec3(cos(uv * vec2(d, a)) * 0.6 + 0.4, cos(a + d) * 0.5 + 0.5);
|
|
1719
|
+
return cos(col * cos(vec3(d, a, 2.5)) * 0.5 + 0.5);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
void main() {}
|
|
1723
|
+
`, le = `// ─── Time Shapers ──────────────────────────────────────────
|
|
1724
|
+
// Convert raw time (u_time) to normalized 0–1 range.
|
|
1725
|
+
// Output feeds directly into easing functions.
|
|
1726
|
+
|
|
1727
|
+
/**
|
|
1728
|
+
* Loop — repeating sawtooth 0→1→0→1...
|
|
1729
|
+
* duration = cycle length in seconds
|
|
1730
|
+
*/
|
|
1731
|
+
float loop(float t, float duration) {
|
|
1732
|
+
return fract(t / duration);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Pingpong — triangle wave 0→1→0→1...
|
|
1737
|
+
* duration = half-cycle length (0→1 takes \`duration\` seconds)
|
|
1738
|
+
*/
|
|
1739
|
+
float pingpong(float t, float duration) {
|
|
1740
|
+
float m = mod(t, duration * 2.0);
|
|
1741
|
+
return m < duration ? m / duration : 2.0 - m / duration;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* Once — single play 0→1, then stays at 1.
|
|
1746
|
+
* duration = animation length in seconds
|
|
1747
|
+
* Use with delay: once(u_time - 2.0, 3.0) starts at 2s, takes 3s.
|
|
1748
|
+
*/
|
|
1749
|
+
float once(float t, float duration) {
|
|
1750
|
+
return clamp(t / duration, 0.0, 1.0);
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
/**
|
|
1754
|
+
* Reverse — flip animation direction.
|
|
1755
|
+
* Turns 0→1 into 1→0. Works with any easing or shaper.
|
|
1756
|
+
* Usage: reverse(spring(loop(u_time, 2.0))) for spring zoom-in.
|
|
1757
|
+
*/
|
|
1758
|
+
float reverse(float t) {
|
|
1759
|
+
return 1.0 - t;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
// ─── Easing Curves ─────────────────────────────────────────
|
|
1763
|
+
// All take t (0–1) and return shaped t (0–1).
|
|
1764
|
+
// Compose with time shapers: ease_in(loop(u_time, 2.0))
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* Ease in — cubic acceleration. Slow start, fast end.
|
|
1768
|
+
*/
|
|
1769
|
+
float ease_in(float t) {
|
|
1770
|
+
return t * t * t;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
/**
|
|
1774
|
+
* Ease out — cubic deceleration. Fast start, slow end.
|
|
1775
|
+
*/
|
|
1776
|
+
float ease_out(float t) {
|
|
1777
|
+
float f = 1.0 - t;
|
|
1778
|
+
return 1.0 - f * f * f;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* Ease in-out — cubic smooth both ends.
|
|
1783
|
+
*/
|
|
1784
|
+
float ease_in_out(float t) {
|
|
1785
|
+
return t < 0.5
|
|
1786
|
+
? 4.0 * t * t * t
|
|
1787
|
+
: 1.0 - pow(-2.0 * t + 2.0, 3.0) * 0.5;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* Spring — damped oscillation. Overshoots then settles at 1.
|
|
1792
|
+
*/
|
|
1793
|
+
float spring(float t) {
|
|
1794
|
+
return 1.0 - exp(-6.0 * t) * cos(12.0 * t);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
/**
|
|
1798
|
+
* Bounce — bouncing ball landing. Multiple bounces settling at 1.
|
|
1799
|
+
*/
|
|
1800
|
+
float bounce(float t) {
|
|
1801
|
+
if (t < 1.0 / 2.75) {
|
|
1802
|
+
return 7.5625 * t * t;
|
|
1803
|
+
} else if (t < 2.0 / 2.75) {
|
|
1804
|
+
t -= 1.5 / 2.75;
|
|
1805
|
+
return 7.5625 * t * t + 0.75;
|
|
1806
|
+
} else if (t < 2.5 / 2.75) {
|
|
1807
|
+
t -= 2.25 / 2.75;
|
|
1808
|
+
return 7.5625 * t * t + 0.9375;
|
|
1809
|
+
} else {
|
|
1810
|
+
t -= 2.625 / 2.75;
|
|
1811
|
+
return 7.5625 * t * t + 0.984375;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* Elastic — springy overshoot with oscillation.
|
|
1817
|
+
*/
|
|
1818
|
+
float elastic(float t) {
|
|
1819
|
+
return sin(13.0 * 3.14159 * 0.5 * t) * pow(2.0, -10.0 * t) + 1.0;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* Overshoot — goes past 1, then pulls back. Slight rubber-band feel.
|
|
1824
|
+
*/
|
|
1825
|
+
float overshoot(float t) {
|
|
1826
|
+
float s = 1.70158;
|
|
1827
|
+
float f = 1.0 - t;
|
|
1828
|
+
return 1.0 - f * f * (f * (s + 1.0) - s);
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Teleport — barely creeps, then near-instant jump with spring settle.
|
|
1833
|
+
* Duration mostly = wait time. Jump happens at ~85% of t.
|
|
1834
|
+
* Tiny slow drift → snap → spring overshoot → settle at 1.
|
|
1835
|
+
*/
|
|
1836
|
+
float teleport(float t) {
|
|
1837
|
+
float jump = smoothstep(0.8, 0.85, t);
|
|
1838
|
+
float creep = pow(t / 0.8, 4.0) * 0.1 * (1.0 - jump);
|
|
1839
|
+
float s = max(t - 0.85, 0.0) * 6.667;
|
|
1840
|
+
float settle = exp(-5.0 * s) * sin(s * 15.0) * 0.15;
|
|
1841
|
+
return creep + jump + settle;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
void main() {}
|
|
1845
|
+
`, ce = `#import hash from 'sandbox'
|
|
1846
|
+
#import noise from 'sandbox'
|
|
1847
|
+
#import fbm from 'sandbox'
|
|
1848
|
+
#import voronoi from 'sandbox'
|
|
1849
|
+
#import worley from 'sandbox'
|
|
1850
|
+
|
|
1851
|
+
uniform float u_intensity;
|
|
1852
|
+
|
|
1853
|
+
// ─── UV Effects ─────────────────────────────────────────────
|
|
1854
|
+
// Each function takes UV and returns modified UV.
|
|
1855
|
+
// All use u_intensity uniform for configuration.
|
|
1856
|
+
|
|
1857
|
+
/**
|
|
1858
|
+
* Pixelate — blocky mosaic effect.
|
|
1859
|
+
* Snaps UV to a grid. Higher intensity = larger pixels.
|
|
1860
|
+
* intensity = pixel count along shortest axis (default: 20)
|
|
1861
|
+
* @uv-modifier
|
|
1862
|
+
*/
|
|
1863
|
+
vec2 pixelate(vec2 uv, float intensity) {
|
|
1864
|
+
float size = length(u_resolution) / (max(intensity, 1.0) * 10.0);
|
|
1865
|
+
return floor(uv / size) * size;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/**
|
|
1869
|
+
* Twist — spiral distortion from center outward.
|
|
1870
|
+
* Expects centered UV (use center() first).
|
|
1871
|
+
* intensity = twist strength (0.4 = subtle, 1.0 = full spiral)
|
|
1872
|
+
* @uv-modifier
|
|
1873
|
+
*/
|
|
1874
|
+
vec2 twist(vec2 uv, float intensity) {
|
|
1875
|
+
float dist = length(uv);
|
|
1876
|
+
float angle = atan(uv.y, uv.x) - intensity * 20.0 * dist;
|
|
1877
|
+
return vec2(cos(angle), sin(angle)) * dist;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* Ripple — concentric wave distortion from center.
|
|
1882
|
+
* Expects centered UV (use center() first).
|
|
1883
|
+
* Creates water-drop-like rings.
|
|
1884
|
+
* intensity = wave amplitude (0.5 = gentle, 2.0 = strong)
|
|
1885
|
+
* @uv-modifier
|
|
1886
|
+
*/
|
|
1887
|
+
vec2 ripple(vec2 uv, float intensity) {
|
|
1888
|
+
float dist = length(uv);
|
|
1889
|
+
float wave = sin(dist * 40.0 - u_time * 3.0) * intensity * 0.02;
|
|
1890
|
+
return uv + normalize(uv + 0.001) * wave;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* Fisheye — barrel distortion from center.
|
|
1895
|
+
* Expects centered UV (use center() first).
|
|
1896
|
+
* Bends space outward like a fisheye lens.
|
|
1897
|
+
* intensity = distortion power (0.5 = subtle, 2.0 = extreme)
|
|
1898
|
+
* @uv-modifier
|
|
1899
|
+
*/
|
|
1900
|
+
vec2 fisheye(vec2 uv, float intensity) {
|
|
1901
|
+
float dist = length(uv);
|
|
1902
|
+
float power = pow(dist, intensity) / dist;
|
|
1903
|
+
return uv * power;
|
|
536
1904
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1905
|
+
|
|
1906
|
+
/**
|
|
1907
|
+
* Wobble — animated sine wave displacement.
|
|
1908
|
+
* Creates a jelly-like horizontal wobble.
|
|
1909
|
+
* intensity = wobble amount (1.0 = gentle, 5.0 = wild)
|
|
1910
|
+
* @uv-modifier
|
|
1911
|
+
*/
|
|
1912
|
+
vec2 wobble(vec2 uv, float intensity) {
|
|
1913
|
+
uv.x += sin(uv.y * 10.0 + u_time * 2.0) * intensity * 0.01;
|
|
1914
|
+
uv.y += cos(uv.x * 10.0 + u_time * 2.0) * intensity * 0.01;
|
|
1915
|
+
return uv;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* Organic — iterative fluid morph distortion.
|
|
1920
|
+
* Creates marble-like flowing patterns.
|
|
1921
|
+
* intensity = animation speed (3.0 = default)
|
|
1922
|
+
* @uv-modifier
|
|
1923
|
+
*/
|
|
1924
|
+
vec2 organic(vec2 uv, float intensity) {
|
|
1925
|
+
float speed = u_time * intensity;
|
|
1926
|
+
vec2 acc = vec2(uv.x + uv.y);
|
|
1927
|
+
|
|
1928
|
+
for (int i = 0; i < 5; i++) {
|
|
1929
|
+
acc += sin(max(uv.x, uv.y)) + uv;
|
|
1930
|
+
uv += 0.5 * vec2(
|
|
1931
|
+
cos(5.1123314 + 0.353 * acc.y + speed * 0.131121),
|
|
1932
|
+
sin(acc.x - 0.113 * speed)
|
|
1933
|
+
);
|
|
1934
|
+
uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
return uv;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/**
|
|
1941
|
+
* Glitch — random horizontal line displacement.
|
|
1942
|
+
* Creates digital glitch artifact bands.
|
|
1943
|
+
* intensity = glitch strength (0.5 = subtle, 3.0 = heavy)
|
|
1944
|
+
* @uv-modifier
|
|
1945
|
+
*/
|
|
1946
|
+
vec2 glitch(vec2 uv, float intensity) {
|
|
1947
|
+
float line = floor(uv.y * 50.0);
|
|
1948
|
+
float shift = hash(vec2(line, floor(u_time * 8.0)));
|
|
1949
|
+
shift = step(0.9, shift) * (shift - 0.9) * 10.0;
|
|
1950
|
+
uv.x += shift * intensity * 0.1 * u_resolution.x;
|
|
1951
|
+
return uv;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* Mirror — reflect UV across a chosen axis.
|
|
1956
|
+
* Creates bilateral symmetry.
|
|
1957
|
+
* intensity = 0 for horizontal mirror, 1 for vertical mirror
|
|
1958
|
+
* @uv-modifier
|
|
1959
|
+
*/
|
|
1960
|
+
vec2 mirror(vec2 uv, float intensity) {
|
|
1961
|
+
if (intensity < 0.5) {
|
|
1962
|
+
uv.x = abs(uv.x);
|
|
1963
|
+
} else {
|
|
1964
|
+
uv.y = abs(uv.y);
|
|
1965
|
+
}
|
|
1966
|
+
return uv;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* Kaleidoscope — repeating angular symmetry.
|
|
1971
|
+
* Expects centered UV (use center() first).
|
|
1972
|
+
* Creates mandala-like radial repeats.
|
|
1973
|
+
* intensity = number of segments (4 = quad, 6 = hex, 8 = octa)
|
|
1974
|
+
* @uv-modifier
|
|
1975
|
+
*/
|
|
1976
|
+
vec2 kaleidoscope(vec2 uv, float intensity) {
|
|
1977
|
+
float segments = max(intensity, 1.0);
|
|
1978
|
+
float angle = atan(uv.y, uv.x);
|
|
1979
|
+
float segment_angle = 6.28318 / segments;
|
|
1980
|
+
angle = mod(angle, segment_angle);
|
|
1981
|
+
angle = abs(angle - segment_angle * 0.5);
|
|
1982
|
+
float r = length(uv);
|
|
1983
|
+
return vec2(cos(angle), sin(angle)) * r;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
/**
|
|
1987
|
+
* Warp — domain warping using fractal noise.
|
|
1988
|
+
* Displaces UV by layered fbm noise for smoke/cloud-like distortion.
|
|
1989
|
+
* Animated over time.
|
|
1990
|
+
* intensity = warp strength (0.5 = subtle haze, 3.0 = heavy distortion)
|
|
1991
|
+
* @uv-modifier
|
|
1992
|
+
*/
|
|
1993
|
+
vec2 warp(vec2 uv, float intensity) {
|
|
1994
|
+
vec2 offset = vec2(
|
|
1995
|
+
fbm(uv + u_time * 0.3),
|
|
1996
|
+
fbm(uv + vec2(5.2, 1.3) + u_time * 0.3)
|
|
1997
|
+
);
|
|
1998
|
+
return uv + offset * intensity * 0.1;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
/**
|
|
2002
|
+
* Displace — noise-based UV displacement.
|
|
2003
|
+
* Shifts UV using smooth value noise for a wavy, heat-haze look.
|
|
2004
|
+
* Animated over time.
|
|
2005
|
+
* intensity = displacement amount (1.0 = gentle, 5.0 = strong)
|
|
2006
|
+
* @uv-modifier
|
|
2007
|
+
*/
|
|
2008
|
+
vec2 displace(vec2 uv, float intensity) {
|
|
2009
|
+
float nx = noise(uv * 5.0 + u_time);
|
|
2010
|
+
float ny = noise(uv * 5.0 + vec2(3.7, 7.1) + u_time);
|
|
2011
|
+
return uv + (vec2(nx, ny) - 0.5) * intensity * 0.05;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
/**
|
|
2015
|
+
* Shatter — voronoi cell-based UV snapping.
|
|
2016
|
+
* Snaps UV to nearest cell point, creating a broken-glass look.
|
|
2017
|
+
* intensity = cell density (5.0 = large shards, 20.0 = fine fragments)
|
|
2018
|
+
* @uv-modifier
|
|
2019
|
+
*/
|
|
2020
|
+
vec2 shatter(vec2 uv, float intensity) {
|
|
2021
|
+
return voronoi(uv * intensity) / intensity;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/**
|
|
2025
|
+
* Cells — cellular distortion using Worley distance.
|
|
2026
|
+
* Displaces UV outward from cell edges, like looking through textured glass.
|
|
2027
|
+
* intensity = cell scale (3.0 = large bubbles, 15.0 = fine texture)
|
|
2028
|
+
* @uv-modifier
|
|
2029
|
+
*/
|
|
2030
|
+
vec2 cells(vec2 uv, float intensity) {
|
|
2031
|
+
float dist = worley(uv * intensity);
|
|
2032
|
+
vec2 grad = vec2(
|
|
2033
|
+
worley(uv * intensity + vec2(0.01, 0.0)) - dist,
|
|
2034
|
+
worley(uv * intensity + vec2(0.0, 0.01)) - dist
|
|
2035
|
+
);
|
|
2036
|
+
return uv + grad * dist * 2.0;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
void main() {}
|
|
2040
|
+
`, ue = `#import hash from 'sandbox'
|
|
2041
|
+
|
|
2042
|
+
uniform float u_intensity;
|
|
2043
|
+
|
|
2044
|
+
// ─── Color Filters ──────────────────────────────────────────
|
|
2045
|
+
// Each function takes a color and returns modified color.
|
|
2046
|
+
// All use u_intensity uniform for configuration.
|
|
2047
|
+
|
|
2048
|
+
/**
|
|
2049
|
+
* Contrast — adjust contrast around mid-gray.
|
|
2050
|
+
* intensity: 1.0 = original, >1 = more contrast, <1 = flatter
|
|
2051
|
+
* @color-modifier
|
|
2052
|
+
*/
|
|
2053
|
+
vec3 contrast(vec3 color, float intensity) {
|
|
2054
|
+
return (color - 0.5) * intensity + 0.5;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
/**
|
|
2058
|
+
* Brightness — multiply overall brightness.
|
|
2059
|
+
* intensity: 1.0 = original, >1 = brighter, <1 = darker
|
|
2060
|
+
* @color-modifier
|
|
2061
|
+
*/
|
|
2062
|
+
vec3 brightness(vec3 color, float intensity) {
|
|
2063
|
+
return color * intensity;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
/**
|
|
2067
|
+
* Saturate — adjust color saturation.
|
|
2068
|
+
* intensity: 0 = grayscale, 1.0 = original, >1 = oversaturated
|
|
2069
|
+
* @color-modifier
|
|
2070
|
+
*/
|
|
2071
|
+
vec3 saturate(vec3 color, float intensity) {
|
|
2072
|
+
float gray = dot(color, vec3(0.299, 0.587, 0.114));
|
|
2073
|
+
return mix(vec3(gray), color, intensity);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
/**
|
|
2077
|
+
* Posterize — reduce color to N discrete levels per channel.
|
|
2078
|
+
* intensity = number of levels (4 = retro, 16 = subtle, 256 = nearly smooth)
|
|
2079
|
+
* @color-modifier
|
|
2080
|
+
*/
|
|
2081
|
+
vec3 posterize(vec3 color, float intensity) {
|
|
2082
|
+
float levels = max(intensity, 1.0);
|
|
2083
|
+
return floor(color * levels + 0.5) / levels;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
/**
|
|
2087
|
+
* Threshold — convert to black and white based on cutoff.
|
|
2088
|
+
* intensity = cutoff point (0.5 = balanced, lower = more white)
|
|
2089
|
+
* @color-modifier
|
|
2090
|
+
*/
|
|
2091
|
+
vec3 threshold(vec3 color, float intensity) {
|
|
2092
|
+
float avg = dot(color, vec3(0.299, 0.587, 0.114));
|
|
2093
|
+
return avg > intensity ? vec3(1.0) : vec3(0.0);
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
/**
|
|
2097
|
+
* Invert — flip all color channels.
|
|
2098
|
+
* intensity: 0.0 = original, 1.0 = fully inverted, 0.5 = mid-gray
|
|
2099
|
+
* @color-modifier
|
|
2100
|
+
*/
|
|
2101
|
+
vec3 invert(vec3 color, float intensity) {
|
|
2102
|
+
return mix(color, 1.0 - color, intensity);
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/**
|
|
2106
|
+
* Glow — luminance-based bloom, bright areas get amplified.
|
|
2107
|
+
* intensity = glow strength (0.5 = subtle, 2.0 = intense)
|
|
2108
|
+
* @color-modifier
|
|
2109
|
+
*/
|
|
2110
|
+
vec3 glow(vec3 color, float intensity) {
|
|
2111
|
+
float luminance = dot(color, vec3(0.299, 0.587, 0.114));
|
|
2112
|
+
return color + color * luminance * intensity;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
/**
|
|
2116
|
+
* Grain — animated film noise overlay.
|
|
2117
|
+
* intensity = noise strength (0.1 = subtle, 0.5 = heavy)
|
|
2118
|
+
* @color-modifier
|
|
2119
|
+
*/
|
|
2120
|
+
vec3 grain(vec3 color, vec2 uv, float intensity) {
|
|
2121
|
+
float n = (hash(uv * 1000.0 + u_time) - 0.5) * intensity;
|
|
2122
|
+
return color + n;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
/**
|
|
2126
|
+
* Vignette — darken canvas edges.
|
|
2127
|
+
* Applies circular falloff from center.
|
|
2128
|
+
* intensity = darkness strength (1.0 = subtle, 2.0 = strong)
|
|
2129
|
+
* @color-modifier
|
|
2130
|
+
*/
|
|
2131
|
+
vec3 vignette(vec3 color, vec2 uv, float intensity) {
|
|
2132
|
+
vec2 centered = uv / u_resolution - 0.5;
|
|
2133
|
+
float dist = length(centered);
|
|
2134
|
+
float falloff = 1.0 - smoothstep(0.3, 0.7, dist * intensity);
|
|
2135
|
+
return color * falloff;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
/**
|
|
2139
|
+
* Sepia — warm brownish tint like old photographs.
|
|
2140
|
+
* intensity: 0.0 = original, 1.0 = full sepia
|
|
2141
|
+
* @color-modifier
|
|
2142
|
+
*/
|
|
2143
|
+
vec3 sepia(vec3 color, float intensity) {
|
|
2144
|
+
vec3 s;
|
|
2145
|
+
s.r = dot(color, vec3(0.393, 0.769, 0.189));
|
|
2146
|
+
s.g = dot(color, vec3(0.349, 0.686, 0.168));
|
|
2147
|
+
s.b = dot(color, vec3(0.272, 0.534, 0.131));
|
|
2148
|
+
return mix(color, s, intensity);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
/**
|
|
2152
|
+
* Gamma — apply gamma correction curve.
|
|
2153
|
+
* intensity = gamma value (1.0 = linear, 2.2 = standard sRGB, <1 = brighten darks)
|
|
2154
|
+
* @color-modifier
|
|
2155
|
+
*/
|
|
2156
|
+
vec3 gamma(vec3 color, float intensity) {
|
|
2157
|
+
return pow(max(color, 0.0), vec3(1.0 / max(intensity, 0.001)));
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
/**
|
|
2161
|
+
* Tint — blend color toward a target tint.
|
|
2162
|
+
* intensity: 0.0 = original, 1.0 = fully tinted
|
|
2163
|
+
* @color-modifier
|
|
2164
|
+
*/
|
|
2165
|
+
vec3 tint(vec3 color, vec3 tintColor, float intensity) {
|
|
2166
|
+
return mix(color, color * tintColor, intensity);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/**
|
|
2170
|
+
* Highlights — add white light to bright areas.
|
|
2171
|
+
* Uses max channel brightness so even saturated colors trigger highlights.
|
|
2172
|
+
* intensity = highlight strength (0.2 = subtle sheen, 1.0 = strong gloss)
|
|
2173
|
+
* @color-modifier
|
|
2174
|
+
*/
|
|
2175
|
+
vec3 highlights(vec3 color, float intensity) {
|
|
2176
|
+
float bright = max(color.r, max(color.g, color.b + 0.3));
|
|
2177
|
+
return color + max(bright * 5.5 - 4.0, 0.0) * intensity;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
/**
|
|
2181
|
+
* Bayer 8×8 threshold matrix — computed mathematically.
|
|
2182
|
+
* Returns threshold value 0–1 for ordered dithering.
|
|
2183
|
+
*/
|
|
2184
|
+
float bayer8(vec2 p) {
|
|
2185
|
+
p = mod(floor(p), 8.0);
|
|
2186
|
+
float value = 0.0;
|
|
2187
|
+
float divisor = 1.0;
|
|
2188
|
+
float multiplier = 16.0;
|
|
2189
|
+
|
|
2190
|
+
for (int i = 0; i < 3; i++) {
|
|
2191
|
+
vec2 q = mod(floor(p / divisor), 2.0);
|
|
2192
|
+
value += (q.x * 2.0 + q.y * 3.0 - q.x * q.y * 4.0) * multiplier;
|
|
2193
|
+
divisor *= 2.0;
|
|
2194
|
+
multiplier *= 0.25;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
return value / 64.0;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
/**
|
|
2201
|
+
* Dither — ordered Bayer 8×8 dithering.
|
|
2202
|
+
* Reduces color to discrete levels with a threshold pattern.
|
|
2203
|
+
* Use pixelate() on UV before coloring for blocky dither cells.
|
|
2204
|
+
* intensity = number of color levels (2 = 1-bit, 4 = retro, 8 = subtle)
|
|
2205
|
+
* @color-modifier
|
|
2206
|
+
*/
|
|
2207
|
+
vec3 dither(vec3 color, vec2 uv, float intensity) {
|
|
2208
|
+
float levels = max(intensity, 2.0);
|
|
2209
|
+
float threshold = bayer8(uv) - 0.5;
|
|
2210
|
+
float stepSize = 1.0 / (levels - 1.0);
|
|
2211
|
+
color += threshold * stepSize;
|
|
2212
|
+
return clamp(floor(color * (levels - 1.0) + 0.5) / (levels - 1.0), 0.0, 1.0);
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
/**
|
|
2216
|
+
* Arcade — pixelated Bayer dithering.
|
|
2217
|
+
* Combines pixelation and ordered dither for retro 8-bit look.
|
|
2218
|
+
* intensity = number of color levels (2 = 1-bit, 4 = retro, 8 = subtle)
|
|
2219
|
+
* @color-modifier
|
|
2220
|
+
*/
|
|
2221
|
+
vec3 arcade(vec3 color, vec2 uv, float intensity) {
|
|
2222
|
+
vec2 grid = uv * u_resolution * 0.5;
|
|
2223
|
+
return dither(color, grid, intensity);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
void main() {}
|
|
2227
|
+
`, b = new V([
|
|
2228
|
+
new v("sandbox", oe),
|
|
2229
|
+
new v("sandbox/colors", ae),
|
|
2230
|
+
new v("sandbox/time", le),
|
|
2231
|
+
new v("sandbox/effects", ce, {
|
|
2232
|
+
default: {
|
|
2233
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2234
|
+
},
|
|
2235
|
+
pixelate: {
|
|
2236
|
+
intensity: { uniform: "u_intensity", default: 20 }
|
|
2237
|
+
},
|
|
2238
|
+
twist: {
|
|
2239
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2240
|
+
},
|
|
2241
|
+
ripple: {
|
|
2242
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2243
|
+
},
|
|
2244
|
+
fisheye: {
|
|
2245
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2246
|
+
},
|
|
2247
|
+
wobble: {
|
|
2248
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2249
|
+
},
|
|
2250
|
+
organic: {
|
|
2251
|
+
intensity: { uniform: "u_intensity", default: 3 }
|
|
2252
|
+
},
|
|
2253
|
+
glitch: {
|
|
2254
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2255
|
+
},
|
|
2256
|
+
mirror: {
|
|
2257
|
+
intensity: { uniform: "u_intensity", default: 0 }
|
|
2258
|
+
},
|
|
2259
|
+
kaleidoscope: {
|
|
2260
|
+
intensity: { uniform: "u_intensity", default: 6 }
|
|
2261
|
+
},
|
|
2262
|
+
zoom: {
|
|
2263
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2264
|
+
},
|
|
2265
|
+
warp: {
|
|
2266
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2267
|
+
},
|
|
2268
|
+
displace: {
|
|
2269
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2270
|
+
},
|
|
2271
|
+
shatter: {
|
|
2272
|
+
intensity: { uniform: "u_intensity", default: 10 }
|
|
2273
|
+
},
|
|
2274
|
+
cells: {
|
|
2275
|
+
intensity: { uniform: "u_intensity", default: 8 }
|
|
2276
|
+
},
|
|
2277
|
+
glass: {
|
|
2278
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2279
|
+
}
|
|
2280
|
+
}),
|
|
2281
|
+
new v("sandbox/filters", ue, {
|
|
2282
|
+
default: {
|
|
2283
|
+
intensity: { uniform: "u_intensity", default: 1 }
|
|
2284
|
+
},
|
|
2285
|
+
posterize: {
|
|
2286
|
+
intensity: { uniform: "u_intensity", default: 8 }
|
|
2287
|
+
},
|
|
2288
|
+
threshold: {
|
|
2289
|
+
intensity: { uniform: "u_intensity", default: 0.5 }
|
|
2290
|
+
},
|
|
2291
|
+
grain: {
|
|
2292
|
+
intensity: { uniform: "u_intensity", default: 0.1 }
|
|
2293
|
+
},
|
|
2294
|
+
vignette: {
|
|
2295
|
+
intensity: { uniform: "u_intensity", default: 1.4 }
|
|
2296
|
+
},
|
|
2297
|
+
glow: {
|
|
2298
|
+
intensity: { uniform: "u_intensity", default: 0.5 }
|
|
2299
|
+
},
|
|
2300
|
+
gamma: {
|
|
2301
|
+
intensity: { uniform: "u_intensity", default: 2.2 }
|
|
2302
|
+
},
|
|
2303
|
+
dither: {
|
|
2304
|
+
intensity: { uniform: "u_intensity", default: 4 }
|
|
2305
|
+
},
|
|
2306
|
+
highlights: {
|
|
2307
|
+
intensity: { uniform: "u_intensity", default: 0.5 }
|
|
2308
|
+
}
|
|
2309
|
+
})
|
|
2310
|
+
]), y = new V(), E = /* @__PURE__ */ new Map([
|
|
2311
|
+
["u_resolution", "vec2"],
|
|
2312
|
+
["u_time", "float"],
|
|
2313
|
+
["u_delta", "float"],
|
|
2314
|
+
["u_mouse", "vec2"],
|
|
2315
|
+
["u_frame", "int"]
|
|
2316
|
+
]);
|
|
2317
|
+
class fe {
|
|
2318
|
+
constructor(e) {
|
|
2319
|
+
a(this, "gl");
|
|
2320
|
+
a(this, "program", null);
|
|
2321
|
+
a(this, "uniforms", /* @__PURE__ */ new Map());
|
|
2322
|
+
this.gl = e;
|
|
543
2323
|
}
|
|
544
2324
|
/**
|
|
545
2325
|
* Attach to a WebGL program.
|
|
546
2326
|
* Invalidates all cached locations since they're program-specific.
|
|
547
2327
|
*/
|
|
548
|
-
attachProgram(
|
|
549
|
-
this.program =
|
|
550
|
-
for (const
|
|
551
|
-
|
|
2328
|
+
attachProgram(e) {
|
|
2329
|
+
this.program = e;
|
|
2330
|
+
for (const n of this.uniforms.values())
|
|
2331
|
+
n.invalidateLocation();
|
|
552
2332
|
return this;
|
|
553
2333
|
}
|
|
554
2334
|
/**
|
|
555
2335
|
* Set a uniform value.
|
|
556
2336
|
* Creates the uniform if it doesn't exist, updates if it does.
|
|
557
2337
|
*/
|
|
558
|
-
set(
|
|
559
|
-
const
|
|
560
|
-
return
|
|
2338
|
+
set(e, n) {
|
|
2339
|
+
const t = this.uniforms.get(e);
|
|
2340
|
+
return t ? t.setValue(n) : this.uniforms.set(e, new k(e, n)), this;
|
|
561
2341
|
}
|
|
562
2342
|
/**
|
|
563
2343
|
* Set multiple uniforms at once.
|
|
564
2344
|
*/
|
|
565
|
-
setMany(
|
|
566
|
-
for (const [
|
|
567
|
-
this.set(
|
|
2345
|
+
setMany(e) {
|
|
2346
|
+
for (const [n, t] of Object.entries(e))
|
|
2347
|
+
this.set(n, t);
|
|
568
2348
|
return this;
|
|
569
2349
|
}
|
|
570
2350
|
/**
|
|
571
2351
|
* Get current uniform value.
|
|
572
2352
|
*/
|
|
573
|
-
get(
|
|
574
|
-
var
|
|
575
|
-
return (
|
|
2353
|
+
get(e) {
|
|
2354
|
+
var n;
|
|
2355
|
+
return (n = this.uniforms.get(e)) == null ? void 0 : n.getValue();
|
|
576
2356
|
}
|
|
577
2357
|
/**
|
|
578
2358
|
* Check if uniform exists.
|
|
579
2359
|
*/
|
|
580
|
-
has(
|
|
581
|
-
return this.uniforms.has(
|
|
2360
|
+
has(e) {
|
|
2361
|
+
return this.uniforms.has(e);
|
|
582
2362
|
}
|
|
583
2363
|
/**
|
|
584
2364
|
* Remove a uniform.
|
|
585
2365
|
*/
|
|
586
|
-
delete(
|
|
587
|
-
return this.uniforms.delete(
|
|
2366
|
+
delete(e) {
|
|
2367
|
+
return this.uniforms.delete(e);
|
|
588
2368
|
}
|
|
589
2369
|
/**
|
|
590
2370
|
* Upload all uniforms to GPU.
|
|
@@ -593,20 +2373,20 @@ const d = class d {
|
|
|
593
2373
|
uploadAll() {
|
|
594
2374
|
if (!this.program)
|
|
595
2375
|
return this;
|
|
596
|
-
for (const
|
|
597
|
-
|
|
2376
|
+
for (const e of this.uniforms.values())
|
|
2377
|
+
e.upload(this.gl, this.program);
|
|
598
2378
|
return this;
|
|
599
2379
|
}
|
|
600
2380
|
/**
|
|
601
2381
|
* Upload only built-in uniforms (u_resolution, u_time, u_delta, u_mouse, u_frame).
|
|
602
2382
|
* Call this every frame with current values.
|
|
603
2383
|
*/
|
|
604
|
-
uploadBuiltIns(
|
|
605
|
-
if (this.set("u_resolution",
|
|
2384
|
+
uploadBuiltIns(e, n, t) {
|
|
2385
|
+
if (this.set("u_resolution", n), this.set("u_time", e.time), this.set("u_delta", e.delta), this.set("u_mouse", t), this.set("u_frame", e.frame), !this.program)
|
|
606
2386
|
return this;
|
|
607
|
-
for (const
|
|
608
|
-
const
|
|
609
|
-
|
|
2387
|
+
for (const i of E.keys()) {
|
|
2388
|
+
const r = this.uniforms.get(i);
|
|
2389
|
+
r && r.upload(this.gl, this.program);
|
|
610
2390
|
}
|
|
611
2391
|
return this;
|
|
612
2392
|
}
|
|
@@ -634,64 +2414,62 @@ const d = class d {
|
|
|
634
2414
|
get size() {
|
|
635
2415
|
return this.uniforms.size;
|
|
636
2416
|
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
r(d, "BUILT_INS", /* @__PURE__ */ new Set([
|
|
640
|
-
"u_resolution",
|
|
641
|
-
"u_time",
|
|
642
|
-
"u_delta",
|
|
643
|
-
"u_mouse",
|
|
644
|
-
"u_frame"
|
|
645
|
-
]));
|
|
646
|
-
let v = d;
|
|
647
|
-
class b {
|
|
2417
|
+
}
|
|
2418
|
+
class O {
|
|
648
2419
|
constructor() {
|
|
649
|
-
|
|
2420
|
+
a(this, "hooks", /* @__PURE__ */ new Map());
|
|
650
2421
|
}
|
|
651
2422
|
id() {
|
|
652
2423
|
return Math.random().toString(36).substring(2, 10);
|
|
653
2424
|
}
|
|
654
2425
|
/** Add a new hook, returns a removal function */
|
|
655
|
-
add(
|
|
656
|
-
const
|
|
657
|
-
return this.hooks.set(
|
|
2426
|
+
add(e) {
|
|
2427
|
+
const n = this.id();
|
|
2428
|
+
return this.hooks.set(n, e), () => this.remove(n);
|
|
658
2429
|
}
|
|
659
2430
|
/** Remove a hook by its ID */
|
|
660
|
-
remove(
|
|
661
|
-
this.hooks.delete(
|
|
2431
|
+
remove(e) {
|
|
2432
|
+
this.hooks.delete(e);
|
|
662
2433
|
}
|
|
663
2434
|
/** Run all hooks with the given state */
|
|
664
|
-
run(
|
|
665
|
-
for (const [
|
|
666
|
-
|
|
2435
|
+
run(e) {
|
|
2436
|
+
for (const [n, t] of this.hooks)
|
|
2437
|
+
try {
|
|
2438
|
+
t(e) === !1 && this.remove(n);
|
|
2439
|
+
} catch (i) {
|
|
2440
|
+
throw new ie(
|
|
2441
|
+
n,
|
|
2442
|
+
i instanceof Error ? i.message : String(i)
|
|
2443
|
+
);
|
|
2444
|
+
}
|
|
667
2445
|
}
|
|
668
2446
|
destroy() {
|
|
669
2447
|
this.hooks.clear();
|
|
670
2448
|
}
|
|
671
2449
|
}
|
|
672
|
-
class
|
|
673
|
-
constructor(
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
this.canvas =
|
|
2450
|
+
class M {
|
|
2451
|
+
constructor(e, n) {
|
|
2452
|
+
a(this, "canvas");
|
|
2453
|
+
a(this, "gl");
|
|
2454
|
+
a(this, "options");
|
|
2455
|
+
a(this, "onBeforeHooks", new O());
|
|
2456
|
+
a(this, "onAfterHooks", new O());
|
|
2457
|
+
a(this, "_program");
|
|
2458
|
+
a(this, "_geometry");
|
|
2459
|
+
a(this, "_uniforms");
|
|
2460
|
+
a(this, "_clock");
|
|
2461
|
+
a(this, "_resolution", [1, 1]);
|
|
2462
|
+
a(this, "_mouse", [0, 0]);
|
|
2463
|
+
a(this, "_version", 1);
|
|
2464
|
+
a(this, "playing", !1);
|
|
2465
|
+
this.canvas = e, this.options = n, this.gl = this.initContext(), this.enableExtensions(), this._program = new se(this.gl), this._geometry = A.fullscreenQuad(this.gl), this._uniforms = new fe(this.gl), this._clock = new re(), this.options.fps && this._clock.setMaxFps(this.options.fps), this.options.onBeforeRender && this.onBeforeHooks.add(this.options.onBeforeRender), this.options.onAfterRender && this.onAfterHooks.add(this.options.onAfterRender), this.onRender = this.onRender.bind(this);
|
|
688
2466
|
}
|
|
689
2467
|
/**
|
|
690
2468
|
* Factory method to create and setup WebGL instance.
|
|
691
2469
|
*/
|
|
692
|
-
static setup(
|
|
693
|
-
const
|
|
694
|
-
return
|
|
2470
|
+
static setup(e, n) {
|
|
2471
|
+
const t = new M(e, n);
|
|
2472
|
+
return n.vertex && n.fragment && t.shader(n.vertex, n.fragment), n.uniforms && t._uniforms.setMany(n.uniforms), y.isEmpty() || t._uniforms.setMany(y.defaults()), t;
|
|
695
2473
|
}
|
|
696
2474
|
/**
|
|
697
2475
|
* Initialize WebGL context.
|
|
@@ -699,20 +2477,20 @@ class _ {
|
|
|
699
2477
|
* Context errors are fatal but still reported via onError.
|
|
700
2478
|
*/
|
|
701
2479
|
initContext() {
|
|
702
|
-
const
|
|
2480
|
+
const e = {
|
|
703
2481
|
antialias: this.options.antialias,
|
|
704
2482
|
preserveDrawingBuffer: this.options.preserveDrawingBuffer,
|
|
705
2483
|
alpha: !0,
|
|
706
2484
|
depth: !1,
|
|
707
2485
|
stencil: !1
|
|
708
|
-
},
|
|
709
|
-
if (
|
|
710
|
-
return this._version = 2,
|
|
711
|
-
const
|
|
712
|
-
if (
|
|
713
|
-
return this._version = 1,
|
|
714
|
-
const
|
|
715
|
-
throw this.options.onError(
|
|
2486
|
+
}, n = this.canvas.getContext("webgl2", e);
|
|
2487
|
+
if (n)
|
|
2488
|
+
return this._version = 2, n;
|
|
2489
|
+
const t = this.canvas.getContext("webgl", e);
|
|
2490
|
+
if (t)
|
|
2491
|
+
return this._version = 1, t;
|
|
2492
|
+
const i = new z();
|
|
2493
|
+
throw this.options.onError(i), i;
|
|
716
2494
|
}
|
|
717
2495
|
/**
|
|
718
2496
|
* Enable useful WebGL extensions.
|
|
@@ -723,50 +2501,62 @@ class _ {
|
|
|
723
2501
|
/**
|
|
724
2502
|
* Set viewport dimensions.
|
|
725
2503
|
*/
|
|
726
|
-
viewport(
|
|
727
|
-
return this.canvas.width =
|
|
2504
|
+
viewport(e, n, t, i) {
|
|
2505
|
+
return this.canvas.width = t, this.canvas.height = i, this.gl.viewport(e, n, t, i), this._resolution = [t, i], this;
|
|
728
2506
|
}
|
|
729
2507
|
/**
|
|
730
2508
|
* Set the clock time
|
|
731
2509
|
*/
|
|
732
|
-
clock(
|
|
733
|
-
return this._clock.setTime(
|
|
2510
|
+
clock(e) {
|
|
2511
|
+
return this._clock.setTime(e), this;
|
|
734
2512
|
}
|
|
735
2513
|
/**
|
|
736
2514
|
* Update mouse position.
|
|
737
2515
|
*/
|
|
738
|
-
mouse(
|
|
739
|
-
return this._mouse = [
|
|
2516
|
+
mouse(e, n) {
|
|
2517
|
+
return this._mouse = [e, n], this;
|
|
740
2518
|
}
|
|
741
2519
|
/**
|
|
742
2520
|
* Set a uniform value.
|
|
743
2521
|
*/
|
|
744
|
-
uniform(
|
|
745
|
-
return this._uniforms.set(
|
|
2522
|
+
uniform(e, n) {
|
|
2523
|
+
return this._uniforms.set(e, n), this;
|
|
746
2524
|
}
|
|
747
2525
|
/**
|
|
748
2526
|
* Set multiple uniforms.
|
|
749
2527
|
*/
|
|
750
|
-
uniforms(
|
|
751
|
-
return this._uniforms.setMany(
|
|
2528
|
+
uniforms(e) {
|
|
2529
|
+
return this._uniforms.setMany(e), this;
|
|
752
2530
|
}
|
|
753
2531
|
/**
|
|
754
2532
|
* Get current uniform value.
|
|
755
2533
|
*/
|
|
756
|
-
getUniform(
|
|
757
|
-
return this._uniforms.get(
|
|
2534
|
+
getUniform(e) {
|
|
2535
|
+
return this._uniforms.get(e);
|
|
758
2536
|
}
|
|
759
2537
|
/**
|
|
760
2538
|
* Compile and link shaders.
|
|
761
2539
|
* Errors are handled via onError callback, never thrown.
|
|
762
2540
|
*/
|
|
763
|
-
shader(
|
|
2541
|
+
shader(e, n) {
|
|
764
2542
|
try {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
2543
|
+
if (y.clear(), e.version() !== n.version())
|
|
2544
|
+
throw new G(
|
|
2545
|
+
e.version(),
|
|
2546
|
+
n.version()
|
|
2547
|
+
);
|
|
2548
|
+
this._program.compile(e.source(), n.compile()), this._version = n.version(), this._geometry.linkAttributes(this._program);
|
|
2549
|
+
const t = this._program.getProgram();
|
|
2550
|
+
t && this._uniforms.attachProgram(t);
|
|
2551
|
+
try {
|
|
2552
|
+
this.options.onLoad();
|
|
2553
|
+
} catch (i) {
|
|
2554
|
+
throw new te(
|
|
2555
|
+
i instanceof Error ? i.message : String(i)
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
} catch (t) {
|
|
2559
|
+
t instanceof h && this.options.onError(t);
|
|
770
2560
|
}
|
|
771
2561
|
return this;
|
|
772
2562
|
}
|
|
@@ -781,8 +2571,19 @@ class _ {
|
|
|
781
2571
|
*/
|
|
782
2572
|
pause() {
|
|
783
2573
|
if (!this.playing) return this;
|
|
784
|
-
const
|
|
785
|
-
|
|
2574
|
+
const e = this._clock.getState();
|
|
2575
|
+
try {
|
|
2576
|
+
this.onBeforeHooks.run(e);
|
|
2577
|
+
} catch (n) {
|
|
2578
|
+
n instanceof h && this.options.onError(n);
|
|
2579
|
+
}
|
|
2580
|
+
this.playing = !1, this._clock.stop();
|
|
2581
|
+
try {
|
|
2582
|
+
this.onAfterHooks.run(e);
|
|
2583
|
+
} catch (n) {
|
|
2584
|
+
n instanceof h && this.options.onError(n);
|
|
2585
|
+
}
|
|
2586
|
+
return this;
|
|
786
2587
|
}
|
|
787
2588
|
/**
|
|
788
2589
|
* Render a single frame.
|
|
@@ -812,17 +2613,34 @@ class _ {
|
|
|
812
2613
|
* Cleanup all resources.
|
|
813
2614
|
*/
|
|
814
2615
|
destroy() {
|
|
815
|
-
this.pause(), this._clock.destroy(), this._geometry.destroy(), this._program.destroy(), this._uniforms.destroy(), this.onAfterHooks.destroy(), this.onBeforeHooks.destroy();
|
|
2616
|
+
this.pause(), this._clock.destroy(), this._geometry.destroy(), this._program.destroy(), this._uniforms.destroy(), this.onAfterHooks.destroy(), this.onBeforeHooks.destroy(), y.clear();
|
|
816
2617
|
}
|
|
817
2618
|
/**
|
|
818
2619
|
* Internal render callback.
|
|
819
2620
|
*/
|
|
820
|
-
onRender(
|
|
821
|
-
const
|
|
822
|
-
|
|
2621
|
+
onRender(e) {
|
|
2622
|
+
const n = this.gl;
|
|
2623
|
+
try {
|
|
2624
|
+
this.onBeforeHooks.run(e);
|
|
2625
|
+
} catch (t) {
|
|
2626
|
+
t instanceof h && this.options.onError(t);
|
|
2627
|
+
}
|
|
2628
|
+
n.clearColor(0, 0, 0, 0), n.clear(n.COLOR_BUFFER_BIT), this._program.use(), this._uniforms.uploadBuiltIns(e, this._resolution, this._mouse), this._uniforms.uploadAll(), this._geometry.bind(), this._geometry.draw();
|
|
2629
|
+
try {
|
|
2630
|
+
this.onAfterHooks.run(e);
|
|
2631
|
+
} catch (t) {
|
|
2632
|
+
t instanceof h && this.options.onError(t);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
class d extends F {
|
|
2637
|
+
constructor(e) {
|
|
2638
|
+
super(e), E.forEach((n, t) => {
|
|
2639
|
+
this.requirements.uniforms.set(t, { name: t, type: n, line: 0 });
|
|
2640
|
+
});
|
|
823
2641
|
}
|
|
824
2642
|
}
|
|
825
|
-
const
|
|
2643
|
+
const S = `#ifdef GL_ES
|
|
826
2644
|
precision mediump float;
|
|
827
2645
|
#endif
|
|
828
2646
|
|
|
@@ -835,7 +2653,7 @@ void main() {
|
|
|
835
2653
|
v_texcoord = a_texcoord;
|
|
836
2654
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
837
2655
|
}
|
|
838
|
-
`,
|
|
2656
|
+
`, L = `#ifdef GL_ES
|
|
839
2657
|
precision mediump float;
|
|
840
2658
|
#endif
|
|
841
2659
|
|
|
@@ -849,7 +2667,7 @@ void main() {
|
|
|
849
2667
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
850
2668
|
gl_FragColor = vec4(color, 1.0);
|
|
851
2669
|
}
|
|
852
|
-
`,
|
|
2670
|
+
`, U = `#version 300 es
|
|
853
2671
|
|
|
854
2672
|
in vec2 a_position;
|
|
855
2673
|
in vec2 a_texcoord;
|
|
@@ -859,7 +2677,7 @@ out vec2 v_texcoord;
|
|
|
859
2677
|
void main() {
|
|
860
2678
|
v_texcoord = a_texcoord;
|
|
861
2679
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
862
|
-
}`,
|
|
2680
|
+
}`, he = `#version 300 es
|
|
863
2681
|
precision highp float;
|
|
864
2682
|
|
|
865
2683
|
uniform vec2 u_resolution;
|
|
@@ -874,19 +2692,22 @@ void main() {
|
|
|
874
2692
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
875
2693
|
fragColor = vec4(color, 1.0);
|
|
876
2694
|
}`;
|
|
877
|
-
class
|
|
878
|
-
constructor(
|
|
2695
|
+
class $ {
|
|
2696
|
+
constructor(e, n) {
|
|
879
2697
|
/** Active event listeners */
|
|
880
|
-
|
|
2698
|
+
a(this, "listeners", []);
|
|
881
2699
|
/** HTML canvas element */
|
|
882
|
-
|
|
2700
|
+
a(this, "canvasEl");
|
|
883
2701
|
/** Resolved options */
|
|
884
|
-
|
|
2702
|
+
a(this, "options");
|
|
885
2703
|
/** WebGL engine */
|
|
886
|
-
|
|
2704
|
+
a(this, "engine");
|
|
887
2705
|
/** User sets custom vertex shader */
|
|
888
|
-
|
|
889
|
-
this.canvasEl =
|
|
2706
|
+
a(this, "usingCustomVertex", !1);
|
|
2707
|
+
if (this.canvasEl = e, this.options = this.resolveOptions(n), this.engine = M.setup(this.canvasEl, this.options), this.setupListeners(), this.setViewport(), this.options.modules)
|
|
2708
|
+
for (const [t, i] of Object.entries(this.options.modules))
|
|
2709
|
+
this.module(t, i);
|
|
2710
|
+
this.options.autoplay && this.play();
|
|
890
2711
|
}
|
|
891
2712
|
/**
|
|
892
2713
|
* Sandbox - A lightweight WebGL wrapper for shader effects.
|
|
@@ -906,23 +2727,49 @@ class y {
|
|
|
906
2727
|
* autoplay: true,
|
|
907
2728
|
* });
|
|
908
2729
|
*/
|
|
909
|
-
static create(
|
|
910
|
-
return new
|
|
2730
|
+
static create(e, n) {
|
|
2731
|
+
return new $(e, n);
|
|
911
2732
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
2733
|
+
/**
|
|
2734
|
+
* Define a shader module that can be imported in shader source with `#import <function> from "module_name"`.
|
|
2735
|
+
* @example
|
|
2736
|
+
* Sandbox.defineModule("my_module", source, { options });
|
|
2737
|
+
* // Then in shader:
|
|
2738
|
+
* // #import myFunc from "my_module"
|
|
2739
|
+
* // void main() {
|
|
2740
|
+
* // myFunc();
|
|
2741
|
+
* // }
|
|
2742
|
+
*/
|
|
2743
|
+
static defineModule(e, n, t = {}) {
|
|
2744
|
+
v.define({ name: e, source: n, options: t });
|
|
2745
|
+
}
|
|
2746
|
+
/**
|
|
2747
|
+
* Get the list of available shader modules that can be used with `#import` in shader source.
|
|
2748
|
+
*/
|
|
2749
|
+
static availableModules() {
|
|
2750
|
+
return b.available();
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Compile a shader source with Sandbox's shader preprocessor and return the final GLSL code.
|
|
2754
|
+
* This is useful for debugging shader code or precompiling shaders for production use.
|
|
2755
|
+
*/
|
|
2756
|
+
static compile(e) {
|
|
2757
|
+
return new d(e).compile();
|
|
2758
|
+
}
|
|
2759
|
+
resolveOptions(e) {
|
|
2760
|
+
const n = {
|
|
2761
|
+
vertex: new d(S),
|
|
2762
|
+
fragment: new d(L),
|
|
916
2763
|
autoplay: !0,
|
|
917
2764
|
pauseWhenHidden: !0,
|
|
918
2765
|
dpr: "auto",
|
|
919
2766
|
fps: 0,
|
|
920
2767
|
preserveDrawingBuffer: !1,
|
|
921
2768
|
antialias: !0,
|
|
922
|
-
onError: (
|
|
2769
|
+
onError: (s) => {
|
|
923
2770
|
console.error(
|
|
924
2771
|
"Oops!",
|
|
925
|
-
|
|
2772
|
+
s,
|
|
926
2773
|
`
|
|
927
2774
|
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`
|
|
928
2775
|
);
|
|
@@ -931,64 +2778,69 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
931
2778
|
},
|
|
932
2779
|
onBeforeRender: null,
|
|
933
2780
|
onAfterRender: null,
|
|
934
|
-
uniforms: {}
|
|
2781
|
+
uniforms: {},
|
|
2782
|
+
modules: {}
|
|
935
2783
|
};
|
|
936
|
-
if (
|
|
937
|
-
|
|
938
|
-
|
|
2784
|
+
if (e != null && e.vertex && (this.usingCustomVertex = !0), e != null && e.vertex && !(e != null && e.fragment)) {
|
|
2785
|
+
n.vertex = new d(e.vertex);
|
|
2786
|
+
const s = n.vertex.version();
|
|
2787
|
+
n.fragment = new d(s === 2 ? he : L);
|
|
939
2788
|
}
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
|
|
2789
|
+
if (e != null && e.fragment && !(e != null && e.vertex)) {
|
|
2790
|
+
n.fragment = new d(e.fragment);
|
|
2791
|
+
const s = n.fragment.version();
|
|
2792
|
+
n.vertex = new d(s === 2 ? U : S);
|
|
943
2793
|
}
|
|
944
|
-
|
|
2794
|
+
e != null && e.vertex && (e != null && e.fragment) && (n.vertex = new d(e.vertex), n.fragment = new d(e.fragment));
|
|
2795
|
+
const { vertex: t, fragment: i, ...r } = e || {};
|
|
2796
|
+
return { ...n, ...r };
|
|
945
2797
|
}
|
|
946
2798
|
setupListeners() {
|
|
947
2799
|
this.listeners.push(
|
|
948
2800
|
// Window resize
|
|
949
|
-
|
|
2801
|
+
x.on(window, "resize", () => {
|
|
950
2802
|
this.setViewport();
|
|
951
2803
|
}),
|
|
952
2804
|
// Canvas resize
|
|
953
|
-
|
|
2805
|
+
x.on(this.canvasEl, "resize", () => {
|
|
954
2806
|
this.setViewport();
|
|
955
2807
|
}),
|
|
956
2808
|
// Visibility check on scroll
|
|
957
2809
|
(() => {
|
|
958
|
-
let
|
|
959
|
-
return
|
|
960
|
-
this.options.pauseWhenHidden && (this.isInViewport() ?
|
|
2810
|
+
let e = !1;
|
|
2811
|
+
return x.on(document, "scroll", (n) => {
|
|
2812
|
+
this.options.pauseWhenHidden && (this.isInViewport() ? e && !this.isPlaying() && (this.play(), e = !1) : this.isPlaying() && (this.pause(), e = !0));
|
|
961
2813
|
});
|
|
962
2814
|
})(),
|
|
963
2815
|
// Mouse tracking
|
|
964
|
-
|
|
965
|
-
this.setMouse(
|
|
2816
|
+
x.on(document, "mousemove", (e) => {
|
|
2817
|
+
this.setMouse(e.clientX || e.pageX, e.clientY || e.pageY);
|
|
966
2818
|
}),
|
|
967
2819
|
// Touch tracking
|
|
968
|
-
|
|
969
|
-
|
|
2820
|
+
x.on(document, "touchmove", (e) => {
|
|
2821
|
+
e.touches.length > 0 && this.setMouse(e.touches[0].clientX, e.touches[0].clientY);
|
|
970
2822
|
})
|
|
971
2823
|
);
|
|
972
2824
|
}
|
|
973
2825
|
destroyListeners() {
|
|
974
|
-
this.listeners.forEach((
|
|
2826
|
+
this.listeners.forEach((e) => e()), this.listeners = [];
|
|
975
2827
|
}
|
|
976
2828
|
setViewport() {
|
|
977
|
-
const
|
|
2829
|
+
const e = this.options.dpr === "auto" ? Math.min(2, window.devicePixelRatio || 1) : this.options.dpr, n = this.canvasEl.clientWidth || this.canvasEl.width || 1, t = this.canvasEl.clientHeight || this.canvasEl.height || 1;
|
|
978
2830
|
this.engine.viewport(
|
|
979
2831
|
0,
|
|
980
2832
|
0,
|
|
981
|
-
Math.max(1, Math.floor(
|
|
982
|
-
Math.max(1, Math.floor(
|
|
2833
|
+
Math.max(1, Math.floor(n * e)),
|
|
2834
|
+
Math.max(1, Math.floor(t * e))
|
|
983
2835
|
);
|
|
984
2836
|
}
|
|
985
2837
|
isInViewport() {
|
|
986
|
-
const
|
|
987
|
-
return
|
|
2838
|
+
const e = this.canvasEl.getBoundingClientRect();
|
|
2839
|
+
return e.bottom >= 0 && e.right >= 0 && e.top <= (window.innerHeight || document.documentElement.clientHeight) && e.left <= (window.innerWidth || document.documentElement.clientWidth);
|
|
988
2840
|
}
|
|
989
|
-
setMouse(
|
|
990
|
-
const
|
|
991
|
-
|
|
2841
|
+
setMouse(e, n) {
|
|
2842
|
+
const t = this.canvasEl.getBoundingClientRect();
|
|
2843
|
+
e >= t.left && e <= t.right && n >= t.top && n <= t.bottom && this.engine.mouse(e - t.left, n - t.top);
|
|
992
2844
|
}
|
|
993
2845
|
/**
|
|
994
2846
|
* Set a single uniform value with type checking.
|
|
@@ -997,8 +2849,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
997
2849
|
* sandbox.setUniform<number>("u_time", 1.5);
|
|
998
2850
|
* sandbox.setUniform<Vec3[]>("u_colors", [[1, 0, 0], [0, 1, 0]]);
|
|
999
2851
|
*/
|
|
1000
|
-
setUniform(
|
|
1001
|
-
return this.engine.uniform(
|
|
2852
|
+
setUniform(e, n) {
|
|
2853
|
+
return this.engine.uniform(e, n), this;
|
|
1002
2854
|
}
|
|
1003
2855
|
/**
|
|
1004
2856
|
* Set multiple uniforms at once with type checking.
|
|
@@ -1014,47 +2866,84 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1014
2866
|
* u_colors: [[1, 0, 0], [0, 1, 0]],
|
|
1015
2867
|
* });
|
|
1016
2868
|
*/
|
|
1017
|
-
setUniforms(
|
|
1018
|
-
return this.engine.uniforms(
|
|
2869
|
+
setUniforms(e) {
|
|
2870
|
+
return this.engine.uniforms(e), this;
|
|
1019
2871
|
}
|
|
1020
2872
|
/**
|
|
1021
2873
|
* Get current uniform value.
|
|
1022
2874
|
*/
|
|
1023
|
-
getUniform(
|
|
1024
|
-
return this.engine.getUniform(
|
|
2875
|
+
getUniform(e) {
|
|
2876
|
+
return this.engine.getUniform(e);
|
|
1025
2877
|
}
|
|
1026
2878
|
/**
|
|
1027
2879
|
* Update shaders.
|
|
1028
2880
|
* @example
|
|
1029
2881
|
* sandbox.setShader(vertexSource, fragmentSource);
|
|
1030
2882
|
*/
|
|
1031
|
-
setShader(
|
|
1032
|
-
return this.options.vertex =
|
|
2883
|
+
setShader(e, n) {
|
|
2884
|
+
return this.options.vertex = new d(e), this.options.fragment = new d(n), this.usingCustomVertex = !0, this.engine.shader(this.options.vertex, this.options.fragment), this;
|
|
1033
2885
|
}
|
|
1034
2886
|
/**
|
|
1035
2887
|
* Update only fragment shader (uses default vertex).
|
|
1036
2888
|
* @example
|
|
1037
2889
|
* sandbox.setFragment(fragmentSource);
|
|
1038
2890
|
*/
|
|
1039
|
-
setFragment(
|
|
1040
|
-
const e =
|
|
1041
|
-
return this.options.fragment =
|
|
2891
|
+
setFragment(e) {
|
|
2892
|
+
const n = new d(e), t = n.version(), i = this.options.vertex.version();
|
|
2893
|
+
return this.options.fragment = n, t !== i && (this.usingCustomVertex || (this.options.vertex = new d(
|
|
2894
|
+
t === 2 ? U : S
|
|
2895
|
+
))), this.engine.shader(this.options.vertex, this.options.fragment), this;
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Get current fragment shader source.
|
|
2899
|
+
*/
|
|
2900
|
+
getFragment() {
|
|
2901
|
+
return this.options.fragment.source();
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Get current vertex shader source.
|
|
2905
|
+
*/
|
|
2906
|
+
getVertex() {
|
|
2907
|
+
return this.options.vertex.source();
|
|
1042
2908
|
}
|
|
1043
2909
|
/**
|
|
1044
2910
|
* Set the max frame rate runtime
|
|
1045
|
-
*
|
|
2911
|
+
*
|
|
1046
2912
|
* @example
|
|
1047
2913
|
* sandbox.setFps(30); // Limit to 30 FPS
|
|
1048
2914
|
* sandbox.setFps(0); // Unlimited FPS
|
|
1049
2915
|
*/
|
|
1050
|
-
setFps(
|
|
1051
|
-
return this.engine.getClock().setMaxFps(
|
|
2916
|
+
setFps(e) {
|
|
2917
|
+
return this.engine.getClock().setMaxFps(e), this;
|
|
1052
2918
|
}
|
|
1053
2919
|
/**
|
|
1054
2920
|
* Add a runtime render hook.
|
|
1055
2921
|
*/
|
|
1056
|
-
hook(
|
|
1057
|
-
return
|
|
2922
|
+
hook(e, n = "before") {
|
|
2923
|
+
return n === "before" ? this.engine.onBeforeHooks.add(e) : this.engine.onAfterHooks.add(e);
|
|
2924
|
+
}
|
|
2925
|
+
/**
|
|
2926
|
+
* Runtime configure the module behavior
|
|
2927
|
+
* @example
|
|
2928
|
+
* sandbox.module("my_module", { intensity: 0.5 });
|
|
2929
|
+
*/
|
|
2930
|
+
module(e, n) {
|
|
2931
|
+
const t = y.resolveOptions(e);
|
|
2932
|
+
if (!t)
|
|
2933
|
+
return console.warn(
|
|
2934
|
+
`Sandbox: Counld not find options for '${e}' function. Make sure you used the correct imported name and the module is currently in use by the shader.`
|
|
2935
|
+
), this;
|
|
2936
|
+
for (const [i, r] of Object.entries(n)) {
|
|
2937
|
+
const s = t[i];
|
|
2938
|
+
if (!s) {
|
|
2939
|
+
console.warn(
|
|
2940
|
+
`Sandbox: Option '${i}' not found for function '${e}'. Make sure to check available options with Sandbox.availableModules() and provide the correct option name.`
|
|
2941
|
+
);
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
this.setUniform(s.uniform, r);
|
|
2945
|
+
}
|
|
2946
|
+
return this;
|
|
1058
2947
|
}
|
|
1059
2948
|
/**
|
|
1060
2949
|
* Start animation loop.
|
|
@@ -1065,8 +2954,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1065
2954
|
/**
|
|
1066
2955
|
* Start animation loop at specific time (in seconds).
|
|
1067
2956
|
*/
|
|
1068
|
-
playAt(
|
|
1069
|
-
return this.engine.clock(
|
|
2957
|
+
playAt(e) {
|
|
2958
|
+
return this.engine.clock(e), this.engine.play(), this;
|
|
1070
2959
|
}
|
|
1071
2960
|
/**
|
|
1072
2961
|
* Stop animation loop.
|
|
@@ -1077,9 +2966,9 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1077
2966
|
/**
|
|
1078
2967
|
* Pause animation loop at specific time (in seconds).
|
|
1079
2968
|
*/
|
|
1080
|
-
pauseAt(
|
|
1081
|
-
const
|
|
1082
|
-
|
|
2969
|
+
pauseAt(e) {
|
|
2970
|
+
const n = this.hook((t) => {
|
|
2971
|
+
t.time >= e && (n(), this.pause());
|
|
1083
2972
|
}, "after");
|
|
1084
2973
|
return this;
|
|
1085
2974
|
}
|
|
@@ -1092,8 +2981,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1092
2981
|
/**
|
|
1093
2982
|
* Set current time (in seconds).
|
|
1094
2983
|
*/
|
|
1095
|
-
time(
|
|
1096
|
-
return this.engine.clock(
|
|
2984
|
+
time(e) {
|
|
2985
|
+
return this.engine.clock(e), this;
|
|
1097
2986
|
}
|
|
1098
2987
|
/**
|
|
1099
2988
|
* Render a single frame (for static rendering).
|
|
@@ -1108,8 +2997,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1108
2997
|
* @example
|
|
1109
2998
|
* sandbox.renderAt(2.5); // Render as if 2.5 seconds elapsed
|
|
1110
2999
|
*/
|
|
1111
|
-
renderAt(
|
|
1112
|
-
return this.engine.clock(
|
|
3000
|
+
renderAt(e) {
|
|
3001
|
+
return this.engine.clock(e), this.engine.render(), this;
|
|
1113
3002
|
}
|
|
1114
3003
|
/**
|
|
1115
3004
|
* Check if currently playing.
|
|
@@ -1141,10 +3030,26 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1141
3030
|
}
|
|
1142
3031
|
}
|
|
1143
3032
|
export {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
3033
|
+
$ as Sandbox,
|
|
3034
|
+
X as SandboxAttemptedToImportDefaultFunctionError,
|
|
3035
|
+
K as SandboxAttemptedToImportMainFunctionError,
|
|
3036
|
+
de as SandboxContextCreationError,
|
|
3037
|
+
h as SandboxError,
|
|
3038
|
+
Q as SandboxForbiddenModuleNameError,
|
|
3039
|
+
_ as SandboxGLSLShaderCompilationError,
|
|
3040
|
+
ne as SandboxMentionCouldNotBeReplacedError,
|
|
3041
|
+
ee as SandboxMentionFunctionNotFoundError,
|
|
3042
|
+
Z as SandboxMentionUniformNotFoundError,
|
|
3043
|
+
Y as SandboxModuleMethodNotFoundError,
|
|
3044
|
+
j as SandboxModuleNotFoundError,
|
|
3045
|
+
ie as SandboxOnHookCallbackError,
|
|
3046
|
+
te as SandboxOnLoadCallbackError,
|
|
3047
|
+
J as SandboxOverwriteModuleError,
|
|
3048
|
+
R as SandboxProgramError,
|
|
3049
|
+
H as SandboxShaderDuplicateImportNameError,
|
|
3050
|
+
q as SandboxShaderImportSyntaxError,
|
|
3051
|
+
C as SandboxShaderRequirementMismatchError,
|
|
3052
|
+
G as SandboxShaderVersionMismatchError,
|
|
3053
|
+
W as SandboxShaderWithoutFunctionError,
|
|
3054
|
+
z as SandboxWebGLNotSupportedError
|
|
1150
3055
|
};
|