Checkpoint WebGL book renderer work
This commit is contained in:
+261
-86
@@ -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';
|
||||
import { PROCEDURAL_BOOK, createProceduralBookModel, snapProceduralPageCount } from './procedural-book-model.js?v=20260606-webgl-no-menu-offscreen-dom';
|
||||
|
||||
const canvas = document.getElementById('scene');
|
||||
canvas.style.cursor = 'grab';
|
||||
@@ -21,8 +21,13 @@ const tableDebugModes = {
|
||||
mirror: 10
|
||||
};
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const appInitialState = window.WebGLBookInitialState || {};
|
||||
const tableDebugName = urlParams.get('tableDebug') || 'none';
|
||||
const tableDebugMode = tableDebugModes[tableDebugName] ?? tableDebugModes.none;
|
||||
const isAppIntegrationMode = appInitialState.appMode === true;
|
||||
const html2CanvasPromise = isAppIntegrationMode
|
||||
? import('https://esm.sh/html2canvas@1.4.1')
|
||||
: null;
|
||||
const labStatus = document.getElementById('lab_status');
|
||||
if (labStatus && tableDebugMode !== tableDebugModes.none) {
|
||||
labStatus.textContent = tableDebugName === 'ao' ? 'scene debug: SSAO' : `table debug: ${tableDebugName}`;
|
||||
@@ -38,8 +43,14 @@ renderer.shadowMap.type = THREE.VSMShadowMap;
|
||||
const generatedTextureCanvases = {};
|
||||
const maxTextureAnisotropy = renderer.capabilities.getMaxAnisotropy();
|
||||
const reflectionPixelRatio = Math.min(window.devicePixelRatio || 1, 2);
|
||||
const pageTextureWidth = 3200;
|
||||
const pageTextureWidth = isAppIntegrationMode ? 1280 : 3200;
|
||||
const appPageTextureInset = 0;
|
||||
const reflectionTargetSize = new THREE.Vector2();
|
||||
const pageRaycaster = new THREE.Raycaster();
|
||||
const pointerNdc = new THREE.Vector2();
|
||||
let pageTextureRenderSerial = 0;
|
||||
let pageTextureRenderInProgress = false;
|
||||
let pageTextureRenderPending = false;
|
||||
let sceneComposerTarget = null;
|
||||
let composer = null;
|
||||
let sceneRenderPass = null;
|
||||
@@ -47,6 +58,7 @@ let sceneAoPass = null;
|
||||
let sceneSmaaPass = null;
|
||||
let sceneOutputPass = null;
|
||||
const aoExcludedObjects = new Set();
|
||||
let renderedFrameCount = 0;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x080604);
|
||||
@@ -59,11 +71,13 @@ let tableDustTexture = null;
|
||||
let tableGreaseTexture = null;
|
||||
const tableTopY = -0.02;
|
||||
const bookTableContactClearance = 0.002;
|
||||
const tableReflectionTarget = new THREE.WebGLRenderTarget(4096, 2304, {
|
||||
const tableReflectionBaseWidth = isAppIntegrationMode ? 1280 : 4096;
|
||||
const tableReflectionBaseHeight = isAppIntegrationMode ? 720 : 2304;
|
||||
const tableReflectionTarget = new THREE.WebGLRenderTarget(tableReflectionBaseWidth, tableReflectionBaseHeight, {
|
||||
colorSpace: THREE.SRGBColorSpace,
|
||||
depthBuffer: true,
|
||||
stencilBuffer: false,
|
||||
samples: renderer.capabilities.isWebGL2 ? 8 : 0
|
||||
samples: renderer.capabilities.isWebGL2 ? (isAppIntegrationMode ? 2 : 8) : 0
|
||||
});
|
||||
tableReflectionTarget.texture.colorSpace = THREE.SRGBColorSpace;
|
||||
tableReflectionTarget.texture.minFilter = THREE.LinearFilter;
|
||||
@@ -82,7 +96,7 @@ const reflectionUp = new THREE.Vector3();
|
||||
const candleShadowSources = [];
|
||||
const candleWorldPosition = new THREE.Vector3();
|
||||
const flameWorldPosition = new THREE.Vector3();
|
||||
const bookShadowMapSize = 1536;
|
||||
const bookShadowMapSize = isAppIntegrationMode ? 512 : 1536;
|
||||
const bookShadowTargets = Array.from({ length: 3 }, () => {
|
||||
const target = new THREE.WebGLRenderTarget(bookShadowMapSize, bookShadowMapSize, {
|
||||
colorSpace: THREE.NoColorSpace,
|
||||
@@ -119,6 +133,7 @@ const cameraRig = {
|
||||
minRadius: 2.4,
|
||||
maxRadius: 9.0,
|
||||
dragging: false,
|
||||
navigationActive: false,
|
||||
pointerX: 0,
|
||||
pointerY: 0,
|
||||
keys: new Set()
|
||||
@@ -135,9 +150,9 @@ configureScenePostprocessing();
|
||||
const clock = new THREE.Clock();
|
||||
const book = new THREE.Group();
|
||||
scene.add(book);
|
||||
const initialReadingProgress = THREE.MathUtils.clamp(Number.parseFloat(urlParams.get('progress') ?? '0.28'), 0, 1);
|
||||
const initialReadingProgress = THREE.MathUtils.clamp(Number.parseFloat(urlParams.get('progress') ?? appInitialState.progress ?? '0.28'), 0, 1);
|
||||
let readingProgress = Number.isFinite(initialReadingProgress) ? initialReadingProgress : 0.28;
|
||||
let bookPageCount = snapProceduralPageCount(urlParams.get('pages') ?? '240');
|
||||
let bookPageCount = snapProceduralPageCount(urlParams.get('pages') ?? appInitialState.pageCount ?? '240');
|
||||
let currentProceduralBookModel = null;
|
||||
const progressInput = document.getElementById('progress_control');
|
||||
const progressValue = document.getElementById('progress_value');
|
||||
@@ -382,12 +397,34 @@ window.BookLabDebug = {
|
||||
setReadingProgress(value);
|
||||
return readingProgress;
|
||||
},
|
||||
setBookPageCount(value) {
|
||||
setBookPageCount(value);
|
||||
return bookPageCount;
|
||||
},
|
||||
redrawPageTextures() {
|
||||
redrawPageTexturesFromDom();
|
||||
return true;
|
||||
},
|
||||
getTextureInfo() {
|
||||
return {
|
||||
pageTextureWidth,
|
||||
pageTextureHeight: leftCanvas.height,
|
||||
appPageTextureInset,
|
||||
debug: getPageTextureDebugState()
|
||||
};
|
||||
},
|
||||
projectPointerToPage(clientX, clientY) {
|
||||
return projectPointerToPage(clientX, clientY);
|
||||
},
|
||||
exportTexture(name) {
|
||||
if (name === 'left' || name === 'leftPage') return leftCanvas.toDataURL('image/png');
|
||||
if (name === 'right' || name === 'rightPage') return rightCanvas.toDataURL('image/png');
|
||||
return generatedTextureCanvases[name]?.toDataURL('image/png') || null;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
document.addEventListener('webgl-book:redraw-pages', redrawPageTexturesFromDom);
|
||||
installBookControls();
|
||||
installCameraControls();
|
||||
resize();
|
||||
@@ -1362,6 +1399,7 @@ function setReadingProgress(value) {
|
||||
readingProgress = nextProgress;
|
||||
buildBook();
|
||||
syncBookControls();
|
||||
window.WebGLBookPreferenceBridge?.updateProgress?.(readingProgress);
|
||||
}
|
||||
|
||||
function setBookPageCount(value) {
|
||||
@@ -1370,6 +1408,7 @@ function setBookPageCount(value) {
|
||||
bookPageCount = nextPageCount;
|
||||
buildBook();
|
||||
syncBookControls();
|
||||
window.WebGLBookPreferenceBridge?.updatePageCount?.(bookPageCount);
|
||||
}
|
||||
|
||||
function stepReadingProgress(pageDelta) {
|
||||
@@ -1405,6 +1444,179 @@ function syncBookControls() {
|
||||
if (fastForwardButton) fastForwardButton.disabled = busy || !canPageFlip(1);
|
||||
}
|
||||
|
||||
function redrawPageTexturesFromDom() {
|
||||
if (pageTextureRenderInProgress) {
|
||||
pageTextureRenderPending = true;
|
||||
return;
|
||||
}
|
||||
const leftSource = document.getElementById('page_left');
|
||||
const rightSource = document.getElementById('page_right');
|
||||
if (!leftSource && !rightSource) return;
|
||||
pageTextureRenderInProgress = true;
|
||||
const serial = ++pageTextureRenderSerial;
|
||||
(async () => {
|
||||
try {
|
||||
if (leftSource && await drawDomPageTexture(leftCanvas, leftSource, 'left')) {
|
||||
leftTexture.needsUpdate = true;
|
||||
}
|
||||
if (rightSource && await drawDomPageTexture(rightCanvas, rightSource, 'right')) {
|
||||
rightTexture.needsUpdate = true;
|
||||
}
|
||||
} finally {
|
||||
pageTextureRenderInProgress = false;
|
||||
if (pageTextureRenderPending && serial === pageTextureRenderSerial) {
|
||||
pageTextureRenderPending = false;
|
||||
redrawPageTexturesFromDom();
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
async function drawDomPageTexture(canvas, source, side) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#fffaf0';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const shade = ctx.createLinearGradient(0, 0, canvas.width, 0);
|
||||
shade.addColorStop(0, 'rgba(93, 55, 24, 0.10)');
|
||||
shade.addColorStop(side === 'left' ? 0.85 : 0.15, 'rgba(255, 255, 255, 0)');
|
||||
shade.addColorStop(1, 'rgba(85, 49, 21, 0.08)');
|
||||
ctx.fillStyle = shade;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const painted = await paintRasterizedDomPage(ctx, canvas, source);
|
||||
updatePageTextureDebugState(side, canvas, source, painted);
|
||||
return painted;
|
||||
}
|
||||
|
||||
function getPageTextureDebugState() {
|
||||
const rawState = document.documentElement.dataset.webglPageTextures;
|
||||
if (!rawState) return {};
|
||||
try {
|
||||
return JSON.parse(rawState);
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function updatePageTextureDebugState(side, canvas, source, painted) {
|
||||
const state = getPageTextureDebugState();
|
||||
state[side] = {
|
||||
painted,
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
sourceId: source.id || '',
|
||||
sourceTextLength: source.textContent?.trim().length || 0,
|
||||
darkPixels: countPageTextureDarkPixels(canvas)
|
||||
};
|
||||
document.documentElement.dataset.webglPageTextures = JSON.stringify(state);
|
||||
}
|
||||
|
||||
function countPageTextureDarkPixels(canvas) {
|
||||
const sampleCanvas = document.createElement('canvas');
|
||||
const sampleSize = 64;
|
||||
sampleCanvas.width = sampleSize;
|
||||
sampleCanvas.height = sampleSize;
|
||||
const sampleContext = sampleCanvas.getContext('2d');
|
||||
sampleContext.drawImage(canvas, 0, 0, sampleSize, sampleSize);
|
||||
const pixels = sampleContext.getImageData(0, 0, sampleSize, sampleSize).data;
|
||||
let darkPixels = 0;
|
||||
for (let index = 0; index < pixels.length; index += 4) {
|
||||
const alpha = pixels[index + 3];
|
||||
if (alpha < 8) continue;
|
||||
const luminance = pixels[index] * 0.2126 + pixels[index + 1] * 0.7152 + pixels[index + 2] * 0.0722;
|
||||
if (luminance < 96) darkPixels += 1;
|
||||
}
|
||||
return darkPixels;
|
||||
}
|
||||
|
||||
async function paintRasterizedDomPage(ctx, canvas, source) {
|
||||
const pageRect = source.getBoundingClientRect();
|
||||
if (pageRect.width <= 0 || pageRect.height <= 0) return false;
|
||||
const captured = await captureDomPageWithHtml2Canvas(source, pageRect, canvas);
|
||||
if (captured) {
|
||||
drawCapturedPageCanvas(ctx, canvas, captured);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function captureDomPageWithHtml2Canvas(source, pageRect, targetCanvas) {
|
||||
if (!html2CanvasPromise) return null;
|
||||
try {
|
||||
const module = await html2CanvasPromise;
|
||||
const html2canvas = module.default || module;
|
||||
return await html2canvas(source, {
|
||||
backgroundColor: null,
|
||||
logging: false,
|
||||
useCORS: true,
|
||||
allowTaint: false,
|
||||
foreignObjectRendering: true,
|
||||
x: pageRect.left,
|
||||
y: pageRect.top,
|
||||
width: pageRect.width,
|
||||
height: pageRect.height,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
windowWidth: Math.ceil(Math.max(window.innerWidth, pageRect.right)),
|
||||
windowHeight: Math.ceil(Math.max(window.innerHeight, pageRect.bottom)),
|
||||
scale: Math.max(1, targetCanvas.width / pageRect.width)
|
||||
});
|
||||
} catch (error) {
|
||||
document.documentElement.dataset.webglLastCaptureError = error?.message || String(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function drawCapturedPageCanvas(ctx, canvas, captured) {
|
||||
const insetX = canvas.width * appPageTextureInset;
|
||||
const insetY = canvas.height * appPageTextureInset * 0.35;
|
||||
ctx.drawImage(
|
||||
captured,
|
||||
insetX,
|
||||
insetY,
|
||||
canvas.width - insetX * 2,
|
||||
canvas.height - insetY * 2
|
||||
);
|
||||
}
|
||||
|
||||
function projectPointerToPage(clientX, clientY) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
if (rect.width <= 0 || rect.height <= 0) return null;
|
||||
pointerNdc.set(
|
||||
((clientX - rect.left) / rect.width) * 2 - 1,
|
||||
-(((clientY - rect.top) / rect.height) * 2 - 1)
|
||||
);
|
||||
pageRaycaster.setFromCamera(pointerNdc, camera);
|
||||
const intersections = pageRaycaster.intersectObjects(book.children, true);
|
||||
for (const hit of intersections) {
|
||||
const pageSide = textureHitPageSide(hit);
|
||||
if (!pageSide || !hit.uv) continue;
|
||||
const insetX = appPageTextureInset;
|
||||
const insetY = appPageTextureInset * 0.35;
|
||||
const mappedX = THREE.MathUtils.clamp((hit.uv.x - insetX) / Math.max(0.001, 1 - insetX * 2), 0, 1);
|
||||
const mappedY = 1 - THREE.MathUtils.clamp((hit.uv.y - insetY) / Math.max(0.001, 1 - insetY * 2), 0, 1);
|
||||
return {
|
||||
pageId: pageSide === 'left' ? 'page_left' : 'page_right',
|
||||
x: mappedX,
|
||||
y: mappedY,
|
||||
uv: { x: hit.uv.x, y: hit.uv.y }
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function textureHitPageSide(hit) {
|
||||
const material = Array.isArray(hit.object.material)
|
||||
? hit.object.material[hit.face?.materialIndex ?? 0]
|
||||
: hit.object.material;
|
||||
if (material === materials.leftPage) return 'left';
|
||||
if (material === materials.rightPage) return 'right';
|
||||
if (material?.map === leftTexture) return 'left';
|
||||
if (material?.map === rightTexture) return 'right';
|
||||
return null;
|
||||
}
|
||||
|
||||
function startPageFlip(direction) {
|
||||
if (activeFlips.length || !currentProceduralBookModel || !canPageFlip(direction)) return false;
|
||||
const flip = createPageFlip(direction, performance.now(), normalFlipDuration);
|
||||
@@ -1956,41 +2168,9 @@ function createPageCanvas(side) {
|
||||
shade.addColorStop(1, 'rgba(85, 49, 21, 0.08)');
|
||||
ctx.fillStyle = shade;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = inkColor;
|
||||
ctx.textBaseline = 'top';
|
||||
const layout = hardcoverPageLayout(canvas, side);
|
||||
if (side === 'left') {
|
||||
drawTitlePage(ctx, layout);
|
||||
} else {
|
||||
drawNovelPage(ctx, layout, 'Click on new game or load to start the game');
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function hardcoverPageLayout(canvas, side) {
|
||||
const inner = canvas.width * 0.125;
|
||||
const outer = canvas.width * 0.075;
|
||||
const top = canvas.height * 0.085;
|
||||
const bottom = canvas.height * 0.115;
|
||||
const margins = {
|
||||
left: side === 'right' ? inner : outer,
|
||||
right: side === 'right' ? outer : inner,
|
||||
top,
|
||||
bottom
|
||||
};
|
||||
const width = canvas.width - margins.left - margins.right;
|
||||
const height = canvas.height - margins.top - margins.bottom;
|
||||
return {
|
||||
margins,
|
||||
x: margins.left,
|
||||
y: margins.top,
|
||||
width,
|
||||
height,
|
||||
em: width / 24
|
||||
};
|
||||
}
|
||||
|
||||
function createLeatherTextures() {
|
||||
const size = 1024;
|
||||
const colorCanvas = document.createElement('canvas');
|
||||
@@ -2424,49 +2604,6 @@ function tintAmbientFromCanvas(canvas) {
|
||||
candleBounceLight.intensity = 0.28;
|
||||
}
|
||||
|
||||
function drawTitlePage(ctx, layout) {
|
||||
const titleX = layout.x;
|
||||
const titleWidth = layout.width;
|
||||
drawCentered(ctx, 'Georg Tomitsch', layout.y + layout.height * 0.18, layout.em * 0.62, titleX, titleWidth);
|
||||
drawCentered(ctx, 'Eibenreith', layout.y + layout.height * 0.235, layout.em * 1.72, titleX, titleWidth);
|
||||
drawCentered(ctx, 'Ein Kaiserpunk Abenteuer', layout.y + layout.height * 0.315, layout.em * 0.76, titleX, titleWidth);
|
||||
drawCentered(ctx, 'speech | autoplay | speed | new game | save | load | options', layout.y + layout.height * 0.47, layout.em * 0.42, titleX, titleWidth);
|
||||
drawCentered(ctx, 'click on page or press spacebar to fast forward text animation', layout.y + layout.height * 0.56, layout.em * 0.42, titleX, titleWidth);
|
||||
}
|
||||
|
||||
function drawNovelPage(ctx, layout, text) {
|
||||
const projectedX = Math.max(layout.margins.left * 0.25, layout.x - layout.margins.left * 0.75);
|
||||
const projectedWidth = layout.width * 0.7;
|
||||
drawParagraph(ctx, text, projectedX, layout.y + layout.height * 0.1, projectedWidth, layout.em * 0.72, 1.36, 0);
|
||||
}
|
||||
|
||||
function drawCentered(ctx, text, y, size, x = 0, width = ctx.canvas.width) {
|
||||
ctx.font = `${Math.round(size)}px Georgia, "Times New Roman", serif`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(text, x + width * 0.5, y);
|
||||
}
|
||||
|
||||
function drawParagraph(ctx, text, x, y, width, size, lineHeight, firstLineIndent = 0) {
|
||||
const fontSize = Math.round(size);
|
||||
ctx.font = `${fontSize}px Georgia, "Times New Roman", serif`;
|
||||
ctx.textAlign = 'left';
|
||||
const words = text.split(/\s+/);
|
||||
let line = '';
|
||||
let indent = firstLineIndent;
|
||||
words.forEach((word) => {
|
||||
const test = line ? `${line} ${word}` : word;
|
||||
if (ctx.measureText(test).width > width - indent && line) {
|
||||
ctx.fillText(line, x + indent, y);
|
||||
line = word;
|
||||
y += fontSize * lineHeight;
|
||||
indent = 0;
|
||||
} else {
|
||||
line = test;
|
||||
}
|
||||
});
|
||||
if (line) ctx.fillText(line, x + indent, y);
|
||||
}
|
||||
|
||||
function resize() {
|
||||
const width = Math.max(1, window.innerWidth);
|
||||
const height = Math.max(1, window.innerHeight);
|
||||
@@ -2481,8 +2618,8 @@ function resize() {
|
||||
4096 / width,
|
||||
2304 / height
|
||||
));
|
||||
const reflectionWidth = Math.floor(width * reflectionScale);
|
||||
const reflectionHeight = Math.floor(height * reflectionScale);
|
||||
const reflectionWidth = Math.min(tableReflectionBaseWidth, Math.floor(width * reflectionScale));
|
||||
const reflectionHeight = Math.min(tableReflectionBaseHeight, Math.floor(height * reflectionScale));
|
||||
reflectionTargetSize.set(reflectionWidth, reflectionHeight);
|
||||
tableReflectionTarget.setSize(
|
||||
reflectionTargetSize.x,
|
||||
@@ -2491,8 +2628,14 @@ function resize() {
|
||||
}
|
||||
|
||||
function installCameraControls() {
|
||||
canvas.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
canvas.addEventListener('pointerdown', (event) => {
|
||||
if (event.button !== 2) return;
|
||||
cameraRig.dragging = true;
|
||||
cameraRig.navigationActive = true;
|
||||
canvas.style.cursor = 'grabbing';
|
||||
cameraRig.pointerX = event.clientX;
|
||||
cameraRig.pointerY = event.clientY;
|
||||
@@ -2515,17 +2658,23 @@ function installCameraControls() {
|
||||
});
|
||||
|
||||
canvas.addEventListener('pointerup', (event) => {
|
||||
if (event.button !== 2) return;
|
||||
cameraRig.dragging = false;
|
||||
cameraRig.navigationActive = false;
|
||||
cameraRig.keys.clear();
|
||||
canvas.style.cursor = 'grab';
|
||||
canvas.releasePointerCapture(event.pointerId);
|
||||
});
|
||||
|
||||
canvas.addEventListener('pointercancel', () => {
|
||||
cameraRig.dragging = false;
|
||||
cameraRig.navigationActive = false;
|
||||
cameraRig.keys.clear();
|
||||
canvas.style.cursor = 'grab';
|
||||
});
|
||||
|
||||
canvas.addEventListener('wheel', (event) => {
|
||||
if (!cameraRig.navigationActive) return;
|
||||
event.preventDefault();
|
||||
const zoom = Math.exp(event.deltaY * 0.001);
|
||||
cameraRig.radius = THREE.MathUtils.clamp(
|
||||
@@ -2537,6 +2686,7 @@ function installCameraControls() {
|
||||
}, { passive: false });
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (!cameraRig.navigationActive) return;
|
||||
if (['KeyW', 'KeyA', 'KeyS', 'KeyD'].includes(event.code)) {
|
||||
cameraRig.keys.add(event.code);
|
||||
event.preventDefault();
|
||||
@@ -2679,6 +2829,7 @@ function updateTableReflection() {
|
||||
const previousXrEnabled = renderer.xr.enabled;
|
||||
const previousShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
||||
const previousToneMappingExposure = renderer.toneMappingExposure;
|
||||
const pageTextureState = suppressPageContentMaps();
|
||||
|
||||
tableMesh.userData.wasVisibleForTableReflection = tableMesh.visible;
|
||||
tableMesh.visible = false;
|
||||
@@ -2692,10 +2843,29 @@ function updateTableReflection() {
|
||||
renderer.toneMappingExposure = previousToneMappingExposure;
|
||||
renderer.shadowMap.autoUpdate = previousShadowAutoUpdate;
|
||||
renderer.xr.enabled = previousXrEnabled;
|
||||
restorePageContentMaps(pageTextureState);
|
||||
tableMesh.visible = tableMesh.userData.wasVisibleForTableReflection;
|
||||
delete tableMesh.userData.wasVisibleForTableReflection;
|
||||
}
|
||||
|
||||
function suppressPageContentMaps() {
|
||||
if (!isAppIntegrationMode) return null;
|
||||
return [materials.leftPage, materials.rightPage].map((material) => {
|
||||
const previousMap = material.map;
|
||||
material.map = null;
|
||||
material.needsUpdate = true;
|
||||
return { material, previousMap };
|
||||
});
|
||||
}
|
||||
|
||||
function restorePageContentMaps(state) {
|
||||
if (!state) return;
|
||||
state.forEach(({ material, previousMap }) => {
|
||||
material.map = previousMap;
|
||||
material.needsUpdate = true;
|
||||
});
|
||||
}
|
||||
|
||||
function renderMirrorDebugView() {
|
||||
const hiddenObjects = [];
|
||||
scene.traverse((object) => {
|
||||
@@ -2746,8 +2916,13 @@ function animate() {
|
||||
});
|
||||
updateActiveFlips(performance.now());
|
||||
updateCandleShadowUniforms();
|
||||
updateBookShadowMaps();
|
||||
updateTableReflection();
|
||||
renderedFrameCount += 1;
|
||||
if (!isAppIntegrationMode || renderedFrameCount % 6 === 1 || activeFlips.length > 0) {
|
||||
updateBookShadowMaps();
|
||||
}
|
||||
if (!isAppIntegrationMode || renderedFrameCount % 4 === 1 || cameraRig.navigationActive || activeFlips.length > 0) {
|
||||
updateTableReflection();
|
||||
}
|
||||
if (tableDebugMode === tableDebugModes.mirror) {
|
||||
renderer.setRenderTarget(null);
|
||||
renderer.clear();
|
||||
|
||||
Reference in New Issue
Block a user