@rosalana/sandbox 0.0.4 → 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 +171 -60
- 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 +56 -4
- package/dist/index.es.js +2323 -378
- package/dist/tools/clock.d.ts +7 -0
- 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 +7 -1
- package/dist/types.d.ts +96 -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,96 +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 = r, 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 r = [
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
206
|
+
a(this, "running", !1);
|
|
207
|
+
/** Smoothed frames per second */
|
|
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);
|
|
99
214
|
this.loop = this.loop.bind(this);
|
|
100
215
|
}
|
|
101
216
|
/**
|
|
102
217
|
* Start the animation loop with a render callback.
|
|
103
218
|
*/
|
|
104
|
-
start(
|
|
219
|
+
start(e) {
|
|
105
220
|
if (this.running) return this;
|
|
106
|
-
this.callback =
|
|
107
|
-
const
|
|
108
|
-
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;
|
|
109
224
|
}
|
|
110
225
|
/**
|
|
111
226
|
* Stop the animation loop.
|
|
@@ -118,7 +233,7 @@ class R {
|
|
|
118
233
|
* Reset clock to initial state.
|
|
119
234
|
*/
|
|
120
235
|
reset() {
|
|
121
|
-
return this.stop(), this.time = 0, this.delta = 0, this.frame = 0, this;
|
|
236
|
+
return this.stop(), this.time = 0, this.delta = 0, this.frame = 0, this.fps = 0, this;
|
|
122
237
|
}
|
|
123
238
|
/**
|
|
124
239
|
* Get current clock state snapshot.
|
|
@@ -127,21 +242,23 @@ class R {
|
|
|
127
242
|
return {
|
|
128
243
|
time: this.time,
|
|
129
244
|
delta: this.delta,
|
|
130
|
-
frame: this.frame
|
|
245
|
+
frame: this.frame,
|
|
246
|
+
running: this.running,
|
|
247
|
+
fps: Math.round(this.fps)
|
|
131
248
|
};
|
|
132
249
|
}
|
|
133
250
|
/**
|
|
134
251
|
* Advance clock by one tick (for single-shot rendering).
|
|
135
252
|
* Useful when autoplay is disabled.
|
|
136
253
|
*/
|
|
137
|
-
tick(
|
|
138
|
-
return this.delta =
|
|
254
|
+
tick(e = 0) {
|
|
255
|
+
return this.delta = e, this.time += e, this.frame++, this.callback && this.callback(this.getState()), this;
|
|
139
256
|
}
|
|
140
257
|
/**
|
|
141
258
|
* Set time directly (for deterministic rendering).
|
|
142
259
|
*/
|
|
143
|
-
setTime(
|
|
144
|
-
return this.time =
|
|
260
|
+
setTime(e) {
|
|
261
|
+
return this.time = e, this;
|
|
145
262
|
}
|
|
146
263
|
/**
|
|
147
264
|
* Cleanup.
|
|
@@ -149,33 +266,49 @@ class R {
|
|
|
149
266
|
destroy() {
|
|
150
267
|
this.reset(), this.callback = null;
|
|
151
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Set maximum frames per second.
|
|
271
|
+
*/
|
|
272
|
+
setMaxFps(e) {
|
|
273
|
+
return this.maxFps = e, this;
|
|
274
|
+
}
|
|
152
275
|
/**
|
|
153
276
|
* Internal animation frame handler.
|
|
154
277
|
*/
|
|
155
|
-
loop(
|
|
156
|
-
|
|
278
|
+
loop(e) {
|
|
279
|
+
if (!this.running) return;
|
|
280
|
+
if (this.maxFps > 0) {
|
|
281
|
+
const t = 1e3 / this.maxFps;
|
|
282
|
+
if (e - this.lastTime < t) {
|
|
283
|
+
this.rafId = requestAnimationFrame(this.loop);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
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);
|
|
157
290
|
}
|
|
158
291
|
}
|
|
159
|
-
class
|
|
160
|
-
constructor(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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);
|
|
168
301
|
// WebGL1 VAO extension (if available)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
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"));
|
|
172
305
|
}
|
|
173
306
|
/**
|
|
174
307
|
* Create a fullscreen quad geometry.
|
|
175
308
|
* This is the most common use case for shader effects.
|
|
176
309
|
*/
|
|
177
|
-
static fullscreenQuad(
|
|
178
|
-
const
|
|
310
|
+
static fullscreenQuad(e) {
|
|
311
|
+
const n = new A(e), t = new Float32Array([
|
|
179
312
|
// position texcoord
|
|
180
313
|
-1,
|
|
181
314
|
-1,
|
|
@@ -197,7 +330,7 @@ class p {
|
|
|
197
330
|
1,
|
|
198
331
|
1
|
|
199
332
|
// top-right
|
|
200
|
-
]),
|
|
333
|
+
]), i = new Uint16Array([
|
|
201
334
|
0,
|
|
202
335
|
1,
|
|
203
336
|
2,
|
|
@@ -207,33 +340,33 @@ class p {
|
|
|
207
340
|
3
|
|
208
341
|
// second triangle
|
|
209
342
|
]);
|
|
210
|
-
return
|
|
343
|
+
return n.setup(t, i), n;
|
|
211
344
|
}
|
|
212
345
|
/**
|
|
213
346
|
* Setup geometry from vertex and index data.
|
|
214
347
|
*/
|
|
215
|
-
setup(
|
|
216
|
-
const
|
|
217
|
-
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;
|
|
218
351
|
}
|
|
219
352
|
/**
|
|
220
353
|
* Link vertex attributes to shader program.
|
|
221
354
|
* Call this after compiling shaders.
|
|
222
355
|
*/
|
|
223
|
-
linkAttributes(
|
|
224
|
-
const
|
|
225
|
-
this.bindVAO(),
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
return
|
|
230
|
-
|
|
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,
|
|
231
364
|
2,
|
|
232
|
-
|
|
365
|
+
n.FLOAT,
|
|
233
366
|
!1,
|
|
234
|
-
|
|
367
|
+
t,
|
|
235
368
|
2 * Float32Array.BYTES_PER_ELEMENT
|
|
236
|
-
)), this.useIndices &&
|
|
369
|
+
)), this.useIndices && n.bindBuffer(n.ELEMENT_ARRAY_BUFFER, this.ibo), this.unbindVAO(), this;
|
|
237
370
|
}
|
|
238
371
|
/**
|
|
239
372
|
* Bind geometry for rendering.
|
|
@@ -251,31 +384,31 @@ class p {
|
|
|
251
384
|
* Draw the geometry.
|
|
252
385
|
*/
|
|
253
386
|
draw() {
|
|
254
|
-
const
|
|
255
|
-
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;
|
|
256
389
|
}
|
|
257
390
|
/**
|
|
258
391
|
* Cleanup all GPU resources.
|
|
259
392
|
*/
|
|
260
393
|
destroy() {
|
|
261
|
-
const
|
|
262
|
-
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);
|
|
263
396
|
}
|
|
264
397
|
/**
|
|
265
398
|
* Get position attribute location.
|
|
266
399
|
* Tries common naming conventions.
|
|
267
400
|
*/
|
|
268
|
-
getPositionLocation(
|
|
269
|
-
let
|
|
270
|
-
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;
|
|
271
404
|
}
|
|
272
405
|
/**
|
|
273
406
|
* Get texcoord attribute location.
|
|
274
407
|
* Tries common naming conventions.
|
|
275
408
|
*/
|
|
276
|
-
getTexcoordLocation(
|
|
277
|
-
let
|
|
278
|
-
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;
|
|
279
412
|
}
|
|
280
413
|
// ============================================================================
|
|
281
414
|
// VAO helpers (handle WebGL1 vs WebGL2 differences)
|
|
@@ -293,33 +426,21 @@ class p {
|
|
|
293
426
|
this.vao && (this.isWebGL2 ? this.gl.deleteVertexArray(this.vao) : this.vaoExt && this.vaoExt.deleteVertexArrayOES(this.vao), this.vao = null);
|
|
294
427
|
}
|
|
295
428
|
}
|
|
296
|
-
class
|
|
297
|
-
constructor(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
this.gl = t;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Detect WebGL version from shader source.
|
|
307
|
-
* Looks for "#version 300 es" directive.
|
|
308
|
-
*/
|
|
309
|
-
static detectVersion(t) {
|
|
310
|
-
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;
|
|
311
436
|
}
|
|
312
437
|
/**
|
|
313
438
|
* Compile shaders and link program.
|
|
314
439
|
* @throws ShaderCompilationError if compilation fails
|
|
315
440
|
* @throws ProgramLinkError if linking fails
|
|
316
441
|
*/
|
|
317
|
-
compile(
|
|
318
|
-
this.destroy();
|
|
319
|
-
const r = h.detectVersion(t), s = h.detectVersion(e);
|
|
320
|
-
if (r != s)
|
|
321
|
-
throw new k(r, s);
|
|
322
|
-
return this.version = Math.max(r, 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;
|
|
323
444
|
}
|
|
324
445
|
/**
|
|
325
446
|
* Bind this program for rendering.
|
|
@@ -333,93 +454,87 @@ class h {
|
|
|
333
454
|
getProgram() {
|
|
334
455
|
return this.program;
|
|
335
456
|
}
|
|
336
|
-
/**
|
|
337
|
-
* Get detected WebGL version.
|
|
338
|
-
*/
|
|
339
|
-
getVersion() {
|
|
340
|
-
return this.version;
|
|
341
|
-
}
|
|
342
457
|
/**
|
|
343
458
|
* Get attribute location.
|
|
344
459
|
*/
|
|
345
|
-
getAttribLocation(
|
|
346
|
-
return this.program ? this.gl.getAttribLocation(this.program,
|
|
460
|
+
getAttribLocation(e) {
|
|
461
|
+
return this.program ? this.gl.getAttribLocation(this.program, e) : -1;
|
|
347
462
|
}
|
|
348
463
|
/**
|
|
349
464
|
* Get uniform location.
|
|
350
465
|
*/
|
|
351
|
-
getUniformLocation(
|
|
352
|
-
return this.program ? this.gl.getUniformLocation(this.program,
|
|
466
|
+
getUniformLocation(e) {
|
|
467
|
+
return this.program ? this.gl.getUniformLocation(this.program, e) : null;
|
|
353
468
|
}
|
|
354
469
|
/**
|
|
355
470
|
* Cleanup all GPU resources.
|
|
356
471
|
*/
|
|
357
472
|
destroy() {
|
|
358
|
-
const
|
|
359
|
-
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);
|
|
360
475
|
}
|
|
361
476
|
/**
|
|
362
477
|
* Compile a single shader.
|
|
363
478
|
* @throws ShaderCompilationError if compilation fails
|
|
364
479
|
*/
|
|
365
|
-
compileShader(
|
|
366
|
-
const
|
|
367
|
-
if (!
|
|
368
|
-
throw new
|
|
369
|
-
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 _(
|
|
370
484
|
e,
|
|
485
|
+
n,
|
|
371
486
|
"Failed to create shader object"
|
|
372
487
|
);
|
|
373
|
-
if (
|
|
374
|
-
const
|
|
375
|
-
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);
|
|
376
491
|
}
|
|
377
|
-
return
|
|
492
|
+
return r;
|
|
378
493
|
}
|
|
379
494
|
/**
|
|
380
495
|
* Link vertex and fragment shaders into a program.
|
|
381
496
|
* @throws ProgramLinkError if linking fails
|
|
382
497
|
*/
|
|
383
498
|
linkProgram() {
|
|
384
|
-
const
|
|
499
|
+
const e = this.gl;
|
|
385
500
|
if (!this.vertexShader || !this.fragmentShader)
|
|
386
|
-
throw new
|
|
387
|
-
const
|
|
388
|
-
if (!
|
|
389
|
-
throw new
|
|
390
|
-
if (
|
|
391
|
-
const
|
|
392
|
-
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);
|
|
393
508
|
}
|
|
394
|
-
this.program =
|
|
509
|
+
this.program = n;
|
|
395
510
|
}
|
|
396
511
|
}
|
|
397
|
-
class
|
|
398
|
-
constructor(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
this.name =
|
|
407
|
-
const
|
|
408
|
-
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;
|
|
409
524
|
}
|
|
410
525
|
/**
|
|
411
526
|
* Infer WebGL method and metadata from value type.
|
|
412
527
|
*/
|
|
413
|
-
static inferMethodInfo(
|
|
414
|
-
if (typeof
|
|
528
|
+
static inferMethodInfo(e) {
|
|
529
|
+
if (typeof e == "boolean")
|
|
415
530
|
return { method: "uniform1i", isArray: !1, isMatrix: !1 };
|
|
416
|
-
if (typeof
|
|
531
|
+
if (typeof e == "number")
|
|
417
532
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
418
|
-
if (!Array.isArray(
|
|
533
|
+
if (!Array.isArray(e))
|
|
419
534
|
return { method: "uniform1f", isArray: !1, isMatrix: !1 };
|
|
420
|
-
const
|
|
421
|
-
if (Array.isArray(
|
|
422
|
-
switch (
|
|
535
|
+
const n = e.length, t = e[0];
|
|
536
|
+
if (Array.isArray(t))
|
|
537
|
+
switch (t.length) {
|
|
423
538
|
case 2:
|
|
424
539
|
return { method: "uniform2fv", isArray: !0, isMatrix: !1 };
|
|
425
540
|
case 3:
|
|
@@ -429,7 +544,7 @@ class x {
|
|
|
429
544
|
default:
|
|
430
545
|
return { method: "uniform1fv", isArray: !0, isMatrix: !1 };
|
|
431
546
|
}
|
|
432
|
-
switch (
|
|
547
|
+
switch (n) {
|
|
433
548
|
case 2:
|
|
434
549
|
return { method: "uniform2fv", isArray: !1, isMatrix: !1 };
|
|
435
550
|
case 3:
|
|
@@ -448,8 +563,8 @@ class x {
|
|
|
448
563
|
* Resolve and cache uniform location from program.
|
|
449
564
|
* Returns null if uniform doesn't exist (optimized out by compiler, etc.)
|
|
450
565
|
*/
|
|
451
|
-
resolveLocation(
|
|
452
|
-
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;
|
|
453
568
|
}
|
|
454
569
|
/**
|
|
455
570
|
* Invalidate cached location (call when program changes).
|
|
@@ -460,8 +575,8 @@ class x {
|
|
|
460
575
|
/**
|
|
461
576
|
* Update value (doesn't upload to GPU until upload() is called).
|
|
462
577
|
*/
|
|
463
|
-
setValue(
|
|
464
|
-
this.value =
|
|
578
|
+
setValue(e) {
|
|
579
|
+
this.value = e;
|
|
465
580
|
}
|
|
466
581
|
/**
|
|
467
582
|
* Get current value.
|
|
@@ -474,96 +589,1782 @@ class x {
|
|
|
474
589
|
* @param gl - WebGL context
|
|
475
590
|
* @param program - Current WebGL program (for location resolution)
|
|
476
591
|
*/
|
|
477
|
-
upload(
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
592
|
+
upload(e, n) {
|
|
593
|
+
const t = this.resolveLocation(e, n);
|
|
594
|
+
if (t === null)
|
|
480
595
|
return;
|
|
481
|
-
const
|
|
482
|
-
let
|
|
483
|
-
switch (typeof
|
|
484
|
-
|
|
485
|
-
) :
|
|
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) {
|
|
486
601
|
case "uniform1f":
|
|
487
|
-
|
|
602
|
+
e.uniform1f(t, r);
|
|
488
603
|
break;
|
|
489
604
|
case "uniform1i":
|
|
490
|
-
|
|
605
|
+
e.uniform1i(t, r);
|
|
491
606
|
break;
|
|
492
607
|
case "uniform1fv":
|
|
493
|
-
|
|
608
|
+
e.uniform1fv(t, r);
|
|
494
609
|
break;
|
|
495
610
|
case "uniform2fv":
|
|
496
|
-
|
|
611
|
+
e.uniform2fv(t, r);
|
|
497
612
|
break;
|
|
498
613
|
case "uniform3fv":
|
|
499
|
-
|
|
614
|
+
e.uniform3fv(t, r);
|
|
500
615
|
break;
|
|
501
616
|
case "uniform4fv":
|
|
502
|
-
|
|
617
|
+
e.uniform4fv(t, r);
|
|
503
618
|
break;
|
|
504
619
|
case "uniformMatrix2fv":
|
|
505
|
-
|
|
620
|
+
e.uniformMatrix2fv(t, !1, r);
|
|
506
621
|
break;
|
|
507
622
|
case "uniformMatrix3fv":
|
|
508
|
-
|
|
623
|
+
e.uniformMatrix3fv(t, !1, r);
|
|
509
624
|
break;
|
|
510
625
|
case "uniformMatrix4fv":
|
|
511
|
-
|
|
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
|
+
}
|
|
512
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
|
+
);
|
|
513
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
|
+
}
|
|
514
1307
|
}
|
|
515
1308
|
}
|
|
516
|
-
|
|
517
|
-
constructor(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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;
|
|
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;
|
|
1904
|
+
}
|
|
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;
|
|
522
2323
|
}
|
|
523
2324
|
/**
|
|
524
2325
|
* Attach to a WebGL program.
|
|
525
2326
|
* Invalidates all cached locations since they're program-specific.
|
|
526
2327
|
*/
|
|
527
|
-
attachProgram(
|
|
528
|
-
this.program =
|
|
529
|
-
for (const
|
|
530
|
-
|
|
2328
|
+
attachProgram(e) {
|
|
2329
|
+
this.program = e;
|
|
2330
|
+
for (const n of this.uniforms.values())
|
|
2331
|
+
n.invalidateLocation();
|
|
531
2332
|
return this;
|
|
532
2333
|
}
|
|
533
2334
|
/**
|
|
534
2335
|
* Set a uniform value.
|
|
535
2336
|
* Creates the uniform if it doesn't exist, updates if it does.
|
|
536
2337
|
*/
|
|
537
|
-
set(
|
|
538
|
-
const
|
|
539
|
-
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;
|
|
540
2341
|
}
|
|
541
2342
|
/**
|
|
542
2343
|
* Set multiple uniforms at once.
|
|
543
2344
|
*/
|
|
544
|
-
setMany(
|
|
545
|
-
for (const [
|
|
546
|
-
this.set(
|
|
2345
|
+
setMany(e) {
|
|
2346
|
+
for (const [n, t] of Object.entries(e))
|
|
2347
|
+
this.set(n, t);
|
|
547
2348
|
return this;
|
|
548
2349
|
}
|
|
549
2350
|
/**
|
|
550
2351
|
* Get current uniform value.
|
|
551
2352
|
*/
|
|
552
|
-
get(
|
|
553
|
-
var
|
|
554
|
-
return (
|
|
2353
|
+
get(e) {
|
|
2354
|
+
var n;
|
|
2355
|
+
return (n = this.uniforms.get(e)) == null ? void 0 : n.getValue();
|
|
555
2356
|
}
|
|
556
2357
|
/**
|
|
557
2358
|
* Check if uniform exists.
|
|
558
2359
|
*/
|
|
559
|
-
has(
|
|
560
|
-
return this.uniforms.has(
|
|
2360
|
+
has(e) {
|
|
2361
|
+
return this.uniforms.has(e);
|
|
561
2362
|
}
|
|
562
2363
|
/**
|
|
563
2364
|
* Remove a uniform.
|
|
564
2365
|
*/
|
|
565
|
-
delete(
|
|
566
|
-
return this.uniforms.delete(
|
|
2366
|
+
delete(e) {
|
|
2367
|
+
return this.uniforms.delete(e);
|
|
567
2368
|
}
|
|
568
2369
|
/**
|
|
569
2370
|
* Upload all uniforms to GPU.
|
|
@@ -572,20 +2373,20 @@ const d = class d {
|
|
|
572
2373
|
uploadAll() {
|
|
573
2374
|
if (!this.program)
|
|
574
2375
|
return this;
|
|
575
|
-
for (const
|
|
576
|
-
|
|
2376
|
+
for (const e of this.uniforms.values())
|
|
2377
|
+
e.upload(this.gl, this.program);
|
|
577
2378
|
return this;
|
|
578
2379
|
}
|
|
579
2380
|
/**
|
|
580
2381
|
* Upload only built-in uniforms (u_resolution, u_time, u_delta, u_mouse, u_frame).
|
|
581
2382
|
* Call this every frame with current values.
|
|
582
2383
|
*/
|
|
583
|
-
uploadBuiltIns(
|
|
584
|
-
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)
|
|
585
2386
|
return this;
|
|
586
|
-
for (const
|
|
587
|
-
const
|
|
588
|
-
|
|
2387
|
+
for (const i of E.keys()) {
|
|
2388
|
+
const r = this.uniforms.get(i);
|
|
2389
|
+
r && r.upload(this.gl, this.program);
|
|
589
2390
|
}
|
|
590
2391
|
return this;
|
|
591
2392
|
}
|
|
@@ -613,64 +2414,62 @@ const d = class d {
|
|
|
613
2414
|
get size() {
|
|
614
2415
|
return this.uniforms.size;
|
|
615
2416
|
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
i(d, "BUILT_INS", /* @__PURE__ */ new Set([
|
|
619
|
-
"u_resolution",
|
|
620
|
-
"u_time",
|
|
621
|
-
"u_delta",
|
|
622
|
-
"u_mouse",
|
|
623
|
-
"u_frame"
|
|
624
|
-
]));
|
|
625
|
-
let v = d;
|
|
626
|
-
class b {
|
|
2417
|
+
}
|
|
2418
|
+
class O {
|
|
627
2419
|
constructor() {
|
|
628
|
-
|
|
2420
|
+
a(this, "hooks", /* @__PURE__ */ new Map());
|
|
629
2421
|
}
|
|
630
2422
|
id() {
|
|
631
2423
|
return Math.random().toString(36).substring(2, 10);
|
|
632
2424
|
}
|
|
633
2425
|
/** Add a new hook, returns a removal function */
|
|
634
|
-
add(
|
|
635
|
-
const
|
|
636
|
-
return this.hooks.set(
|
|
2426
|
+
add(e) {
|
|
2427
|
+
const n = this.id();
|
|
2428
|
+
return this.hooks.set(n, e), () => this.remove(n);
|
|
637
2429
|
}
|
|
638
2430
|
/** Remove a hook by its ID */
|
|
639
|
-
remove(
|
|
640
|
-
this.hooks.delete(
|
|
2431
|
+
remove(e) {
|
|
2432
|
+
this.hooks.delete(e);
|
|
641
2433
|
}
|
|
642
2434
|
/** Run all hooks with the given state */
|
|
643
|
-
run(
|
|
644
|
-
for (const [
|
|
645
|
-
|
|
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
|
+
}
|
|
646
2445
|
}
|
|
647
2446
|
destroy() {
|
|
648
2447
|
this.hooks.clear();
|
|
649
2448
|
}
|
|
650
2449
|
}
|
|
651
|
-
class
|
|
652
|
-
constructor(
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
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);
|
|
667
2466
|
}
|
|
668
2467
|
/**
|
|
669
2468
|
* Factory method to create and setup WebGL instance.
|
|
670
2469
|
*/
|
|
671
|
-
static setup(
|
|
672
|
-
const
|
|
673
|
-
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;
|
|
674
2473
|
}
|
|
675
2474
|
/**
|
|
676
2475
|
* Initialize WebGL context.
|
|
@@ -678,20 +2477,20 @@ class _ {
|
|
|
678
2477
|
* Context errors are fatal but still reported via onError.
|
|
679
2478
|
*/
|
|
680
2479
|
initContext() {
|
|
681
|
-
const
|
|
2480
|
+
const e = {
|
|
682
2481
|
antialias: this.options.antialias,
|
|
683
2482
|
preserveDrawingBuffer: this.options.preserveDrawingBuffer,
|
|
684
2483
|
alpha: !0,
|
|
685
2484
|
depth: !1,
|
|
686
2485
|
stencil: !1
|
|
687
|
-
},
|
|
688
|
-
if (
|
|
689
|
-
return this._version = 2,
|
|
690
|
-
const
|
|
691
|
-
if (
|
|
692
|
-
return this._version = 1,
|
|
693
|
-
const
|
|
694
|
-
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;
|
|
695
2494
|
}
|
|
696
2495
|
/**
|
|
697
2496
|
* Enable useful WebGL extensions.
|
|
@@ -702,50 +2501,62 @@ class _ {
|
|
|
702
2501
|
/**
|
|
703
2502
|
* Set viewport dimensions.
|
|
704
2503
|
*/
|
|
705
|
-
viewport(
|
|
706
|
-
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;
|
|
707
2506
|
}
|
|
708
2507
|
/**
|
|
709
2508
|
* Set the clock time
|
|
710
2509
|
*/
|
|
711
|
-
clock(
|
|
712
|
-
return this._clock.setTime(
|
|
2510
|
+
clock(e) {
|
|
2511
|
+
return this._clock.setTime(e), this;
|
|
713
2512
|
}
|
|
714
2513
|
/**
|
|
715
2514
|
* Update mouse position.
|
|
716
2515
|
*/
|
|
717
|
-
mouse(
|
|
718
|
-
return this._mouse = [
|
|
2516
|
+
mouse(e, n) {
|
|
2517
|
+
return this._mouse = [e, n], this;
|
|
719
2518
|
}
|
|
720
2519
|
/**
|
|
721
2520
|
* Set a uniform value.
|
|
722
2521
|
*/
|
|
723
|
-
uniform(
|
|
724
|
-
return this._uniforms.set(
|
|
2522
|
+
uniform(e, n) {
|
|
2523
|
+
return this._uniforms.set(e, n), this;
|
|
725
2524
|
}
|
|
726
2525
|
/**
|
|
727
2526
|
* Set multiple uniforms.
|
|
728
2527
|
*/
|
|
729
|
-
uniforms(
|
|
730
|
-
return this._uniforms.setMany(
|
|
2528
|
+
uniforms(e) {
|
|
2529
|
+
return this._uniforms.setMany(e), this;
|
|
731
2530
|
}
|
|
732
2531
|
/**
|
|
733
2532
|
* Get current uniform value.
|
|
734
2533
|
*/
|
|
735
|
-
getUniform(
|
|
736
|
-
return this._uniforms.get(
|
|
2534
|
+
getUniform(e) {
|
|
2535
|
+
return this._uniforms.get(e);
|
|
737
2536
|
}
|
|
738
2537
|
/**
|
|
739
2538
|
* Compile and link shaders.
|
|
740
2539
|
* Errors are handled via onError callback, never thrown.
|
|
741
2540
|
*/
|
|
742
|
-
shader(
|
|
2541
|
+
shader(e, n) {
|
|
743
2542
|
try {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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);
|
|
749
2560
|
}
|
|
750
2561
|
return this;
|
|
751
2562
|
}
|
|
@@ -760,8 +2571,19 @@ class _ {
|
|
|
760
2571
|
*/
|
|
761
2572
|
pause() {
|
|
762
2573
|
if (!this.playing) return this;
|
|
763
|
-
const
|
|
764
|
-
|
|
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;
|
|
765
2587
|
}
|
|
766
2588
|
/**
|
|
767
2589
|
* Render a single frame.
|
|
@@ -781,21 +2603,44 @@ class _ {
|
|
|
781
2603
|
getVersion() {
|
|
782
2604
|
return this._version;
|
|
783
2605
|
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Get the clock instance
|
|
2608
|
+
*/
|
|
2609
|
+
getClock() {
|
|
2610
|
+
return this._clock;
|
|
2611
|
+
}
|
|
784
2612
|
/**
|
|
785
2613
|
* Cleanup all resources.
|
|
786
2614
|
*/
|
|
787
2615
|
destroy() {
|
|
788
|
-
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();
|
|
789
2617
|
}
|
|
790
2618
|
/**
|
|
791
2619
|
* Internal render callback.
|
|
792
2620
|
*/
|
|
793
|
-
onRender(
|
|
794
|
-
const
|
|
795
|
-
|
|
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
|
+
});
|
|
796
2641
|
}
|
|
797
2642
|
}
|
|
798
|
-
const
|
|
2643
|
+
const S = `#ifdef GL_ES
|
|
799
2644
|
precision mediump float;
|
|
800
2645
|
#endif
|
|
801
2646
|
|
|
@@ -808,7 +2653,7 @@ void main() {
|
|
|
808
2653
|
v_texcoord = a_texcoord;
|
|
809
2654
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
810
2655
|
}
|
|
811
|
-
`,
|
|
2656
|
+
`, L = `#ifdef GL_ES
|
|
812
2657
|
precision mediump float;
|
|
813
2658
|
#endif
|
|
814
2659
|
|
|
@@ -822,7 +2667,7 @@ void main() {
|
|
|
822
2667
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
823
2668
|
gl_FragColor = vec4(color, 1.0);
|
|
824
2669
|
}
|
|
825
|
-
`,
|
|
2670
|
+
`, U = `#version 300 es
|
|
826
2671
|
|
|
827
2672
|
in vec2 a_position;
|
|
828
2673
|
in vec2 a_texcoord;
|
|
@@ -832,7 +2677,7 @@ out vec2 v_texcoord;
|
|
|
832
2677
|
void main() {
|
|
833
2678
|
v_texcoord = a_texcoord;
|
|
834
2679
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
835
|
-
}`,
|
|
2680
|
+
}`, he = `#version 300 es
|
|
836
2681
|
precision highp float;
|
|
837
2682
|
|
|
838
2683
|
uniform vec2 u_resolution;
|
|
@@ -847,17 +2692,22 @@ void main() {
|
|
|
847
2692
|
vec3 color = vec3(uv.x, uv.y, 0.5 + 0.5 * sin(u_time));
|
|
848
2693
|
fragColor = vec4(color, 1.0);
|
|
849
2694
|
}`;
|
|
850
|
-
class
|
|
851
|
-
constructor(
|
|
2695
|
+
class $ {
|
|
2696
|
+
constructor(e, n) {
|
|
852
2697
|
/** Active event listeners */
|
|
853
|
-
|
|
2698
|
+
a(this, "listeners", []);
|
|
854
2699
|
/** HTML canvas element */
|
|
855
|
-
|
|
2700
|
+
a(this, "canvasEl");
|
|
856
2701
|
/** Resolved options */
|
|
857
|
-
|
|
2702
|
+
a(this, "options");
|
|
858
2703
|
/** WebGL engine */
|
|
859
|
-
|
|
860
|
-
|
|
2704
|
+
a(this, "engine");
|
|
2705
|
+
/** User sets custom vertex shader */
|
|
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();
|
|
861
2711
|
}
|
|
862
2712
|
/**
|
|
863
2713
|
* Sandbox - A lightweight WebGL wrapper for shader effects.
|
|
@@ -877,22 +2727,49 @@ class E {
|
|
|
877
2727
|
* autoplay: true,
|
|
878
2728
|
* });
|
|
879
2729
|
*/
|
|
880
|
-
static create(
|
|
881
|
-
return new
|
|
2730
|
+
static create(e, n) {
|
|
2731
|
+
return new $(e, n);
|
|
882
2732
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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),
|
|
887
2763
|
autoplay: !0,
|
|
888
2764
|
pauseWhenHidden: !0,
|
|
889
2765
|
dpr: "auto",
|
|
2766
|
+
fps: 0,
|
|
890
2767
|
preserveDrawingBuffer: !1,
|
|
891
2768
|
antialias: !0,
|
|
892
|
-
onError: (
|
|
2769
|
+
onError: (s) => {
|
|
893
2770
|
console.error(
|
|
894
2771
|
"Oops!",
|
|
895
|
-
|
|
2772
|
+
s,
|
|
896
2773
|
`
|
|
897
2774
|
You can handle errors programmatically by providing an onError callback to suppress this log and implement custom fallback behavior.`
|
|
898
2775
|
);
|
|
@@ -901,64 +2778,69 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
901
2778
|
},
|
|
902
2779
|
onBeforeRender: null,
|
|
903
2780
|
onAfterRender: null,
|
|
904
|
-
uniforms: {}
|
|
2781
|
+
uniforms: {},
|
|
2782
|
+
modules: {}
|
|
905
2783
|
};
|
|
906
|
-
if (
|
|
907
|
-
|
|
908
|
-
|
|
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);
|
|
909
2788
|
}
|
|
910
|
-
if (
|
|
911
|
-
|
|
912
|
-
|
|
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);
|
|
913
2793
|
}
|
|
914
|
-
|
|
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 };
|
|
915
2797
|
}
|
|
916
2798
|
setupListeners() {
|
|
917
2799
|
this.listeners.push(
|
|
918
2800
|
// Window resize
|
|
919
|
-
|
|
2801
|
+
x.on(window, "resize", () => {
|
|
920
2802
|
this.setViewport();
|
|
921
2803
|
}),
|
|
922
2804
|
// Canvas resize
|
|
923
|
-
|
|
2805
|
+
x.on(this.canvasEl, "resize", () => {
|
|
924
2806
|
this.setViewport();
|
|
925
2807
|
}),
|
|
926
2808
|
// Visibility check on scroll
|
|
927
2809
|
(() => {
|
|
928
|
-
let
|
|
929
|
-
return
|
|
930
|
-
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));
|
|
931
2813
|
});
|
|
932
2814
|
})(),
|
|
933
2815
|
// Mouse tracking
|
|
934
|
-
|
|
935
|
-
this.setMouse(
|
|
2816
|
+
x.on(document, "mousemove", (e) => {
|
|
2817
|
+
this.setMouse(e.clientX || e.pageX, e.clientY || e.pageY);
|
|
936
2818
|
}),
|
|
937
2819
|
// Touch tracking
|
|
938
|
-
|
|
939
|
-
|
|
2820
|
+
x.on(document, "touchmove", (e) => {
|
|
2821
|
+
e.touches.length > 0 && this.setMouse(e.touches[0].clientX, e.touches[0].clientY);
|
|
940
2822
|
})
|
|
941
2823
|
);
|
|
942
2824
|
}
|
|
943
2825
|
destroyListeners() {
|
|
944
|
-
this.listeners.forEach((
|
|
2826
|
+
this.listeners.forEach((e) => e()), this.listeners = [];
|
|
945
2827
|
}
|
|
946
2828
|
setViewport() {
|
|
947
|
-
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;
|
|
948
2830
|
this.engine.viewport(
|
|
949
2831
|
0,
|
|
950
2832
|
0,
|
|
951
|
-
Math.max(1, Math.floor(
|
|
952
|
-
Math.max(1, Math.floor(
|
|
2833
|
+
Math.max(1, Math.floor(n * e)),
|
|
2834
|
+
Math.max(1, Math.floor(t * e))
|
|
953
2835
|
);
|
|
954
2836
|
}
|
|
955
2837
|
isInViewport() {
|
|
956
|
-
const
|
|
957
|
-
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);
|
|
958
2840
|
}
|
|
959
|
-
setMouse(
|
|
960
|
-
const
|
|
961
|
-
|
|
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);
|
|
962
2844
|
}
|
|
963
2845
|
/**
|
|
964
2846
|
* Set a single uniform value with type checking.
|
|
@@ -967,8 +2849,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
967
2849
|
* sandbox.setUniform<number>("u_time", 1.5);
|
|
968
2850
|
* sandbox.setUniform<Vec3[]>("u_colors", [[1, 0, 0], [0, 1, 0]]);
|
|
969
2851
|
*/
|
|
970
|
-
setUniform(
|
|
971
|
-
return this.engine.uniform(
|
|
2852
|
+
setUniform(e, n) {
|
|
2853
|
+
return this.engine.uniform(e, n), this;
|
|
972
2854
|
}
|
|
973
2855
|
/**
|
|
974
2856
|
* Set multiple uniforms at once with type checking.
|
|
@@ -984,37 +2866,84 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
984
2866
|
* u_colors: [[1, 0, 0], [0, 1, 0]],
|
|
985
2867
|
* });
|
|
986
2868
|
*/
|
|
987
|
-
setUniforms(
|
|
988
|
-
return this.engine.uniforms(
|
|
2869
|
+
setUniforms(e) {
|
|
2870
|
+
return this.engine.uniforms(e), this;
|
|
989
2871
|
}
|
|
990
2872
|
/**
|
|
991
2873
|
* Get current uniform value.
|
|
992
2874
|
*/
|
|
993
|
-
getUniform(
|
|
994
|
-
return this.engine.getUniform(
|
|
2875
|
+
getUniform(e) {
|
|
2876
|
+
return this.engine.getUniform(e);
|
|
995
2877
|
}
|
|
996
2878
|
/**
|
|
997
2879
|
* Update shaders.
|
|
998
2880
|
* @example
|
|
999
2881
|
* sandbox.setShader(vertexSource, fragmentSource);
|
|
1000
2882
|
*/
|
|
1001
|
-
setShader(
|
|
1002
|
-
return this.engine.shader(
|
|
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;
|
|
1003
2885
|
}
|
|
1004
2886
|
/**
|
|
1005
2887
|
* Update only fragment shader (uses default vertex).
|
|
1006
2888
|
* @example
|
|
1007
2889
|
* sandbox.setFragment(fragmentSource);
|
|
1008
2890
|
*/
|
|
1009
|
-
setFragment(
|
|
1010
|
-
const
|
|
1011
|
-
return this.
|
|
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();
|
|
2908
|
+
}
|
|
2909
|
+
/**
|
|
2910
|
+
* Set the max frame rate runtime
|
|
2911
|
+
*
|
|
2912
|
+
* @example
|
|
2913
|
+
* sandbox.setFps(30); // Limit to 30 FPS
|
|
2914
|
+
* sandbox.setFps(0); // Unlimited FPS
|
|
2915
|
+
*/
|
|
2916
|
+
setFps(e) {
|
|
2917
|
+
return this.engine.getClock().setMaxFps(e), this;
|
|
1012
2918
|
}
|
|
1013
2919
|
/**
|
|
1014
2920
|
* Add a runtime render hook.
|
|
1015
2921
|
*/
|
|
1016
|
-
hook(
|
|
1017
|
-
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;
|
|
1018
2947
|
}
|
|
1019
2948
|
/**
|
|
1020
2949
|
* Start animation loop.
|
|
@@ -1025,8 +2954,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1025
2954
|
/**
|
|
1026
2955
|
* Start animation loop at specific time (in seconds).
|
|
1027
2956
|
*/
|
|
1028
|
-
playAt(
|
|
1029
|
-
return this.engine.clock(
|
|
2957
|
+
playAt(e) {
|
|
2958
|
+
return this.engine.clock(e), this.engine.play(), this;
|
|
1030
2959
|
}
|
|
1031
2960
|
/**
|
|
1032
2961
|
* Stop animation loop.
|
|
@@ -1037,9 +2966,9 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1037
2966
|
/**
|
|
1038
2967
|
* Pause animation loop at specific time (in seconds).
|
|
1039
2968
|
*/
|
|
1040
|
-
pauseAt(
|
|
1041
|
-
const
|
|
1042
|
-
|
|
2969
|
+
pauseAt(e) {
|
|
2970
|
+
const n = this.hook((t) => {
|
|
2971
|
+
t.time >= e && (n(), this.pause());
|
|
1043
2972
|
}, "after");
|
|
1044
2973
|
return this;
|
|
1045
2974
|
}
|
|
@@ -1052,8 +2981,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1052
2981
|
/**
|
|
1053
2982
|
* Set current time (in seconds).
|
|
1054
2983
|
*/
|
|
1055
|
-
time(
|
|
1056
|
-
return this.engine.clock(
|
|
2984
|
+
time(e) {
|
|
2985
|
+
return this.engine.clock(e), this;
|
|
1057
2986
|
}
|
|
1058
2987
|
/**
|
|
1059
2988
|
* Render a single frame (for static rendering).
|
|
@@ -1068,8 +2997,8 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1068
2997
|
* @example
|
|
1069
2998
|
* sandbox.renderAt(2.5); // Render as if 2.5 seconds elapsed
|
|
1070
2999
|
*/
|
|
1071
|
-
renderAt(
|
|
1072
|
-
return this.engine.clock(
|
|
3000
|
+
renderAt(e) {
|
|
3001
|
+
return this.engine.clock(e), this.engine.render(), this;
|
|
1073
3002
|
}
|
|
1074
3003
|
/**
|
|
1075
3004
|
* Check if currently playing.
|
|
@@ -1080,14 +3009,14 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1080
3009
|
/**
|
|
1081
3010
|
* Get WebGL version using (1 or 2).
|
|
1082
3011
|
*/
|
|
1083
|
-
|
|
3012
|
+
get version() {
|
|
1084
3013
|
return this.engine.getVersion();
|
|
1085
3014
|
}
|
|
1086
3015
|
/**
|
|
1087
3016
|
* Get canvas element.
|
|
1088
3017
|
*/
|
|
1089
|
-
|
|
1090
|
-
return this.
|
|
3018
|
+
get canvas() {
|
|
3019
|
+
return this.canvasEl;
|
|
1091
3020
|
}
|
|
1092
3021
|
/**
|
|
1093
3022
|
* Destroy sandbox and release all resources.
|
|
@@ -1101,10 +3030,26 @@ You can handle errors programmatically by providing an onError callback to suppr
|
|
|
1101
3030
|
}
|
|
1102
3031
|
}
|
|
1103
3032
|
export {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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
|
|
1110
3055
|
};
|