Rework WebGL book playback to single ownership and fix flip/reveal pipeline
Establish book-playback-timeline as the sole playback owner driving the scene through formal webgl-book:* events (not the BookLabDebug surface), with a single reveal clock in the scene render loop and webgl-page-cache as the only texture cache. Remove the legacy dual playback path and the ownsPageFlipCommit gating. Fixes: - Flip page detached/folded at the spine: restore the raw page-cap line for flip geometry (matches the prototype/pre-regression), removing normalizeFlipLineToVisiblePage which moved the pivot off the spine arc. - Flip textures: distance-based UVs (no horizontal compression), direction-aware face material (source on the camera-facing side), source meta derived from the visible spread (manual flips), prewarm shape fix. - Reveal: flash removed on the static page and the flip back surface; spanning blocks rebuild the reveal plan at activate and continue the reveal on the next spread after the fill flip. - Cache staleness is contentVersion-primary; nav clamps to spreadCount. Docs updated to describe the intended single-owner architecture. Regression checks updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,10 @@ class WebGLBookSceneModule extends BaseModule {
|
||||
this.gameConfig = null;
|
||||
this.mode = '2d';
|
||||
this.is3dSupported = false;
|
||||
// Production control surface + visible-spread accessor for the dynamically
|
||||
// imported webgl-book-lab. Populated by the lab once the scene is built.
|
||||
this.sceneControl = null;
|
||||
this.getVisibleSpreadIndex = null;
|
||||
this.labImportPromise = null;
|
||||
this.textureRefreshTimer = null;
|
||||
this.textureRefreshAnimationId = null;
|
||||
@@ -330,7 +334,7 @@ class WebGLBookSceneModule extends BaseModule {
|
||||
this.persistenceManager?.updatePreference?.('webgl', 'pageReserve', value);
|
||||
this.preferenceWriteGuard = false;
|
||||
},
|
||||
getBookState: () => window.BookLabDebug?.getBookState?.() || {
|
||||
getBookState: () => this.sceneControl?.getBookState?.() || {
|
||||
pageCount: this.persistenceManager?.getPreference?.('webgl', 'bookPageCount', DEFAULT_BOOK_PAGE_COUNT) ?? DEFAULT_BOOK_PAGE_COUNT,
|
||||
pageReserve: this.persistenceManager?.getPreference?.('webgl', 'pageReserve', DEFAULT_PAGE_RESERVE) ?? DEFAULT_PAGE_RESERVE,
|
||||
progress: this.persistenceManager?.getPreference?.('webgl', 'bookProgress', DEFAULT_BOOK_PROGRESS) ?? DEFAULT_BOOK_PROGRESS
|
||||
@@ -341,19 +345,19 @@ class WebGLBookSceneModule extends BaseModule {
|
||||
const progress = Number(state.progress);
|
||||
if (Number.isFinite(pageCount)) {
|
||||
this.persistenceManager?.updatePreference?.('webgl', 'bookPageCount', pageCount);
|
||||
window.BookLabDebug?.setBookPageCount?.(pageCount);
|
||||
this.sceneControl?.setBookPageCount?.(pageCount);
|
||||
}
|
||||
if (Number.isFinite(pageReserve)) {
|
||||
this.persistenceManager?.updatePreference?.('webgl', 'pageReserve', pageReserve);
|
||||
window.BookLabDebug?.setPageReserve?.(pageReserve);
|
||||
this.sceneControl?.setPageReserve?.(pageReserve);
|
||||
}
|
||||
if (Number.isFinite(progress)) {
|
||||
this.persistenceManager?.updatePreference?.('webgl', 'bookProgress', progress);
|
||||
window.BookLabDebug?.setReadingProgress?.(progress);
|
||||
this.sceneControl?.setReadingProgress?.(progress);
|
||||
}
|
||||
const maxVisitedPagePosition = Number(state.maxVisitedPagePosition ?? state.pagePosition);
|
||||
if (Number.isFinite(maxVisitedPagePosition)) {
|
||||
window.BookLabDebug?.setMaxVisitedPagePosition?.(maxVisitedPagePosition);
|
||||
this.sceneControl?.setMaxVisitedPagePosition?.(maxVisitedPagePosition);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -456,7 +460,7 @@ class WebGLBookSceneModule extends BaseModule {
|
||||
}
|
||||
|
||||
projectCanvasEventTarget(event) {
|
||||
const projection = window.BookLabDebug?.projectPointerToPage?.(event.clientX, event.clientY);
|
||||
const projection = this.sceneControl?.projectPointerToPage?.(event.clientX, event.clientY);
|
||||
if (!projection) {
|
||||
document.documentElement.dataset.webglLastProjection = JSON.stringify({
|
||||
hit: false,
|
||||
@@ -531,11 +535,11 @@ class WebGLBookSceneModule extends BaseModule {
|
||||
this.initializeScene();
|
||||
}
|
||||
} else if (key === 'bookProgress' && !this.preferenceWriteGuard) {
|
||||
window.BookLabDebug?.setReadingProgress?.(value);
|
||||
this.sceneControl?.setReadingProgress?.(value);
|
||||
} else if (key === 'bookPageCount' && !this.preferenceWriteGuard) {
|
||||
window.BookLabDebug?.setBookPageCount?.(value);
|
||||
this.sceneControl?.setBookPageCount?.(value);
|
||||
} else if (key === 'pageReserve' && !this.preferenceWriteGuard) {
|
||||
window.BookLabDebug?.setPageReserve?.(value);
|
||||
this.sceneControl?.setPageReserve?.(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,7 +560,7 @@ class WebGLBookSceneModule extends BaseModule {
|
||||
triggerTextureRefresh() {
|
||||
clearTimeout(this.textureRefreshTimer);
|
||||
this.textureRefreshTimer = setTimeout(() => {
|
||||
window.BookLabDebug?.redrawPageTextures?.();
|
||||
this.sceneControl?.redrawPageTextures?.();
|
||||
}, 60);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user