@sangwonl/pocato-core 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -27,7 +27,10 @@ var EventEmitter = class {
27
27
  };
28
28
 
29
29
  // src/renderer/index.ts
30
- import * as THREE2 from "three";
30
+ import * as THREE4 from "three";
31
+
32
+ // src/renderer/face-renderer.ts
33
+ import * as THREE3 from "three";
31
34
 
32
35
  // src/renderer/shader-bootstrap.ts
33
36
  import * as THREE from "three";
@@ -361,6 +364,153 @@ function bootstrapShaders() {
361
364
  ThreeShaderChunk["utils/defaultLighting"] = default_lighting_glsl_default;
362
365
  }
363
366
 
367
+ // src/renderer/texture-loader.ts
368
+ import * as THREE2 from "three";
369
+ var VIDEO_EXTENSIONS = /\.(mp4|webm|mov|ogg)(\?|$)/i;
370
+ function isVideoSource(layer) {
371
+ if (layer.type) return layer.type === "video";
372
+ return VIDEO_EXTENSIONS.test(layer.src);
373
+ }
374
+ var transparentTexture = null;
375
+ function getTransparentTexture() {
376
+ if (!transparentTexture) {
377
+ const data = new Uint8Array([0, 0, 0, 0]);
378
+ transparentTexture = new THREE2.DataTexture(data, 1, 1, THREE2.RGBAFormat);
379
+ transparentTexture.needsUpdate = true;
380
+ }
381
+ return transparentTexture;
382
+ }
383
+ var pendingVideos = /* @__PURE__ */ new Set();
384
+ function loadLayerTexture(layer, onError) {
385
+ if (isVideoSource(layer)) {
386
+ if (layer.freeze != null) {
387
+ return loadFrozenVideoTexture(layer.src, layer.freeze === "random" ? -1 : layer.freeze, onError);
388
+ }
389
+ return loadVideoTexture(layer.src, onError);
390
+ }
391
+ return loadImageTexture(layer.src, onError);
392
+ }
393
+ var inflight = /* @__PURE__ */ new Map();
394
+ function loadImageTexture(src, onError) {
395
+ let pending = inflight.get(src);
396
+ if (!pending) {
397
+ pending = new Promise((resolve) => {
398
+ const loader = new THREE2.TextureLoader();
399
+ loader.load(
400
+ src,
401
+ (texture) => {
402
+ inflight.delete(src);
403
+ resolve(texture);
404
+ },
405
+ void 0,
406
+ () => {
407
+ inflight.delete(src);
408
+ onError?.(new Error(`Failed to load image texture: ${src}`));
409
+ resolve(getTransparentTexture());
410
+ }
411
+ );
412
+ });
413
+ inflight.set(src, pending);
414
+ }
415
+ return pending.then((texture) => ({ texture }));
416
+ }
417
+ function loadVideoTexture(src, onError) {
418
+ return new Promise((resolve) => {
419
+ const video = document.createElement("video");
420
+ video.src = src;
421
+ video.muted = true;
422
+ video.loop = true;
423
+ video.playsInline = true;
424
+ video.preload = "auto";
425
+ video.style.position = "fixed";
426
+ video.style.width = "1px";
427
+ video.style.height = "1px";
428
+ video.style.opacity = "0.01";
429
+ video.style.pointerEvents = "none";
430
+ video.style.zIndex = "-9999";
431
+ document.body.appendChild(video);
432
+ pendingVideos.add(video);
433
+ video.addEventListener("error", () => {
434
+ pendingVideos.delete(video);
435
+ const msg = video.error?.message ?? "Unknown video error";
436
+ onError?.(new Error(`Failed to load video texture: ${src} (${msg})`));
437
+ video.remove();
438
+ resolve({ texture: getTransparentTexture() });
439
+ }, { once: true });
440
+ video.play().then(() => {
441
+ pendingVideos.delete(video);
442
+ const texture = new THREE2.VideoTexture(video);
443
+ resolve({ texture, videoEl: video });
444
+ }).catch(() => {
445
+ pendingVideos.delete(video);
446
+ onError?.(new Error(`Video autoplay blocked: ${src}`));
447
+ video.remove();
448
+ resolve({ texture: getTransparentTexture() });
449
+ });
450
+ });
451
+ }
452
+ function loadFrozenVideoTexture(src, freezeTime, onError) {
453
+ return new Promise((resolve) => {
454
+ const video = document.createElement("video");
455
+ video.src = src;
456
+ video.muted = true;
457
+ video.playsInline = true;
458
+ video.preload = "auto";
459
+ video.style.position = "fixed";
460
+ video.style.width = "1px";
461
+ video.style.height = "1px";
462
+ video.style.opacity = "0.01";
463
+ video.style.pointerEvents = "none";
464
+ video.style.zIndex = "-9999";
465
+ document.body.appendChild(video);
466
+ pendingVideos.add(video);
467
+ video.addEventListener("error", () => {
468
+ pendingVideos.delete(video);
469
+ const msg = video.error?.message ?? "Unknown video error";
470
+ onError?.(new Error(`Failed to load video texture: ${src} (${msg})`));
471
+ video.remove();
472
+ resolve({ texture: getTransparentTexture() });
473
+ }, { once: true });
474
+ video.addEventListener("loadeddata", () => {
475
+ const time = freezeTime < 0 ? Math.random() * (video.duration || 1) : Math.min(freezeTime, video.duration || 0);
476
+ video.currentTime = time;
477
+ }, { once: true });
478
+ video.addEventListener("seeked", () => {
479
+ const canvas = document.createElement("canvas");
480
+ canvas.width = video.videoWidth;
481
+ canvas.height = video.videoHeight;
482
+ const ctx = canvas.getContext("2d");
483
+ ctx.drawImage(video, 0, 0);
484
+ const texture = new THREE2.CanvasTexture(canvas);
485
+ texture.needsUpdate = true;
486
+ pendingVideos.delete(video);
487
+ video.pause();
488
+ video.src = "";
489
+ video.remove();
490
+ resolve({ texture });
491
+ }, { once: true });
492
+ video.load();
493
+ });
494
+ }
495
+ function disposeLoadedLayer(loaded) {
496
+ if (loaded.videoEl) {
497
+ loaded.videoEl.pause();
498
+ loaded.videoEl.src = "";
499
+ loaded.videoEl.remove();
500
+ }
501
+ if (loaded.texture !== getTransparentTexture()) {
502
+ loaded.texture.dispose();
503
+ }
504
+ }
505
+ function cleanupPendingVideos() {
506
+ for (const video of pendingVideos) {
507
+ video.pause();
508
+ video.src = "";
509
+ video.remove();
510
+ }
511
+ pendingVideos.clear();
512
+ }
513
+
364
514
  // src/shaders/common.vert.ts
365
515
  var common_vert_default = glsl`
366
516
  // three.js built-in attributes
@@ -409,8 +559,9 @@ uniform vec2 uMouse;
409
559
  uniform vec2 uMove;
410
560
  uniform vec2 uRotate;
411
561
  uniform float uTime;
412
- uniform sampler2D uImgBase;
413
- uniform sampler2D uImgPopup;
562
+ uniform sampler2D uLayer0;
563
+ uniform sampler2D uLayer1;
564
+ uniform int uLayerCount;
414
565
 
415
566
  varying vec2 vUv;
416
567
 
@@ -418,12 +569,12 @@ varying vec2 vUv;
418
569
 
419
570
  void main() {
420
571
  // 기본 카드 텍스처 샘플링
421
- vec4 baseColor = texture2D(uImgBase, vUv);
572
+ vec4 baseColor = texture2D(uLayer0, vUv);
422
573
 
423
574
  // 팝업 텍스처 샘플링 (움직임 반영)
424
575
  float popupOffset = 0.0;
425
576
  vec2 popupUv = vUv + (uMove / uResolution) * popupOffset; // 팝업 이동 효과
426
- vec4 popupColor = texture2D(uImgPopup, popupUv);
577
+ vec4 popupColor = texture2D(uLayer1, popupUv);
427
578
 
428
579
  float aSoft = smoothstep(0.4, 1.0, popupColor.a);
429
580
  vec4 pocaColor = mix(baseColor, popupColor, aSoft);
@@ -443,8 +594,9 @@ uniform vec2 uMouse;
443
594
  uniform vec2 uMove;
444
595
  uniform vec2 uRotate;
445
596
  uniform float uTime;
446
- uniform sampler2D uImgBase;
447
- uniform sampler2D uImgPopup;
597
+ uniform sampler2D uLayer0;
598
+ uniform sampler2D uLayer1;
599
+ uniform int uLayerCount;
448
600
 
449
601
  varying vec2 vUv;
450
602
 
@@ -452,12 +604,12 @@ varying vec2 vUv;
452
604
 
453
605
  void main() {
454
606
  // 기본 카드 텍스처 샘플링
455
- vec4 baseColor = texture2D(uImgBase, vUv);
607
+ vec4 baseColor = texture2D(uLayer0, vUv);
456
608
 
457
609
  // 팝업 텍스처 샘플링 (움직임 반영)
458
610
  vec2 popupOffset = vec2(-uRotate.x * 0.08, -uRotate.y * 0.06);
459
611
  vec2 popupUv = vUv + popupOffset; // 팝업 이동 효과
460
- vec4 popupColor = texture2D(uImgPopup, popupUv);
612
+ vec4 popupColor = texture2D(uLayer1, popupUv);
461
613
 
462
614
  float aSoft = smoothstep(0.4, 1.0, popupColor.a);
463
615
  vec4 pocaColor = mix(baseColor, popupColor, aSoft);
@@ -477,8 +629,9 @@ uniform vec2 uMouse;
477
629
  uniform vec2 uMove;
478
630
  uniform vec2 uRotate;
479
631
  uniform float uTime;
480
- uniform sampler2D uImgBase;
481
- uniform sampler2D uImgPopup;
632
+ uniform sampler2D uLayer0;
633
+ uniform sampler2D uLayer1;
634
+ uniform int uLayerCount;
482
635
 
483
636
  varying vec2 vUv;
484
637
 
@@ -567,7 +720,7 @@ float computeSnow(in vec2 uv, int layerStart, int layerEnd) {
567
720
  }
568
721
 
569
722
  void main() {
570
- vec4 baseTexture = texture2D(uImgBase, vUv);
723
+ vec4 baseTexture = texture2D(uLayer0, vUv);
571
724
  gl_FragColor = mix(gl_FragColor, baseTexture, baseTexture.a);
572
725
 
573
726
  float snowEffect = computeSnow(vUv, 5, 15);
@@ -575,7 +728,7 @@ void main() {
575
728
 
576
729
  float popupMoveOffset = 0.0;
577
730
  vec2 popupOffsetUv = vUv + (uMove.xy / uResolution.xy) * popupMoveOffset;
578
- vec4 popupTexture = texture2D(uImgPopup, popupOffsetUv);
731
+ vec4 popupTexture = texture2D(uLayer1, popupOffsetUv);
579
732
  gl_FragColor = mix(gl_FragColor, popupTexture, popupTexture.a);
580
733
 
581
734
  snowEffect = computeSnow(vUv, 16, 20);
@@ -594,8 +747,9 @@ uniform vec2 uMouse;
594
747
  uniform vec2 uMove;
595
748
  uniform vec2 uRotate;
596
749
  uniform float uTime;
597
- uniform sampler2D uImgBase;
598
- uniform sampler2D uImgPopup;
750
+ uniform sampler2D uLayer0;
751
+ uniform sampler2D uLayer1;
752
+ uniform int uLayerCount;
599
753
 
600
754
  varying vec2 vUv;
601
755
 
@@ -684,7 +838,7 @@ float computeSnow(in vec2 uv, int layerStart, int layerEnd) {
684
838
  }
685
839
 
686
840
  void main() {
687
- vec4 baseTexture = texture2D(uImgBase, vUv);
841
+ vec4 baseTexture = texture2D(uLayer0, vUv);
688
842
  gl_FragColor = mix(gl_FragColor, baseTexture, baseTexture.a);
689
843
 
690
844
  float snowEffect = computeSnow(vUv, 5, 15);
@@ -693,7 +847,7 @@ void main() {
693
847
  // 3D parallax offset for popup layer
694
848
  vec2 popupOffset = vec2(-uRotate.x * 0.08, -uRotate.y * 0.06);
695
849
  vec2 popupOffsetUv = vUv + popupOffset;
696
- vec4 popupTexture = texture2D(uImgPopup, popupOffsetUv);
850
+ vec4 popupTexture = texture2D(uLayer1, popupOffsetUv);
697
851
  gl_FragColor = mix(gl_FragColor, popupTexture, popupTexture.a);
698
852
 
699
853
  snowEffect = computeSnow(vUv, 16, 20);
@@ -712,8 +866,9 @@ uniform vec2 uMouse;
712
866
  uniform vec2 uMove;
713
867
  uniform vec2 uRotate;
714
868
  uniform float uTime;
715
- uniform sampler2D uImgBase;
716
- uniform sampler2D uImgPopup;
869
+ uniform sampler2D uLayer0;
870
+ uniform sampler2D uLayer1;
871
+ uniform int uLayerCount;
717
872
 
718
873
  varying vec2 vUv;
719
874
 
@@ -727,8 +882,8 @@ void main() {
727
882
  vec2 pixel = 1.0 / uResolution.xy;
728
883
  vec2 popUpUv = vUv + (uMove.xy / uResolution.xy) * 0.008;
729
884
 
730
- vec4 baseColor = kuwahara(uImgBase, vUv, pixel, 4.0);
731
- vec4 popupColor = kuwahara(uImgPopup, popUpUv, pixel, 4.0);
885
+ vec4 baseColor = kuwahara(uLayer0, vUv, pixel, 4.0);
886
+ vec4 popupColor = kuwahara(uLayer1, popUpUv, pixel, 4.0);
732
887
 
733
888
  float aSoft = smoothstep(0.4, 1.0, popupColor.a);
734
889
  vec4 pocaColor = mix(baseColor, popupColor, aSoft);
@@ -748,8 +903,9 @@ uniform vec2 uMouse;
748
903
  uniform vec2 uMove;
749
904
  uniform vec2 uRotate;
750
905
  uniform float uTime;
751
- uniform sampler2D uImgBase;
752
- uniform sampler2D uImgPopup;
906
+ uniform sampler2D uLayer0;
907
+ uniform sampler2D uLayer1;
908
+ uniform int uLayerCount;
753
909
 
754
910
  varying vec2 vUv;
755
911
 
@@ -766,8 +922,8 @@ void main() {
766
922
  vec2 popupOffset = vec2(-uRotate.x * 0.08, -uRotate.y * 0.06);
767
923
  vec2 popUpUv = vUv + popupOffset;
768
924
 
769
- vec4 baseColor = kuwahara(uImgBase, vUv, pixel, 4.0);
770
- vec4 popupColor = kuwahara(uImgPopup, popUpUv, pixel, 4.0);
925
+ vec4 baseColor = kuwahara(uLayer0, vUv, pixel, 4.0);
926
+ vec4 popupColor = kuwahara(uLayer1, popUpUv, pixel, 4.0);
771
927
 
772
928
  float aSoft = smoothstep(0.4, 1.0, popupColor.a);
773
929
  vec4 pocaColor = mix(baseColor, popupColor, aSoft);
@@ -787,8 +943,9 @@ uniform vec2 uMouse;
787
943
  uniform vec2 uMove;
788
944
  uniform vec2 uRotate;
789
945
  uniform float uTime;
790
- uniform sampler2D uImgBase;
791
- uniform sampler2D uImgPopup;
946
+ uniform sampler2D uLayer0;
947
+ uniform sampler2D uLayer1;
948
+ uniform int uLayerCount;
792
949
 
793
950
  varying vec2 vUv;
794
951
 
@@ -803,8 +960,8 @@ void main() {
803
960
  vec2 pixel = 1.0 / uResolution.xy;
804
961
  vec2 popUpUv = vUv + (uMove.xy / uResolution.xy) * 0.008;
805
962
 
806
- vec4 baseColor = gaussianBlur(uImgBase, vUv, pixel, 12);
807
- vec4 popupColor = texture2D(uImgPopup, popUpUv);
963
+ vec4 baseColor = gaussianBlur(uLayer0, vUv, pixel, 12);
964
+ vec4 popupColor = texture2D(uLayer1, popUpUv);
808
965
 
809
966
  float aSoft = smoothstep(0.4, 1.0, popupColor.a);
810
967
  vec4 pocaColor = mix(baseColor, popupColor, aSoft);
@@ -824,8 +981,9 @@ uniform vec2 uMouse;
824
981
  uniform vec2 uMove;
825
982
  uniform vec2 uRotate;
826
983
  uniform float uTime;
827
- uniform sampler2D uImgBase;
828
- uniform sampler2D uImgPopup;
984
+ uniform sampler2D uLayer0;
985
+ uniform sampler2D uLayer1;
986
+ uniform int uLayerCount;
829
987
 
830
988
  varying vec2 vUv;
831
989
 
@@ -843,8 +1001,8 @@ void main() {
843
1001
  vec2 popupOffset = vec2(-uRotate.x * 0.08, -uRotate.y * 0.06);
844
1002
  vec2 popUpUv = vUv + popupOffset;
845
1003
 
846
- vec4 baseColor = gaussianBlur(uImgBase, vUv, pixel, 12);
847
- vec4 popupColor = texture2D(uImgPopup, popUpUv);
1004
+ vec4 baseColor = gaussianBlur(uLayer0, vUv, pixel, 12);
1005
+ vec4 popupColor = texture2D(uLayer1, popUpUv);
848
1006
 
849
1007
  float aSoft = smoothstep(0.4, 1.0, popupColor.a);
850
1008
  vec4 pocaColor = mix(baseColor, popupColor, aSoft);
@@ -864,8 +1022,9 @@ uniform vec2 uMouse;
864
1022
  uniform vec2 uMove;
865
1023
  uniform vec2 uRotate;
866
1024
  uniform float uTime;
867
- uniform sampler2D uImgBase;
868
- uniform sampler2D uImgPopup;
1025
+ uniform sampler2D uLayer0;
1026
+ uniform sampler2D uLayer1;
1027
+ uniform int uLayerCount;
869
1028
 
870
1029
  varying vec2 vUv;
871
1030
 
@@ -879,8 +1038,8 @@ vec3 hsv2rgb(vec3 c) {
879
1038
  }
880
1039
 
881
1040
  void main() {
882
- vec4 baseColor = texture2D(uImgBase, vUv);
883
- vec4 popupColor = texture2D(uImgPopup, vUv);
1041
+ vec4 baseColor = texture2D(uLayer0, vUv);
1042
+ vec4 popupColor = texture2D(uLayer1, vUv);
884
1043
 
885
1044
  // Iridescence on base only
886
1045
  float angle = uRotate.x * 2.0 + uRotate.y * 1.5;
@@ -911,8 +1070,9 @@ uniform vec2 uMouse;
911
1070
  uniform vec2 uMove;
912
1071
  uniform vec2 uRotate;
913
1072
  uniform float uTime;
914
- uniform sampler2D uImgBase;
915
- uniform sampler2D uImgPopup;
1073
+ uniform sampler2D uLayer0;
1074
+ uniform sampler2D uLayer1;
1075
+ uniform int uLayerCount;
916
1076
 
917
1077
  varying vec2 vUv;
918
1078
 
@@ -926,12 +1086,12 @@ vec3 hsv2rgb(vec3 c) {
926
1086
  }
927
1087
 
928
1088
  void main() {
929
- vec4 baseColor = texture2D(uImgBase, vUv);
1089
+ vec4 baseColor = texture2D(uLayer0, vUv);
930
1090
 
931
1091
  // 3D parallax offset for popup layer
932
1092
  vec2 popupOffset = vec2(-uRotate.x * 0.08, -uRotate.y * 0.06);
933
1093
  vec2 popupUv = vUv + popupOffset;
934
- vec4 popupColor = texture2D(uImgPopup, popupUv);
1094
+ vec4 popupColor = texture2D(uLayer1, popupUv);
935
1095
 
936
1096
  // Iridescence on base only
937
1097
  float angle = uRotate.x * 2.0 + uRotate.y * 1.5;
@@ -951,7 +1111,7 @@ void main() {
951
1111
  }
952
1112
  `;
953
1113
 
954
- // src/renderer/index.ts
1114
+ // src/renderer/face-renderer.ts
955
1115
  var FRAG_SHADERS = {
956
1116
  "glare": glare_frag_default,
957
1117
  "glare-3d": glare_3d_frag_default,
@@ -964,21 +1124,111 @@ var FRAG_SHADERS = {
964
1124
  "holo": holo_frag_default,
965
1125
  "holo-3d": holo_3d_frag_default
966
1126
  };
1127
+ var MAX_LAYERS = 8;
1128
+ var FaceRenderer = class {
1129
+ constructor(shader, width, height) {
1130
+ this.loadedLayers = [];
1131
+ bootstrapShaders();
1132
+ this.scene = new THREE3.Scene();
1133
+ this.camera = new THREE3.Camera();
1134
+ const uniforms = this.createUniforms(width, height);
1135
+ const rawFrag = shader ?? FRAG_SHADERS["glare"];
1136
+ const fragmentShader = resolveIncludes(rawFrag);
1137
+ this.material = new THREE3.ShaderMaterial({
1138
+ vertexShader: resolveIncludes(common_vert_default),
1139
+ fragmentShader,
1140
+ uniforms,
1141
+ transparent: true
1142
+ });
1143
+ const geometry = new THREE3.PlaneGeometry(2, 2);
1144
+ this.mesh = new THREE3.Mesh(geometry, this.material);
1145
+ this.scene.add(this.mesh);
1146
+ }
1147
+ createUniforms(width, height) {
1148
+ const uniforms = {
1149
+ uTime: { value: 0 },
1150
+ uResolution: { value: new THREE3.Vector2(width, height) },
1151
+ uMouse: { value: new THREE3.Vector2(0, 0) },
1152
+ uMove: { value: new THREE3.Vector2(0, 0) },
1153
+ uRotate: { value: new THREE3.Vector2(0, 0) },
1154
+ uCardOpacity: { value: 1 },
1155
+ uLayerCount: { value: 0 }
1156
+ };
1157
+ const transparent = getTransparentTexture();
1158
+ for (let i = 0; i < MAX_LAYERS; i++) {
1159
+ uniforms[`uLayer${i}`] = { value: transparent };
1160
+ }
1161
+ return uniforms;
1162
+ }
1163
+ async loadLayers(layers, onError) {
1164
+ this.disposeLayers();
1165
+ const count = Math.min(layers.length, MAX_LAYERS);
1166
+ this.material.uniforms.uLayerCount.value = count;
1167
+ const promises = layers.slice(0, MAX_LAYERS).map(
1168
+ (layer) => loadLayerTexture(layer, onError)
1169
+ );
1170
+ const results = await Promise.all(promises);
1171
+ this.loadedLayers = results;
1172
+ for (let i = 0; i < results.length; i++) {
1173
+ this.material.uniforms[`uLayer${i}`].value = results[i].texture;
1174
+ }
1175
+ }
1176
+ updateUniforms(updates) {
1177
+ const u = this.material.uniforms;
1178
+ if (updates.rotate) u.uRotate.value.set(
1179
+ updates.rotate.x * (Math.PI / 180),
1180
+ updates.rotate.y * (Math.PI / 180)
1181
+ );
1182
+ if (updates.mouse) u.uMouse.value.set(updates.mouse.x, updates.mouse.y);
1183
+ if (updates.move) u.uMove.value.set(updates.move.x, updates.move.y);
1184
+ if (updates.opacity !== void 0) u.uCardOpacity.value = updates.opacity;
1185
+ if (updates.time !== void 0) u.uTime.value = updates.time;
1186
+ }
1187
+ updateResolution(width, height) {
1188
+ this.material.uniforms.uResolution.value.set(width, height);
1189
+ }
1190
+ updateShader(fragmentShader) {
1191
+ this.material.fragmentShader = resolveIncludes(fragmentShader);
1192
+ this.material.needsUpdate = true;
1193
+ }
1194
+ disposeLayers() {
1195
+ for (const loaded of this.loadedLayers) {
1196
+ disposeLoadedLayer(loaded);
1197
+ }
1198
+ this.loadedLayers = [];
1199
+ const transparent = getTransparentTexture();
1200
+ for (let i = 0; i < MAX_LAYERS; i++) {
1201
+ this.material.uniforms[`uLayer${i}`].value = transparent;
1202
+ }
1203
+ this.material.uniforms.uLayerCount.value = 0;
1204
+ }
1205
+ destroy() {
1206
+ this.disposeLayers();
1207
+ this.mesh.geometry.dispose();
1208
+ this.material.dispose();
1209
+ }
1210
+ };
1211
+
1212
+ // src/renderer/index.ts
967
1213
  var Renderer = class {
968
1214
  constructor(container, options, onError, onReady) {
969
1215
  this.container = container;
970
1216
  this.options = options;
971
1217
  this.onError = onError;
972
1218
  this.onReady = onReady;
973
- this.scene = null;
974
- this.camera = null;
975
- this.webglRenderer = null;
976
- this.material = null;
977
- this.mesh = null;
978
- this.clock = new THREE2.Clock();
1219
+ this.backFace = null;
1220
+ this.frontRenderer = null;
1221
+ this.backRenderer = null;
1222
+ this.clock = new THREE4.Clock();
979
1223
  this.rafId = null;
980
- this.textures = [];
1224
+ this.backCanvas = null;
981
1225
  this.resizeObserver = null;
1226
+ this.intersectionObserver = null;
1227
+ this.visible = false;
1228
+ this.destroyed = false;
1229
+ if (!options.front?.layers?.length) {
1230
+ console.warn("[pocato] front.layers must have at least 1 element");
1231
+ }
982
1232
  this.injectStyles();
983
1233
  this.cardEl = document.createElement("div");
984
1234
  this.cardEl.className = "pocato-card pocato-loading";
@@ -986,29 +1236,133 @@ var Renderer = class {
986
1236
  this.rotatorEl.className = "pocato-rotator";
987
1237
  this.backEl = document.createElement("div");
988
1238
  this.backEl.className = "pocato-back";
989
- if (options.backImage) {
990
- const backImg = document.createElement("img");
991
- backImg.src = options.backImage;
992
- backImg.className = "pocato-back-img";
993
- this.backEl.appendChild(backImg);
994
- }
995
1239
  this.frontEl = document.createElement("div");
996
1240
  this.frontEl.className = "pocato-front";
997
- this.canvas = document.createElement("canvas");
998
- this.canvas.className = "pocato-canvas";
1241
+ this.frontCanvas = document.createElement("canvas");
1242
+ this.frontCanvas.className = "pocato-canvas";
999
1243
  this.frontContentEl = document.createElement("div");
1000
1244
  this.frontContentEl.className = "pocato-content pocato-front-content";
1001
1245
  this.backContentEl = document.createElement("div");
1002
1246
  this.backContentEl.className = "pocato-content pocato-back-content";
1003
- this.frontEl.appendChild(this.canvas);
1247
+ this.frontEl.appendChild(this.frontCanvas);
1004
1248
  this.frontEl.appendChild(this.frontContentEl);
1005
1249
  this.backEl.appendChild(this.backContentEl);
1006
1250
  this.rotatorEl.appendChild(this.backEl);
1007
1251
  this.rotatorEl.appendChild(this.frontEl);
1008
1252
  this.cardEl.appendChild(this.rotatorEl);
1009
1253
  this.container.appendChild(this.cardEl);
1010
- bootstrapShaders();
1011
- this.init();
1254
+ const { width, height } = this.container.getBoundingClientRect();
1255
+ const dpr = window.devicePixelRatio || 1;
1256
+ const pixelWidth = Math.floor(width * dpr);
1257
+ const pixelHeight = Math.floor(height * dpr);
1258
+ this.frontCanvas.width = pixelWidth;
1259
+ this.frontCanvas.height = pixelHeight;
1260
+ this.frontFace = new FaceRenderer(
1261
+ options.front.shader,
1262
+ pixelWidth,
1263
+ pixelHeight
1264
+ );
1265
+ this.backFace = new FaceRenderer(
1266
+ this.options.back?.shader,
1267
+ pixelWidth,
1268
+ pixelHeight
1269
+ );
1270
+ this.backCanvas = document.createElement("canvas");
1271
+ this.backCanvas.className = "pocato-canvas";
1272
+ this.backCanvas.width = pixelWidth;
1273
+ this.backCanvas.height = pixelHeight;
1274
+ this.backEl.insertBefore(this.backCanvas, this.backEl.firstChild);
1275
+ this.loadAllLayers();
1276
+ this.setupResizeObserver();
1277
+ this.setupIntersectionObserver();
1278
+ requestAnimationFrame(() => {
1279
+ this.cardEl.classList.remove("pocato-loading");
1280
+ this.cardEl.classList.add("pocato-ready");
1281
+ });
1282
+ }
1283
+ createFrontRenderer() {
1284
+ if (this.frontRenderer) return;
1285
+ const newCanvas = document.createElement("canvas");
1286
+ newCanvas.className = "pocato-canvas";
1287
+ this.frontEl.replaceChild(newCanvas, this.frontCanvas);
1288
+ this.frontCanvas = newCanvas;
1289
+ this.frontRenderer = new THREE4.WebGLRenderer({
1290
+ canvas: this.frontCanvas,
1291
+ alpha: true
1292
+ });
1293
+ this.frontRenderer.setPixelRatio(window.devicePixelRatio);
1294
+ const { width, height } = this.container.getBoundingClientRect();
1295
+ this.frontRenderer.setSize(width, height);
1296
+ }
1297
+ createBackRenderer() {
1298
+ if (this.backRenderer || !this.backCanvas || !this.backFace) return;
1299
+ const newCanvas = document.createElement("canvas");
1300
+ newCanvas.className = "pocato-canvas";
1301
+ this.backEl.replaceChild(newCanvas, this.backCanvas);
1302
+ this.backCanvas = newCanvas;
1303
+ this.backRenderer = new THREE4.WebGLRenderer({
1304
+ canvas: this.backCanvas,
1305
+ alpha: true
1306
+ });
1307
+ this.backRenderer.setPixelRatio(window.devicePixelRatio);
1308
+ const { width, height } = this.container.getBoundingClientRect();
1309
+ this.backRenderer.setSize(width, height);
1310
+ }
1311
+ disposeFrontRenderer() {
1312
+ if (!this.frontRenderer) return;
1313
+ this.frontRenderer.render(this.frontFace.scene, this.frontFace.camera);
1314
+ this.frontRenderer.dispose();
1315
+ this.frontRenderer = null;
1316
+ }
1317
+ disposeBackRenderer() {
1318
+ if (!this.backRenderer) return;
1319
+ if (this.backFace) {
1320
+ this.backRenderer.render(this.backFace.scene, this.backFace.camera);
1321
+ }
1322
+ this.backRenderer.dispose();
1323
+ this.backRenderer = null;
1324
+ }
1325
+ activate() {
1326
+ if (this.visible) return;
1327
+ this.visible = true;
1328
+ this.createFrontRenderer();
1329
+ if (this.backFace) this.createBackRenderer();
1330
+ this.clock.getDelta();
1331
+ this.startRenderLoop();
1332
+ }
1333
+ deactivate() {
1334
+ if (!this.visible) return;
1335
+ this.visible = false;
1336
+ this.stopRenderLoop();
1337
+ this.disposeFrontRenderer();
1338
+ this.disposeBackRenderer();
1339
+ }
1340
+ setupIntersectionObserver() {
1341
+ this.intersectionObserver = new IntersectionObserver(
1342
+ (entries) => {
1343
+ const entry = entries[0];
1344
+ if (!entry || this.destroyed) return;
1345
+ if (entry.isIntersecting) {
1346
+ this.activate();
1347
+ } else {
1348
+ this.deactivate();
1349
+ }
1350
+ },
1351
+ { rootMargin: "100px" }
1352
+ // activate slightly before entering viewport
1353
+ );
1354
+ this.intersectionObserver.observe(this.cardEl);
1355
+ }
1356
+ async loadAllLayers() {
1357
+ const promises = [];
1358
+ if (this.options.front?.layers?.length) {
1359
+ promises.push(this.frontFace.loadLayers(this.options.front.layers, this.onError));
1360
+ }
1361
+ if (this.backFace && this.options.back?.layers?.length) {
1362
+ promises.push(this.backFace.loadLayers(this.options.back.layers, this.onError));
1363
+ }
1364
+ await Promise.all(promises);
1365
+ this.onReady?.();
1012
1366
  }
1013
1367
  injectStyles() {
1014
1368
  const id = "pocato-styles";
@@ -1067,12 +1421,6 @@ var Renderer = class {
1067
1421
  overflow: hidden;
1068
1422
  background-color: #1a1a2e;
1069
1423
  }
1070
- .pocato-back-img {
1071
- width: 100%;
1072
- height: 100%;
1073
- object-fit: cover;
1074
- pointer-events: none;
1075
- }
1076
1424
  .pocato-canvas {
1077
1425
  width: 100%;
1078
1426
  height: 100%;
@@ -1092,134 +1440,83 @@ var Renderer = class {
1092
1440
  `;
1093
1441
  document.head.appendChild(style);
1094
1442
  }
1095
- init() {
1096
- const { width, height } = this.container.getBoundingClientRect();
1097
- this.scene = new THREE2.Scene();
1098
- this.camera = new THREE2.Camera();
1099
- this.webglRenderer = new THREE2.WebGLRenderer({
1100
- canvas: this.canvas,
1101
- alpha: true
1102
- });
1103
- this.webglRenderer.setPixelRatio(window.devicePixelRatio);
1104
- this.webglRenderer.setSize(width, height);
1105
- const pixelWidth = this.canvas.width;
1106
- const pixelHeight = this.canvas.height;
1107
- const uniforms = this.createUniforms(pixelWidth, pixelHeight);
1108
- const rawFragmentShader = this.options.customShader ?? FRAG_SHADERS[this.options.type];
1109
- const fragmentShader = resolveIncludes(rawFragmentShader);
1110
- this.material = new THREE2.ShaderMaterial({
1111
- vertexShader: resolveIncludes(common_vert_default),
1112
- fragmentShader,
1113
- uniforms,
1114
- transparent: true
1115
- });
1116
- const geometry = new THREE2.PlaneGeometry(2, 2);
1117
- this.mesh = new THREE2.Mesh(geometry, this.material);
1118
- this.scene.add(this.mesh);
1119
- this.loadTextures();
1120
- this.setupResizeObserver();
1121
- this.startRenderLoop();
1122
- requestAnimationFrame(() => {
1123
- this.cardEl.classList.remove("pocato-loading");
1124
- this.cardEl.classList.add("pocato-ready");
1125
- });
1126
- }
1127
- createUniforms(width, height) {
1128
- return {
1129
- uTime: { value: 0 },
1130
- uResolution: { value: new THREE2.Vector2(width, height) },
1131
- uMouse: { value: new THREE2.Vector2(0, 0) },
1132
- uMove: { value: new THREE2.Vector2(0, 0) },
1133
- uRotate: { value: new THREE2.Vector2(0, 0) },
1134
- uCardOpacity: { value: 1 },
1135
- uImgBase: { value: null },
1136
- uImgPopup: { value: null },
1137
- uImgMask: { value: null }
1138
- };
1139
- }
1140
- loadTextures() {
1141
- const loader = new THREE2.TextureLoader();
1142
- const load = (url, uniform) => {
1143
- if (!url) return;
1144
- loader.load(
1145
- url,
1146
- (texture) => {
1147
- this.textures.push(texture);
1148
- if (this.material) {
1149
- this.material.uniforms[uniform].value = texture;
1150
- }
1151
- },
1152
- void 0,
1153
- () => {
1154
- this.onError?.(new Error(`Failed to load texture: ${url}`));
1155
- }
1156
- );
1157
- };
1158
- load(this.options.baseImage, "uImgBase");
1159
- load(this.options.popupImage, "uImgPopup");
1160
- load(this.options.maskImage, "uImgMask");
1161
- this.onReady?.();
1162
- }
1163
1443
  setupResizeObserver() {
1164
1444
  this.resizeObserver = new ResizeObserver((entries) => {
1165
1445
  const entry = entries[0];
1166
1446
  if (!entry) return;
1167
1447
  const { width, height } = entry.contentRect;
1168
1448
  if (width === 0 || height === 0) return;
1169
- this.webglRenderer?.setSize(width, height);
1170
- if (this.material) {
1171
- this.material.uniforms.uResolution.value.set(this.canvas.width, this.canvas.height);
1449
+ if (this.frontRenderer) {
1450
+ this.frontRenderer.setSize(width, height);
1451
+ }
1452
+ this.frontFace.updateResolution(this.frontCanvas.width, this.frontCanvas.height);
1453
+ if (this.backFace && this.backCanvas) {
1454
+ if (this.backRenderer) {
1455
+ this.backRenderer.setSize(width, height);
1456
+ }
1457
+ this.backFace.updateResolution(this.backCanvas.width, this.backCanvas.height);
1172
1458
  }
1173
1459
  });
1174
1460
  this.resizeObserver.observe(this.container);
1175
1461
  }
1176
1462
  startRenderLoop() {
1463
+ if (this.rafId !== null) return;
1177
1464
  const animate = () => {
1465
+ if (!this.visible || this.destroyed) return;
1178
1466
  this.rafId = requestAnimationFrame(animate);
1179
- if (!this.material || !this.scene || !this.camera || !this.webglRenderer) return;
1180
1467
  const delta = this.clock.getDelta();
1181
- this.material.uniforms.uTime.value += delta;
1182
- this.webglRenderer.render(this.scene, this.camera);
1468
+ this.frontFace.material.uniforms.uTime.value += delta;
1469
+ if (this.frontRenderer) {
1470
+ this.frontRenderer.render(this.frontFace.scene, this.frontFace.camera);
1471
+ }
1472
+ if (this.backFace && this.backRenderer) {
1473
+ this.backFace.material.uniforms.uTime.value += delta;
1474
+ this.backRenderer.render(this.backFace.scene, this.backFace.camera);
1475
+ }
1183
1476
  };
1184
1477
  this.rafId = requestAnimationFrame(animate);
1185
1478
  }
1479
+ stopRenderLoop() {
1480
+ if (this.rafId !== null) {
1481
+ cancelAnimationFrame(this.rafId);
1482
+ this.rafId = null;
1483
+ }
1484
+ }
1186
1485
  updateUniforms(updates) {
1187
- if (!this.material) return;
1188
- const u = this.material.uniforms;
1189
1486
  if (updates.rotate) {
1190
1487
  this.rotatorEl.style.setProperty("--pocato-rotate-x", `${updates.rotate.x}deg`);
1191
1488
  this.rotatorEl.style.setProperty("--pocato-rotate-y", `${updates.rotate.y}deg`);
1192
- u.uRotate.value.set(
1193
- updates.rotate.x * (Math.PI / 180),
1194
- updates.rotate.y * (Math.PI / 180)
1195
- );
1196
1489
  }
1197
- if (updates.mouse) u.uMouse.value.set(updates.mouse.x, updates.mouse.y);
1198
- if (updates.move) u.uMove.value.set(updates.move.x, updates.move.y);
1199
- if (updates.opacity !== void 0) u.uCardOpacity.value = updates.opacity;
1490
+ this.frontFace.updateUniforms({
1491
+ rotate: updates.rotate,
1492
+ mouse: updates.mouse,
1493
+ move: updates.move,
1494
+ opacity: updates.opacity
1495
+ });
1496
+ if (this.backFace) {
1497
+ this.backFace.updateUniforms({
1498
+ rotate: updates.rotate,
1499
+ mouse: updates.mouse,
1500
+ move: updates.move,
1501
+ opacity: updates.opacity
1502
+ });
1503
+ }
1200
1504
  }
1201
1505
  updateShader(fragmentShader) {
1202
- if (!this.material) return;
1203
- this.material.fragmentShader = resolveIncludes(fragmentShader);
1204
- this.material.needsUpdate = true;
1506
+ this.frontFace.updateShader(fragmentShader);
1205
1507
  }
1206
- updateTextures(options) {
1207
- const loader = new THREE2.TextureLoader();
1208
- const load = (url, uniform) => {
1209
- if (!url) return;
1210
- loader.load(url, (texture) => {
1211
- this.textures.push(texture);
1212
- if (this.material) {
1213
- this.material.uniforms[uniform].value = texture;
1214
- }
1215
- });
1216
- };
1217
- if (options.baseImage) load(options.baseImage, "uImgBase");
1218
- if (options.popupImage) load(options.popupImage, "uImgPopup");
1219
- if (options.maskImage) load(options.maskImage, "uImgMask");
1508
+ updateBackShader(fragmentShader) {
1509
+ this.backFace?.updateShader(fragmentShader);
1220
1510
  }
1221
- getContainerRect() {
1222
- return this.container.getBoundingClientRect();
1511
+ async updateLayers(frontLayers, backLayers) {
1512
+ const promises = [];
1513
+ if (frontLayers) {
1514
+ promises.push(this.frontFace.loadLayers(frontLayers, this.onError));
1515
+ }
1516
+ if (backLayers && this.backFace) {
1517
+ promises.push(this.backFace.loadLayers(backLayers, this.onError));
1518
+ }
1519
+ await Promise.all(promises);
1223
1520
  }
1224
1521
  getRotatorEl() {
1225
1522
  return this.rotatorEl;
@@ -1231,20 +1528,20 @@ var Renderer = class {
1231
1528
  return this.backContentEl;
1232
1529
  }
1233
1530
  destroy() {
1234
- if (this.rafId !== null) cancelAnimationFrame(this.rafId);
1531
+ this.destroyed = true;
1532
+ this.stopRenderLoop();
1235
1533
  this.resizeObserver?.disconnect();
1236
- this.textures.forEach((t) => t.dispose());
1237
- this.mesh?.geometry.dispose();
1238
- this.material?.dispose();
1239
- this.webglRenderer?.dispose();
1534
+ this.intersectionObserver?.disconnect();
1535
+ cleanupPendingVideos();
1536
+ this.frontFace.destroy();
1537
+ this.frontRenderer?.dispose();
1538
+ if (this.backFace) {
1539
+ this.backFace.destroy();
1540
+ this.backRenderer?.dispose();
1541
+ }
1240
1542
  if (this.cardEl.parentNode) {
1241
1543
  this.cardEl.parentNode.removeChild(this.cardEl);
1242
1544
  }
1243
- this.scene = null;
1244
- this.camera = null;
1245
- this.webglRenderer = null;
1246
- this.material = null;
1247
- this.mesh = null;
1248
1545
  }
1249
1546
  };
1250
1547
 
@@ -1296,6 +1593,7 @@ function spring(initialValue, onChange, opts = {}) {
1296
1593
  invMass: 1
1297
1594
  };
1298
1595
  let currentResolve = null;
1596
+ let currentTask = null;
1299
1597
  const s = {
1300
1598
  stiffness,
1301
1599
  damping,
@@ -1305,10 +1603,16 @@ function spring(initialValue, onChange, opts = {}) {
1305
1603
  },
1306
1604
  set(value, updateOpts) {
1307
1605
  ctx.target = value;
1606
+ if (currentTask) {
1607
+ tasks.delete(currentTask);
1608
+ currentTask = null;
1609
+ }
1308
1610
  if (updateOpts?.hard) {
1309
1611
  ctx.val = value;
1310
1612
  ctx.lastVal = value;
1311
1613
  onChange(value);
1614
+ currentResolve?.();
1615
+ currentResolve = null;
1312
1616
  return Promise.resolve();
1313
1617
  }
1314
1618
  if (updateOpts?.soft) {
@@ -1317,7 +1621,7 @@ function spring(initialValue, onChange, opts = {}) {
1317
1621
  }
1318
1622
  return new Promise((resolve) => {
1319
1623
  currentResolve = resolve;
1320
- scheduleTask(() => {
1624
+ const task = () => {
1321
1625
  const invMassTarget = 1;
1322
1626
  ctx.invMass = Math.min(ctx.invMass + 0.02, invMassTarget);
1323
1627
  const moving = tickSpring(ctx, s.stiffness, s.damping, s.precision);
@@ -1325,9 +1629,12 @@ function spring(initialValue, onChange, opts = {}) {
1325
1629
  if (!moving) {
1326
1630
  currentResolve?.();
1327
1631
  currentResolve = null;
1632
+ currentTask = null;
1328
1633
  }
1329
1634
  return moving;
1330
- });
1635
+ };
1636
+ currentTask = task;
1637
+ scheduleTask(task);
1331
1638
  });
1332
1639
  }
1333
1640
  };
@@ -1471,6 +1778,7 @@ var InteractionHandler = class {
1471
1778
  this.dblClickTimer = null;
1472
1779
  this.activePointerId = null;
1473
1780
  this._flipSpeed = 1;
1781
+ this._flipId = 0;
1474
1782
  this.flipped = initialFlipped;
1475
1783
  this.springRotate = springVec2({ x: 0, y: 0 }, (v) => {
1476
1784
  callbacks.onRotate(v);
@@ -1615,11 +1923,16 @@ var InteractionHandler = class {
1615
1923
  flip(flipped) {
1616
1924
  this.flipped = flipped ?? !this.flipped;
1617
1925
  const targetX = this.flipped ? 180 : 0;
1926
+ const current = this.springRotate.get();
1927
+ this.springRotate.set({ x: current.x, y: current.y }, { hard: true });
1928
+ const flipId = ++this._flipId;
1618
1929
  this.springRotate.stiffness = SPRING_FLIP.stiffness * this._flipSpeed;
1619
1930
  this.springRotate.damping = SPRING_FLIP.damping;
1620
1931
  this.springRotate.set({ x: targetX, y: 0 }).then(() => {
1621
- this.springRotate.stiffness = SPRING_INTERACT.stiffness;
1622
- this.springRotate.damping = SPRING_INTERACT.damping;
1932
+ if (this._flipId === flipId) {
1933
+ this.springRotate.stiffness = SPRING_INTERACT.stiffness;
1934
+ this.springRotate.damping = SPRING_INTERACT.damping;
1935
+ }
1623
1936
  });
1624
1937
  this.callbacks.onFlip(this.flipped);
1625
1938
  return this.flipped;
@@ -1644,6 +1957,9 @@ var InteractionHandler = class {
1644
1957
  simulateEndInteract() {
1645
1958
  this.endInteract();
1646
1959
  }
1960
+ isInteracting() {
1961
+ return this.interacting;
1962
+ }
1647
1963
  isFlipped() {
1648
1964
  return this.flipped;
1649
1965
  }
@@ -1772,12 +2088,8 @@ var PocaCard = class extends EventEmitter {
1772
2088
  this.renderer = new Renderer(
1773
2089
  container,
1774
2090
  {
1775
- type: options.type,
1776
- baseImage: options.baseImage,
1777
- popupImage: options.popupImage,
1778
- maskImage: options.maskImage,
1779
- backImage: options.backImage,
1780
- customShader: options.customShader
2091
+ front: options.front,
2092
+ back: options.back
1781
2093
  },
1782
2094
  (error) => this.emit("error", error),
1783
2095
  () => this.emit("ready")
@@ -1792,11 +2104,19 @@ var PocaCard = class extends EventEmitter {
1792
2104
  onMoveDelta: (delta) => this.renderer.updateUniforms({ move: delta }),
1793
2105
  onDistFromCenter: (_dist) => {
1794
2106
  },
1795
- onFlip: (flipped) => this.emit("flip", flipped)
2107
+ onFlip: (flipped) => {
2108
+ this.emit("flip", flipped);
2109
+ }
1796
2110
  },
1797
2111
  options.flippable ?? false,
1798
2112
  options.initialFlipped ?? false
1799
2113
  );
2114
+ this.renderer.getRotatorEl().addEventListener("pointerdown", () => {
2115
+ if (this.wiggleController) {
2116
+ this.wiggleController.stop();
2117
+ this.wiggleController = null;
2118
+ }
2119
+ });
1800
2120
  if (options.flipSpeed != null) {
1801
2121
  this.interaction.flipSpeed = options.flipSpeed;
1802
2122
  }
@@ -1811,6 +2131,7 @@ var PocaCard = class extends EventEmitter {
1811
2131
  this.interaction.flip();
1812
2132
  }
1813
2133
  wiggle() {
2134
+ if (this.interaction.isInteracting()) return;
1814
2135
  this.wiggleController?.stop();
1815
2136
  this.interaction.startSyntheticInteraction();
1816
2137
  this.wiggleController = wiggle(0, 0, 100, (pos, done) => {
@@ -1831,11 +2152,14 @@ var PocaCard = class extends EventEmitter {
1831
2152
  if (options.flipSpeed != null) {
1832
2153
  this.interaction.flipSpeed = options.flipSpeed;
1833
2154
  }
1834
- if (options.customShader) {
1835
- this.renderer.updateShader(options.customShader);
2155
+ if (options.front?.shader) {
2156
+ this.renderer.updateShader(options.front.shader);
2157
+ }
2158
+ if (options.back?.shader) {
2159
+ this.renderer.updateBackShader(options.back.shader);
1836
2160
  }
1837
- if (options.baseImage || options.popupImage || options.maskImage) {
1838
- this.renderer.updateTextures(options);
2161
+ if (options.front?.layers || options.back?.layers) {
2162
+ this.renderer.updateLayers(options.front?.layers, options.back?.layers);
1839
2163
  }
1840
2164
  }
1841
2165
  destroy() {
@@ -1845,7 +2169,24 @@ var PocaCard = class extends EventEmitter {
1845
2169
  this.removeAllListeners();
1846
2170
  }
1847
2171
  };
2172
+
2173
+ // src/shaders/registry.ts
2174
+ var builtinShaders = {
2175
+ glare: glare_frag_default,
2176
+ "glare-3d": glare_3d_frag_default,
2177
+ holo: holo_frag_default,
2178
+ "holo-3d": holo_3d_frag_default,
2179
+ snowfall: snowfall_frag_default,
2180
+ "snowfall-3d": snowfall_3d_frag_default,
2181
+ blur: blur_frag_default,
2182
+ "blur-3d": blur_3d_frag_default,
2183
+ brush: brush_frag_default,
2184
+ "brush-3d": brush_3d_frag_default
2185
+ };
1848
2186
  export {
1849
- PocaCard
2187
+ FRAG_SHADERS,
2188
+ PocaCard,
2189
+ builtinShaders,
2190
+ resolveIncludes
1850
2191
  };
1851
2192
  //# sourceMappingURL=index.js.map