[object Object]

← 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 →