Checkpoint WebGL page and mirror debug fixes
This commit is contained in:
@@ -329,15 +329,14 @@ function addSimulatedStackBodies(group, context, model) {
|
|||||||
const sideLines = model.lines.filter((line) => line.side === side);
|
const sideLines = model.lines.filter((line) => line.side === side);
|
||||||
if (!sideLines.length) return;
|
if (!sideLines.length) return;
|
||||||
const isSinglePage = sideLines.length === 1;
|
const isSinglePage = sideLines.length === 1;
|
||||||
const isHairOnlyPage = isSinglePage && sideLines[0].isHairPage === true;
|
|
||||||
const bodyLines = isSinglePage ? createSinglePageBodyLines(context, model, sideLines[0]) : sideLines;
|
const bodyLines = isSinglePage ? createSinglePageBodyLines(context, model, sideLines[0]) : sideLines;
|
||||||
const mesh = new THREE.Mesh(createLoftedLineBody(model, bodyLines, model.pageDepth), createStackBodyMaterials(context, model, side, isSinglePage, isHairOnlyPage));
|
const mesh = new THREE.Mesh(createLoftedLineBody(model, bodyLines, model.pageDepth), createStackBodyMaterials(context, model, side, isSinglePage));
|
||||||
mesh.userData.bookPart = side < 0 ? 'leftPages' : 'rightPages';
|
mesh.userData.bookPart = side < 0 ? 'leftPages' : 'rightPages';
|
||||||
group.add(mesh);
|
group.add(mesh);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStackBodyMaterials(context, model, side, isSinglePage = false, isHairOnlyPage = false) {
|
function createStackBodyMaterials(context, model, side, isSinglePage = false) {
|
||||||
const baseColor = side < 0 ? '#d8c7a4' : '#e7d6b4';
|
const baseColor = side < 0 ? '#d8c7a4' : '#e7d6b4';
|
||||||
const lineColor = '#9a8058';
|
const lineColor = '#9a8058';
|
||||||
const layerTexture = createStackLayerTexture(context, model.bundleCount, baseColor, lineColor);
|
const layerTexture = createStackLayerTexture(context, model.bundleCount, baseColor, lineColor);
|
||||||
@@ -351,9 +350,7 @@ function createStackBodyMaterials(context, model, side, isSinglePage = false, is
|
|||||||
const edge = surface.clone();
|
const edge = surface.clone();
|
||||||
edge.map = layerTexture;
|
edge.map = layerTexture;
|
||||||
const bottom = context.materials.pageTop.clone();
|
const bottom = context.materials.pageTop.clone();
|
||||||
const top = isHairOnlyPage
|
const top = side < 0 && context.materials.leftPage
|
||||||
? context.materials.pageTop.clone()
|
|
||||||
: side < 0 && context.materials.leftPage
|
|
||||||
? context.materials.leftPage
|
? context.materials.leftPage
|
||||||
: side > 0 && context.materials.rightPage
|
: side > 0 && context.materials.rightPage
|
||||||
? context.materials.rightPage
|
? context.materials.rightPage
|
||||||
@@ -406,7 +403,6 @@ function createLoftedLineBody(model, lines, depth) {
|
|||||||
const indices = [];
|
const indices = [];
|
||||||
const smoothLines = lines.map((line) => line.points);
|
const smoothLines = lines.map((line) => line.points);
|
||||||
const bundleCount = model.bundleCount;
|
const bundleCount = model.bundleCount;
|
||||||
const allPagesOnOneSide = lines.length === model.bundleCount && lines.every((line) => line.isHairPage !== true);
|
|
||||||
const push = (point, z, uv) => {
|
const push = (point, z, uv) => {
|
||||||
const index = positions.length / 3;
|
const index = positions.length / 3;
|
||||||
positions.push(point.x, point.y, z);
|
positions.push(point.x, point.y, z);
|
||||||
@@ -434,13 +430,10 @@ function createLoftedLineBody(model, lines, depth) {
|
|||||||
});
|
});
|
||||||
const topCapUv = (point, z, col, row) => {
|
const topCapUv = (point, z, col, row) => {
|
||||||
const side = lines[row]?.side ?? 1;
|
const side = lines[row]?.side ?? 1;
|
||||||
const originalU = smoothLines[row].length <= 1 ? 0.5 : col / (smoothLines[row].length - 1);
|
|
||||||
const pageDistance = side > 0
|
const pageDistance = side > 0
|
||||||
? point.x - model.spineHalf
|
? point.x - model.spineHalf
|
||||||
: -model.spineHalf - point.x;
|
: -model.spineHalf - point.x;
|
||||||
const pageU = allPagesOnOneSide
|
const pageU = THREE.MathUtils.clamp(pageDistance / model.pageWidth, 0, 1);
|
||||||
? THREE.MathUtils.clamp(pageDistance / model.pageWidth, 0, 1)
|
|
||||||
: originalU;
|
|
||||||
return {
|
return {
|
||||||
u: side < 0 ? 1 - pageU : pageU,
|
u: side < 0 ? 1 - pageU : pageU,
|
||||||
v: 1 - ((z + depth * 0.5) / depth)
|
v: 1 - ((z + depth * 0.5) / depth)
|
||||||
@@ -476,8 +469,8 @@ function createLoftedLineBody(model, lines, depth) {
|
|||||||
indices.push(frontA, backA, frontB);
|
indices.push(frontA, backA, frontB);
|
||||||
indices.push(frontB, backA, backB);
|
indices.push(frontB, backA, backB);
|
||||||
}
|
}
|
||||||
const bottomRow = allPagesOnOneSide ? smoothLines.length - 1 : 0;
|
const bottomRow = 0;
|
||||||
const topRow = allPagesOnOneSide ? 0 : smoothLines.length - 1;
|
const topRow = smoothLines.length - 1;
|
||||||
const bottomStart = indices.length;
|
const bottomStart = indices.length;
|
||||||
const bottomFront = smoothLines[bottomRow].map((point, col) => push(point, depth * 0.5, capUv(point, depth * 0.5, col, bottomRow)));
|
const bottomFront = smoothLines[bottomRow].map((point, col) => push(point, depth * 0.5, capUv(point, depth * 0.5, col, bottomRow)));
|
||||||
const bottomBack = smoothLines[bottomRow].map((point, col) => push(point, -depth * 0.5, capUv(point, -depth * 0.5, col, bottomRow)));
|
const bottomBack = smoothLines[bottomRow].map((point, col) => push(point, -depth * 0.5, capUv(point, -depth * 0.5, col, bottomRow)));
|
||||||
@@ -513,14 +506,10 @@ function createLoftedLineBody(model, lines, depth) {
|
|||||||
indices.push(frontA, backA, frontB);
|
indices.push(frontA, backA, frontB);
|
||||||
indices.push(frontB, backA, backB);
|
indices.push(frontB, backA, backB);
|
||||||
}
|
}
|
||||||
const pointA = smoothLines[topRow][col];
|
|
||||||
const pointB = smoothLines[topRow][col + 1];
|
|
||||||
const middleX = (pointA.x + pointB.x) * 0.5;
|
|
||||||
const isSpineArc = Math.abs(middleX) < model.spineHalf;
|
|
||||||
topGroups.push({
|
topGroups.push({
|
||||||
start: groupStart,
|
start: groupStart,
|
||||||
count: indices.length - groupStart,
|
count: indices.length - groupStart,
|
||||||
materialIndex: allPagesOnOneSide && isSpineArc ? 4 : 3
|
materialIndex: 3
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new THREE.BufferGeometry();
|
||||||
@@ -537,13 +526,20 @@ function createLoftedLineBody(model, lines, depth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createSinglePageBodyLines(context, model, line) {
|
function createSinglePageBodyLines(context, model, line) {
|
||||||
const supportPoints = line.points.map((point) => ({
|
const topPoints = line.points.map((point) => ({
|
||||||
|
x: point.x,
|
||||||
|
y: Math.max(
|
||||||
|
coverTopYAtX(context, point.x) + coverClearance(model.bundleCount) + PROCEDURAL_BOOK.PROFILE.singlePageCoverGap + model.bundleSpacing,
|
||||||
|
point.y
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
const supportPoints = topPoints.map((point) => ({
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: Math.max(coverTopYAtX(context, point.x) + coverClearance(model.bundleCount) + PROCEDURAL_BOOK.PROFILE.singlePageCoverGap, point.y - model.bundleSpacing)
|
y: Math.max(coverTopYAtX(context, point.x) + coverClearance(model.bundleCount) + PROCEDURAL_BOOK.PROFILE.singlePageCoverGap, point.y - model.bundleSpacing)
|
||||||
}));
|
}));
|
||||||
return [
|
return [
|
||||||
{ ...line, points: supportPoints, endpoint: supportPoints[supportPoints.length - 1] },
|
{ ...line, points: supportPoints, endpoint: supportPoints[supportPoints.length - 1] },
|
||||||
line
|
{ ...line, points: topPoints, endpoint: topPoints[topPoints.length - 1] }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,7 +752,7 @@ function calculateBundleSpacing(bundleCount, spineWidth, leftCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function calculateLeftBundleCount(context, bundleCount) {
|
function calculateLeftBundleCount(context, bundleCount) {
|
||||||
return THREE.MathUtils.clamp(Math.round(bundleCount * context.readingProgress), 0, bundleCount);
|
return THREE.MathUtils.clamp(Math.round(bundleCount * context.readingProgress), 1, bundleCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSpineArcSamples(spineWidth) {
|
function buildSpineArcSamples(spineWidth) {
|
||||||
|
|||||||
+73
-27
@@ -17,7 +17,8 @@ const tableDebugModes = {
|
|||||||
scene: 5,
|
scene: 5,
|
||||||
mask: 6,
|
mask: 6,
|
||||||
ao: 7,
|
ao: 7,
|
||||||
grease: 8
|
grease: 8,
|
||||||
|
mirror: 10
|
||||||
};
|
};
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const tableDebugName = urlParams.get('tableDebug') || 'none';
|
const tableDebugName = urlParams.get('tableDebug') || 'none';
|
||||||
@@ -37,6 +38,7 @@ renderer.shadowMap.type = THREE.VSMShadowMap;
|
|||||||
const generatedTextureCanvases = {};
|
const generatedTextureCanvases = {};
|
||||||
const maxTextureAnisotropy = renderer.capabilities.getMaxAnisotropy();
|
const maxTextureAnisotropy = renderer.capabilities.getMaxAnisotropy();
|
||||||
const reflectionPixelRatio = Math.min(window.devicePixelRatio || 1, 2);
|
const reflectionPixelRatio = Math.min(window.devicePixelRatio || 1, 2);
|
||||||
|
const pageTextureWidth = 3200;
|
||||||
const reflectionTargetSize = new THREE.Vector2();
|
const reflectionTargetSize = new THREE.Vector2();
|
||||||
let sceneComposerTarget = null;
|
let sceneComposerTarget = null;
|
||||||
let composer = null;
|
let composer = null;
|
||||||
@@ -1151,6 +1153,7 @@ function configureTableShader(material) {
|
|||||||
if (tableDebugMode == 5) outgoingLight = sceneReflection;
|
if (tableDebugMode == 5) outgoingLight = sceneReflection;
|
||||||
if (tableDebugMode == 6) outgoingLight = vec3(tableReflectionMask);
|
if (tableDebugMode == 6) outgoingLight = vec3(tableReflectionMask);
|
||||||
if (tableDebugMode == 8) outgoingLight = vec3(grease);
|
if (tableDebugMode == 8) outgoingLight = vec3(grease);
|
||||||
|
if (tableDebugMode == 10) outgoingLight = combinedReflection;
|
||||||
#include <opaque_fragment>`
|
#include <opaque_fragment>`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1787,8 +1790,8 @@ function createGutterGeometry(width, height, depth) {
|
|||||||
|
|
||||||
function createPageCanvas(side) {
|
function createPageCanvas(side) {
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = 3000;
|
canvas.width = pageTextureWidth;
|
||||||
canvas.height = 4500;
|
canvas.height = Math.round(pageTextureWidth * PROCEDURAL_BOOK.PAGE_DEPTH / PROCEDURAL_BOOK.PAGE_WIDTH);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
ctx.fillStyle = '#f5dfab';
|
ctx.fillStyle = '#f5dfab';
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
@@ -1804,29 +1807,35 @@ function createPageCanvas(side) {
|
|||||||
|
|
||||||
ctx.fillStyle = inkColor;
|
ctx.fillStyle = inkColor;
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
const margins = hardcoverPageMargins(canvas, side);
|
const layout = hardcoverPageLayout(canvas, side);
|
||||||
const contentWidth = canvas.width - margins.left - margins.right;
|
|
||||||
const contentHeight = canvas.height - margins.top - margins.bottom;
|
|
||||||
if (side === 'left') {
|
if (side === 'left') {
|
||||||
drawCentered(ctx, 'Georg Tomitsch', margins.top + contentHeight * 0.07, 58, margins.left, contentWidth);
|
drawTitlePage(ctx, layout);
|
||||||
drawCentered(ctx, 'Eibenreith', margins.top + contentHeight * 0.12, 132, margins.left, contentWidth);
|
|
||||||
drawCentered(ctx, 'Ein Kaiserpunk Abenteuer', margins.top + contentHeight * 0.185, 70, margins.left, contentWidth);
|
|
||||||
drawCentered(ctx, 'speech | autoplay | speed | new game | save | load | options', margins.top + contentHeight * 0.30, 42, margins.left, contentWidth);
|
|
||||||
drawCentered(ctx, 'click on page or press spacebar to fast forward text animation', margins.top + contentHeight * 0.36, 42, margins.left, contentWidth);
|
|
||||||
} else {
|
} else {
|
||||||
drawParagraph(ctx, 'Click on new game or load to start the game', margins.left, margins.top, contentWidth, 86, 1.42);
|
drawNovelPage(ctx, layout, 'Click on new game or load to start the game');
|
||||||
}
|
}
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hardcoverPageMargins(canvas, side) {
|
function hardcoverPageLayout(canvas, side) {
|
||||||
const gutter = canvas.width * 0.17;
|
const inner = canvas.width * 0.125;
|
||||||
const outer = canvas.width * 0.12;
|
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 {
|
return {
|
||||||
left: side === 'right' ? gutter : outer,
|
margins,
|
||||||
right: side === 'right' ? outer : gutter,
|
x: margins.left,
|
||||||
top: canvas.height * 0.14,
|
y: margins.top,
|
||||||
bottom: canvas.height * 0.18
|
width,
|
||||||
|
height,
|
||||||
|
em: width / 24
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1983,28 +1992,45 @@ function tintAmbientFromCanvas(canvas) {
|
|||||||
candleBounceLight.intensity = 0.28;
|
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) {
|
||||||
|
drawParagraph(ctx, text, layout.x, layout.y, layout.width, layout.em * 0.98, 1.36, layout.em * 1.25);
|
||||||
|
}
|
||||||
|
|
||||||
function drawCentered(ctx, text, y, size, x = 0, width = ctx.canvas.width) {
|
function drawCentered(ctx, text, y, size, x = 0, width = ctx.canvas.width) {
|
||||||
ctx.font = `${size}px Georgia, "Times New Roman", serif`;
|
ctx.font = `${Math.round(size)}px Georgia, "Times New Roman", serif`;
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.fillText(text, x + width * 0.5, y);
|
ctx.fillText(text, x + width * 0.5, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawParagraph(ctx, text, x, y, width, size, lineHeight) {
|
function drawParagraph(ctx, text, x, y, width, size, lineHeight, firstLineIndent = 0) {
|
||||||
ctx.font = `${size}px Georgia, "Times New Roman", serif`;
|
const fontSize = Math.round(size);
|
||||||
|
ctx.font = `${fontSize}px Georgia, "Times New Roman", serif`;
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
const words = text.split(/\s+/);
|
const words = text.split(/\s+/);
|
||||||
let line = '';
|
let line = '';
|
||||||
|
let indent = firstLineIndent;
|
||||||
words.forEach((word) => {
|
words.forEach((word) => {
|
||||||
const test = line ? `${line} ${word}` : word;
|
const test = line ? `${line} ${word}` : word;
|
||||||
if (ctx.measureText(test).width > width && line) {
|
if (ctx.measureText(test).width > width - indent && line) {
|
||||||
ctx.fillText(line, x, y);
|
ctx.fillText(line, x + indent, y);
|
||||||
line = word;
|
line = word;
|
||||||
y += size * lineHeight;
|
y += fontSize * lineHeight;
|
||||||
|
indent = 0;
|
||||||
} else {
|
} else {
|
||||||
line = test;
|
line = test;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (line) ctx.fillText(line, x, y);
|
if (line) ctx.fillText(line, x + indent, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
@@ -2236,6 +2262,22 @@ function updateTableReflection() {
|
|||||||
delete tableMesh.userData.wasVisibleForTableReflection;
|
delete tableMesh.userData.wasVisibleForTableReflection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderMirrorDebugView() {
|
||||||
|
const hiddenObjects = [];
|
||||||
|
scene.traverse((object) => {
|
||||||
|
if (object === tableMesh || !object.visible) return;
|
||||||
|
if (!object.isMesh && !object.isLine && !object.isPoints && !object.isSprite) return;
|
||||||
|
object.userData.wasVisibleForMirrorDebug = true;
|
||||||
|
object.visible = false;
|
||||||
|
hiddenObjects.push(object);
|
||||||
|
});
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
hiddenObjects.forEach((object) => {
|
||||||
|
object.visible = object.userData.wasVisibleForMirrorDebug;
|
||||||
|
delete object.userData.wasVisibleForMirrorDebug;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
const delta = clock.getDelta();
|
const delta = clock.getDelta();
|
||||||
@@ -2272,7 +2314,11 @@ function animate() {
|
|||||||
updateCandleShadowUniforms();
|
updateCandleShadowUniforms();
|
||||||
updateBookShadowMaps();
|
updateBookShadowMaps();
|
||||||
updateTableReflection();
|
updateTableReflection();
|
||||||
if (composer) {
|
if (tableDebugMode === tableDebugModes.mirror) {
|
||||||
|
renderer.setRenderTarget(null);
|
||||||
|
renderer.clear();
|
||||||
|
renderMirrorDebugView();
|
||||||
|
} else if (composer) {
|
||||||
composer.render();
|
composer.render();
|
||||||
} else {
|
} else {
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
|
|||||||
Reference in New Issue
Block a user