[object Object]

← back to Quadrille Showroom

CONTACT GROUNDING: soft radial-gradient contact shadows on the floor under each wingboard + the planter + the console — a shared warm-charcoal alpha sprite (no light/shadow-map cost, depthWrite off, polygonOffset). Boards now sit IN the room instead of floating ON it — the #1 'is it real' tell after the shell. FPS 72, errorCount 0, specs intact (27x27, China Seas)

c49c3dba937f800102042479b37bb1850f637ff4 · 2026-06-27 12:27:51 -0700 · Steve

Files touched

Diff

commit c49c3dba937f800102042479b37bb1850f637ff4
Author: Steve <steve@designerwallcoverings.com>
Date:   Sat Jun 27 12:27:51 2026 -0700

    CONTACT GROUNDING: soft radial-gradient contact shadows on the floor under each wingboard + the planter + the console — a shared warm-charcoal alpha sprite (no light/shadow-map cost, depthWrite off, polygonOffset). Boards now sit IN the room instead of floating ON it — the #1 'is it real' tell after the shell. FPS 72, errorCount 0, specs intact (27x27, China Seas)
---
 public/js/showroom.js | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/public/js/showroom.js b/public/js/showroom.js
index aa6d00e..83a57c8 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -265,6 +265,42 @@ function initMaterials() {
   });
 }
 
+// ============================================================
+// CONTACT GROUNDING — cheap baked-AO contact shadows under objects
+// ============================================================
+// A single soft radial-gradient alpha sprite, shared by every contact shadow, so
+// boards / planter / console sit IN the room instead of floating ON it — the single
+// biggest "is it real" tell after the room shell. No light, no shadow-map cost: each
+// is one transparent dark plane laid flat just above the floor. (REVIEW.md #3.)
+let _contactTex = null;
+function contactShadowTexture() {
+  if (_contactTex) return _contactTex;
+  const c = document.createElement('canvas'); c.width = c.height = 128;
+  const ctx = c.getContext('2d');
+  const g = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);
+  g.addColorStop(0.0, 'rgba(20,18,14,0.55)');   // warm charcoal core (never pure black)
+  g.addColorStop(0.45, 'rgba(20,18,14,0.30)');
+  g.addColorStop(1.0, 'rgba(20,18,14,0.0)');     // fades to nothing at the rim
+  ctx.fillStyle = g; ctx.fillRect(0, 0, 128, 128);
+  _contactTex = new THREE.CanvasTexture(c);
+  return _contactTex;
+}
+
+// Drop a soft contact shadow on the floor. (cx,cz) = world centre; w,d = footprint;
+// opacity scales the darkness. Returns the mesh so callers can group/parent it.
+function addContactShadow(cx, cz, w, d, opacity) {
+  const mat = new THREE.MeshBasicMaterial({
+    map: contactShadowTexture(), transparent: true, opacity: opacity == null ? 1 : opacity,
+    depthWrite: false, polygonOffset: true, polygonOffsetFactor: -1, polygonOffsetUnits: -1
+  });
+  const m = new THREE.Mesh(new THREE.PlaneGeometry(w, d), mat);
+  m.rotation.x = -Math.PI / 2;
+  m.position.set(cx, 0.006, cz);   // 6 mm above the floor → no z-fight, reads as contact
+  m.renderOrder = 1;               // draw after the floor
+  scene.add(m);
+  return m;
+}
+
 // ============================================================
 // TEXTURE POOL — 24 shared patterns (instead of 1 per wing)
 // ============================================================
@@ -585,6 +621,11 @@ function buildFurniture() {
   // ONE restrained olive in a honed-stone planter — quiet, expensive, off to the side
   // by the left wall. Replaces the two sphere "toy" plants.
   buildOliveTree(-W/2 + 0.55, 0, D/2 - 0.55);
+  // Contact shadow grounds the planter on the floor.
+  addContactShadow(-W/2 + 0.55, D/2 - 0.55, 0.6, 0.6, 0.85);
+
+  // Contact shadow grounds the console/table.
+  addContactShadow(-1.0, 1.5, 2.0, 1.1, 0.7);
 
   // (Removed: framed abstract "art" on the right wall — the sample boards are the art.)
 }
@@ -943,6 +984,21 @@ function buildWingWall() {
     };
     wallGroup.add(pivot);
     wingBoards.push(pivot);
+
+    // CONTACT GROUNDING — a soft radial contact shadow on the floor directly under
+    // this wingboard so it sits IN the room, not floating ON it. Parented to the
+    // wallGroup at local (wingX,0,0) → lands at world (wingX, floor, backZ). The
+    // footprint is a touch wider than the 30" board face and shallow front-to-back
+    // (the board is thin), the strongest "is it real" tell after the room shell.
+    const shadeMat = new THREE.MeshBasicMaterial({
+      map: contactShadowTexture(), transparent: true, opacity: 0.85,
+      depthWrite: false, polygonOffset: true, polygonOffsetFactor: -1, polygonOffsetUnits: -1
+    });
+    const shade = new THREE.Mesh(new THREE.PlaneGeometry(wingW + 0.12, 0.34), shadeMat);
+    shade.rotation.x = -Math.PI / 2;
+    shade.position.set(wingX, 0.006, 0.02);   // 6 mm above floor, nudged just forward of the wall
+    shade.renderOrder = 1;
+    wallGroup.add(shade);
   }
 
   // Vendor section labels and dividers — colors from the loaded brand list

← 7dcf846 CEILING: replace crude 64px procedural grid (read as a wiref  ·  back to Quadrille Showroom  ·  PHOTOGRAPHIC DEPTH: subtle CSS vignette + soft top-down dire d2c6df4 →