Add timeline owner for WebGL book playback
This commit is contained in:
@@ -11,7 +11,7 @@ class UIDisplayHandlerModule extends BaseModule {
|
||||
super('ui-display-handler', 'UI Display Handler');
|
||||
|
||||
// Module dependencies
|
||||
this.dependencies = ['layout-renderer', 'webgl-book-scene', 'playback-coordinator', 'game-config', 'localization', 'story-history', 'sentence-queue', 'persistence-manager', 'markup-parser', 'book-pagination', 'book-texture-renderer'];
|
||||
this.dependencies = ['layout-renderer', 'webgl-book-scene', 'playback-coordinator', 'book-playback-timeline', 'game-config', 'localization', 'story-history', 'sentence-queue', 'persistence-manager', 'markup-parser', 'book-pagination', 'book-texture-renderer'];
|
||||
|
||||
// DOM elements
|
||||
this.container = null;
|
||||
@@ -69,6 +69,7 @@ class UIDisplayHandlerModule extends BaseModule {
|
||||
'applyTranslations',
|
||||
'renderSentence',
|
||||
'isWebGLMode',
|
||||
'playWebGLBookSentence',
|
||||
'prepareWebGLBookReveal',
|
||||
'waitForWebGLPageFlip',
|
||||
'renderStoryBlock',
|
||||
@@ -985,9 +986,7 @@ class UIDisplayHandlerModule extends BaseModule {
|
||||
|
||||
try {
|
||||
if (useWebGLBookReveal) {
|
||||
await this.prepareWebGLBookReveal(sentence);
|
||||
if (!isCurrent()) return null;
|
||||
await this.playbackCoordinator.play(sentence);
|
||||
await this.playWebGLBookSentence(sentence);
|
||||
if (!isCurrent()) return null;
|
||||
if (sentence.blockId != null) this.markBlockRendered(sentence.blockId);
|
||||
this.dispatchDeferredTagsForBlock(sentence);
|
||||
@@ -1055,73 +1054,20 @@ class UIDisplayHandlerModule extends BaseModule {
|
||||
|| document.body?.classList?.contains('webgl-mode');
|
||||
}
|
||||
|
||||
async playWebGLBookSentence(sentence) {
|
||||
const timeline = this.getModule('book-playback-timeline');
|
||||
if (!timeline || typeof timeline.playSentence !== 'function') {
|
||||
throw new Error('WebGL book playback timeline is required for 3D sentence playback');
|
||||
}
|
||||
return timeline.playSentence(sentence);
|
||||
}
|
||||
|
||||
async prepareWebGLBookReveal(sentence) {
|
||||
const bookPagination = this.getModule('book-pagination');
|
||||
const bookTextureRenderer = this.getModule('book-texture-renderer');
|
||||
if (!bookPagination || !bookTextureRenderer || sentence.blockId == null) return;
|
||||
const sentenceQueue = this.getModule('sentence-queue');
|
||||
if (!Array.isArray(sentence.animation?.wordTimings) || sentence.animation.wordTimings.length === 0) {
|
||||
const words = String(sentence.layoutText || sentence.text || '').match(/\S+/g) || [];
|
||||
sentence.animation = sentenceQueue?.calculateAnimationTiming?.(words, sentence.tts?.duration || 0, sentence.cueMarkers || [])
|
||||
|| { wordTimings: [], cueTimings: [], totalDuration: 0 };
|
||||
}
|
||||
|
||||
let preparedSpread = null;
|
||||
if (typeof bookPagination.preparePendingBlock === 'function') {
|
||||
const currentSpreadIndex = Math.max(0, Number(bookPagination.currentSpreadIndex || 0));
|
||||
const previewSpread = sentence.webglBookPresentation?.spread || await bookPagination.preparePendingBlock(sentence, {
|
||||
activate: false,
|
||||
publish: false,
|
||||
includeUnrenderedHistory: true
|
||||
});
|
||||
const previewRevealDetail = {
|
||||
id: sentence.id,
|
||||
blockId: sentence.blockId,
|
||||
wordTimings: sentence.animation?.wordTimings || [],
|
||||
cueTimings: sentence.animation?.cueTimings || [],
|
||||
totalDuration: sentence.animation?.totalDuration || 0,
|
||||
spread: previewSpread,
|
||||
phase: 'prepare'
|
||||
};
|
||||
if (previewSpread && typeof bookTextureRenderer.prepareRevealBlock === 'function') {
|
||||
bookTextureRenderer.prepareRevealBlock(previewRevealDetail, { phase: 'prepare' });
|
||||
}
|
||||
if (Number(previewSpread?.index || 0) > currentSpreadIndex) {
|
||||
const flipped = await this.waitForWebGLPageFlip({
|
||||
direction: 1,
|
||||
reason: 'pending-block-overflow',
|
||||
targetSpread: previewSpread.index
|
||||
});
|
||||
if (!flipped) {
|
||||
throw new Error(`WebGL book page flip did not start for prepared spread ${previewSpread.index}`);
|
||||
}
|
||||
}
|
||||
preparedSpread = await bookPagination.preparePendingBlock(sentence, {
|
||||
includeUnrenderedHistory: true
|
||||
});
|
||||
} else {
|
||||
document.dispatchEvent(new CustomEvent('book-pagination:prepare-block', {
|
||||
detail: {
|
||||
block: sentence
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const revealDetail = {
|
||||
id: sentence.id,
|
||||
blockId: sentence.blockId,
|
||||
wordTimings: sentence.animation?.wordTimings || [],
|
||||
cueTimings: sentence.animation?.cueTimings || [],
|
||||
totalDuration: sentence.animation?.totalDuration || 0,
|
||||
spread: preparedSpread
|
||||
};
|
||||
if (typeof bookTextureRenderer.prepareRevealBlock === 'function') {
|
||||
bookTextureRenderer.prepareRevealBlock(revealDetail);
|
||||
} else {
|
||||
document.dispatchEvent(new CustomEvent('book-texture:prepare-reveal-block', {
|
||||
detail: revealDetail
|
||||
}));
|
||||
const timeline = this.getModule('book-playback-timeline');
|
||||
if (!timeline || typeof timeline.prepareSentence !== 'function') {
|
||||
throw new Error('WebGL book playback timeline is required for 3D reveal preparation');
|
||||
}
|
||||
return timeline.prepareSentence(sentence, { immediate: true });
|
||||
}
|
||||
|
||||
waitForWebGLPageFlip(detail = {}) {
|
||||
|
||||
Reference in New Issue
Block a user