Gate WebGL book texture fonts
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Defines the canonical page geometry used by the WebGL book renderer.
|
||||
*/
|
||||
import { BaseModule } from './base-module.js';
|
||||
import { calculateProceduralBookThickness, snapProceduralPageCount } from './procedural-book-model.js?v=20260607-webgl-queued-mask-reveal';
|
||||
import { calculateProceduralBookThickness, snapProceduralPageCount } from './procedural-book-model.js?v=20260607-webgl-forced-font-mask';
|
||||
|
||||
export const BOOK_TEXTURE_WIDTH = 3072;
|
||||
|
||||
|
||||
@@ -37,8 +37,9 @@ class BookTextureRendererModule extends BaseModule {
|
||||
|
||||
this.bindMethods([
|
||||
'initialize',
|
||||
'waitForTextureFonts',
|
||||
'ensureTextureFontFace',
|
||||
'createPageCanvases',
|
||||
'drawEmptySpread',
|
||||
'drawSpread',
|
||||
'drawPageBase',
|
||||
'drawPageLines',
|
||||
@@ -60,8 +61,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
'publishSpread',
|
||||
'getPageCanvas',
|
||||
'getHitMap',
|
||||
'handlePageCountChanged',
|
||||
'handleSceneReady'
|
||||
'handlePageCountChanged'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -70,12 +70,10 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.pagination = this.getModule('book-pagination');
|
||||
this.localization = this.getModule('localization');
|
||||
this.reportProgress(10, 'Waiting for book fonts');
|
||||
if (document.fonts?.ready) await document.fonts.ready;
|
||||
await this.waitForTextureFonts();
|
||||
this.reportProgress(20, 'Preparing page texture canvases');
|
||||
this.createPageCanvases();
|
||||
this.drawEmptySpread();
|
||||
this.addEventListener(document, 'webgl-book:page-count-changed', this.handlePageCountChanged);
|
||||
this.addEventListener(document, 'webgl-book:scene-ready', this.handleSceneReady);
|
||||
this.addEventListener(document, 'book-pagination:spread-updated', (event) => {
|
||||
const spread = event.detail?.spread || this.pagination?.getCurrentSpread?.();
|
||||
const latestBlockId = event.detail?.latestBlockId;
|
||||
@@ -106,6 +104,28 @@ class BookTextureRendererModule extends BaseModule {
|
||||
return true;
|
||||
}
|
||||
|
||||
async waitForTextureFonts() {
|
||||
if (!document.fonts) return;
|
||||
await Promise.all([
|
||||
this.ensureTextureFontFace('EB Garamond', '/fonts/EBGaramond12-Regular.otf'),
|
||||
this.ensureTextureFontFace('EB Garamond 12', '/fonts/EBGaramond12/webfonts/EBGaramond-Regular.woff2'),
|
||||
this.ensureTextureFontFace('EB Garamond Initials', '/fonts/EB-Garamond-Initials/EBGaramond-0.016/otf/EBGaramond-Initials.otf')
|
||||
]);
|
||||
await Promise.all([
|
||||
document.fonts.load('24px "EB Garamond"'),
|
||||
document.fonts.load('24px "EB Garamond 12"'),
|
||||
document.fonts.load('72px "EB Garamond Initials"')
|
||||
]);
|
||||
await document.fonts.ready;
|
||||
}
|
||||
|
||||
async ensureTextureFontFace(family, url) {
|
||||
if (!window.FontFace) return;
|
||||
const face = new FontFace(family, `url(${url})`);
|
||||
const loadedFace = await face.load();
|
||||
document.fonts.add(loadedFace);
|
||||
}
|
||||
|
||||
createPageCanvases(textureWidth = this.pageFormat?.getTextureWidth?.() || 3072) {
|
||||
this.metrics = this.pageFormat.getTextureMetrics(textureWidth);
|
||||
['left', 'right'].forEach((side) => {
|
||||
@@ -117,12 +137,6 @@ class BookTextureRendererModule extends BaseModule {
|
||||
});
|
||||
}
|
||||
|
||||
drawEmptySpread() {
|
||||
this.drawPageBase('left');
|
||||
this.drawPageBase('right');
|
||||
this.publishSpread();
|
||||
}
|
||||
|
||||
drawSpread(spread = null, sides = null) {
|
||||
this.currentSpread = spread || { left: [], right: [] };
|
||||
const sidesToDraw = Array.isArray(sides) && sides.length ? sides : ['left', 'right'];
|
||||
@@ -542,9 +556,6 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.drawSpread(this.currentSpread || this.pagination?.getCurrentSpread?.());
|
||||
}
|
||||
|
||||
handleSceneReady() {
|
||||
this.publishSpread();
|
||||
}
|
||||
}
|
||||
|
||||
const bookTextureRenderer = new BookTextureRendererModule();
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ const ModuleState = {
|
||||
ERROR: 'ERROR'
|
||||
};
|
||||
|
||||
const MODULE_CACHE_BUSTER = '20260607-webgl-queued-mask-reveal';
|
||||
const MODULE_CACHE_BUSTER = '20260607-webgl-forced-font-mask';
|
||||
window.MODULE_CACHE_BUSTER = MODULE_CACHE_BUSTER;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1047,6 +1047,12 @@ class UIDisplayHandlerModule extends BaseModule {
|
||||
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 };
|
||||
}
|
||||
|
||||
if (typeof bookPagination.preparePendingBlock === 'function') {
|
||||
await bookPagination.preparePendingBlock(sentence);
|
||||
|
||||
+35
-12
@@ -4,7 +4,7 @@ import { RenderPass } from 'https://esm.sh/three@0.165.0/examples/jsm/postproces
|
||||
import { SSAOPass } from 'https://esm.sh/three@0.165.0/examples/jsm/postprocessing/SSAOPass.js';
|
||||
import { SMAAPass } from 'https://esm.sh/three@0.165.0/examples/jsm/postprocessing/SMAAPass.js';
|
||||
import { OutputPass } from 'https://esm.sh/three@0.165.0/examples/jsm/postprocessing/OutputPass.js';
|
||||
import { PROCEDURAL_BOOK, createProceduralBookModel, snapProceduralPageCount } from './procedural-book-model.js?v=20260607-webgl-queued-mask-reveal';
|
||||
import { PROCEDURAL_BOOK, createProceduralBookModel, snapProceduralPageCount } from './procedural-book-model.js?v=20260607-webgl-forced-font-mask';
|
||||
|
||||
const canvas = document.getElementById('scene');
|
||||
canvas.style.cursor = 'grab';
|
||||
@@ -184,7 +184,7 @@ let pendingPageFlips = 0;
|
||||
|
||||
const paperColor = new THREE.Color(0xece4ca);
|
||||
const inkColor = '#1a1009';
|
||||
const maxRevealWords = 128;
|
||||
const maxRevealWords = 256;
|
||||
const completedRevealElapsedMs = 1000000000;
|
||||
|
||||
await reportLabStep(48, 'Preparing high-resolution page textures');
|
||||
@@ -570,6 +570,7 @@ function configureBookShadowReceiver(material, strength) {
|
||||
shader.uniforms.bookRevealPaperColor = { value: paperColor.clone() };
|
||||
shader.uniforms.bookRevealSoftness = { value: 0.035 };
|
||||
material.userData.bookRevealShader = shader;
|
||||
applyPendingPageReveal(pageReveal.side, shader);
|
||||
}
|
||||
|
||||
shader.vertexShader = shader.vertexShader
|
||||
@@ -607,14 +608,14 @@ function configureBookShadowReceiver(material, strength) {
|
||||
${pageReveal ? `uniform float bookRevealActive;
|
||||
uniform float bookRevealElapsedMs;
|
||||
uniform int bookRevealWordCount;
|
||||
uniform vec4 bookRevealWordRects[128];
|
||||
uniform vec4 bookRevealWordTimings[128];
|
||||
uniform vec4 bookRevealWordRects[256];
|
||||
uniform vec4 bookRevealWordTimings[256];
|
||||
uniform vec3 bookRevealPaperColor;
|
||||
uniform float bookRevealSoftness;
|
||||
|
||||
float bookRevealVisibleMask(vec2 uv) {
|
||||
float hidden = 0.0;
|
||||
for (int i = 0; i < 128; i++) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (i >= bookRevealWordCount) break;
|
||||
vec4 rect = bookRevealWordRects[i];
|
||||
vec2 local = (uv - rect.xy) / max(rect.zw, vec2(0.0001));
|
||||
@@ -1652,22 +1653,44 @@ function beginPageReveal(side, sourceCanvas, revealDetail = {}) {
|
||||
const canvas = side === 'left' ? leftCanvas : rightCanvas;
|
||||
const texture = side === 'left' ? leftTexture : rightTexture;
|
||||
const shader = getPageRevealShader(side);
|
||||
if (!shader?.uniforms) {
|
||||
uploadPageTextureDirect(side, sourceCanvas);
|
||||
return;
|
||||
}
|
||||
|
||||
drawCanvasPageTexture(canvas, sourceCanvas, side);
|
||||
texture.needsUpdate = true;
|
||||
applyPageRevealWords(shader, revealDetail.wordRects || []);
|
||||
shader.uniforms.bookRevealActive.value = 1;
|
||||
shader.uniforms.bookRevealElapsedMs.value = 0;
|
||||
|
||||
pageRevealState[side] = {
|
||||
startedAt: revealDetail.startNow ? performance.now() : null,
|
||||
durationMs: Math.max(1, Number(revealDetail.durationMs || 1)),
|
||||
blockIds: Array.isArray(revealDetail.blockIds) ? revealDetail.blockIds : []
|
||||
};
|
||||
const material = side === 'left' ? materials.leftPage : materials.rightPage;
|
||||
if (material?.userData) material.userData.pendingPageReveal = revealDetail;
|
||||
if (shader?.uniforms) applyPendingPageReveal(side, shader);
|
||||
else if (material) material.needsUpdate = true;
|
||||
document.documentElement.dataset.webglRevealDebug = JSON.stringify({
|
||||
side,
|
||||
blockIds: pageRevealState[side].blockIds,
|
||||
wordCount: Array.isArray(revealDetail.wordRects) ? revealDetail.wordRects.length : 0,
|
||||
shaderReady: Boolean(shader?.uniforms),
|
||||
started: pageRevealState[side].startedAt != null
|
||||
});
|
||||
}
|
||||
|
||||
function applyPendingPageReveal(side, shader = getPageRevealShader(side)) {
|
||||
const material = side === 'left' ? materials.leftPage : materials.rightPage;
|
||||
const revealDetail = material?.userData?.pendingPageReveal;
|
||||
if (!revealDetail || !shader?.uniforms) return false;
|
||||
applyPageRevealWords(shader, revealDetail.wordRects || []);
|
||||
shader.uniforms.bookRevealActive.value = 1;
|
||||
shader.uniforms.bookRevealElapsedMs.value = 0;
|
||||
document.documentElement.dataset.webglRevealDebug = JSON.stringify({
|
||||
side,
|
||||
blockIds: pageRevealState[side]?.blockIds || revealDetail.blockIds || [],
|
||||
wordCount: Array.isArray(revealDetail.wordRects) ? revealDetail.wordRects.length : 0,
|
||||
shaderReady: true,
|
||||
started: pageRevealState[side]?.startedAt != null
|
||||
});
|
||||
delete material.userData.pendingPageReveal;
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyPageRevealWords(shader, words = []) {
|
||||
|
||||
Reference in New Issue
Block a user