@fjandin/react-shader 0.0.12 → 0.0.14
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/dist/example/frontend.d.ts.map +1 -1
- package/dist/example/glsl/texture-demo.glsl.d.ts +2 -0
- package/dist/example/glsl/texture-demo.glsl.d.ts.map +1 -0
- package/dist/hooks/useWebGL.d.ts.map +1 -1
- package/dist/index.cjs +156 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -4
- package/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/textures.d.ts +23 -0
- package/dist/utils/textures.d.ts.map +1 -0
- package/dist/utils/uniforms.d.ts +2 -1
- package/dist/utils/uniforms.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../../src/example/frontend.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,aAAa,CAAA;AAiHpB,wBAAgB,GAAG,4CAgGlB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"texture-demo.glsl.d.ts","sourceRoot":"","sources":["../../../src/example/glsl/texture-demo.glsl.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,QAsC3B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"useWebGL.d.ts","sourceRoot":"","sources":["../../src/hooks/useWebGL.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAKvD,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACrC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe;;;EAyRhD"}
|
package/dist/index.cjs
CHANGED
|
@@ -88,6 +88,147 @@ function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// src/utils/textures.ts
|
|
92
|
+
function isTextureSource(value) {
|
|
93
|
+
if (typeof window === "undefined")
|
|
94
|
+
return false;
|
|
95
|
+
return value instanceof HTMLImageElement || value instanceof HTMLCanvasElement || value instanceof HTMLVideoElement || typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap || value instanceof ImageData || typeof OffscreenCanvas !== "undefined" && value instanceof OffscreenCanvas;
|
|
96
|
+
}
|
|
97
|
+
function isTextureOptions(value) {
|
|
98
|
+
return typeof value === "object" && value !== null && "source" in value && isTextureSource(value.source);
|
|
99
|
+
}
|
|
100
|
+
function isTexture(value) {
|
|
101
|
+
return isTextureSource(value) || isTextureOptions(value);
|
|
102
|
+
}
|
|
103
|
+
function getWrapMode(gl, wrap) {
|
|
104
|
+
switch (wrap) {
|
|
105
|
+
case "repeat":
|
|
106
|
+
return gl.REPEAT;
|
|
107
|
+
case "mirror":
|
|
108
|
+
return gl.MIRRORED_REPEAT;
|
|
109
|
+
default:
|
|
110
|
+
return gl.CLAMP_TO_EDGE;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function getMinFilter(gl, filter) {
|
|
114
|
+
switch (filter) {
|
|
115
|
+
case "nearest":
|
|
116
|
+
return gl.NEAREST;
|
|
117
|
+
case "mipmap":
|
|
118
|
+
return gl.LINEAR_MIPMAP_LINEAR;
|
|
119
|
+
default:
|
|
120
|
+
return gl.LINEAR;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function getMagFilter(gl, filter) {
|
|
124
|
+
switch (filter) {
|
|
125
|
+
case "nearest":
|
|
126
|
+
return gl.NEAREST;
|
|
127
|
+
default:
|
|
128
|
+
return gl.LINEAR;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function isPowerOfTwo(value) {
|
|
132
|
+
return (value & value - 1) === 0 && value !== 0;
|
|
133
|
+
}
|
|
134
|
+
function getSourceDimensions(source) {
|
|
135
|
+
if (source instanceof HTMLImageElement) {
|
|
136
|
+
return { width: source.naturalWidth, height: source.naturalHeight };
|
|
137
|
+
}
|
|
138
|
+
if (source instanceof HTMLVideoElement) {
|
|
139
|
+
return { width: source.videoWidth, height: source.videoHeight };
|
|
140
|
+
}
|
|
141
|
+
if (source instanceof ImageData) {
|
|
142
|
+
return { width: source.width, height: source.height };
|
|
143
|
+
}
|
|
144
|
+
return { width: source.width, height: source.height };
|
|
145
|
+
}
|
|
146
|
+
function createTextureManager(gl) {
|
|
147
|
+
const maxUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
148
|
+
return {
|
|
149
|
+
cache: new Map,
|
|
150
|
+
nextUnit: 0,
|
|
151
|
+
maxUnits
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function createTexture(gl, source, options = {}) {
|
|
155
|
+
const texture = gl.createTexture();
|
|
156
|
+
if (!texture) {
|
|
157
|
+
throw new Error("Failed to create WebGL texture");
|
|
158
|
+
}
|
|
159
|
+
const { wrapS = "clamp", wrapT = "clamp", minFilter = "linear", magFilter = "linear", flipY = true } = options;
|
|
160
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
161
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
|
|
162
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
163
|
+
const { width, height } = getSourceDimensions(source);
|
|
164
|
+
const pot = isPowerOfTwo(width) && isPowerOfTwo(height);
|
|
165
|
+
const isWebGL2 = "texStorage2D" in gl;
|
|
166
|
+
const actualMinFilter = minFilter === "mipmap" && (pot || isWebGL2) ? minFilter : minFilter === "mipmap" ? "linear" : minFilter;
|
|
167
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, getWrapMode(gl, wrapS));
|
|
168
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, getWrapMode(gl, wrapT));
|
|
169
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, getMinFilter(gl, actualMinFilter));
|
|
170
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, getMagFilter(gl, magFilter));
|
|
171
|
+
if (actualMinFilter === "mipmap") {
|
|
172
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
173
|
+
}
|
|
174
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
175
|
+
return texture;
|
|
176
|
+
}
|
|
177
|
+
function updateTexture(gl, texture, source, flipY = true) {
|
|
178
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
179
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
|
|
180
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
181
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
182
|
+
}
|
|
183
|
+
function needsVideoUpdate(source) {
|
|
184
|
+
if (!(source instanceof HTMLVideoElement))
|
|
185
|
+
return false;
|
|
186
|
+
return !source.paused && !source.ended && source.readyState >= 2;
|
|
187
|
+
}
|
|
188
|
+
function bindTextureUniform(gl, program, name, value, manager, locationCache) {
|
|
189
|
+
const source = isTextureOptions(value) ? value.source : value;
|
|
190
|
+
const options = isTextureOptions(value) ? value : {};
|
|
191
|
+
let entry = manager.cache.get(name);
|
|
192
|
+
if (entry && entry.source !== source) {
|
|
193
|
+
gl.deleteTexture(entry.texture);
|
|
194
|
+
entry = undefined;
|
|
195
|
+
}
|
|
196
|
+
if (!entry) {
|
|
197
|
+
if (manager.nextUnit >= manager.maxUnits) {
|
|
198
|
+
console.warn(`Maximum texture units (${manager.maxUnits}) exceeded for uniform "${name}"`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const texture = createTexture(gl, source, options);
|
|
202
|
+
entry = {
|
|
203
|
+
texture,
|
|
204
|
+
unit: manager.nextUnit++,
|
|
205
|
+
source
|
|
206
|
+
};
|
|
207
|
+
manager.cache.set(name, entry);
|
|
208
|
+
}
|
|
209
|
+
if (needsVideoUpdate(source)) {
|
|
210
|
+
const flipY = isTextureOptions(value) ? value.flipY ?? true : true;
|
|
211
|
+
updateTexture(gl, entry.texture, source, flipY);
|
|
212
|
+
}
|
|
213
|
+
gl.activeTexture(gl.TEXTURE0 + entry.unit);
|
|
214
|
+
gl.bindTexture(gl.TEXTURE_2D, entry.texture);
|
|
215
|
+
let location = locationCache.get(name);
|
|
216
|
+
if (location === undefined) {
|
|
217
|
+
location = gl.getUniformLocation(program, name);
|
|
218
|
+
locationCache.set(name, location);
|
|
219
|
+
}
|
|
220
|
+
if (location !== null) {
|
|
221
|
+
gl.uniform1i(location, entry.unit);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function cleanupTextures(gl, manager) {
|
|
225
|
+
for (const entry of manager.cache.values()) {
|
|
226
|
+
gl.deleteTexture(entry.texture);
|
|
227
|
+
}
|
|
228
|
+
manager.cache.clear();
|
|
229
|
+
manager.nextUnit = 0;
|
|
230
|
+
}
|
|
231
|
+
|
|
91
232
|
// src/utils/uniforms.ts
|
|
92
233
|
var MAX_ARRAY_LENGTH = 100;
|
|
93
234
|
function isVec2(value) {
|
|
@@ -139,8 +280,14 @@ function setUniform(gl, location, value) {
|
|
|
139
280
|
function getUniformLocation(gl, program, name) {
|
|
140
281
|
return gl.getUniformLocation(program, name);
|
|
141
282
|
}
|
|
142
|
-
function setUniforms(gl, program, uniforms, locationCache) {
|
|
283
|
+
function setUniforms(gl, program, uniforms, locationCache, textureManager) {
|
|
143
284
|
for (const [name, value] of Object.entries(uniforms)) {
|
|
285
|
+
if (isTexture(value)) {
|
|
286
|
+
if (textureManager) {
|
|
287
|
+
bindTextureUniform(gl, program, name, value, textureManager, locationCache);
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
144
291
|
let location = locationCache.get(name);
|
|
145
292
|
if (location === undefined) {
|
|
146
293
|
location = getUniformLocation(gl, program, name);
|
|
@@ -164,6 +311,9 @@ function createUniformLocationCache() {
|
|
|
164
311
|
return new Map;
|
|
165
312
|
}
|
|
166
313
|
function getUniformType(value) {
|
|
314
|
+
if (isTexture(value)) {
|
|
315
|
+
return "sampler2D";
|
|
316
|
+
}
|
|
167
317
|
if (typeof value === "number") {
|
|
168
318
|
return "float";
|
|
169
319
|
}
|
|
@@ -247,10 +397,12 @@ function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
|
|
|
247
397
|
program,
|
|
248
398
|
positionBuffer,
|
|
249
399
|
positionAttributeLocation,
|
|
250
|
-
uniformLocationCache: createUniformLocationCache()
|
|
400
|
+
uniformLocationCache: createUniformLocationCache(),
|
|
401
|
+
textureManager: createTextureManager(gl)
|
|
251
402
|
};
|
|
252
403
|
}
|
|
253
404
|
function cleanupWebGL(gl, state) {
|
|
405
|
+
cleanupTextures(gl, state.textureManager);
|
|
254
406
|
gl.deleteBuffer(state.positionBuffer);
|
|
255
407
|
gl.deleteProgram(state.program);
|
|
256
408
|
}
|
|
@@ -303,7 +455,7 @@ function useWebGL(options) {
|
|
|
303
455
|
const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
|
|
304
456
|
lastFrameTimeRef.current = time;
|
|
305
457
|
elapsedTimeRef.current += deltaTime * timeScaleRef.current;
|
|
306
|
-
const { gl, program, positionAttributeLocation, uniformLocationCache } = state;
|
|
458
|
+
const { gl, program, positionAttributeLocation, uniformLocationCache, textureManager } = state;
|
|
307
459
|
const elapsedTime = elapsedTimeRef.current;
|
|
308
460
|
const dpr = dprRef.current;
|
|
309
461
|
const displayWidth = canvas.clientWidth;
|
|
@@ -331,7 +483,7 @@ function useWebGL(options) {
|
|
|
331
483
|
defaultUniforms.iMouseNormalized = mouseNormalizedRef.current;
|
|
332
484
|
defaultUniforms.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
|
|
333
485
|
defaultUniforms.iResolution = [canvas.width, canvas.height];
|
|
334
|
-
setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache);
|
|
486
|
+
setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache, textureManager);
|
|
335
487
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
336
488
|
if (onFrameRef.current) {
|
|
337
489
|
onFrameRef.current({
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { generateDistortionRippleFunction } from "./shaders/distortion-ripple";
|
|
|
4
4
|
export { generateSceneCirclesFunction } from "./shaders/scene-circles";
|
|
5
5
|
export { generateSimplexNoiseFunction } from "./shaders/simplex-noise";
|
|
6
6
|
export { generateUtilsFunction } from "./shaders/utils";
|
|
7
|
-
export type { DefaultUniforms, FloatArray, FrameInfo, ReactShaderProps, UniformValue, Vec2, Vec2Array, Vec3, Vec3Array, Vec4, Vec4Array, } from "./types";
|
|
7
|
+
export type { DefaultUniforms, FloatArray, FrameInfo, ReactShaderProps, TextureMagFilter, TextureMinFilter, TextureOptions, TextureSource, TextureWrap, UniformValue, Vec2, Vec2Array, Vec3, Vec3Array, Vec4, Vec4Array, } from "./types";
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAA;AAC9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAA;AAC9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,WAAW,EACX,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,MAAM,SAAS,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,147 @@ function createShaderProgram(gl, vertexSource, fragmentSource) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// src/utils/textures.ts
|
|
52
|
+
function isTextureSource(value) {
|
|
53
|
+
if (typeof window === "undefined")
|
|
54
|
+
return false;
|
|
55
|
+
return value instanceof HTMLImageElement || value instanceof HTMLCanvasElement || value instanceof HTMLVideoElement || typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap || value instanceof ImageData || typeof OffscreenCanvas !== "undefined" && value instanceof OffscreenCanvas;
|
|
56
|
+
}
|
|
57
|
+
function isTextureOptions(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && "source" in value && isTextureSource(value.source);
|
|
59
|
+
}
|
|
60
|
+
function isTexture(value) {
|
|
61
|
+
return isTextureSource(value) || isTextureOptions(value);
|
|
62
|
+
}
|
|
63
|
+
function getWrapMode(gl, wrap) {
|
|
64
|
+
switch (wrap) {
|
|
65
|
+
case "repeat":
|
|
66
|
+
return gl.REPEAT;
|
|
67
|
+
case "mirror":
|
|
68
|
+
return gl.MIRRORED_REPEAT;
|
|
69
|
+
default:
|
|
70
|
+
return gl.CLAMP_TO_EDGE;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getMinFilter(gl, filter) {
|
|
74
|
+
switch (filter) {
|
|
75
|
+
case "nearest":
|
|
76
|
+
return gl.NEAREST;
|
|
77
|
+
case "mipmap":
|
|
78
|
+
return gl.LINEAR_MIPMAP_LINEAR;
|
|
79
|
+
default:
|
|
80
|
+
return gl.LINEAR;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function getMagFilter(gl, filter) {
|
|
84
|
+
switch (filter) {
|
|
85
|
+
case "nearest":
|
|
86
|
+
return gl.NEAREST;
|
|
87
|
+
default:
|
|
88
|
+
return gl.LINEAR;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function isPowerOfTwo(value) {
|
|
92
|
+
return (value & value - 1) === 0 && value !== 0;
|
|
93
|
+
}
|
|
94
|
+
function getSourceDimensions(source) {
|
|
95
|
+
if (source instanceof HTMLImageElement) {
|
|
96
|
+
return { width: source.naturalWidth, height: source.naturalHeight };
|
|
97
|
+
}
|
|
98
|
+
if (source instanceof HTMLVideoElement) {
|
|
99
|
+
return { width: source.videoWidth, height: source.videoHeight };
|
|
100
|
+
}
|
|
101
|
+
if (source instanceof ImageData) {
|
|
102
|
+
return { width: source.width, height: source.height };
|
|
103
|
+
}
|
|
104
|
+
return { width: source.width, height: source.height };
|
|
105
|
+
}
|
|
106
|
+
function createTextureManager(gl) {
|
|
107
|
+
const maxUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
|
|
108
|
+
return {
|
|
109
|
+
cache: new Map,
|
|
110
|
+
nextUnit: 0,
|
|
111
|
+
maxUnits
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createTexture(gl, source, options = {}) {
|
|
115
|
+
const texture = gl.createTexture();
|
|
116
|
+
if (!texture) {
|
|
117
|
+
throw new Error("Failed to create WebGL texture");
|
|
118
|
+
}
|
|
119
|
+
const { wrapS = "clamp", wrapT = "clamp", minFilter = "linear", magFilter = "linear", flipY = true } = options;
|
|
120
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
121
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
|
|
122
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
123
|
+
const { width, height } = getSourceDimensions(source);
|
|
124
|
+
const pot = isPowerOfTwo(width) && isPowerOfTwo(height);
|
|
125
|
+
const isWebGL2 = "texStorage2D" in gl;
|
|
126
|
+
const actualMinFilter = minFilter === "mipmap" && (pot || isWebGL2) ? minFilter : minFilter === "mipmap" ? "linear" : minFilter;
|
|
127
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, getWrapMode(gl, wrapS));
|
|
128
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, getWrapMode(gl, wrapT));
|
|
129
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, getMinFilter(gl, actualMinFilter));
|
|
130
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, getMagFilter(gl, magFilter));
|
|
131
|
+
if (actualMinFilter === "mipmap") {
|
|
132
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
133
|
+
}
|
|
134
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
135
|
+
return texture;
|
|
136
|
+
}
|
|
137
|
+
function updateTexture(gl, texture, source, flipY = true) {
|
|
138
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
139
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
|
|
140
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
141
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
142
|
+
}
|
|
143
|
+
function needsVideoUpdate(source) {
|
|
144
|
+
if (!(source instanceof HTMLVideoElement))
|
|
145
|
+
return false;
|
|
146
|
+
return !source.paused && !source.ended && source.readyState >= 2;
|
|
147
|
+
}
|
|
148
|
+
function bindTextureUniform(gl, program, name, value, manager, locationCache) {
|
|
149
|
+
const source = isTextureOptions(value) ? value.source : value;
|
|
150
|
+
const options = isTextureOptions(value) ? value : {};
|
|
151
|
+
let entry = manager.cache.get(name);
|
|
152
|
+
if (entry && entry.source !== source) {
|
|
153
|
+
gl.deleteTexture(entry.texture);
|
|
154
|
+
entry = undefined;
|
|
155
|
+
}
|
|
156
|
+
if (!entry) {
|
|
157
|
+
if (manager.nextUnit >= manager.maxUnits) {
|
|
158
|
+
console.warn(`Maximum texture units (${manager.maxUnits}) exceeded for uniform "${name}"`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const texture = createTexture(gl, source, options);
|
|
162
|
+
entry = {
|
|
163
|
+
texture,
|
|
164
|
+
unit: manager.nextUnit++,
|
|
165
|
+
source
|
|
166
|
+
};
|
|
167
|
+
manager.cache.set(name, entry);
|
|
168
|
+
}
|
|
169
|
+
if (needsVideoUpdate(source)) {
|
|
170
|
+
const flipY = isTextureOptions(value) ? value.flipY ?? true : true;
|
|
171
|
+
updateTexture(gl, entry.texture, source, flipY);
|
|
172
|
+
}
|
|
173
|
+
gl.activeTexture(gl.TEXTURE0 + entry.unit);
|
|
174
|
+
gl.bindTexture(gl.TEXTURE_2D, entry.texture);
|
|
175
|
+
let location = locationCache.get(name);
|
|
176
|
+
if (location === undefined) {
|
|
177
|
+
location = gl.getUniformLocation(program, name);
|
|
178
|
+
locationCache.set(name, location);
|
|
179
|
+
}
|
|
180
|
+
if (location !== null) {
|
|
181
|
+
gl.uniform1i(location, entry.unit);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function cleanupTextures(gl, manager) {
|
|
185
|
+
for (const entry of manager.cache.values()) {
|
|
186
|
+
gl.deleteTexture(entry.texture);
|
|
187
|
+
}
|
|
188
|
+
manager.cache.clear();
|
|
189
|
+
manager.nextUnit = 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
51
192
|
// src/utils/uniforms.ts
|
|
52
193
|
var MAX_ARRAY_LENGTH = 100;
|
|
53
194
|
function isVec2(value) {
|
|
@@ -99,8 +240,14 @@ function setUniform(gl, location, value) {
|
|
|
99
240
|
function getUniformLocation(gl, program, name) {
|
|
100
241
|
return gl.getUniformLocation(program, name);
|
|
101
242
|
}
|
|
102
|
-
function setUniforms(gl, program, uniforms, locationCache) {
|
|
243
|
+
function setUniforms(gl, program, uniforms, locationCache, textureManager) {
|
|
103
244
|
for (const [name, value] of Object.entries(uniforms)) {
|
|
245
|
+
if (isTexture(value)) {
|
|
246
|
+
if (textureManager) {
|
|
247
|
+
bindTextureUniform(gl, program, name, value, textureManager, locationCache);
|
|
248
|
+
}
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
104
251
|
let location = locationCache.get(name);
|
|
105
252
|
if (location === undefined) {
|
|
106
253
|
location = getUniformLocation(gl, program, name);
|
|
@@ -124,6 +271,9 @@ function createUniformLocationCache() {
|
|
|
124
271
|
return new Map;
|
|
125
272
|
}
|
|
126
273
|
function getUniformType(value) {
|
|
274
|
+
if (isTexture(value)) {
|
|
275
|
+
return "sampler2D";
|
|
276
|
+
}
|
|
127
277
|
if (typeof value === "number") {
|
|
128
278
|
return "float";
|
|
129
279
|
}
|
|
@@ -207,10 +357,12 @@ function initializeWebGL(canvas, vertexSource, fragmentSource, customUniforms) {
|
|
|
207
357
|
program,
|
|
208
358
|
positionBuffer,
|
|
209
359
|
positionAttributeLocation,
|
|
210
|
-
uniformLocationCache: createUniformLocationCache()
|
|
360
|
+
uniformLocationCache: createUniformLocationCache(),
|
|
361
|
+
textureManager: createTextureManager(gl)
|
|
211
362
|
};
|
|
212
363
|
}
|
|
213
364
|
function cleanupWebGL(gl, state) {
|
|
365
|
+
cleanupTextures(gl, state.textureManager);
|
|
214
366
|
gl.deleteBuffer(state.positionBuffer);
|
|
215
367
|
gl.deleteProgram(state.program);
|
|
216
368
|
}
|
|
@@ -263,7 +415,7 @@ function useWebGL(options) {
|
|
|
263
415
|
const deltaTime = lastFrameTimeRef.current === 0 ? 0 : (time - lastFrameTimeRef.current) / 1000;
|
|
264
416
|
lastFrameTimeRef.current = time;
|
|
265
417
|
elapsedTimeRef.current += deltaTime * timeScaleRef.current;
|
|
266
|
-
const { gl, program, positionAttributeLocation, uniformLocationCache } = state;
|
|
418
|
+
const { gl, program, positionAttributeLocation, uniformLocationCache, textureManager } = state;
|
|
267
419
|
const elapsedTime = elapsedTimeRef.current;
|
|
268
420
|
const dpr = dprRef.current;
|
|
269
421
|
const displayWidth = canvas.clientWidth;
|
|
@@ -291,7 +443,7 @@ function useWebGL(options) {
|
|
|
291
443
|
defaultUniforms.iMouseNormalized = mouseNormalizedRef.current;
|
|
292
444
|
defaultUniforms.iMouseLeftDown = mouseLeftDownRef.current ? 1 : 0;
|
|
293
445
|
defaultUniforms.iResolution = [canvas.width, canvas.height];
|
|
294
|
-
setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache);
|
|
446
|
+
setUniforms(gl, program, { ...defaultUniforms, ...uniformsRef.current }, uniformLocationCache, textureManager);
|
|
295
447
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
296
448
|
if (onFrameRef.current) {
|
|
297
449
|
onFrameRef.current({
|
package/dist/types.d.ts
CHANGED
|
@@ -5,7 +5,19 @@ export type FloatArray = number[];
|
|
|
5
5
|
export type Vec2Array = Vec2[];
|
|
6
6
|
export type Vec3Array = Vec3[];
|
|
7
7
|
export type Vec4Array = Vec4[];
|
|
8
|
-
export type
|
|
8
|
+
export type TextureSource = HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap | ImageData | OffscreenCanvas;
|
|
9
|
+
export type TextureWrap = "repeat" | "clamp" | "mirror";
|
|
10
|
+
export type TextureMinFilter = "nearest" | "linear" | "mipmap";
|
|
11
|
+
export type TextureMagFilter = "nearest" | "linear";
|
|
12
|
+
export interface TextureOptions {
|
|
13
|
+
source: TextureSource;
|
|
14
|
+
wrapS?: TextureWrap;
|
|
15
|
+
wrapT?: TextureWrap;
|
|
16
|
+
minFilter?: TextureMinFilter;
|
|
17
|
+
magFilter?: TextureMagFilter;
|
|
18
|
+
flipY?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type UniformValue = number | Vec2 | Vec3 | Vec4 | FloatArray | Vec2Array | Vec3Array | Vec4Array | TextureSource | TextureOptions;
|
|
9
21
|
export interface FrameInfo {
|
|
10
22
|
deltaTime: number;
|
|
11
23
|
time: number;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AACjC,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAE9B,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AACnC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG,MAAM,EAAE,CAAA;AACjC,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAC9B,MAAM,MAAM,SAAS,GAAG,IAAI,EAAE,CAAA;AAE9B,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,eAAe,CAAA;AAEnB,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;AACvD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC9D,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEnD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,UAAU,GACV,SAAS,GACT,SAAS,GACT,SAAS,GACT,aAAa,GACb,cAAc,CAAA;AAElB,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,aAAa,EAAE,OAAO,CAAA;CACvB;AACD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,IAAI,CAAA;IACZ,gBAAgB,EAAE,IAAI,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,IAAI,CAAA;CAClB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TextureOptions, TextureSource } from "../types";
|
|
2
|
+
type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
3
|
+
interface TextureEntry {
|
|
4
|
+
texture: WebGLTexture;
|
|
5
|
+
unit: number;
|
|
6
|
+
source: TextureSource;
|
|
7
|
+
}
|
|
8
|
+
export interface TextureManager {
|
|
9
|
+
cache: Map<string, TextureEntry>;
|
|
10
|
+
nextUnit: number;
|
|
11
|
+
maxUnits: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function isTextureSource(value: unknown): value is TextureSource;
|
|
14
|
+
export declare function isTextureOptions(value: unknown): value is TextureOptions;
|
|
15
|
+
export declare function isTexture(value: unknown): value is TextureSource | TextureOptions;
|
|
16
|
+
export declare function createTextureManager(gl: WebGLContext): TextureManager;
|
|
17
|
+
export declare function createTexture(gl: WebGLContext, source: TextureSource, options?: Partial<TextureOptions>): WebGLTexture;
|
|
18
|
+
export declare function updateTexture(gl: WebGLContext, texture: WebGLTexture, source: TextureSource, flipY?: boolean): void;
|
|
19
|
+
export declare function needsVideoUpdate(source: TextureSource): boolean;
|
|
20
|
+
export declare function bindTextureUniform(gl: WebGLContext, program: WebGLProgram, name: string, value: TextureSource | TextureOptions, manager: TextureManager, locationCache: Map<string, WebGLUniformLocation | null>): void;
|
|
21
|
+
export declare function cleanupTextures(gl: WebGLContext, manager: TextureManager): void;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=textures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textures.d.ts","sourceRoot":"","sources":["../../src/utils/textures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsC,cAAc,EAAE,aAAa,EAAe,MAAM,UAAU,CAAA;AAE9G,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,UAAU,YAAY;IACpB,OAAO,EAAE,YAAY,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAUtE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAOxE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,GAAG,cAAc,CAEjF;AAmDD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,cAAc,CAOrE;AAED,wBAAgB,aAAa,CAC3B,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE,OAAO,CAAC,cAAc,CAAM,GACpC,YAAY,CAqCd;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,UAAO,GAAG,IAAI,CAKhH;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAG/D;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,aAAa,GAAG,cAAc,EACrC,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,GACtD,IAAI,CA+CN;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAM/E"}
|
package/dist/utils/uniforms.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { UniformValue } from "../types";
|
|
2
|
+
import { type TextureManager } from "./textures";
|
|
2
3
|
type WebGLContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
3
4
|
export declare const MAX_ARRAY_LENGTH = 100;
|
|
4
5
|
export declare function setUniform(gl: WebGLContext, location: WebGLUniformLocation | null, value: UniformValue): void;
|
|
5
6
|
export declare function getUniformLocation(gl: WebGLContext, program: WebGLProgram, name: string): WebGLUniformLocation | null;
|
|
6
|
-
export declare function setUniforms(gl: WebGLContext, program: WebGLProgram, uniforms: Record<string, UniformValue>, locationCache: Map<string, WebGLUniformLocation | null
|
|
7
|
+
export declare function setUniforms(gl: WebGLContext, program: WebGLProgram, uniforms: Record<string, UniformValue>, locationCache: Map<string, WebGLUniformLocation | null>, textureManager?: TextureManager): void;
|
|
7
8
|
export declare function createUniformLocationCache(): Map<string, WebGLUniformLocation | null>;
|
|
8
9
|
export declare function generateUniformDeclarations(uniforms: Record<string, UniformValue>): string;
|
|
9
10
|
export declare function injectUniformDeclarations(shaderSource: string, customUniforms: Record<string, UniformValue> | undefined, defaultUniforms: Record<string, UniformValue>): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAqD,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"uniforms.d.ts","sourceRoot":"","sources":["../../src/utils/uniforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,YAAY,EAAqD,MAAM,UAAU,CAAA;AAC3G,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAE/E,KAAK,YAAY,GAAG,qBAAqB,GAAG,sBAAsB,CAAA;AAElE,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAkCnC,wBAAgB,UAAU,CAAC,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAsB7G;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAErH;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EACtC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,EACvD,cAAc,CAAC,EAAE,cAAc,GAC9B,IAAI,CA8BN;AAED,wBAAgB,0BAA0B,IAAI,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAErF;AAiCD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAa1F;AAID,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,SAAS,EACxD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAC5C,MAAM,CAQR"}
|