Texture procedural page stack lines
This commit is contained in:
@@ -197,7 +197,6 @@ function rebuildBook() {
|
|||||||
addCoverAssembly(pageWidth, coverDepth, coverThickness, spineWidth);
|
addCoverAssembly(pageWidth, coverDepth, coverThickness, spineWidth);
|
||||||
addClothSpine(pageDepth, spineWidth);
|
addClothSpine(pageDepth, spineWidth);
|
||||||
addSimulatedStackBodies(lines, pageDepth);
|
addSimulatedStackBodies(lines, pageDepth);
|
||||||
addSimulatedPageLines(lines, pageDepth);
|
|
||||||
updateFlipControls();
|
updateFlipControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,24 +723,13 @@ function currentSpineHalf() {
|
|||||||
return Math.max(0.16, Math.round(pageCount / 10) * BOOK_PROFILE.bundleSpacing) * 0.5;
|
return Math.max(0.16, Math.round(pageCount / 10) * BOOK_PROFILE.bundleSpacing) * 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSimulatedPageLines(lines, depth) {
|
|
||||||
const leftMaterial = new THREE.LineBasicMaterial({ color: 0x8f7750, transparent: true, opacity: 0.72 });
|
|
||||||
const rightMaterial = new THREE.LineBasicMaterial({ color: 0x9a8058, transparent: true, opacity: 0.72 });
|
|
||||||
const z = depth * 0.5 + 0.006;
|
|
||||||
lines.forEach((line) => {
|
|
||||||
const points = line.points.map((point) => new THREE.Vector3(point.x, point.y, z));
|
|
||||||
book.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(points), line.side < 0 ? leftMaterial : rightMaterial));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSimulatedStackBodies(lines, depth) {
|
function addSimulatedStackBodies(lines, depth) {
|
||||||
[-1, 1].forEach((side) => {
|
[-1, 1].forEach((side) => {
|
||||||
const sideLines = lines.filter((line) => line.side === side);
|
const sideLines = lines.filter((line) => line.side === side);
|
||||||
if (!sideLines.length) return;
|
if (!sideLines.length) return;
|
||||||
const material = side < 0 ? materials.pagesLeft : materials.pagesRight;
|
|
||||||
const bodyLines = sideLines.length === 1 ? createSinglePageBodyLines(sideLines[0]) : sideLines;
|
const bodyLines = sideLines.length === 1 ? createSinglePageBodyLines(sideLines[0]) : sideLines;
|
||||||
book.add(new THREE.Mesh(createLoftedLineBody(bodyLines, depth), material));
|
const stackMaterials = createStackBodyMaterials(bodyLines, side);
|
||||||
book.add(new THREE.Line(createEndpointPolyline(bodyLines, depth), new THREE.LineBasicMaterial({ color: 0xb99a68, transparent: true, opacity: 0.62 })));
|
book.add(new THREE.Mesh(createLoftedLineBody(bodyLines, depth), stackMaterials));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,17 +745,78 @@ function createSinglePageBodyLines(line) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createStackBodyMaterials(lines, side) {
|
||||||
|
const baseColor = side < 0 ? '#d8c7a4' : '#e7d6b4';
|
||||||
|
const lineColor = '#9a8058';
|
||||||
|
const layerTexture = createStackLayerTexture(lines.length, baseColor, lineColor);
|
||||||
|
return [
|
||||||
|
new THREE.MeshBasicMaterial({ map: layerTexture, side: THREE.DoubleSide }),
|
||||||
|
new THREE.MeshBasicMaterial({ map: layerTexture, side: THREE.DoubleSide }),
|
||||||
|
new THREE.MeshBasicMaterial({ color: baseColor, side: THREE.DoubleSide })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function createStackLayerTexture(lineCount, baseColor, lineColor) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 2048;
|
||||||
|
canvas.height = 1024;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.fillStyle = baseColor;
|
||||||
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
context.strokeStyle = lineColor;
|
||||||
|
context.globalAlpha = 0.95;
|
||||||
|
context.lineWidth = 4.2;
|
||||||
|
context.lineCap = 'square';
|
||||||
|
for (let row = 0; row < lineCount; row += 1) {
|
||||||
|
const v = lineCount <= 1 ? 0.5 : row / (lineCount - 1);
|
||||||
|
const y = (1 - v) * canvas.height;
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(-8, y);
|
||||||
|
context.lineTo(canvas.width + 8, y);
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
return createCanvasTexture(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCanvasTexture(canvas) {
|
||||||
|
const texture = new THREE.CanvasTexture(canvas);
|
||||||
|
texture.colorSpace = THREE.SRGBColorSpace;
|
||||||
|
texture.anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy());
|
||||||
|
texture.needsUpdate = true;
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
function createLoftedLineBody(lines, depth) {
|
function createLoftedLineBody(lines, depth) {
|
||||||
const positions = [];
|
const positions = [];
|
||||||
|
const uvs = [];
|
||||||
const indices = [];
|
const indices = [];
|
||||||
const smoothLines = lines.map((line) => line.points);
|
const smoothLines = lines.map((line) => line.points);
|
||||||
const push = (point, z) => {
|
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);
|
||||||
|
uvs.push(uv.u, uv.v);
|
||||||
return index;
|
return index;
|
||||||
};
|
};
|
||||||
const front = smoothLines.map((points) => points.map((point) => push(point, depth * 0.5)));
|
const rowUv = (row) => (
|
||||||
const back = smoothLines.map((points) => points.map((point) => push(point, -depth * 0.5)));
|
smoothLines.length <= 1 ? 0.5 : row / (smoothLines.length - 1)
|
||||||
|
);
|
||||||
|
const colUv = (points, col) => (
|
||||||
|
points.length <= 1 ? 0.5 : col / (points.length - 1)
|
||||||
|
);
|
||||||
|
const lineUv = (row, col) => ({
|
||||||
|
u: colUv(smoothLines[row], col),
|
||||||
|
v: rowUv(row)
|
||||||
|
});
|
||||||
|
const backLineUv = (row, col) => ({
|
||||||
|
u: colUv(smoothLines[row], col),
|
||||||
|
v: rowUv(row)
|
||||||
|
});
|
||||||
|
const sideUv = (row, z) => ({
|
||||||
|
u: (z + depth * 0.5) / depth,
|
||||||
|
v: rowUv(row)
|
||||||
|
});
|
||||||
|
const front = smoothLines.map((points, row) => points.map((point, col) => push(point, depth * 0.5, lineUv(row, col))));
|
||||||
|
const back = smoothLines.map((points, row) => points.map((point, col) => push(point, -depth * 0.5, backLineUv(row, col))));
|
||||||
for (let row = 0; row < smoothLines.length - 1; row += 1) {
|
for (let row = 0; row < smoothLines.length - 1; row += 1) {
|
||||||
for (let col = 0; col < smoothLines[row].length - 1; col += 1) {
|
for (let col = 0; col < smoothLines[row].length - 1; col += 1) {
|
||||||
indices.push(front[row][col], front[row + 1][col], front[row][col + 1]);
|
indices.push(front[row][col], front[row + 1][col], front[row][col + 1]);
|
||||||
@@ -776,11 +825,19 @@ function createLoftedLineBody(lines, depth) {
|
|||||||
indices.push(back[row][col + 1], back[row + 1][col + 1], back[row + 1][col]);
|
indices.push(back[row][col + 1], back[row + 1][col + 1], back[row + 1][col]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const sideStart = indices.length;
|
||||||
for (let row = 0; row < smoothLines.length - 1; row += 1) {
|
for (let row = 0; row < smoothLines.length - 1; row += 1) {
|
||||||
const last = smoothLines[row].length - 1;
|
const last = smoothLines[row].length - 1;
|
||||||
indices.push(front[row][last], front[row + 1][last], back[row][last]);
|
const a = smoothLines[row][last];
|
||||||
indices.push(front[row + 1][last], back[row + 1][last], back[row][last]);
|
const b = smoothLines[row + 1][last];
|
||||||
|
const frontA = push(a, depth * 0.5, sideUv(row, depth * 0.5));
|
||||||
|
const frontB = push(b, depth * 0.5, sideUv(row + 1, depth * 0.5));
|
||||||
|
const backA = push(a, -depth * 0.5, sideUv(row, -depth * 0.5));
|
||||||
|
const backB = push(b, -depth * 0.5, sideUv(row + 1, -depth * 0.5));
|
||||||
|
indices.push(frontA, frontB, backA);
|
||||||
|
indices.push(frontB, backB, backA);
|
||||||
}
|
}
|
||||||
|
const topStart = indices.length;
|
||||||
for (let col = 0; col < smoothLines[0].length - 1; col += 1) {
|
for (let col = 0; col < smoothLines[0].length - 1; col += 1) {
|
||||||
const topRow = smoothLines.length - 1;
|
const topRow = smoothLines.length - 1;
|
||||||
indices.push(front[topRow][col], back[topRow][col], front[topRow][col + 1]);
|
indices.push(front[topRow][col], back[topRow][col], front[topRow][col + 1]);
|
||||||
@@ -789,15 +846,15 @@ function createLoftedLineBody(lines, depth) {
|
|||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new THREE.BufferGeometry();
|
||||||
geometry.setIndex(indices);
|
geometry.setIndex(indices);
|
||||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||||||
|
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
||||||
|
geometry.clearGroups();
|
||||||
|
geometry.addGroup(0, sideStart, 0);
|
||||||
|
geometry.addGroup(sideStart, topStart - sideStart, 1);
|
||||||
|
geometry.addGroup(topStart, indices.length - topStart, 2);
|
||||||
geometry.computeVertexNormals();
|
geometry.computeVertexNormals();
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEndpointPolyline(lines, depth) {
|
|
||||||
const points = lines.map((line) => new THREE.Vector3(line.endpoint.x, line.endpoint.y, depth * 0.5 + 0.008));
|
|
||||||
return new THREE.BufferGeometry().setFromPoints(points);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startPageFlip(direction) {
|
function startPageFlip(direction) {
|
||||||
if (activeFlips.length || !lastBookModel || !canPageFlip(direction)) return false;
|
if (activeFlips.length || !lastBookModel || !canPageFlip(direction)) return false;
|
||||||
const flip = createPageFlip(direction, performance.now(), NORMAL_FLIP_DURATION);
|
const flip = createPageFlip(direction, performance.now(), NORMAL_FLIP_DURATION);
|
||||||
|
|||||||
@@ -74,6 +74,6 @@
|
|||||||
<button id="fast_forward" type="button">Fast Forward</button>
|
<button id="fast_forward" type="button">Fast Forward</button>
|
||||||
<output id="flip_count">0 / 10</output>
|
<output id="flip_count">0 / 10</output>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/js/webgl-book-shape-lab.js?v=flip-burst-1"></script>
|
<script type="module" src="/js/webgl-book-shape-lab.js?v=stack-texture-lines-5"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user