← back to Quadrille Showroom
Center book: open leaves FORWARD at 65° + wire Open° slider to live splay
80267e9ab8986fc3e20587b0edd21deb8e34c500 · 2026-06-29 15:26:37 -0700 · Steve
Files touched
Diff
commit 80267e9ab8986fc3e20587b0edd21deb8e34c500
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jun 29 15:26:37 2026 -0700
Center book: open leaves FORWARD at 65° + wire Open° slider to live splay
---
public/js/showroom.js | 38 ++++++++++++++++++++++++++++++++++----
1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/public/js/showroom.js b/public/js/showroom.js
index f596d77..b978a8c 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -988,6 +988,10 @@ function buildFurniture() {
// when you glance/pitch down.
// ============================================================
let avatarRig = null; // yaw-only group the whole body hangs from
+let avatarWanted = false; // the first-person body renders ONLY in Walk-Through; every
+ // framed view (Hero/Angled/Orbit/…) hides it so the camera
+ // never looks straight through your own torso. Default OFF
+ // because boot view is Hero. viewmodes flips this per mode.
const AVATAR_EYE_Y = 1.62; // eye height (matches camera y) — body origin is the floor under it
function capsuleMesh(radius, length, mat, radialSegs) {
@@ -1243,6 +1247,7 @@ function buildAvatar(age) {
rig.traverse(o => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } });
scene.add(rig);
avatarRig = rig;
+ avatarRig.visible = avatarWanted; // honour the current view's visibility across age rebuilds
avatarUpperRig = upperRig;
updateAvatarRig(); // seat yaw + position immediately
requestShadowUpdate(2);
@@ -2044,10 +2049,10 @@ function buildCenterBook() {
faceR.userData = { isWingFace: true, parentPivot: centerBookPivotProxy(), leaf: 'R' };
leafR.add(faceR);
- // LEFT leaf — mirror twin: face centred at -x, splayed by -y so its outer (-x) edge swings
- // back symmetrically. Its texture is horizontally mirrored (set in setCenterBook).
+ // LEFT leaf — mirror twin: face centred at -x, splayed by +y so its outer (-x) edge swings
+ // FORWARD (+Z) symmetrically, opening toward the viewer. Texture horizontally mirrored (setCenterBook).
const leafL = new THREE.Group();
- leafL.rotation.y = -BOOK_LEAF_SPLAY;
+ leafL.rotation.y = BOOK_LEAF_SPLAY;
const faceL = new THREE.Mesh(faceGeo.clone(), new THREE.MeshStandardMaterial({ color: 0xece4d2, roughness: 0.7 }));
faceL.position.set(-leafW / 2, yC, 0);
faceL.receiveShadow = true; faceL.castShadow = true;
@@ -2140,6 +2145,19 @@ function pageCenterBook(delta) {
setCenterBook(bookIndex + delta);
}
+// Live splay control — drag the Open° slider to fan the open book between 0° (flat) and 90°.
+// Both leaves swing FORWARD symmetrically (R = -deg, L = +deg). Persisted to localStorage.
+function setBookSplay(deg) {
+ BOOK_SPLAY_DEG = Math.max(0, Math.min(90, deg));
+ BOOK_LEAF_SPLAY = BOOK_SPLAY_DEG * Math.PI / 180;
+ if (centerBook) {
+ centerBook.leafR.rotation.y = -BOOK_LEAF_SPLAY;
+ centerBook.leafL.rotation.y = BOOK_LEAF_SPLAY;
+ if (window._requestShadowUpdate) window._requestShadowUpdate(6);
+ }
+ localStorage.setItem('qh_book_splay', BOOK_SPLAY_DEG);
+}
+
// Normalize a raw product row into the display shape used by the book + caption.
// (Mirrors the per-board normalization in buildWingWall, kept small + shared.)
function normalizeProduct(p) {
@@ -3464,9 +3482,16 @@ function initHUD() {
const opn = document.getElementById('open-range');
const opnVal = document.getElementById('open-val');
if (opn) {
- opn.value = Math.round(OPEN_ANGLE_DEG);
+ // In BOOK_MODE this slider drives the centered open book's forward splay (default 65°,
+ // Steve's ask); otherwise it drives the legacy arc's resting-board open angle.
+ opn.value = Math.round(BOOK_MODE ? BOOK_SPLAY_DEG : OPEN_ANGLE_DEG);
if (opnVal) opnVal.textContent = opn.value + '°';
opn.addEventListener('input', () => {
+ if (BOOK_MODE) {
+ setBookSplay(parseInt(opn.value) || 65);
+ if (opnVal) opnVal.textContent = Math.round(BOOK_SPLAY_DEG) + '°';
+ return;
+ }
OPEN_ANGLE_DEG = parseInt(opn.value) || 45;
if (opnVal) opnVal.textContent = OPEN_ANGLE_DEG + '°';
localStorage.setItem('qh_open_angle', OPEN_ANGLE_DEG);
@@ -3846,6 +3871,11 @@ window._qh = {
// bar can assert YAW-ONLY parenting (rig.rotation.y tracks spinYaw, rig.rotation.x stays
// 0 even when the camera pitches down) and that the body is pinned under the fixed eye.
get avatarPresent() { return !!avatarRig; },
+ // Show/hide the first-person body. Called by viewmodes: visible only in Walk-Through
+ // (true first-person), hidden in every framed view so the camera doesn't render the
+ // avatar's back across the whole frame. Persists across setAvatarAge() rebuilds.
+ setAvatarVisible(v) { avatarWanted = !!v; if (avatarRig) avatarRig.visible = avatarWanted; },
+ get avatarVisible() { return avatarWanted; },
get spinPitch() { return spinPitch; },
avatarState() {
if (!avatarRig) return null;
← 6145090 auto-save: 2026-06-29T15:24:16 (3 files) — public/js/showroo
·
back to Quadrille Showroom
·
Center book starts open at 20 degrees (was 65) a785d8a →