Fix WebGL reveal timing and flip prewarm
This commit is contained in:
+90
-24
@@ -211,6 +211,7 @@ const fastFlipOverlap = 5;
|
||||
let activeFlips = [];
|
||||
let pendingPageFlips = 0;
|
||||
const pendingRevealStartBlockIds = new Set();
|
||||
const activeRevealBlockStarts = new Map();
|
||||
|
||||
const paperColor = new THREE.Color(0xece4ca);
|
||||
const inkColor = '#1a1009';
|
||||
@@ -2020,28 +2021,30 @@ function handlePageCanvases(event) {
|
||||
preloadOnly: Boolean(detail.preloadOnly),
|
||||
pageMeta: currentPageMeta
|
||||
});
|
||||
const leftReveal = attachRevealPageMeta(detail.reveal?.left, detail.pageMeta?.left || currentPageMeta.left || null);
|
||||
const rightReveal = attachRevealPageMeta(detail.reveal?.right, detail.pageMeta?.right || currentPageMeta.right || null);
|
||||
if (detail.preloadOnly) {
|
||||
if (detail.left) {
|
||||
const texture = preloadPageTexture('left', detail.left, detail.reveal?.left);
|
||||
rememberResidentPageTexture(currentPageMeta.left, texture, detail.left);
|
||||
const texture = preloadPageTexture('left', detail.left, leftReveal, detail.pageMeta?.left || null);
|
||||
rememberResidentPageTexture(detail.pageMeta?.left || null, texture, detail.left);
|
||||
}
|
||||
if (detail.right) {
|
||||
const texture = preloadPageTexture('right', detail.right, detail.reveal?.right);
|
||||
rememberResidentPageTexture(currentPageMeta.right, texture, detail.right);
|
||||
const texture = preloadPageTexture('right', detail.right, rightReveal, detail.pageMeta?.right || null);
|
||||
rememberResidentPageTexture(detail.pageMeta?.right || null, texture, detail.right);
|
||||
}
|
||||
markPageTextureTiming('handlePageCanvases:preloadOnly:end');
|
||||
return;
|
||||
}
|
||||
if (detail.left) {
|
||||
if (detail.reveal?.left) {
|
||||
beginPageReveal('left', detail.left, detail.reveal.left);
|
||||
if (leftReveal) {
|
||||
beginPageReveal('left', detail.left, leftReveal);
|
||||
} else {
|
||||
uploadPageTextureDirect('left', detail.left, currentPageMeta.left);
|
||||
}
|
||||
}
|
||||
if (detail.right) {
|
||||
if (detail.reveal?.right) {
|
||||
beginPageReveal('right', detail.right, detail.reveal.right);
|
||||
if (rightReveal) {
|
||||
beginPageReveal('right', detail.right, rightReveal);
|
||||
} else {
|
||||
uploadPageTextureDirect('right', detail.right, currentPageMeta.right);
|
||||
}
|
||||
@@ -2055,16 +2058,26 @@ function handlePageCanvases(event) {
|
||||
markPageTextureTiming('handlePageCanvases:end');
|
||||
}
|
||||
|
||||
function getRevealCacheKey(revealDetail = {}) {
|
||||
const ids = Array.isArray(revealDetail.blockIds) ? revealDetail.blockIds : [];
|
||||
return ids.map(id => String(id)).join('|') || 'direct';
|
||||
function attachRevealPageMeta(revealDetail = null, pageMeta = null) {
|
||||
if (!revealDetail) return null;
|
||||
return {
|
||||
...revealDetail,
|
||||
pageMeta: pageMeta ? { ...pageMeta } : null
|
||||
};
|
||||
}
|
||||
|
||||
function preloadPageTexture(side, sourceCanvas, revealDetail = {}) {
|
||||
function getRevealCacheKey(revealDetail = {}) {
|
||||
const ids = Array.isArray(revealDetail.blockIds) ? revealDetail.blockIds : [];
|
||||
const pageIndex = Number(revealDetail.pageMeta?.pageIndex);
|
||||
const pageKey = Number.isFinite(pageIndex) ? `page:${Math.max(0, Math.round(pageIndex))}` : 'page:unknown';
|
||||
return `${pageKey}:${ids.map(id => String(id)).join('|') || 'direct'}`;
|
||||
}
|
||||
|
||||
function preloadPageTexture(side, sourceCanvas, revealDetail = {}, pageMeta = null) {
|
||||
if (!sourceCanvas) return null;
|
||||
const texture = createPageCanvasTexture(sourceCanvas);
|
||||
const baseTexture = revealDetail?.baseCanvas ? createPageCanvasTexture(revealDetail.baseCanvas) : null;
|
||||
const key = getRevealCacheKey(revealDetail);
|
||||
const key = getRevealCacheKey({ ...(revealDetail || {}), pageMeta: revealDetail?.pageMeta || pageMeta || null });
|
||||
markPageTextureTiming('preloadTexture:start', {
|
||||
side,
|
||||
key,
|
||||
@@ -2312,13 +2325,19 @@ function beginPageReveal(side, sourceCanvas, revealDetail = {}) {
|
||||
}
|
||||
const baseTexture = prepared?.baseTexture || (revealDetail?.baseCanvas ? createPageCanvasTexture(revealDetail.baseCanvas) : null);
|
||||
|
||||
const revealBlockIds = Array.isArray(revealDetail.blockIds) ? revealDetail.blockIds.map(value => String(value)) : [];
|
||||
const activeStartedAt = revealBlockIds
|
||||
.map(blockId => activeRevealBlockStarts.get(blockId))
|
||||
.filter(value => Number.isFinite(Number(value)))
|
||||
.sort((a, b) => a - b)[0] ?? null;
|
||||
|
||||
pageRevealState[side] = {
|
||||
startedAt: revealDetail.startNow ? performance.now() : null,
|
||||
startedAt: activeStartedAt ?? (revealDetail.startNow ? performance.now() : null),
|
||||
pendingStart: false,
|
||||
lastRevealFrameAt: null,
|
||||
visualElapsedMs: 0,
|
||||
visualElapsedMs: activeStartedAt ? Math.max(0, performance.now() - activeStartedAt) : 0,
|
||||
durationMs: Math.max(1, Number(revealDetail.durationMs || 1)),
|
||||
blockIds: Array.isArray(revealDetail.blockIds) ? revealDetail.blockIds : [],
|
||||
blockIds: revealBlockIds,
|
||||
baseTexture,
|
||||
fastForwarding: false,
|
||||
fastForwardStartedAt: null,
|
||||
@@ -2328,6 +2347,21 @@ function beginPageReveal(side, sourceCanvas, revealDetail = {}) {
|
||||
if (material?.userData) material.userData.pendingPageReveal = revealDetail;
|
||||
if (shader?.uniforms) applyPendingPageReveal(side, shader);
|
||||
else if (material) material.needsUpdate = true;
|
||||
if (shader?.uniforms?.bookRevealElapsedMs) {
|
||||
shader.uniforms.bookRevealElapsedMs.value = pageRevealState[side].visualElapsedMs;
|
||||
}
|
||||
if (side === 'right' && isRightBodyPageComplete()) {
|
||||
const targetSpread = Math.max(0, Math.round(Number(bookPaginationState.spreadIndex || 0)) + 1);
|
||||
prewarmFlipTextures(1, targetSpread).then(() => {
|
||||
markPageTextureTiming('rightPageReveal:flip-prewarm-ready', { targetSpread });
|
||||
}).catch((error) => {
|
||||
recordPageCacheProblem({
|
||||
type: 'right-page-flip-prewarm-error',
|
||||
targetSpread,
|
||||
message: error?.message || String(error)
|
||||
});
|
||||
});
|
||||
}
|
||||
document.documentElement.dataset.webglRevealDebug = JSON.stringify({
|
||||
side,
|
||||
blockIds: pageRevealState[side].blockIds,
|
||||
@@ -2450,6 +2484,7 @@ function clearPageReveal(side, reason = 'clear') {
|
||||
function startPageRevealForBlock(blockId) {
|
||||
const id = String(blockId ?? '');
|
||||
if (!id) return;
|
||||
if (!activeRevealBlockStarts.has(id)) activeRevealBlockStarts.set(id, performance.now());
|
||||
if (activeFlips.length > 0) {
|
||||
pendingRevealStartBlockIds.add(id);
|
||||
markPageTextureTiming('revealStart:deferred-for-flip', {
|
||||
@@ -2463,6 +2498,7 @@ function startPageRevealForBlock(blockId) {
|
||||
if (!state || state.startedAt != null) return;
|
||||
if (!state.blockIds.map(value => String(value)).includes(id)) return;
|
||||
state.pendingStart = true;
|
||||
state.startedAt = activeRevealBlockStarts.get(id) || performance.now();
|
||||
const shader = getPageRevealShader(side);
|
||||
if (shader?.uniforms?.bookRevealElapsedMs) shader.uniforms.bookRevealElapsedMs.value = 0;
|
||||
});
|
||||
@@ -2493,18 +2529,17 @@ function updatePageRevealAnimations(now) {
|
||||
return;
|
||||
}
|
||||
if (state.pendingStart) {
|
||||
state.startedAt = now;
|
||||
if (state.startedAt == null) state.startedAt = now;
|
||||
state.pendingStart = false;
|
||||
state.lastRevealFrameAt = now;
|
||||
state.visualElapsedMs = 0;
|
||||
shader.uniforms.bookRevealElapsedMs.value = 0;
|
||||
state.visualElapsedMs = Math.max(0, now - state.startedAt);
|
||||
shader.uniforms.bookRevealElapsedMs.value = state.visualElapsedMs;
|
||||
return;
|
||||
}
|
||||
if (state.startedAt == null) {
|
||||
shader.uniforms.bookRevealElapsedMs.value = 0;
|
||||
return;
|
||||
}
|
||||
const revealFrameDeltaMs = state.lastRevealFrameAt == null ? 0 : Math.max(0, now - state.lastRevealFrameAt);
|
||||
state.lastRevealFrameAt = now;
|
||||
if (state.fastForwarding) {
|
||||
const fastElapsed = Math.max(0, now - Number(state.fastForwardStartedAt || now));
|
||||
@@ -2515,7 +2550,7 @@ function updatePageRevealAnimations(now) {
|
||||
fastProgress
|
||||
);
|
||||
} else {
|
||||
state.visualElapsedMs = Math.max(0, Number(state.visualElapsedMs || 0)) + Math.min(revealFrameDeltaMs, targetFrameDurationMs);
|
||||
state.visualElapsedMs = Math.max(0, now - state.startedAt);
|
||||
}
|
||||
const progress = THREE.MathUtils.clamp(state.visualElapsedMs / state.durationMs, 0, 1);
|
||||
shader.uniforms.bookRevealElapsedMs.value = state.visualElapsedMs;
|
||||
@@ -2995,8 +3030,8 @@ function lineYAtX(points, x) {
|
||||
}
|
||||
|
||||
function setActivePageGeometry(flip, surface) {
|
||||
const geometry = createFlippingPageGeometry(surface);
|
||||
if (!flip.mesh) {
|
||||
const geometry = createFlippingPageGeometry(surface);
|
||||
flip.mesh = new THREE.Mesh(geometry, [
|
||||
materials.flipPageSurface,
|
||||
materials.flipPageBackSurface,
|
||||
@@ -3009,8 +3044,11 @@ function setActivePageGeometry(flip, surface) {
|
||||
book.add(flip.mesh);
|
||||
return;
|
||||
}
|
||||
flip.mesh.geometry.dispose();
|
||||
flip.mesh.geometry = geometry;
|
||||
if (!updateFlippingPageGeometry(flip.mesh.geometry, surface)) {
|
||||
const geometry = createFlippingPageGeometry(surface);
|
||||
flip.mesh.geometry.dispose();
|
||||
flip.mesh.geometry = geometry;
|
||||
}
|
||||
}
|
||||
|
||||
function createFlippingPageGeometry(surface) {
|
||||
@@ -3085,6 +3123,34 @@ function createFlippingPageGeometry(surface) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateFlippingPageGeometry(geometry, surface) {
|
||||
const position = geometry?.getAttribute?.('position');
|
||||
if (!position || !surface?.length || !surface[0]?.length) return false;
|
||||
const widthSegments = surface.length - 1;
|
||||
const depthSegments = surface[0].length - 1;
|
||||
const expectedVertexCount = (widthSegments + 1) * (depthSegments + 1) * 2;
|
||||
if (position.count !== expectedVertexCount) return false;
|
||||
const pageThickness = Math.max(0.0008, Number(PROCEDURAL_BOOK.SHEET_THICKNESS_MODEL || 0.001));
|
||||
const array = position.array;
|
||||
let offset = 0;
|
||||
surface.forEach((rowPoints) => {
|
||||
rowPoints.forEach((point) => {
|
||||
array[offset] = point.x;
|
||||
array[offset + 1] = point.y + pageThickness;
|
||||
array[offset + 2] = point.z;
|
||||
offset += 3;
|
||||
array[offset] = point.x;
|
||||
array[offset + 1] = point.y;
|
||||
array[offset + 2] = point.z;
|
||||
offset += 3;
|
||||
});
|
||||
});
|
||||
position.needsUpdate = true;
|
||||
geometry.computeVertexNormals();
|
||||
geometry.computeBoundingSphere();
|
||||
return true;
|
||||
}
|
||||
|
||||
function finishActiveFlip(flip) {
|
||||
removeFlipMesh(flip);
|
||||
activeFlips = activeFlips.filter((active) => active !== flip);
|
||||
|
||||
Reference in New Issue
Block a user