@sangwonl/pocato-core 0.4.9 → 0.4.11
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 +69 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2022,11 +2022,15 @@ var FRAG_SHADERS = {
|
|
|
2022
2022
|
lenticular: lenticular_frag_default
|
|
2023
2023
|
};
|
|
2024
2024
|
var MAX_LAYERS = 8;
|
|
2025
|
+
var VIDEO_FRAME_WAIT_TIMEOUT_MS = 750;
|
|
2025
2026
|
var FaceRenderer = class {
|
|
2026
2027
|
constructor(shader, width, height, effectUniforms) {
|
|
2027
2028
|
this.loadedLayers = [];
|
|
2028
2029
|
this.layerLoadVersion = 0;
|
|
2029
2030
|
this.layerLoadAbort = null;
|
|
2031
|
+
this.videoActivationVersion = 0;
|
|
2032
|
+
this.videoActivationAbort = null;
|
|
2033
|
+
this.destroyed = false;
|
|
2030
2034
|
bootstrapShaders();
|
|
2031
2035
|
this.scene = new THREE3.Scene();
|
|
2032
2036
|
this.camera = new THREE3.Camera();
|
|
@@ -2077,6 +2081,9 @@ var FaceRenderer = class {
|
|
|
2077
2081
|
}
|
|
2078
2082
|
async loadLayers(layers, onError) {
|
|
2079
2083
|
const version = ++this.layerLoadVersion;
|
|
2084
|
+
this.videoActivationVersion++;
|
|
2085
|
+
this.videoActivationAbort?.abort();
|
|
2086
|
+
this.videoActivationAbort = null;
|
|
2080
2087
|
this.layerLoadAbort?.abort();
|
|
2081
2088
|
const abort = new AbortController();
|
|
2082
2089
|
this.layerLoadAbort = abort;
|
|
@@ -2115,13 +2122,25 @@ var FaceRenderer = class {
|
|
|
2115
2122
|
updateResolution(width, height) {
|
|
2116
2123
|
this.material.uniforms.uResolution.value.set(width, height);
|
|
2117
2124
|
}
|
|
2118
|
-
playLoadedVideos() {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2125
|
+
async playLoadedVideos() {
|
|
2126
|
+
const activationVersion = ++this.videoActivationVersion;
|
|
2127
|
+
this.videoActivationAbort?.abort();
|
|
2128
|
+
const activationAbort = new AbortController();
|
|
2129
|
+
this.videoActivationAbort = activationAbort;
|
|
2130
|
+
await Promise.all(this.loadedLayers.map(async (loaded, i) => {
|
|
2131
|
+
if (this.destroyed || activationAbort.signal.aborted || activationVersion !== this.videoActivationVersion) return;
|
|
2132
|
+
if (!loaded.liveTexture) {
|
|
2133
|
+
loaded.play?.();
|
|
2134
|
+
return;
|
|
2123
2135
|
}
|
|
2124
2136
|
loaded.play?.();
|
|
2137
|
+
const hasFrame = await waitForVideoFrame(loaded.videoEl, activationAbort.signal);
|
|
2138
|
+
if (hasFrame && !this.destroyed && !activationAbort.signal.aborted && activationVersion === this.videoActivationVersion) {
|
|
2139
|
+
this.material.uniforms[`uLayer${i}`].value = loaded.liveTexture;
|
|
2140
|
+
}
|
|
2141
|
+
}));
|
|
2142
|
+
if (this.videoActivationAbort === activationAbort) {
|
|
2143
|
+
this.videoActivationAbort = null;
|
|
2125
2144
|
}
|
|
2126
2145
|
}
|
|
2127
2146
|
updateShader(fragmentShader) {
|
|
@@ -2140,7 +2159,11 @@ var FaceRenderer = class {
|
|
|
2140
2159
|
this.material.uniforms.uLayerCount.value = 0;
|
|
2141
2160
|
}
|
|
2142
2161
|
destroy() {
|
|
2162
|
+
this.destroyed = true;
|
|
2143
2163
|
this.layerLoadVersion++;
|
|
2164
|
+
this.videoActivationVersion++;
|
|
2165
|
+
this.videoActivationAbort?.abort();
|
|
2166
|
+
this.videoActivationAbort = null;
|
|
2144
2167
|
this.layerLoadAbort?.abort();
|
|
2145
2168
|
this.layerLoadAbort = null;
|
|
2146
2169
|
this.disposeLayers();
|
|
@@ -2148,6 +2171,39 @@ var FaceRenderer = class {
|
|
|
2148
2171
|
this.material.dispose();
|
|
2149
2172
|
}
|
|
2150
2173
|
};
|
|
2174
|
+
function waitForVideoFrame(video, signal) {
|
|
2175
|
+
return new Promise((resolve) => {
|
|
2176
|
+
if (signal?.aborted) {
|
|
2177
|
+
resolve(false);
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
let settled = false;
|
|
2181
|
+
let timeoutId = null;
|
|
2182
|
+
let videoFrameId = null;
|
|
2183
|
+
const finish = (hasFrame) => {
|
|
2184
|
+
if (settled) return;
|
|
2185
|
+
settled = true;
|
|
2186
|
+
if (timeoutId != null) globalThis.clearTimeout(timeoutId);
|
|
2187
|
+
if (video && videoFrameId != null && "cancelVideoFrameCallback" in video) {
|
|
2188
|
+
video.cancelVideoFrameCallback(videoFrameId);
|
|
2189
|
+
}
|
|
2190
|
+
signal?.removeEventListener("abort", abort);
|
|
2191
|
+
resolve(hasFrame);
|
|
2192
|
+
};
|
|
2193
|
+
const abort = () => finish(false);
|
|
2194
|
+
if (video && "requestVideoFrameCallback" in video) {
|
|
2195
|
+
timeoutId = globalThis.setTimeout(() => finish(false), VIDEO_FRAME_WAIT_TIMEOUT_MS);
|
|
2196
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
2197
|
+
videoFrameId = video.requestVideoFrameCallback(() => finish(true));
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
const rafId2 = requestAnimationFrame(() => finish(true));
|
|
2201
|
+
signal?.addEventListener("abort", () => {
|
|
2202
|
+
cancelAnimationFrame(rafId2);
|
|
2203
|
+
finish(false);
|
|
2204
|
+
}, { once: true });
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2151
2207
|
function toThreeUniformValue(value) {
|
|
2152
2208
|
if (typeof value === "string") {
|
|
2153
2209
|
return value.startsWith("#") ? new THREE3.Color(value) : value;
|
|
@@ -2182,6 +2238,7 @@ var Renderer = class {
|
|
|
2182
2238
|
this.firstFrameEmitted = false;
|
|
2183
2239
|
this.firstFrameScheduled = false;
|
|
2184
2240
|
this.mediaActivated = false;
|
|
2241
|
+
this.mediaActivationPromise = null;
|
|
2185
2242
|
if (!options.front?.layers?.length) {
|
|
2186
2243
|
console.warn("[pocato] front.layers must have at least 1 element");
|
|
2187
2244
|
}
|
|
@@ -2345,10 +2402,15 @@ var Renderer = class {
|
|
|
2345
2402
|
if (!this.layersReady || this.firstFrameEmitted || this.firstFrameScheduled) return;
|
|
2346
2403
|
if (!this.mediaActivated) {
|
|
2347
2404
|
this.mediaActivated = true;
|
|
2348
|
-
this.
|
|
2349
|
-
|
|
2405
|
+
this.mediaActivationPromise = Promise.all([
|
|
2406
|
+
this.frontFace.playLoadedVideos(),
|
|
2407
|
+
this.backFace?.playLoadedVideos() ?? Promise.resolve()
|
|
2408
|
+
]).then(() => {
|
|
2409
|
+
this.mediaActivationPromise = null;
|
|
2410
|
+
});
|
|
2350
2411
|
return;
|
|
2351
2412
|
}
|
|
2413
|
+
if (this.mediaActivationPromise) return;
|
|
2352
2414
|
requestAnimationFrame(() => {
|
|
2353
2415
|
if (this.destroyed || this.firstFrameEmitted) return;
|
|
2354
2416
|
this.firstFrameEmitted = true;
|