openvibers/breakroomsimulator Commits
Remove the games-corner emergency exit door; reframe as built from break-room trips
Changes
@@@ -4,7 +4,7 @@[](LICENSE)A **free, open-source** realtime multiplayer 3D web game recreating an Amazon fulfillment--center break room, modeled from real photos. Runs on desktop and mobile — no build step,++center break room. Runs on desktop and mobile — no build step,vanilla JS + Three.js.🎮 **Play it live: [simulator.rest](https://simulator.rest)** · 💻 **Source: [github.com/OpenVibers/BreakRoomSimulator](https://github.com/OpenVibers/BreakRoomSimulator)**@@@ -23,7 +23,7 @@ npm start # → http://localhost:3000Open the URL on any device on your network (`http://<your-ip>:3000`) and everyone sharesthe same break room. `?autoguest` in the URL skips the login screen with a guest account.--## The building (photo-accurate route)++## The building (the real route, from countless break-room trips)You spawn on the **landscaped front walkway** (planter beds, Amazon Tours signs,food truck, flag pole, bollarded plaza) under the huge white facade with its@@@ -56,7 +56,7 @@ smoke cage outside). All exterior cafeteria doors are emergency-exit-only.Vest color and win stats persist per account.- **Realtime multiplayer** — websocket sync (12 Hz snapshots): avatars, walk/run/sitanimation, name tags, chat with speech bubbles, join/leave feed.--- **The room, from the photos**:++- **The room**:- Lounge: leather couches inside blue-tape outlines labeled *COUCH*, oriental rug,coffee tables, bookshelves with board games, palm plants, *Together at Amazon* banner- Games corner: two JOOLA-style ping pong tables, yellow *PRIME BREAKER* arcade@@@ -65,7 +65,7 @@ smoke cage outside). All exterior cafeteria doors are emergency-exit-only.green → brown → orange → yellow, napkin dispensers, *July Newsletter* table tents- *avenue C* micro-market: glass-door coolers, snack slat-walls, self-checkout kiosks,Pepsi vending machines, Red Bull cooler, coffee station, microwaves, lockers-- - Annex corridor through the wide passage (from the panorama): day lockers,++ - Annex corridor through the wide passage: day lockers,avenue C café/servery nook, glass-walled wellness room, teal walking-pathtape with red STOP floor decals, second entrance doors- International flags around the whole perimeter + strung across the hall,@@@ -1,7 +1,7 @@{"name": "amazon-break-room-simulator","version": "1.0.0",-- "description": "Amazon Break Room Simulator — realtime multiplayer 3D break room based on real photos",++ "description": "Amazon Break Room Simulator — realtime multiplayer 3D break room","type": "module","main": "server/server.js","scripts": {@@@ -166,11 +166,11 @@<section id="features"><div class="wrap"><h2 class="rv">What's inside</h2>-- <p class="lead rv">The whole journey of a shift break, recreated room by room from photos of the real building.</p>++ <p class="lead rv">The whole journey of a shift break, recreated room by room from many, many trips to the real thing.</p><div class="grid"><div class="card rv"><div class="ic">🌐</div><h3>Realtime multiplayer</h3><p>Everyone shares one world over websockets — chat, emotes, and other players walking, driving and playing live around you.</p></div><div class="card rv"><div class="ic">🏓</div><h3>Playable minigames</h3><p>Ping pong duels, giant Connect 4, full chess, and the PRIME BREAKER arcade cabinet with global highscores.</p></div>-- <div class="card rv"><div class="ic">🗺️</div><h3>Photo-accurate map</h3><p>Parking lot, badge gates, PAE2 security desk, locker hallway, avenue C market, dining hall, couch corner and the smoke cage.</p></div>++ <div class="card rv"><div class="ic">🗺️</div><h3>True-to-life map</h3><p>Parking lot, badge gates, PAE2 security desk, locker hallway, avenue C market, dining hall, couch corner and the smoke cage.</p></div><div class="card rv"><div class="ic">🚗</div><h3>Drivable cars</h3><p>Every car in the parking lot starts. Arcade handling, handbrake slides, synced across players.</p></div><div class="card rv"><div class="ic">🎒</div><h3>Items & loadout</h3><p>24-slot inventory and hotbar with drag & drop — snacks, hats, vests, and break-room "weapons" like paddles and bananas.</p></div><div class="card rv"><div class="ic">🕹️</div><h3>Source-style movement</h3><p>First or third person, air-strafing, bunny hops, crouch-sneaking — movement that feels like the classics.</p></div>@@@ -244,7 +244,7 @@<div class="disclaimer"><strong>Fan project.</strong> Amazon Break Room Simulator is an unofficial fan-made game and isnot affiliated with, endorsed by, or sponsored by Amazon.com, Inc. All trademarks are the-- property of their respective owners. Made with ❤️ and way too many break-room photos.++ property of their respective owners. Made with ❤️ and way too many trips to the break room.</div><div><div><a href="/play">Play the game</a></div>@@@ -1,4 +1,4 @@--// Builds the break room from the reference photos:++// Builds the break room from many trips to the real break room:// west (x=-38): glass entrance, lounge (couches w/ tape outlines, rug, giant Connect 4, bookshelves)// games corner: 2 JOOLA-style ping pong tables, arcade cabinet, chess table// center/east: long rows of folding tables, chairs color-zoned green→brown→orange→yellow@@@ -65,7 +65,7 @@ function shell(scene) {scene.add(blocker(box(76, .2, 34, M.ceiling, 0, 6.3, 0)));// ---- dining ceiling (x -12..38): white acoustic tile field at 4.9 with-- // recessed higher sections framed in yellow fascia (photos 3/5/7) ----++ // recessed higher sections framed in yellow fascia (from trips) ----const tile = TX.ct(512, 512, (g, w, h) => {g.fillStyle = '#eceeef'; g.fillRect(0, 0, w, h);g.strokeStyle = '#c9ccd0'; g.lineWidth = 3;@@@ -91,7 +91,7 @@ function shell(scene) {scene.add(blocker(fieldMesh));for (const [cx, cz, cw, cd] of RECESSES) {// recessed white upper ceiling; light-blue fascia band with a yellow-- // accent stripe at its base (panorama shows blue-edged clouds)++ // accent stripe at its base (walk-through shows blue-edged clouds)scene.add(blocker(box(cw, .1, cd, M.white, cx, 5.75, cz)));scene.add(box(cw + .16, .8, .1, M.blueSoffit, cx, 5.35, cz - cd / 2));scene.add(box(cw + .16, .8, .1, M.blueSoffit, cx, 5.35, cz + cd / 2));@@@ -106,7 +106,7 @@ function shell(scene) {for (const oz of [-cd / 4, cd / 4]) scene.add(box(cw * .7, .08, .15, lin0, cx, 5.15, cz + oz));}-- // ---- big sloped blue-gray soffit at the tall-section transition (photo 3) ----++ // ---- big sloped blue-gray soffit at the tall-section transition (from trips) ----const slope = box(.16, 3.0, 34, M.blueSoffit, 0, 0, 0);slope.rotation.z = -.55;slope.position.set(-12.7, 5.45, 0);@@@ -116,26 +116,24 @@ function shell(scene) {scene.add(box(24, .5, 1.0, M.blueSoffit, -26, 5.55, 8.6));// walls — north (z=-17): glass only at the lounge corner; the games/dining-- // stretch is solid gray with the emergency-exit door + arcade (photo 9)++ // stretch is solid gray with the emergency-exit door + arcade (from trips)wallWithWindows(scene, { axis: 'z', at: -17, from: -38, to: -30 });scene.add(blocker(box(16.7, 6.2, .3, M.wallDark, -21.65, 3.1, -17)));-- scene.add(blocker(box(1.7, 6.2, .3, M.wallDark, -12.35 + 1.5, 3.1, -17)));-- // wide corridor opening at x 12..17 into the service hallway (pano 191856299)++ // solid through to the corridor opening — the hallway (restrooms, microwaves,++ // smoke cage) is reached through the wide walkway, not an emergency door++ scene.add(blocker(box(3.4, 6.2, .3, M.wallDark, -11.65, 3.1, -17)));++ // wide corridor opening at x 12..17 into the service hallway (from trips)scene.add(blocker(box(22, 6.2, .3, M.wall, 1, 3.1, -17)));scene.add(blocker(box(21, 6.2, .3, M.wall, 27.5, 3.1, -17)));scene.add(blocker(box(5.4, 3.6, .3, M.wall, 14.5, 4.4, -17)));-- // metal corner guards on the opening (photo 205701520)++ // metal corner guards on the opening (from trips)scene.add(box(.12, 2.6, .35, M.chrome, 12.05, 1.3, -17));scene.add(box(.12, 2.6, .35, M.chrome, 16.95, 1.3, -17));-- collide(-21.65, -17.1, 16.7, .6); collide(-10.85, -17.1, 1.7, .6);++ collide(-21.65, -17.1, 16.7, .6); collide(-11.65, -17.1, 3.4, .6);collide(1, -17.1, 22, .6); collide(27.5, -17.1, 21, .6);serviceHallway(scene);-- // full-height backing panel in the exit-door bay — the gap above/around the-- // 2.4 m door slab looked straight through into the lit hallway (Bug30)-- scene.add(blocker(box(1.8, 6.2, .14, M.wallDark, -12.5, 3.1, -17.02)));-- exitDoor(scene, -12.6, -16.85, 0);-- // south (z=17): long glass run with parking outside (photos 1/2/4) and a second-- // set of entry doors near the market end (panorama), wall far east++ // south (z=17): long glass run with parking outside (from trips) and a second++ // set of entry doors near the market end (from trips), wall far eastwallWithWindows(scene, { axis: 'z', at: 17, from: -38, to: 10, doorAt: 6, doorW: 2.0 });// these south doors are emergency-exit-only: closed leaves + signage + sealedconst sSignM = new THREE.MeshBasicMaterial({ map: emergencySignTexture() });@@@ -149,15 +147,15 @@ function shell(scene) {collide(6, 16.95, 2.3, .5);scene.add(blocker(box(28, 6.2, .3, M.wall, 24, 3.1, 17)));collide(24, 17.1, 28, .6);-- // east wall (x=38): wide open passage into the locker/wellness annex (panorama)++ // east wall (x=38): wide open passage into the locker/wellness annex (from trips)scene.add(blocker(box(.3, 6.2, 14.5, M.wall, 38, 3.1, -9.75)));scene.add(blocker(box(.3, 6.2, 12.8, M.wall, 38, 3.1, 10.6)));scene.add(blocker(box(.3, 2.3, 6.7, M.wall, 38, 5.05, .85))); // header over the opening-- scene.add(box(.24, .55, 6.7, M.blueSoffit, 38, 3.65, .85)); // blue accent band (panorama)++ scene.add(box(.24, .55, 6.7, M.blueSoffit, 38, 3.65, .85)); // blue accent band (from trips)collide(38.1, -9.75, .6, 14.5); collide(38.1, 10.6, .6, 12.8);// close the height step between cafeteria (6.2) and the taller hall (7.2)scene.add(blocker(box(.3, 1.1, 34, M.white, 38, 6.75, 0)));-- // double doors propped open at the passage (photo 192150685) + flags + clock above++ // double doors propped open at the passage (from trips) + flags + clock abovefor (const [dz, ry] of [[-2.7, .95], [4.4, -.95]]) {const leaf = box(.08, 2.3, 1.5, new THREE.MeshStandardMaterial({ color: 0xf2f1ec, roughness: .7 }), 0, 1.15, 0);leaf.add(box(.09, .5, .5, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .3, metalness: .5 }), 0, -.7, 0)); // kick plate@@@ -178,7 +176,7 @@ function shell(scene) {wallWithWindows(scene, { axis: 'x', at: -38, from: 2, to: 17, tall: true });entrance(scene);-- // interior gray column pair (photo 4)++ // interior gray column pair (from trips)for (const z of [-6, 6]) { scene.add(blocker(box(1, 6.2, 1, M.white, -10, 3.1, z))); collide(-10, z, 1, 1); }// pendant dome lights + linear fixtures (emissive, no real lights for perf)@@@ -192,9 +190,9 @@ function shell(scene) {const d = new THREE.Mesh(dome, domeMat); d.position.set(x, hangY, z); dGroup.add(d);const b = new THREE.Mesh(bulb, domeMat); b.position.set(x, hangY - .04, z); dGroup.add(b);};-- // tall games/lounge section: dense rows of long-drop pendants (photos 8-10)++ // tall games/lounge section: dense rows of long-drop pendants (from trips)for (let gx = -36; gx <= -15; gx += 4.2) for (let gz = -14.5; gz <= 15; gz += 5.8) hang(gx, gz, 3.35, 6.2);-- // dining: pendants drop below the tile ceiling along the rows (photos 3/5/7)++ // dining: pendants drop below the tile ceiling along the rows (from trips)for (let gx = -9; gx <= 36; gx += 6.4) for (let gz = -13.5; gz <= 13.5; gz += 6.6) hang(gx, gz, 3.95, 4.9);scene.add(dGroup);// long continuous linear fluorescent runs (suspended twin tubes)@@@ -260,9 +258,9 @@ function wallWithWindows(scene, { axis, at, from, to, doorAt = null, doorW = 0,}// ============================================================ LOCKER HALLWAY (z 17..31, x 58..92)--// Satellite map: the lockers strip sits between the security block (north) and++// site plan: the lockers strip sits between the security block (north) and// the cafeteria (south). You come south through the PAE2 detector lanes, walk a--// straight aisle flanked by tan locker banks (photo 192150685) and reach the++// straight aisle flanked by tan locker banks (from trips) and reach the// cafeteria double doors at z=31.function lockerHall(scene) {const floor = new THREE.Mesh(new THREE.PlaneGeometry(34, 14), new THREE.MeshStandardMaterial({ map: TX.concreteTexture(), roughness: .38, metalness: .08 }));@@@ -340,7 +338,7 @@ function lockerHall(scene) {ex2.position.set(68.5, 2.95, 17.25);scene.add(ex2);-- // gray prep table + cart just past the screening lanes (photo 192244996)++ // gray prep table + cart just past the screening lanes (from trips)scene.add(box(1.5, .05, .65, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .5, metalness: .3 }), 77.6, .74, 18.5));for (const [lx3, lz3] of [[-.55, -.22], [.55, -.22], [-.55, .22], [.55, .22]]) scene.add(cyl(.02, .02, .72, M.blackMetal, 77.6 + lx3, .37, 18.5 + lz3, 6));collide(77.6, 18.5, 1.6, .75);@@@ -363,7 +361,7 @@ function lockerHall(scene) {}// ============================================================ SECURITY ROOM (x 62..80)--// Pano 153354733 + photos 153342206/153343798: the white PAE2 soffit structure is a++// From break-room trips: the white PAE2 soffit structure is a// FREESTANDING ISLAND in the middle of the room — curved mural desk wrapping its// SE corner facing the gates, metal-detector lanes through its west side. Behind it// (north wall) sit the Safe to Go arch into the FC, the elevator, the US flag and@@@ -393,7 +391,7 @@ function securityLobby(scene) {collide(68.1, -17.1, 12.2, .6);collide(76.2, -17.1, 1.2, .6); collide(85.1, -17.1, 13.8, .6);-- // elevator doors on the north wall (photo 153342206: right of the arch corner)++ // elevator doors on the north wall (right of the arch corner)scene.add(box(2.0, 2.35, .12, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .35, metalness: .6 }), 68.5, 1.18, -16.86));scene.add(box(.05, 2.2, .1, new THREE.MeshStandardMaterial({ color: 0x3d4045, roughness: .5 }), 68.5, 1.15, -16.8));scene.add(box(.14, .2, .06, new THREE.MeshStandardMaterial({ color: 0xd8d4cc, roughness: .4 }), 69.7, 1.1, -16.8));@@@ -405,11 +403,11 @@ function securityLobby(scene) {const elS = new THREE.Mesh(new THREE.PlaneGeometry(1.2, .3), new THREE.MeshBasicMaterial({ map: elTex }));elS.position.set(68.5, 2.72, -16.8);scene.add(elS);-- // red fire strobe beside the Day One door (photo 153339387)++ // red fire strobe beside the Day One door (from trips)scene.add(box(.18, .26, .12, new THREE.MeshStandardMaterial({ color: 0xc22127, roughness: .5 }), 78.6, 2.2, -16.86));// ---- WEST WALL (x=62): solid mountain/airplane mural with the FC arch opening-- // (z -12..-6). The FC mass lies west of here (satellite map). ----++ // (z -12..-6). The FC mass lies west of here (site plan). ----scene.add(blocker(box(.3, 7.2, 5, M.white, 62, 3.6, -14.5))); // z -17..-12scene.add(blocker(box(.3, 7.2, 23, M.white, 62, 3.6, 5.5))); // z -6..17scene.add(blocker(box(.3, 2.8, 6, M.white, 62, 5.8, -9))); // header over the FC opening@@@ -445,7 +443,7 @@ function securityLobby(scene) {low.rotation.y = Math.PI / 2; low.position.set(62.32, 1.32, lz);scene.add(low);}-- // hanging US flag + Pack One Goal banner flanking the arch (pano)++ // hanging US flag + Pack One Goal banner flanking the arch (from trips)const usFlag = new THREE.Mesh(new THREE.PlaneGeometry(2.8, 1.7), new THREE.MeshStandardMaterial({ map: TX.flagTexture('usa'), side: THREE.DoubleSide }));usFlag.rotation.y = Math.PI / 2;usFlag.position.set(62.5, 5.8, -13.9);@@@ -542,7 +540,7 @@ function securityLobby(scene) {scene.add(stopFC);// ---- SOUTH WALL (z=17): mural-faced wall with a plain OPENING into the locker-- // area (FrontAreaPhoto: "the locker area has an opening on the middle wall" —++ // area ("the locker area has an opening on the middle wall" —// no detectors here). Entry-lobby stretch (x 80..92) stays solid blue. ----scene.add(blocker(box(18, 5.9, .3, structM, 71, 5.45, 17))); // white header 62..80scene.add(blocker(box(12, 5.9, .3, blueWall, 86, 5.45, 17))); // blue header 80..92@@@ -570,7 +568,7 @@ function securityLobby(scene) {exS.rotation.y = Math.PI; exS.position.set(70.5, 2.9, 16.82);scene.add(exS);-- // ---- METAL DETECTORS (FrontAreaPhoto / 153342206): three lanes rotated 90°,++ // ---- METAL DETECTORS (from trips): three lanes rotated 90°,// in line with the Safe to Go arch — everyone leaving the warehouse walks the// arch, then a detector lane, under a white soffit joined to the desk island ----scene.add(blocker(box(4.6, 2.6, 7.4, structM, 63.9, 4.9, -9.1))); // soffit bridge wall→island@@@ -597,7 +595,7 @@ function securityLobby(scene) {for (const [lx3, lz3] of [[-.5, -.2], [.5, -.2], [-.5, .2], [.5, .2]]) scene.add(cyl(.02, .02, .72, M.blackMetal, 63.2 + lx3, .37, -13.3 + lz3, 6));collide(63.2, -13.3, 1.5, .7);-- // ---- secondary screening (southwest corner, photo 153343798): ONE detector +++ // ---- secondary screening (southwest corner, from trips): ONE detector +// one table, tucked against the mural clear of the locker-room doorway ----scene.add(box(.6, 2.15, .18, detM, 63.4, 1.08, 12.85));scene.add(box(.6, 2.15, .18, detM, 63.4, 1.08, 13.95));@@@ -621,7 +619,7 @@ function securityLobby(scene) {ssW.rotation.y = Math.PI / 2; ssW.position.set(62.4, 2.15, 13.4);scene.add(ssW);-- // ---- THE PAE2 ISLAND (photo 153342206): one long freestanding white soffit++ // ---- THE PAE2 ISLAND (from trips): one long freestanding white soffit// structure facing the gates — desk on the RIGHT (north) end, exit-screening// metal-detector bays on the LEFT (south) end feeding the exit turnstiles.// The FC arch sits further right (north wall). ----@@@ -641,7 +639,7 @@ function securityLobby(scene) {const paeS = new THREE.Mesh(new THREE.PlaneGeometry(5.6, 2.0), new THREE.MeshStandardMaterial({ map: paeTex }));paeS.position.set(68.6, 4.9, -1.14);scene.add(paeS);-- // ---- curved mural desk on the north end of the front (photo 153342206:++ // ---- curved mural desk on the north end of the front (from trips:// chevron at the right end, curved cap toward the detectors) ----const muralTex = TX.ct(1024, 128, (g, w, h) => {g.fillStyle = '#f5f4f0'; g.fillRect(0, 0, w, h);@@@ -708,7 +706,7 @@ function securityLobby(scene) {kite.position.set(77, 6.2, 4);scene.add(kite);-- // ---- display cluster between the arch and Career Choice (pano): light-up PAE2++ // ---- display cluster between the arch and Career Choice (from trips): light-up PAE2// letters, the big Peccy statue, black tours table with TV ----const letterTex = (ch) => TX.ct(128, 160, (g, w, h) => {g.clearRect(0, 0, w, h);@@@ -795,7 +793,7 @@ function securityLobby(scene) {moD.rotation.x = -Math.PI / 2; moD.position.set(73.0, .012, -10.4); moD.renderOrder = 1;scene.add(moD);-- // ---- Career Choice alcove dressing (photo 153339387): Grow banner, wire rack,++ // ---- Career Choice alcove dressing (from trips): Grow banner, wire rack,// black cubby lockers, LOCKERS tape, New Hires decals, safety board ----const growTex = TX.ct(192, 384, (g, w, h) => {g.fillStyle = '#f2f5f8'; g.fillRect(0, 0, w, h);@@@ -901,7 +899,7 @@ function securityLobby(scene) {// User-described: right of the security desk leads into the FC; beyond that are// the "Day One" and "Career Choice" meeting rooms.function dayOneRooms(scene) {-- // photo 153339387: Career Choice alcove on the LEFT (west), Day 1 Training++ // Career Choice alcove on the LEFT (west), Day 1 Training// room on the RIGHT (east), safety board east of Day One, jamb corner guardsconst roomFloor = new THREE.Mesh(new THREE.PlaneGeometry(7.4, 7), new THREE.MeshStandardMaterial({ color: 0x8a8d90, roughness: .9 }));roomFloor.rotation.x = -Math.PI / 2; roomFloor.position.set(76.6, .005, -20.5);@@@ -931,7 +929,7 @@ function dayOneRooms(scene) {});scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.15, 2.3), new THREE.MeshBasicMaterial({ map: ccDoorTex })).translateX(74.9).translateY(1.15).translateZ(-16.86));scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.15, 2.3), new THREE.MeshBasicMaterial({ map: dayDoorTex })).translateX(77.5).translateY(1.15).translateZ(-16.86));-- // stainless corner guards on the jambs (photo)++ // stainless corner guards on the jambs (from trips)for (const gx of [74.2, 75.6, 76.8, 78.2]) scene.add(box(.1, 2.5, .18, M.chrome, gx, 1.25, -16.9));// signs: Career Choice wall flag (west) + Day 1 Training room hanging sign (east)const ccSignTex = TX.ct(192, 96, (g, w, h) => {@@@ -972,7 +970,7 @@ function dayOneRooms(scene) {}// ============================================================ HEDGE GATE WALL (x=80)--// Pano 193021419: green hedge feature wall with neon "Welcome to PAE2" script,++// From break-room trips: green hedge feature wall with neon "Welcome to PAE2" script,// red garland, Enter + Do-Not-Enter turnstile openings, and a window into PAE2.function hedgeGateWall(scene) {const hedgeTex = TX.ct(256, 256, (g, w, h) => {@@@ -1059,13 +1057,13 @@ function hedgeGateWall(scene) {inter({ id: gate.id, type: 'gate', x: 80, z: lz, r: 1.5, label: kind === 'enter' ? 'Badge through the gate' : 'Badge out (Do Not Enter side)', data: { gate: gate.id } });}};-- mkGates([-8.3, -7.05, -5.8, -4.55], 'enter'); // 4 entry lanes (pano 193021419)++ mkGates([-8.3, -7.05, -5.8, -4.55], 'enter'); // 4 entry lanes (from trips)mkGates([4.55, 5.8, 7.05, 8.3], 'exit'); // 4 exit lanes}// ============================================================ ENTRY LOBBY (x 80..92)function entryLobby(scene) {-- // Amazon Family World Map mural on the north wall (photo/pano)++ // Amazon Family World Map mural on the north wall (from trips)const mapTex = TX.ct(1024, 512, (g, w, h) => {g.fillStyle = '#2a72b8'; g.fillRect(0, 0, w, h);g.fillStyle = '#fff'; g.font = '600 44px "Segoe UI", sans-serif'; g.textAlign = 'left';@@@ -1093,7 +1091,7 @@ function entryLobby(scene) {const aed = new THREE.Mesh(new THREE.PlaneGeometry(.7, .7), new THREE.MeshBasicMaterial({ map: aedTex, transparent: true, side: THREE.DoubleSide }));aed.position.set(82, 5.6, -16.7);scene.add(aed);-- // cocktail table + literature stand (south side, like pano left area)++ // cocktail table + literature stand (south side, from trips)scene.add(cyl(.45, .45, .04, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .7 }), 84, 1.08, 13.5, 14));scene.add(cyl(.09, .34, 1.08, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .7 }), 84, .54, 13.5, 10));collide(84, 13.5, 1.0, 1.0);@@@ -1111,7 +1109,7 @@ function entryLobby(scene) {b.position.set(80.9, 1.1, bz);scene.add(b);}-- // traffic cone by the enter gates (pano)++ // traffic cone by the enter gates (from trips)scene.add(cyl(.03, .18, .55, new THREE.MeshStandardMaterial({ color: 0xe06a1e, roughness: .6 }), 81.6, .28, -8.9, 10));// floor guidance (Bug29): one coherent enter path from the front doors to the// Enter lanes — alternating smile arrows and walk decals, evenly spaced and@@@ -1168,7 +1166,7 @@ function entryLobby(scene) {}scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.6, .25), new THREE.MeshBasicMaterial({ map: bayLabel(label) })).translateX(91.85).translateY(2.72).translateZ(bz).rotateY(-Math.PI / 2));}-- // black trash barrels between the bays (photos)++ // black trash barrels between the bays (from trips)for (const bz of [-5.2, 5.2, 12.5]) {scene.add(cyl(.34, .3, .85, new THREE.MeshStandardMaterial({ color: 0x1d2126, roughness: .8 }), 92.9, .43, bz, 12));collide(92.9, bz, .7, .7);@@@ -1188,21 +1186,6 @@ function entryLobby(scene) {}}--function exitDoor(scene, x, z, ry) {-- const g = new THREE.Group();-- g.add(box(1.1, 2.4, .1, new THREE.MeshStandardMaterial({ color: 0x2e3238, roughness: .6 }), 0, 1.2, 0));-- g.add(box(.9, .5, .02, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .3, metalness: .4 }), 0, 1.55, .06)); // push bar? window-- const signTex = TX.ct(128, 96, (gg, w, h) => {-- gg.fillStyle = '#c22127'; gg.fillRect(0, 0, w, h);-- gg.fillStyle = '#fff'; gg.font = '700 17px Arial'; gg.textAlign = 'center';-- gg.fillText('EMERGENCY', w / 2, 34); gg.fillText('EXIT ONLY', w / 2, 58);-- });-- g.add(new THREE.Mesh(new THREE.PlaneGeometry(.42, .3), new THREE.MeshBasicMaterial({ map: signTex })).translateY(1.6).translateZ(.07));-- g.position.set(x, 0, z); g.rotation.y = ry;-- scene.add(g);-- collide(x, z, 1.2, 1.2);--}--function emergencySignTexture() {return TX.ct(128, 64, (g, w, h) => {g.fillStyle = '#c22127'; g.fillRect(0, 0, w, h);@@@ -1280,7 +1263,7 @@ function exterior(scene) {t.position.set(x, 0, z);scene.add(t);}-- // stop sign (photo 8)++ // stop sign (from trips)const sign = new THREE.Group();sign.add(cyl(.04, .04, 2.6, M.chrome, 0, 1.3, 0, 8));const oct = new THREE.Mesh(new THREE.CylinderGeometry(.45, .45, .04, 8), new THREE.MeshStandardMaterial({ color: 0xc22127, roughness: .5 }));@@@ -1296,7 +1279,7 @@ function exterior(scene) {sign.add(stopF);sign.position.set(-44.5, 0, -5.5); sign.rotation.y = Math.PI / 2;scene.add(sign);-- // traffic cones (photo 2)++ // traffic cones (from trips)for (const [x, z] of [[-42.5, -9.5], [-42.5, -11], [-43.5, -12.5]]) {const cone = cyl(.03, .18, .55, new THREE.MeshStandardMaterial({ color: 0xe06a1e, roughness: .6 }), x, .28, z, 10);scene.add(cone);@@@ -1311,7 +1294,7 @@ function exterior(scene) {}// ============================================================ LOUNGE--// Photos 8/10: the couch corner sits in the NW glass corner (two window walls++// From many break-room trips: the couch corner sits in the NW glass corner (two window walls// meeting), right next to the ping pong tables. Exactly 2 couches + 1 armchair.function lounge(scene) {// rug centered in the corner group@@@ -1336,7 +1319,7 @@ function lounge(scene) {bookshelf(scene, -37.2, 3.6, Math.PI / 2);for (const [x, z] of [[-36.8, 6.4], [-25.6, 15.8], [-35.8, 8.6]]) plant(scene, x, z);-- // hazard tape square just inside the entrance (photo 4)++ // hazard tape square just inside the entrance (from trips)const hz = new THREE.Mesh(new THREE.PlaneGeometry(1.4, 1.4), new THREE.MeshBasicMaterial({ map: TX.hazardSquareTexture(), transparent: true }));hz.rotation.x = -Math.PI / 2; hz.position.set(-30.5, .014, 1.6);scene.add(hz);@@@ -1455,23 +1438,23 @@ function trash(scene, x, z, color) {}// ============================================================ GAMES CORNER--// Photo 9: both ping pong tables sit side by side along the solid north wall,++// From trips: both ping pong tables sit side by side along the solid north wall,// immediately east of the couch corner; arcade + exit door further along.function gamesCorner(scene) {pingPongTable(scene, 'a', -28.7, -12.4);pingPongTable(scene, 'b', -21.3, -12.4);arcade(scene, -15.6, -16.2);chessTable(scene, -33.2, 4.2);-- connect4(scene, 'a', -36.9, -6.6, Math.PI / 2); // against west glass by the lounge (photo 8)-- connect4(scene, 'b', -27.3, 12.4, -.4); // mid-floor near the south windows (photo 10)-- // loose lime chairs by the pong tables (photo 9)++ connect4(scene, 'a', -36.9, -6.6, Math.PI / 2); // against west glass by the lounge (from trips)++ connect4(scene, 'b', -27.3, 12.4, -.4); // mid-floor near the south windows (from trips)++ // loose lime chairs by the pong tables (from trips)for (const [x, z, ry] of [[-31.9, -10.6, 2.4], [-25.2, -10.4, -2.8], [-18.2, -13.6, 1.2]]) {const c = simpleChair(M.chairGreen);c.position.set(x, 0, z); c.rotation.y = ry;scene.add(c);W.seats.push({ id: `loose-${x.toFixed(0)}`, x, z, y: .5, ry: ry + Math.PI, type: 'chair', exitX: x + Math.sin(ry), exitZ: z + Math.cos(ry) });}-- // cornhole board leaning on wall (photo 9)++ // cornhole board leaning on wall (from trips)const ch = box(.62, .04, 1.2, M.wood, 0, 0, 0);ch.position.set(-17.8, .6, -16.55); ch.rotation.x = -1.15;const hole = cyl(.09, .09, .05, M.blackMetal, -17.8, 1.0, -16.42); hole.rotation.x = -1.15;@@@ -1642,7 +1625,7 @@ function connect4(scene, id, x, z, ry) {g.add(box(.12, .32, .7, blue, -.89, .16, 0));g.add(box(.12, .32, .7, blue, .89, .16, 0));g.position.set(x, 0, z); g.rotation.y = ry;-- g.scale.setScalar(.72); // real one is ~4 ft tall (photo 8)++ g.scale.setScalar(.72); // real one is ~4 ft tall (from trips)scene.add(g);collide(x, z, 1.6, .8);// disc pool (42) — laid out by minigames.js in board-local coords then transformed@@@ -1678,14 +1661,14 @@ function connect4(scene, id, x, z, ry) {// ============================================================ DININGfunction dining(scene) {const clusters = [];-- // zone order from the photos, west→east: brown, green, orange, yellow++ // zone order from trips, west→east: brown, green, orange, yellowconst zoneMats = [M.chairBrown, M.chairGreen, M.chairOrange, M.chairYellow];const xStarts = [-8.2, 2.0, 12.2, 22.4];const rows = [-14, -11.3, -8.6, -5.9, -3.2, 3.2, 5.9, 8.6, 11.3, 14];for (let zi = 0; zi < 4; zi++) for (const rz of rows) clusters.push({ x: xStarts[zi] + 3.6, z: rz, zone: zi });// each cluster = four 6-ft (1.78 m) folding tables butted end to end, with-- // visible seams and folding legs at each table end (photos 3/5/7)++ // visible seams and folding legs at each table end (from trips)const TL = 1.78, GAP = .045, PER = 4;const topGeo = new THREE.BoxGeometry(TL, .05, .76);const edgeGeo = new THREE.BoxGeometry(TL + .02, .07, .8);@@@ -1715,7 +1698,7 @@ function dining(scene) {});scene.add(tops, edges, legs);-- // chairs packed tight, ~0.72 m pitch, 10 per side (photos show shoulder-to-shoulder)++ // chairs packed tight, ~0.72 m pitch, 10 per side (shoulder-to-shoulder)const seatGeo = new THREE.BoxGeometry(.46, .05, .45);const backGeo = new THREE.BoxGeometry(.46, .48, .05);const sledGeo = new THREE.CylinderGeometry(.016, .016, .5, 6);@@@ -1779,7 +1762,7 @@ function dining(scene) {});scene.add(disps, tents, sans);-- // window-side laptop cart with orange chair + blue CABLE tape (photo 4)++ // window-side laptop cart with orange chair + blue CABLE tape (from trips)const cart = new THREE.Group();cart.add(box(.7, 1.1, .55, new THREE.MeshStandardMaterial({ color: 0x33373d, roughness: .5 }), 0, .55, 0));cart.add(box(.42, .26, .03, new THREE.MeshStandardMaterial({ color: 0x1b2731, roughness: .25 }), 0, 1.28, -.1)); // laptop screen@@@ -1806,7 +1789,7 @@ function market(scene) {const signB = new THREE.Mesh(new THREE.PlaneGeometry(3.6, .58), new THREE.MeshStandardMaterial({ map: TX.avenueCTexture() }));signB.rotation.y = -Math.PI / 2; signB.position.set(37.5, 3.3, 8);scene.add(signB);-- // sign above north lockers too (photo 5)++ // sign above north lockers too (from trips)const sign2 = new THREE.Mesh(new THREE.PlaneGeometry(3.4, .55), new THREE.MeshStandardMaterial({ map: TX.avenueCTexture() }));sign2.position.set(29, 3.1, -16.8);scene.add(sign2);@@@ -1827,9 +1810,9 @@ function market(scene) {counterUnit(scene, 34, -13.5);// ---- north wall fixtures ------ // Pepsi machines (photo 3)++ // Pepsi machines (from trips)for (let i = 0; i < 4; i++) vending(scene, -2.5 + i * 1.15, -16.35, i < 2 ? 'pepsi' : 'snack');-- // lockers (photo 5)++ // lockers (from trips)for (let i = 0; i < 5; i++) lockers(scene, 23.5 + i * 2.1, -16.5);// trash + recycle near markettrash(scene, 20.5, -16.2, 0x2a63c9);@@@ -1911,7 +1894,7 @@ function lockers(scene, x, z, ry = 0) {const body = box(2.0, 1.9, .5, new THREE.MeshStandardMaterial({ color: 0xd6cfc0, roughness: .7 }), 0, .95, 0);g.add(body);W.camBlockers.push(body);-- // sloped crown like the photo's locker banks++ // sloped crown like the real locker banksconst crown = box(2.0, .06, .6, new THREE.MeshStandardMaterial({ color: 0xc7bfae, roughness: .7 }), 0, 2.0, -.04);crown.rotation.x = -.4;g.add(crown);@@@ -1926,7 +1909,7 @@ function lockers(scene, x, z, ry = 0) {// ============================================================ FLAGS / TVS / CLOCKSfunction decor(scene) {-- // flags at the top, all the way around the room (per the photos every wall++ // flags at the top, all the way around the room (on every wall// carries a band of international flags near the ceiling)const flagAt = (name, x, z, ry, big = false) => {const fl = new THREE.Mesh(@@@ -1939,18 +1922,18 @@ function decor(scene) {};let fi = 0;const next = () => TX.FLAG_NAMES[fi++ % TX.FLAG_NAMES.length];-- // west glass: big flags hanging over the windows (photos 1-2)++ // west glass: big flags hanging over the windows (from trips)for (let z = -15; z <= 15; z += 3.4) flagAt(next(), -37.4, z, Math.PI / 2, true);-- // games-corner gray wall row (photo 9: El Salvador, Haiti, USA, Finland, Brazil, Nepal)++ // games-corner gray wall row (El Salvador, Haiti, USA, Finland, Brazil, Nepal)for (const [i, f] of ['elsalvador', 'haiti', 'usa', 'kyrgyzstan', 'finland', 'brazil', 'nepal'].entries())flagAt(f, -26 + i * 1.9, -16.72, 0);// north wall band, dining stretchfor (let x = -10; x <= 36; x += 2.6) flagAt(next(), x, -16.72, 0);-- // south wall band (hangs in front of the glass, like the photos)++ // south wall band (hangs in front of the glass, like the real one)for (let x = -28; x <= 36; x += 2.6) flagAt(next(), x, 16.72, Math.PI);// east wall band above the marketfor (let z = -15; z <= 15; z += 2.6) flagAt(next(), 37.6, z, -Math.PI / 2);-- // flags strung on wires ACROSS the hall (panorama shows transverse strings)++ // flags strung on wires ACROSS the hall (walk-through shows transverse strings)for (const wx of [-2, 14]) {scene.add(box(.02, .02, 31, M.blackMetal, wx, 4.62, 0));for (let z = -13.8; z <= 13.8; z += 2.3) flagAt(next(), wx, z, Math.PI / 2);@@@ -1959,7 +1942,7 @@ function decor(scene) {// TVs — dynamic canvasesW.dynamic.tvs = [];const tvSpots = [-- { x: -37.6, y: 3.4, z: -8, ry: Math.PI / 2 }, // west glass wall TV (photo 1)++ { x: -37.6, y: 3.4, z: -8, ry: Math.PI / 2 }, // west glass wall TV (from trips){ x: 4, y: 3.6, z: -16.8, ry: 0 },{ x: 12, y: 3.6, z: 16.8, ry: Math.PI },{ x: 37.6, y: 4.65, z: -8, ry: -Math.PI / 2 },@@@ -1976,7 +1959,7 @@ function decor(scene) {scene.add(frame);W.dynamic.tvs.push(tv);}-- // red LED clocks (photos 8-9)++ // red LED clocks (from trips)W.dynamic.clocks = [];for (const s of [{ x: -25, y: 3.9, z: -16.85, ry: 0 }, { x: 30, y: 3.4, z: 16.85, ry: Math.PI }, { x: 37.8, y: 3.9, z: 2.5, ry: -Math.PI / 2 }]) {const ck = TX.makeClock();@@@ -1987,7 +1970,7 @@ function decor(scene) {}// exit signsconst exitM = new THREE.MeshBasicMaterial({ map: TX.ct(128, 48, (g, w, h) => { g.fillStyle = '#200'; g.fillRect(0, 0, w, h); g.fillStyle = '#f33'; g.font = '900 30px Arial'; g.textAlign = 'center'; g.fillText('EXIT', w / 2, 36); }) });-- for (const s of [{ x: -12.5, z: -16.6, ry: 0 }, { x: -36.5, z: 0, ry: Math.PI / 2, y: 2.9 }]) {++ for (const s of [{ x: 14.5, z: -16.6, ry: 0 }, { x: -36.5, z: 0, ry: Math.PI / 2, y: 2.9 }]) {const e = new THREE.Mesh(new THREE.PlaneGeometry(.5, .2), exitM);e.position.set(s.x, s.y || 2.6, s.z); e.rotation.y = s.ry;scene.add(e);@@@ -2007,7 +1990,7 @@ function lights(scene) {}// ============================================================ SERVICE HALLWAY (z -17.3..-22.3)--// Photos 205701520/205713252: long corridor behind the cafeteria north wall —++// From many break-room trips: long corridor behind the cafeteria north wall —// microwave/coffee counter, ice machine, chest freezer, bag racks, bulletin// boards, bathrooms, V.STORAGE door; smoke-cage patio at the far west end.function serviceHallway(scene) {@@@ -2043,7 +2026,7 @@ function serviceHallway(scene) {const lin = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xffffff, emissiveIntensity: .8 });for (let i = -4; i <= 4; i++) scene.add(box(4.2, .05, .28, lin, i * 6.2, 3.08, -19.8));-- // ---- south side: microwave/coffee counter run (photo 205713252) ----++ // ---- south side: microwave/coffee counter run (from trips) ----const counter = new THREE.Group();counter.add(box(24, .9, .7, new THREE.MeshStandardMaterial({ color: 0x6a6e73, roughness: .6, metalness: .3 }), 0, .45, 0));counter.add(box(24, .05, .78, new THREE.MeshStandardMaterial({ color: 0xd8d4c8, roughness: .4 }), 0, .93, 0));@@@ -2062,12 +2045,12 @@ function serviceHallway(scene) {}scene.add(mws);inter({ id: 'hall-micro', type: 'micro', x: -14, z: -19, r: 2.6, label: 'Heat up lunch' });-- // coffee machine + cup rack (photo 205808782-style)++ // coffee machine + cup rack (from trips)scene.add(box(.55, .85, .5, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .35 }), -27.3, 1.35, -17.9));scene.add(box(.3, .2, .02, new THREE.MeshStandardMaterial({ color: 0x2a72b8, roughness: .2, emissive: 0x1a4a80, emissiveIntensity: .4 }), -27.35, 1.55, -17.62));for (let i = 0; i < 3; i++) scene.add(cyl(.055, .04, .3, new THREE.MeshStandardMaterial({ color: 0xf2f0ea, roughness: .5 }), -26.6 + i * .18, 1.85, -17.9, 8));inter({ id: 'hall-coffee', type: 'coffee', x: -27, z: -19, r: 1.6, label: 'Brew a coffee' });-- // ice/water machine + chest freezer + caution cone (photo 205811592)++ // ice/water machine + chest freezer + caution cone (from trips)scene.add(box(1.0, 1.7, .8, new THREE.MeshStandardMaterial({ color: 0xb9bdc2, roughness: .3, metalness: .5 }), .8, .85, -17.9));scene.add(box(.5, .3, .1, new THREE.MeshStandardMaterial({ color: 0x14181d }), .8, .8, -17.44));collide(.8, -17.9, 1.1, .9);@@@ -2200,7 +2183,7 @@ function serviceHallway(scene) {}// ============================================================ SMOKE CAGE PATIO--// Photos 205540801/205541712/205604519/205628978: attached covered patio with++// From many break-room trips: attached covered patio with// black chain-link + privacy mesh, black picnic tables, ash receptacles, signs.function smokeCagePatio(scene) {const X0 = -30, X1 = -18, Z0 = -29.2, Z1 = -22.6; // footprint north of the hallway@@@ -2240,7 +2223,7 @@ function smokeCagePatio(scene) {g.fillText('EMERGENCY', w / 2, 40); g.fillText('EXIT ONLY', w / 2, 62);});scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.55, .4), new THREE.MeshBasicMaterial({ map: emTex, side: THREE.DoubleSide })).translateX(X1 - .01).translateY(1.7).translateZ(cz).rotateY(Math.PI / 2));-- // smoking-rules signs on the fences (photo 205628978)++ // smoking-rules signs on the fences (from trips)const ruleTex = TX.ct(160, 200, (g, w, h) => {g.fillStyle = '#fff'; g.fillRect(0, 0, w, h);g.fillStyle = '#232f3e'; g.font = '600 15px "Segoe UI", sans-serif'; g.textAlign = 'center';@@@ -2255,7 +2238,7 @@ function smokeCagePatio(scene) {g.fillText('E010', w / 2, 33);});scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.5, .25), new THREE.MeshBasicMaterial({ map: doorTex })).translateX(-27.2).translateY(2.5).translateZ(-22.38));-- // black expanded-metal picnic tables (photo): table + two attached benches++ // black expanded-metal picnic tables (from trips): table + two attached benchesconst metalM = new THREE.MeshStandardMaterial({ color: 0x191b1e, roughness: .55, metalness: .35 });const picnic = (x, z, ry) => {const g = new THREE.Group();@@@ -2291,7 +2274,7 @@ function smokeCagePatio(scene) {}// ============================================================ EAST EXTERIOR--// Pano 193056495 + photo 193115123: the building front — huge white facade with++// The building front — huge white facade with// blue roofline stripe, wood-band tower with the Amazon smile, blue "main entry"// canopy, bollards, landscaped central walkway (spawn), Amazon Tours signs,// food truck, flag pole, hydrant. Player spawns on the walkway and badges in.@@@ -2303,7 +2286,7 @@ function exteriorEast(scene) {const lot = new THREE.Mesh(new THREE.PlaneGeometry(28, 76), new THREE.MeshStandardMaterial({ map: TX.asphaltTexture(), roughness: .95 }));lot.rotation.x = -Math.PI / 2; lot.position.set(112, -.02, 0);scene.add(lot);-- // central landscaped walkway (photo 193115123) — the spawn path++ // central landscaped walkway (from trips) — the spawn pathconst walkway = new THREE.Mesh(new THREE.PlaneGeometry(22, 3.2), new THREE.MeshStandardMaterial({ color: 0xc3c6c8, roughness: .85 }));walkway.rotation.x = -Math.PI / 2; walkway.position.set(109, -.004, 0);scene.add(walkway);@@@ -2411,7 +2394,7 @@ function exteriorEast(scene) {W.cars.push({ id, group: g, col, driver: null, x: g.position.x, z: g.position.z, ry: Math.PI / 2 });inter({ id, type: 'car', x: g.position.x, z: g.position.z, r: 3.4, label: 'Drive the car 🚗', data: { car: id } });}-- // flag pole + hydrant + food truck (pano)++ // flag pole + hydrant + food truck (from trips)const pole = cyl(.05, .07, 9, M.chrome, 94.5, 4.5, -10, 8);scene.add(pole);const flag = new THREE.Mesh(new THREE.PlaneGeometry(1.5, .95), new THREE.MeshStandardMaterial({ map: TX.flagTexture('usa'), side: THREE.DoubleSide }));@@@ -2456,7 +2439,7 @@ function exteriorEast(scene) {const grayM = new THREE.MeshStandardMaterial({ color: 0xb6bcc1, roughness: .9 });// greige feature band above the storefront carries the smilescene.add(box(.5, 7.2, 34.4, greigeM, 92.35, 9.8, 0));-- // textured pilaster strips flanking the smile (photo: vertical scratter panels)++ // textured pilaster strips flanking the smile (vertical scratter panels)for (const pz of [-8.5, 8.5]) scene.add(box(.56, 7, 1.6, new THREE.MeshStandardMaterial({ color: 0x8f8b84, roughness: .95 }), 92.3, 9.7, pz));// side wings — the building continues far north/southfor (const s of [-1, 1]) {@@@ -2468,7 +2451,7 @@ function exteriorEast(scene) {}// blue roofline stripe across everythingscene.add(box(.56, .8, 82, new THREE.MeshStandardMaterial({ color: 0x2a7de1, roughness: .7 }), 92.3, 13.1, 0));-- // center tower (gray) with a second blue stripe like photo 193115123++ // center tower (gray) with a second blue stripe like the real towerscene.add(box(3.5, 5.2, 15, grayM, 93.8, 15.5, 0));scene.add(box(3.56, .55, 15.1, new THREE.MeshStandardMaterial({ color: 0x2a7de1, roughness: .7 }), 93.8, 14.6, 0));const smileTex2 = TX.ct(512, 256, (g, w, h) => {@@@ -2504,8 +2487,8 @@ function exteriorEast(scene) {}}--// teal walking-path tape + red STOP floor decals (panorama)--// ground + parking apron along the relocated cafeteria's east glass (satellite:++// teal walking-path tape + red STOP floor decals (from trips)++// ground + parking apron along the relocated cafeteria's east glass (site plan:// the lot wraps the whole front) and base plane under the south wingfunction exteriorSouth(scene) {const lot = new THREE.Mesh(new THREE.PlaneGeometry(32, 84), new THREE.MeshStandardMaterial({ map: TX.asphaltTexture(), roughness: .95 }));@@@ -2546,7 +2529,7 @@ function floorPaths(scene) {scene.add(p);};// main route: front doors → badge gates → past the island desk → south through-- // a detector lane → locker hallway → cafeteria double doors (satellite map)++ // a detector lane → locker hallway → cafeteria double doors (site plan)seg(79, -5.2, 75.5, -3.6);seg(75.5, -3.6, 73.5, .5);seg(73.5, .5, 69.4, 7);@@@ -2582,7 +2565,7 @@ export function buildWorld(scene) {scene.fog = new THREE.Fog(0xbcd2e2, 70, 160);lights(scene);// ---- cafeteria block: built in its legacy local frame, then rotated 90 deg and-- // moved SOUTH of the security block per the satellite map — the old east door++ // moved SOUTH of the security block per the site plan — the old east door// wall now faces north into the locker hallway, and the old south window run// becomes the east building face at x=92 (windows onto the parking lot).const caf = new THREE.Group();
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
[](LICENSE)
[](LICENSE)
A **free, open-source** realtime multiplayer 3D web game recreating an Amazon fulfillment
A **free, open-source** realtime multiplayer 3D web game recreating an Amazon fulfillment
-center break room, modeled from real photos. Runs on desktop and mobile — no build step,
+center break room. Runs on desktop and mobile — no build step,
vanilla JS + Three.js.
vanilla JS + Three.js.
🎮 **Play it live: [simulator.rest](https://simulator.rest)** · 💻 **Source: [github.com/OpenVibers/BreakRoomSimulator](https://github.com/OpenVibers/BreakRoomSimulator)**
🎮 **Play it live: [simulator.rest](https://simulator.rest)** · 💻 **Source: [github.com/OpenVibers/BreakRoomSimulator](https://github.com/OpenVibers/BreakRoomSimulator)**
@@ -23,7 +23,7 @@ npm start # → http://localhost:3000
@@ -23,7 +23,7 @@ npm start # → http://localhost:3000
Open the URL on any device on your network (`http://<your-ip>:3000`) and everyone shares
Open the URL on any device on your network (`http://<your-ip>:3000`) and everyone shares
the same break room. `?autoguest` in the URL skips the login screen with a guest account.
the same break room. `?autoguest` in the URL skips the login screen with a guest account.
-## The building (photo-accurate route)
+## The building (the real route, from countless break-room trips)
You spawn on the **landscaped front walkway** (planter beds, Amazon Tours signs,
You spawn on the **landscaped front walkway** (planter beds, Amazon Tours signs,
food truck, flag pole, bollarded plaza) under the huge white facade with its
food truck, flag pole, bollarded plaza) under the huge white facade with its
@@ -56,7 +56,7 @@ smoke cage outside). All exterior cafeteria doors are emergency-exit-only.
@@ -56,7 +56,7 @@ smoke cage outside). All exterior cafeteria doors are emergency-exit-only.
Vest color and win stats persist per account.
Vest color and win stats persist per account.
- **Realtime multiplayer** — websocket sync (12 Hz snapshots): avatars, walk/run/sit
- **Realtime multiplayer** — websocket sync (12 Hz snapshots): avatars, walk/run/sit
animation, name tags, chat with speech bubbles, join/leave feed.
animation, name tags, chat with speech bubbles, join/leave feed.
-- **The room, from the photos**:
+- **The room**:
- Lounge: leather couches inside blue-tape outlines labeled *COUCH*, oriental rug,
- Lounge: leather couches inside blue-tape outlines labeled *COUCH*, oriental rug,
coffee tables, bookshelves with board games, palm plants, *Together at Amazon* banner
coffee tables, bookshelves with board games, palm plants, *Together at Amazon* banner
- Games corner: two JOOLA-style ping pong tables, yellow *PRIME BREAKER* arcade
- Games corner: two JOOLA-style ping pong tables, yellow *PRIME BREAKER* arcade
@@ -65,7 +65,7 @@ smoke cage outside). All exterior cafeteria doors are emergency-exit-only.
@@ -65,7 +65,7 @@ smoke cage outside). All exterior cafeteria doors are emergency-exit-only.
green → brown → orange → yellow, napkin dispensers, *July Newsletter* table tents
green → brown → orange → yellow, napkin dispensers, *July Newsletter* table tents
- *avenue C* micro-market: glass-door coolers, snack slat-walls, self-checkout kiosks,
- *avenue C* micro-market: glass-door coolers, snack slat-walls, self-checkout kiosks,
Pepsi vending machines, Red Bull cooler, coffee station, microwaves, lockers
Pepsi vending machines, Red Bull cooler, coffee station, microwaves, lockers
- - Annex corridor through the wide passage (from the panorama): day lockers,
+ - Annex corridor through the wide passage: day lockers,
avenue C café/servery nook, glass-walled wellness room, teal walking-path
avenue C café/servery nook, glass-walled wellness room, teal walking-path
tape with red STOP floor decals, second entrance doors
tape with red STOP floor decals, second entrance doors
- International flags around the whole perimeter + strung across the hall,
- International flags around the whole perimeter + strung across the hall,
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
{
{
"name": "amazon-break-room-simulator",
"name": "amazon-break-room-simulator",
"version": "1.0.0",
"version": "1.0.0",
- "description": "Amazon Break Room Simulator — realtime multiplayer 3D break room based on real photos",
+ "description": "Amazon Break Room Simulator — realtime multiplayer 3D break room",
"type": "module",
"type": "module",
"main": "server/server.js",
"main": "server/server.js",
"scripts": {
"scripts": {
@@ -166,11 +166,11 @@
@@ -166,11 +166,11 @@
<section id="features">
<section id="features">
<div class="wrap">
<div class="wrap">
<h2 class="rv">What's inside</h2>
<h2 class="rv">What's inside</h2>
- <p class="lead rv">The whole journey of a shift break, recreated room by room from photos of the real building.</p>
+ <p class="lead rv">The whole journey of a shift break, recreated room by room from many, many trips to the real thing.</p>
<div class="grid">
<div class="grid">
<div class="card rv"><div class="ic">🌐</div><h3>Realtime multiplayer</h3><p>Everyone shares one world over websockets — chat, emotes, and other players walking, driving and playing live around you.</p></div>
<div class="card rv"><div class="ic">🌐</div><h3>Realtime multiplayer</h3><p>Everyone shares one world over websockets — chat, emotes, and other players walking, driving and playing live around you.</p></div>
<div class="card rv"><div class="ic">🏓</div><h3>Playable minigames</h3><p>Ping pong duels, giant Connect 4, full chess, and the PRIME BREAKER arcade cabinet with global highscores.</p></div>
<div class="card rv"><div class="ic">🏓</div><h3>Playable minigames</h3><p>Ping pong duels, giant Connect 4, full chess, and the PRIME BREAKER arcade cabinet with global highscores.</p></div>
- <div class="card rv"><div class="ic">🗺️</div><h3>Photo-accurate map</h3><p>Parking lot, badge gates, PAE2 security desk, locker hallway, avenue C market, dining hall, couch corner and the smoke cage.</p></div>
+ <div class="card rv"><div class="ic">🗺️</div><h3>True-to-life map</h3><p>Parking lot, badge gates, PAE2 security desk, locker hallway, avenue C market, dining hall, couch corner and the smoke cage.</p></div>
<div class="card rv"><div class="ic">🚗</div><h3>Drivable cars</h3><p>Every car in the parking lot starts. Arcade handling, handbrake slides, synced across players.</p></div>
<div class="card rv"><div class="ic">🚗</div><h3>Drivable cars</h3><p>Every car in the parking lot starts. Arcade handling, handbrake slides, synced across players.</p></div>
<div class="card rv"><div class="ic">🎒</div><h3>Items & loadout</h3><p>24-slot inventory and hotbar with drag & drop — snacks, hats, vests, and break-room "weapons" like paddles and bananas.</p></div>
<div class="card rv"><div class="ic">🎒</div><h3>Items & loadout</h3><p>24-slot inventory and hotbar with drag & drop — snacks, hats, vests, and break-room "weapons" like paddles and bananas.</p></div>
<div class="card rv"><div class="ic">🕹️</div><h3>Source-style movement</h3><p>First or third person, air-strafing, bunny hops, crouch-sneaking — movement that feels like the classics.</p></div>
<div class="card rv"><div class="ic">🕹️</div><h3>Source-style movement</h3><p>First or third person, air-strafing, bunny hops, crouch-sneaking — movement that feels like the classics.</p></div>
@@ -244,7 +244,7 @@
@@ -244,7 +244,7 @@
<div class="disclaimer">
<div class="disclaimer">
<strong>Fan project.</strong> Amazon Break Room Simulator is an unofficial fan-made game and is
<strong>Fan project.</strong> Amazon Break Room Simulator is an unofficial fan-made game and is
not affiliated with, endorsed by, or sponsored by Amazon.com, Inc. All trademarks are the
not affiliated with, endorsed by, or sponsored by Amazon.com, Inc. All trademarks are the
- property of their respective owners. Made with ❤️ and way too many break-room photos.
+ property of their respective owners. Made with ❤️ and way too many trips to the break room.
</div>
</div>
<div>
<div>
<div><a href="/play">Play the game</a></div>
<div><a href="/play">Play the game</a></div>
@@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
-// Builds the break room from the reference photos:
+// Builds the break room from many trips to the real break room:
// west (x=-38): glass entrance, lounge (couches w/ tape outlines, rug, giant Connect 4, bookshelves)
// west (x=-38): glass entrance, lounge (couches w/ tape outlines, rug, giant Connect 4, bookshelves)
// games corner: 2 JOOLA-style ping pong tables, arcade cabinet, chess table
// games corner: 2 JOOLA-style ping pong tables, arcade cabinet, chess table
// center/east: long rows of folding tables, chairs color-zoned green→brown→orange→yellow
// center/east: long rows of folding tables, chairs color-zoned green→brown→orange→yellow
@@ -65,7 +65,7 @@ function shell(scene) {
@@ -65,7 +65,7 @@ function shell(scene) {
scene.add(blocker(box(76, .2, 34, M.ceiling, 0, 6.3, 0)));
scene.add(blocker(box(76, .2, 34, M.ceiling, 0, 6.3, 0)));
// ---- dining ceiling (x -12..38): white acoustic tile field at 4.9 with
// ---- dining ceiling (x -12..38): white acoustic tile field at 4.9 with
- // recessed higher sections framed in yellow fascia (photos 3/5/7) ----
+ // recessed higher sections framed in yellow fascia (from trips) ----
const tile = TX.ct(512, 512, (g, w, h) => {
const tile = TX.ct(512, 512, (g, w, h) => {
g.fillStyle = '#eceeef'; g.fillRect(0, 0, w, h);
g.fillStyle = '#eceeef'; g.fillRect(0, 0, w, h);
g.strokeStyle = '#c9ccd0'; g.lineWidth = 3;
g.strokeStyle = '#c9ccd0'; g.lineWidth = 3;
@@ -91,7 +91,7 @@ function shell(scene) {
@@ -91,7 +91,7 @@ function shell(scene) {
scene.add(blocker(fieldMesh));
scene.add(blocker(fieldMesh));
for (const [cx, cz, cw, cd] of RECESSES) {
for (const [cx, cz, cw, cd] of RECESSES) {
// recessed white upper ceiling; light-blue fascia band with a yellow
// recessed white upper ceiling; light-blue fascia band with a yellow
- // accent stripe at its base (panorama shows blue-edged clouds)
+ // accent stripe at its base (walk-through shows blue-edged clouds)
scene.add(blocker(box(cw, .1, cd, M.white, cx, 5.75, cz)));
scene.add(blocker(box(cw, .1, cd, M.white, cx, 5.75, cz)));
scene.add(box(cw + .16, .8, .1, M.blueSoffit, cx, 5.35, cz - cd / 2));
scene.add(box(cw + .16, .8, .1, M.blueSoffit, cx, 5.35, cz - cd / 2));
scene.add(box(cw + .16, .8, .1, M.blueSoffit, cx, 5.35, cz + cd / 2));
scene.add(box(cw + .16, .8, .1, M.blueSoffit, cx, 5.35, cz + cd / 2));
@@ -106,7 +106,7 @@ function shell(scene) {
@@ -106,7 +106,7 @@ function shell(scene) {
for (const oz of [-cd / 4, cd / 4]) scene.add(box(cw * .7, .08, .15, lin0, cx, 5.15, cz + oz));
for (const oz of [-cd / 4, cd / 4]) scene.add(box(cw * .7, .08, .15, lin0, cx, 5.15, cz + oz));
}
}
- // ---- big sloped blue-gray soffit at the tall-section transition (photo 3) ----
+ // ---- big sloped blue-gray soffit at the tall-section transition (from trips) ----
const slope = box(.16, 3.0, 34, M.blueSoffit, 0, 0, 0);
const slope = box(.16, 3.0, 34, M.blueSoffit, 0, 0, 0);
slope.rotation.z = -.55;
slope.rotation.z = -.55;
slope.position.set(-12.7, 5.45, 0);
slope.position.set(-12.7, 5.45, 0);
@@ -116,26 +116,24 @@ function shell(scene) {
@@ -116,26 +116,24 @@ function shell(scene) {
scene.add(box(24, .5, 1.0, M.blueSoffit, -26, 5.55, 8.6));
scene.add(box(24, .5, 1.0, M.blueSoffit, -26, 5.55, 8.6));
// walls — north (z=-17): glass only at the lounge corner; the games/dining
// walls — north (z=-17): glass only at the lounge corner; the games/dining
- // stretch is solid gray with the emergency-exit door + arcade (photo 9)
+ // stretch is solid gray with the emergency-exit door + arcade (from trips)
wallWithWindows(scene, { axis: 'z', at: -17, from: -38, to: -30 });
wallWithWindows(scene, { axis: 'z', at: -17, from: -38, to: -30 });
scene.add(blocker(box(16.7, 6.2, .3, M.wallDark, -21.65, 3.1, -17)));
scene.add(blocker(box(16.7, 6.2, .3, M.wallDark, -21.65, 3.1, -17)));
- scene.add(blocker(box(1.7, 6.2, .3, M.wallDark, -12.35 + 1.5, 3.1, -17)));
- // wide corridor opening at x 12..17 into the service hallway (pano 191856299)
+ // solid through to the corridor opening — the hallway (restrooms, microwaves,
+ // smoke cage) is reached through the wide walkway, not an emergency door
+ scene.add(blocker(box(3.4, 6.2, .3, M.wallDark, -11.65, 3.1, -17)));
+ // wide corridor opening at x 12..17 into the service hallway (from trips)
scene.add(blocker(box(22, 6.2, .3, M.wall, 1, 3.1, -17)));
scene.add(blocker(box(22, 6.2, .3, M.wall, 1, 3.1, -17)));
scene.add(blocker(box(21, 6.2, .3, M.wall, 27.5, 3.1, -17)));
scene.add(blocker(box(21, 6.2, .3, M.wall, 27.5, 3.1, -17)));
scene.add(blocker(box(5.4, 3.6, .3, M.wall, 14.5, 4.4, -17)));
scene.add(blocker(box(5.4, 3.6, .3, M.wall, 14.5, 4.4, -17)));
- // metal corner guards on the opening (photo 205701520)
+ // metal corner guards on the opening (from trips)
scene.add(box(.12, 2.6, .35, M.chrome, 12.05, 1.3, -17));
scene.add(box(.12, 2.6, .35, M.chrome, 12.05, 1.3, -17));
scene.add(box(.12, 2.6, .35, M.chrome, 16.95, 1.3, -17));
scene.add(box(.12, 2.6, .35, M.chrome, 16.95, 1.3, -17));
- collide(-21.65, -17.1, 16.7, .6); collide(-10.85, -17.1, 1.7, .6);
+ collide(-21.65, -17.1, 16.7, .6); collide(-11.65, -17.1, 3.4, .6);
collide(1, -17.1, 22, .6); collide(27.5, -17.1, 21, .6);
collide(1, -17.1, 22, .6); collide(27.5, -17.1, 21, .6);
serviceHallway(scene);
serviceHallway(scene);
- // full-height backing panel in the exit-door bay — the gap above/around the
- // 2.4 m door slab looked straight through into the lit hallway (Bug30)
- scene.add(blocker(box(1.8, 6.2, .14, M.wallDark, -12.5, 3.1, -17.02)));
- exitDoor(scene, -12.6, -16.85, 0);
- // south (z=17): long glass run with parking outside (photos 1/2/4) and a second
- // set of entry doors near the market end (panorama), wall far east
+ // south (z=17): long glass run with parking outside (from trips) and a second
+ // set of entry doors near the market end (from trips), wall far east
wallWithWindows(scene, { axis: 'z', at: 17, from: -38, to: 10, doorAt: 6, doorW: 2.0 });
wallWithWindows(scene, { axis: 'z', at: 17, from: -38, to: 10, doorAt: 6, doorW: 2.0 });
// these south doors are emergency-exit-only: closed leaves + signage + sealed
// these south doors are emergency-exit-only: closed leaves + signage + sealed
const sSignM = new THREE.MeshBasicMaterial({ map: emergencySignTexture() });
const sSignM = new THREE.MeshBasicMaterial({ map: emergencySignTexture() });
@@ -149,15 +147,15 @@ function shell(scene) {
@@ -149,15 +147,15 @@ function shell(scene) {
collide(6, 16.95, 2.3, .5);
collide(6, 16.95, 2.3, .5);
scene.add(blocker(box(28, 6.2, .3, M.wall, 24, 3.1, 17)));
scene.add(blocker(box(28, 6.2, .3, M.wall, 24, 3.1, 17)));
collide(24, 17.1, 28, .6);
collide(24, 17.1, 28, .6);
- // east wall (x=38): wide open passage into the locker/wellness annex (panorama)
+ // east wall (x=38): wide open passage into the locker/wellness annex (from trips)
scene.add(blocker(box(.3, 6.2, 14.5, M.wall, 38, 3.1, -9.75)));
scene.add(blocker(box(.3, 6.2, 14.5, M.wall, 38, 3.1, -9.75)));
scene.add(blocker(box(.3, 6.2, 12.8, M.wall, 38, 3.1, 10.6)));
scene.add(blocker(box(.3, 6.2, 12.8, M.wall, 38, 3.1, 10.6)));
scene.add(blocker(box(.3, 2.3, 6.7, M.wall, 38, 5.05, .85))); // header over the opening
scene.add(blocker(box(.3, 2.3, 6.7, M.wall, 38, 5.05, .85))); // header over the opening
- scene.add(box(.24, .55, 6.7, M.blueSoffit, 38, 3.65, .85)); // blue accent band (panorama)
+ scene.add(box(.24, .55, 6.7, M.blueSoffit, 38, 3.65, .85)); // blue accent band (from trips)
collide(38.1, -9.75, .6, 14.5); collide(38.1, 10.6, .6, 12.8);
collide(38.1, -9.75, .6, 14.5); collide(38.1, 10.6, .6, 12.8);
// close the height step between cafeteria (6.2) and the taller hall (7.2)
// close the height step between cafeteria (6.2) and the taller hall (7.2)
scene.add(blocker(box(.3, 1.1, 34, M.white, 38, 6.75, 0)));
scene.add(blocker(box(.3, 1.1, 34, M.white, 38, 6.75, 0)));
- // double doors propped open at the passage (photo 192150685) + flags + clock above
+ // double doors propped open at the passage (from trips) + flags + clock above
for (const [dz, ry] of [[-2.7, .95], [4.4, -.95]]) {
for (const [dz, ry] of [[-2.7, .95], [4.4, -.95]]) {
const leaf = box(.08, 2.3, 1.5, new THREE.MeshStandardMaterial({ color: 0xf2f1ec, roughness: .7 }), 0, 1.15, 0);
const leaf = box(.08, 2.3, 1.5, new THREE.MeshStandardMaterial({ color: 0xf2f1ec, roughness: .7 }), 0, 1.15, 0);
leaf.add(box(.09, .5, .5, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .3, metalness: .5 }), 0, -.7, 0)); // kick plate
leaf.add(box(.09, .5, .5, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .3, metalness: .5 }), 0, -.7, 0)); // kick plate
@@ -178,7 +176,7 @@ function shell(scene) {
@@ -178,7 +176,7 @@ function shell(scene) {
wallWithWindows(scene, { axis: 'x', at: -38, from: 2, to: 17, tall: true });
wallWithWindows(scene, { axis: 'x', at: -38, from: 2, to: 17, tall: true });
entrance(scene);
entrance(scene);
- // interior gray column pair (photo 4)
+ // interior gray column pair (from trips)
for (const z of [-6, 6]) { scene.add(blocker(box(1, 6.2, 1, M.white, -10, 3.1, z))); collide(-10, z, 1, 1); }
for (const z of [-6, 6]) { scene.add(blocker(box(1, 6.2, 1, M.white, -10, 3.1, z))); collide(-10, z, 1, 1); }
// pendant dome lights + linear fixtures (emissive, no real lights for perf)
// pendant dome lights + linear fixtures (emissive, no real lights for perf)
@@ -192,9 +190,9 @@ function shell(scene) {
@@ -192,9 +190,9 @@ function shell(scene) {
const d = new THREE.Mesh(dome, domeMat); d.position.set(x, hangY, z); dGroup.add(d);
const d = new THREE.Mesh(dome, domeMat); d.position.set(x, hangY, z); dGroup.add(d);
const b = new THREE.Mesh(bulb, domeMat); b.position.set(x, hangY - .04, z); dGroup.add(b);
const b = new THREE.Mesh(bulb, domeMat); b.position.set(x, hangY - .04, z); dGroup.add(b);
};
};
- // tall games/lounge section: dense rows of long-drop pendants (photos 8-10)
+ // tall games/lounge section: dense rows of long-drop pendants (from trips)
for (let gx = -36; gx <= -15; gx += 4.2) for (let gz = -14.5; gz <= 15; gz += 5.8) hang(gx, gz, 3.35, 6.2);
for (let gx = -36; gx <= -15; gx += 4.2) for (let gz = -14.5; gz <= 15; gz += 5.8) hang(gx, gz, 3.35, 6.2);
- // dining: pendants drop below the tile ceiling along the rows (photos 3/5/7)
+ // dining: pendants drop below the tile ceiling along the rows (from trips)
for (let gx = -9; gx <= 36; gx += 6.4) for (let gz = -13.5; gz <= 13.5; gz += 6.6) hang(gx, gz, 3.95, 4.9);
for (let gx = -9; gx <= 36; gx += 6.4) for (let gz = -13.5; gz <= 13.5; gz += 6.6) hang(gx, gz, 3.95, 4.9);
scene.add(dGroup);
scene.add(dGroup);
// long continuous linear fluorescent runs (suspended twin tubes)
// long continuous linear fluorescent runs (suspended twin tubes)
@@ -260,9 +258,9 @@ function wallWithWindows(scene, { axis, at, from, to, doorAt = null, doorW = 0,
@@ -260,9 +258,9 @@ function wallWithWindows(scene, { axis, at, from, to, doorAt = null, doorW = 0,
}
}
// ============================================================ LOCKER HALLWAY (z 17..31, x 58..92)
// ============================================================ LOCKER HALLWAY (z 17..31, x 58..92)
-// Satellite map: the lockers strip sits between the security block (north) and
+// site plan: the lockers strip sits between the security block (north) and
// the cafeteria (south). You come south through the PAE2 detector lanes, walk a
// the cafeteria (south). You come south through the PAE2 detector lanes, walk a
-// straight aisle flanked by tan locker banks (photo 192150685) and reach the
+// straight aisle flanked by tan locker banks (from trips) and reach the
// cafeteria double doors at z=31.
// cafeteria double doors at z=31.
function lockerHall(scene) {
function lockerHall(scene) {
const floor = new THREE.Mesh(new THREE.PlaneGeometry(34, 14), new THREE.MeshStandardMaterial({ map: TX.concreteTexture(), roughness: .38, metalness: .08 }));
const floor = new THREE.Mesh(new THREE.PlaneGeometry(34, 14), new THREE.MeshStandardMaterial({ map: TX.concreteTexture(), roughness: .38, metalness: .08 }));
@@ -340,7 +338,7 @@ function lockerHall(scene) {
@@ -340,7 +338,7 @@ function lockerHall(scene) {
ex2.position.set(68.5, 2.95, 17.25);
ex2.position.set(68.5, 2.95, 17.25);
scene.add(ex2);
scene.add(ex2);
- // gray prep table + cart just past the screening lanes (photo 192244996)
+ // gray prep table + cart just past the screening lanes (from trips)
scene.add(box(1.5, .05, .65, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .5, metalness: .3 }), 77.6, .74, 18.5));
scene.add(box(1.5, .05, .65, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .5, metalness: .3 }), 77.6, .74, 18.5));
for (const [lx3, lz3] of [[-.55, -.22], [.55, -.22], [-.55, .22], [.55, .22]]) scene.add(cyl(.02, .02, .72, M.blackMetal, 77.6 + lx3, .37, 18.5 + lz3, 6));
for (const [lx3, lz3] of [[-.55, -.22], [.55, -.22], [-.55, .22], [.55, .22]]) scene.add(cyl(.02, .02, .72, M.blackMetal, 77.6 + lx3, .37, 18.5 + lz3, 6));
collide(77.6, 18.5, 1.6, .75);
collide(77.6, 18.5, 1.6, .75);
@@ -363,7 +361,7 @@ function lockerHall(scene) {
@@ -363,7 +361,7 @@ function lockerHall(scene) {
}
}
// ============================================================ SECURITY ROOM (x 62..80)
// ============================================================ SECURITY ROOM (x 62..80)
-// Pano 153354733 + photos 153342206/153343798: the white PAE2 soffit structure is a
+// From break-room trips: the white PAE2 soffit structure is a
// FREESTANDING ISLAND in the middle of the room — curved mural desk wrapping its
// FREESTANDING ISLAND in the middle of the room — curved mural desk wrapping its
// SE corner facing the gates, metal-detector lanes through its west side. Behind it
// SE corner facing the gates, metal-detector lanes through its west side. Behind it
// (north wall) sit the Safe to Go arch into the FC, the elevator, the US flag and
// (north wall) sit the Safe to Go arch into the FC, the elevator, the US flag and
@@ -393,7 +391,7 @@ function securityLobby(scene) {
@@ -393,7 +391,7 @@ function securityLobby(scene) {
collide(68.1, -17.1, 12.2, .6);
collide(68.1, -17.1, 12.2, .6);
collide(76.2, -17.1, 1.2, .6); collide(85.1, -17.1, 13.8, .6);
collide(76.2, -17.1, 1.2, .6); collide(85.1, -17.1, 13.8, .6);
- // elevator doors on the north wall (photo 153342206: right of the arch corner)
+ // elevator doors on the north wall (right of the arch corner)
scene.add(box(2.0, 2.35, .12, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .35, metalness: .6 }), 68.5, 1.18, -16.86));
scene.add(box(2.0, 2.35, .12, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .35, metalness: .6 }), 68.5, 1.18, -16.86));
scene.add(box(.05, 2.2, .1, new THREE.MeshStandardMaterial({ color: 0x3d4045, roughness: .5 }), 68.5, 1.15, -16.8));
scene.add(box(.05, 2.2, .1, new THREE.MeshStandardMaterial({ color: 0x3d4045, roughness: .5 }), 68.5, 1.15, -16.8));
scene.add(box(.14, .2, .06, new THREE.MeshStandardMaterial({ color: 0xd8d4cc, roughness: .4 }), 69.7, 1.1, -16.8));
scene.add(box(.14, .2, .06, new THREE.MeshStandardMaterial({ color: 0xd8d4cc, roughness: .4 }), 69.7, 1.1, -16.8));
@@ -405,11 +403,11 @@ function securityLobby(scene) {
@@ -405,11 +403,11 @@ function securityLobby(scene) {
const elS = new THREE.Mesh(new THREE.PlaneGeometry(1.2, .3), new THREE.MeshBasicMaterial({ map: elTex }));
const elS = new THREE.Mesh(new THREE.PlaneGeometry(1.2, .3), new THREE.MeshBasicMaterial({ map: elTex }));
elS.position.set(68.5, 2.72, -16.8);
elS.position.set(68.5, 2.72, -16.8);
scene.add(elS);
scene.add(elS);
- // red fire strobe beside the Day One door (photo 153339387)
+ // red fire strobe beside the Day One door (from trips)
scene.add(box(.18, .26, .12, new THREE.MeshStandardMaterial({ color: 0xc22127, roughness: .5 }), 78.6, 2.2, -16.86));
scene.add(box(.18, .26, .12, new THREE.MeshStandardMaterial({ color: 0xc22127, roughness: .5 }), 78.6, 2.2, -16.86));
// ---- WEST WALL (x=62): solid mountain/airplane mural with the FC arch opening
// ---- WEST WALL (x=62): solid mountain/airplane mural with the FC arch opening
- // (z -12..-6). The FC mass lies west of here (satellite map). ----
+ // (z -12..-6). The FC mass lies west of here (site plan). ----
scene.add(blocker(box(.3, 7.2, 5, M.white, 62, 3.6, -14.5))); // z -17..-12
scene.add(blocker(box(.3, 7.2, 5, M.white, 62, 3.6, -14.5))); // z -17..-12
scene.add(blocker(box(.3, 7.2, 23, M.white, 62, 3.6, 5.5))); // z -6..17
scene.add(blocker(box(.3, 7.2, 23, M.white, 62, 3.6, 5.5))); // z -6..17
scene.add(blocker(box(.3, 2.8, 6, M.white, 62, 5.8, -9))); // header over the FC opening
scene.add(blocker(box(.3, 2.8, 6, M.white, 62, 5.8, -9))); // header over the FC opening
@@ -445,7 +443,7 @@ function securityLobby(scene) {
@@ -445,7 +443,7 @@ function securityLobby(scene) {
low.rotation.y = Math.PI / 2; low.position.set(62.32, 1.32, lz);
low.rotation.y = Math.PI / 2; low.position.set(62.32, 1.32, lz);
scene.add(low);
scene.add(low);
}
}
- // hanging US flag + Pack One Goal banner flanking the arch (pano)
+ // hanging US flag + Pack One Goal banner flanking the arch (from trips)
const usFlag = new THREE.Mesh(new THREE.PlaneGeometry(2.8, 1.7), new THREE.MeshStandardMaterial({ map: TX.flagTexture('usa'), side: THREE.DoubleSide }));
const usFlag = new THREE.Mesh(new THREE.PlaneGeometry(2.8, 1.7), new THREE.MeshStandardMaterial({ map: TX.flagTexture('usa'), side: THREE.DoubleSide }));
usFlag.rotation.y = Math.PI / 2;
usFlag.rotation.y = Math.PI / 2;
usFlag.position.set(62.5, 5.8, -13.9);
usFlag.position.set(62.5, 5.8, -13.9);
@@ -542,7 +540,7 @@ function securityLobby(scene) {
@@ -542,7 +540,7 @@ function securityLobby(scene) {
scene.add(stopFC);
scene.add(stopFC);
// ---- SOUTH WALL (z=17): mural-faced wall with a plain OPENING into the locker
// ---- SOUTH WALL (z=17): mural-faced wall with a plain OPENING into the locker
- // area (FrontAreaPhoto: "the locker area has an opening on the middle wall" —
+ // area ("the locker area has an opening on the middle wall" —
// no detectors here). Entry-lobby stretch (x 80..92) stays solid blue. ----
// no detectors here). Entry-lobby stretch (x 80..92) stays solid blue. ----
scene.add(blocker(box(18, 5.9, .3, structM, 71, 5.45, 17))); // white header 62..80
scene.add(blocker(box(18, 5.9, .3, structM, 71, 5.45, 17))); // white header 62..80
scene.add(blocker(box(12, 5.9, .3, blueWall, 86, 5.45, 17))); // blue header 80..92
scene.add(blocker(box(12, 5.9, .3, blueWall, 86, 5.45, 17))); // blue header 80..92
@@ -570,7 +568,7 @@ function securityLobby(scene) {
@@ -570,7 +568,7 @@ function securityLobby(scene) {
exS.rotation.y = Math.PI; exS.position.set(70.5, 2.9, 16.82);
exS.rotation.y = Math.PI; exS.position.set(70.5, 2.9, 16.82);
scene.add(exS);
scene.add(exS);
- // ---- METAL DETECTORS (FrontAreaPhoto / 153342206): three lanes rotated 90°,
+ // ---- METAL DETECTORS (from trips): three lanes rotated 90°,
// in line with the Safe to Go arch — everyone leaving the warehouse walks the
// in line with the Safe to Go arch — everyone leaving the warehouse walks the
// arch, then a detector lane, under a white soffit joined to the desk island ----
// arch, then a detector lane, under a white soffit joined to the desk island ----
scene.add(blocker(box(4.6, 2.6, 7.4, structM, 63.9, 4.9, -9.1))); // soffit bridge wall→island
scene.add(blocker(box(4.6, 2.6, 7.4, structM, 63.9, 4.9, -9.1))); // soffit bridge wall→island
@@ -597,7 +595,7 @@ function securityLobby(scene) {
@@ -597,7 +595,7 @@ function securityLobby(scene) {
for (const [lx3, lz3] of [[-.5, -.2], [.5, -.2], [-.5, .2], [.5, .2]]) scene.add(cyl(.02, .02, .72, M.blackMetal, 63.2 + lx3, .37, -13.3 + lz3, 6));
for (const [lx3, lz3] of [[-.5, -.2], [.5, -.2], [-.5, .2], [.5, .2]]) scene.add(cyl(.02, .02, .72, M.blackMetal, 63.2 + lx3, .37, -13.3 + lz3, 6));
collide(63.2, -13.3, 1.5, .7);
collide(63.2, -13.3, 1.5, .7);
- // ---- secondary screening (southwest corner, photo 153343798): ONE detector +
+ // ---- secondary screening (southwest corner, from trips): ONE detector +
// one table, tucked against the mural clear of the locker-room doorway ----
// one table, tucked against the mural clear of the locker-room doorway ----
scene.add(box(.6, 2.15, .18, detM, 63.4, 1.08, 12.85));
scene.add(box(.6, 2.15, .18, detM, 63.4, 1.08, 12.85));
scene.add(box(.6, 2.15, .18, detM, 63.4, 1.08, 13.95));
scene.add(box(.6, 2.15, .18, detM, 63.4, 1.08, 13.95));
@@ -621,7 +619,7 @@ function securityLobby(scene) {
@@ -621,7 +619,7 @@ function securityLobby(scene) {
ssW.rotation.y = Math.PI / 2; ssW.position.set(62.4, 2.15, 13.4);
ssW.rotation.y = Math.PI / 2; ssW.position.set(62.4, 2.15, 13.4);
scene.add(ssW);
scene.add(ssW);
- // ---- THE PAE2 ISLAND (photo 153342206): one long freestanding white soffit
+ // ---- THE PAE2 ISLAND (from trips): one long freestanding white soffit
// structure facing the gates — desk on the RIGHT (north) end, exit-screening
// structure facing the gates — desk on the RIGHT (north) end, exit-screening
// metal-detector bays on the LEFT (south) end feeding the exit turnstiles.
// metal-detector bays on the LEFT (south) end feeding the exit turnstiles.
// The FC arch sits further right (north wall). ----
// The FC arch sits further right (north wall). ----
@@ -641,7 +639,7 @@ function securityLobby(scene) {
@@ -641,7 +639,7 @@ function securityLobby(scene) {
const paeS = new THREE.Mesh(new THREE.PlaneGeometry(5.6, 2.0), new THREE.MeshStandardMaterial({ map: paeTex }));
const paeS = new THREE.Mesh(new THREE.PlaneGeometry(5.6, 2.0), new THREE.MeshStandardMaterial({ map: paeTex }));
paeS.position.set(68.6, 4.9, -1.14);
paeS.position.set(68.6, 4.9, -1.14);
scene.add(paeS);
scene.add(paeS);
- // ---- curved mural desk on the north end of the front (photo 153342206:
+ // ---- curved mural desk on the north end of the front (from trips:
// chevron at the right end, curved cap toward the detectors) ----
// chevron at the right end, curved cap toward the detectors) ----
const muralTex = TX.ct(1024, 128, (g, w, h) => {
const muralTex = TX.ct(1024, 128, (g, w, h) => {
g.fillStyle = '#f5f4f0'; g.fillRect(0, 0, w, h);
g.fillStyle = '#f5f4f0'; g.fillRect(0, 0, w, h);
@@ -708,7 +706,7 @@ function securityLobby(scene) {
@@ -708,7 +706,7 @@ function securityLobby(scene) {
kite.position.set(77, 6.2, 4);
kite.position.set(77, 6.2, 4);
scene.add(kite);
scene.add(kite);
- // ---- display cluster between the arch and Career Choice (pano): light-up PAE2
+ // ---- display cluster between the arch and Career Choice (from trips): light-up PAE2
// letters, the big Peccy statue, black tours table with TV ----
// letters, the big Peccy statue, black tours table with TV ----
const letterTex = (ch) => TX.ct(128, 160, (g, w, h) => {
const letterTex = (ch) => TX.ct(128, 160, (g, w, h) => {
g.clearRect(0, 0, w, h);
g.clearRect(0, 0, w, h);
@@ -795,7 +793,7 @@ function securityLobby(scene) {
@@ -795,7 +793,7 @@ function securityLobby(scene) {
moD.rotation.x = -Math.PI / 2; moD.position.set(73.0, .012, -10.4); moD.renderOrder = 1;
moD.rotation.x = -Math.PI / 2; moD.position.set(73.0, .012, -10.4); moD.renderOrder = 1;
scene.add(moD);
scene.add(moD);
- // ---- Career Choice alcove dressing (photo 153339387): Grow banner, wire rack,
+ // ---- Career Choice alcove dressing (from trips): Grow banner, wire rack,
// black cubby lockers, LOCKERS tape, New Hires decals, safety board ----
// black cubby lockers, LOCKERS tape, New Hires decals, safety board ----
const growTex = TX.ct(192, 384, (g, w, h) => {
const growTex = TX.ct(192, 384, (g, w, h) => {
g.fillStyle = '#f2f5f8'; g.fillRect(0, 0, w, h);
g.fillStyle = '#f2f5f8'; g.fillRect(0, 0, w, h);
@@ -901,7 +899,7 @@ function securityLobby(scene) {
@@ -901,7 +899,7 @@ function securityLobby(scene) {
// User-described: right of the security desk leads into the FC; beyond that are
// User-described: right of the security desk leads into the FC; beyond that are
// the "Day One" and "Career Choice" meeting rooms.
// the "Day One" and "Career Choice" meeting rooms.
function dayOneRooms(scene) {
function dayOneRooms(scene) {
- // photo 153339387: Career Choice alcove on the LEFT (west), Day 1 Training
+ // Career Choice alcove on the LEFT (west), Day 1 Training
// room on the RIGHT (east), safety board east of Day One, jamb corner guards
// room on the RIGHT (east), safety board east of Day One, jamb corner guards
const roomFloor = new THREE.Mesh(new THREE.PlaneGeometry(7.4, 7), new THREE.MeshStandardMaterial({ color: 0x8a8d90, roughness: .9 }));
const roomFloor = new THREE.Mesh(new THREE.PlaneGeometry(7.4, 7), new THREE.MeshStandardMaterial({ color: 0x8a8d90, roughness: .9 }));
roomFloor.rotation.x = -Math.PI / 2; roomFloor.position.set(76.6, .005, -20.5);
roomFloor.rotation.x = -Math.PI / 2; roomFloor.position.set(76.6, .005, -20.5);
@@ -931,7 +929,7 @@ function dayOneRooms(scene) {
@@ -931,7 +929,7 @@ function dayOneRooms(scene) {
});
});
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.15, 2.3), new THREE.MeshBasicMaterial({ map: ccDoorTex })).translateX(74.9).translateY(1.15).translateZ(-16.86));
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.15, 2.3), new THREE.MeshBasicMaterial({ map: ccDoorTex })).translateX(74.9).translateY(1.15).translateZ(-16.86));
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.15, 2.3), new THREE.MeshBasicMaterial({ map: dayDoorTex })).translateX(77.5).translateY(1.15).translateZ(-16.86));
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(1.15, 2.3), new THREE.MeshBasicMaterial({ map: dayDoorTex })).translateX(77.5).translateY(1.15).translateZ(-16.86));
- // stainless corner guards on the jambs (photo)
+ // stainless corner guards on the jambs (from trips)
for (const gx of [74.2, 75.6, 76.8, 78.2]) scene.add(box(.1, 2.5, .18, M.chrome, gx, 1.25, -16.9));
for (const gx of [74.2, 75.6, 76.8, 78.2]) scene.add(box(.1, 2.5, .18, M.chrome, gx, 1.25, -16.9));
// signs: Career Choice wall flag (west) + Day 1 Training room hanging sign (east)
// signs: Career Choice wall flag (west) + Day 1 Training room hanging sign (east)
const ccSignTex = TX.ct(192, 96, (g, w, h) => {
const ccSignTex = TX.ct(192, 96, (g, w, h) => {
@@ -972,7 +970,7 @@ function dayOneRooms(scene) {
@@ -972,7 +970,7 @@ function dayOneRooms(scene) {
}
}
// ============================================================ HEDGE GATE WALL (x=80)
// ============================================================ HEDGE GATE WALL (x=80)
-// Pano 193021419: green hedge feature wall with neon "Welcome to PAE2" script,
+// From break-room trips: green hedge feature wall with neon "Welcome to PAE2" script,
// red garland, Enter + Do-Not-Enter turnstile openings, and a window into PAE2.
// red garland, Enter + Do-Not-Enter turnstile openings, and a window into PAE2.
function hedgeGateWall(scene) {
function hedgeGateWall(scene) {
const hedgeTex = TX.ct(256, 256, (g, w, h) => {
const hedgeTex = TX.ct(256, 256, (g, w, h) => {
@@ -1059,13 +1057,13 @@ function hedgeGateWall(scene) {
@@ -1059,13 +1057,13 @@ function hedgeGateWall(scene) {
inter({ id: gate.id, type: 'gate', x: 80, z: lz, r: 1.5, label: kind === 'enter' ? 'Badge through the gate' : 'Badge out (Do Not Enter side)', data: { gate: gate.id } });
inter({ id: gate.id, type: 'gate', x: 80, z: lz, r: 1.5, label: kind === 'enter' ? 'Badge through the gate' : 'Badge out (Do Not Enter side)', data: { gate: gate.id } });
}
}
};
};
- mkGates([-8.3, -7.05, -5.8, -4.55], 'enter'); // 4 entry lanes (pano 193021419)
+ mkGates([-8.3, -7.05, -5.8, -4.55], 'enter'); // 4 entry lanes (from trips)
mkGates([4.55, 5.8, 7.05, 8.3], 'exit'); // 4 exit lanes
mkGates([4.55, 5.8, 7.05, 8.3], 'exit'); // 4 exit lanes
}
}
// ============================================================ ENTRY LOBBY (x 80..92)
// ============================================================ ENTRY LOBBY (x 80..92)
function entryLobby(scene) {
function entryLobby(scene) {
- // Amazon Family World Map mural on the north wall (photo/pano)
+ // Amazon Family World Map mural on the north wall (from trips)
const mapTex = TX.ct(1024, 512, (g, w, h) => {
const mapTex = TX.ct(1024, 512, (g, w, h) => {
g.fillStyle = '#2a72b8'; g.fillRect(0, 0, w, h);
g.fillStyle = '#2a72b8'; g.fillRect(0, 0, w, h);
g.fillStyle = '#fff'; g.font = '600 44px "Segoe UI", sans-serif'; g.textAlign = 'left';
g.fillStyle = '#fff'; g.font = '600 44px "Segoe UI", sans-serif'; g.textAlign = 'left';
@@ -1093,7 +1091,7 @@ function entryLobby(scene) {
@@ -1093,7 +1091,7 @@ function entryLobby(scene) {
const aed = new THREE.Mesh(new THREE.PlaneGeometry(.7, .7), new THREE.MeshBasicMaterial({ map: aedTex, transparent: true, side: THREE.DoubleSide }));
const aed = new THREE.Mesh(new THREE.PlaneGeometry(.7, .7), new THREE.MeshBasicMaterial({ map: aedTex, transparent: true, side: THREE.DoubleSide }));
aed.position.set(82, 5.6, -16.7);
aed.position.set(82, 5.6, -16.7);
scene.add(aed);
scene.add(aed);
- // cocktail table + literature stand (south side, like pano left area)
+ // cocktail table + literature stand (south side, from trips)
scene.add(cyl(.45, .45, .04, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .7 }), 84, 1.08, 13.5, 14));
scene.add(cyl(.45, .45, .04, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .7 }), 84, 1.08, 13.5, 14));
scene.add(cyl(.09, .34, 1.08, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .7 }), 84, .54, 13.5, 10));
scene.add(cyl(.09, .34, 1.08, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .7 }), 84, .54, 13.5, 10));
collide(84, 13.5, 1.0, 1.0);
collide(84, 13.5, 1.0, 1.0);
@@ -1111,7 +1109,7 @@ function entryLobby(scene) {
@@ -1111,7 +1109,7 @@ function entryLobby(scene) {
b.position.set(80.9, 1.1, bz);
b.position.set(80.9, 1.1, bz);
scene.add(b);
scene.add(b);
}
}
- // traffic cone by the enter gates (pano)
+ // traffic cone by the enter gates (from trips)
scene.add(cyl(.03, .18, .55, new THREE.MeshStandardMaterial({ color: 0xe06a1e, roughness: .6 }), 81.6, .28, -8.9, 10));
scene.add(cyl(.03, .18, .55, new THREE.MeshStandardMaterial({ color: 0xe06a1e, roughness: .6 }), 81.6, .28, -8.9, 10));
// floor guidance (Bug29): one coherent enter path from the front doors to the
// floor guidance (Bug29): one coherent enter path from the front doors to the
// Enter lanes — alternating smile arrows and walk decals, evenly spaced and
// Enter lanes — alternating smile arrows and walk decals, evenly spaced and
@@ -1168,7 +1166,7 @@ function entryLobby(scene) {
@@ -1168,7 +1166,7 @@ function entryLobby(scene) {
}
}
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.6, .25), new THREE.MeshBasicMaterial({ map: bayLabel(label) })).translateX(91.85).translateY(2.72).translateZ(bz).rotateY(-Math.PI / 2));
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.6, .25), new THREE.MeshBasicMaterial({ map: bayLabel(label) })).translateX(91.85).translateY(2.72).translateZ(bz).rotateY(-Math.PI / 2));
}
}
- // black trash barrels between the bays (photos)
+ // black trash barrels between the bays (from trips)
for (const bz of [-5.2, 5.2, 12.5]) {
for (const bz of [-5.2, 5.2, 12.5]) {
scene.add(cyl(.34, .3, .85, new THREE.MeshStandardMaterial({ color: 0x1d2126, roughness: .8 }), 92.9, .43, bz, 12));
scene.add(cyl(.34, .3, .85, new THREE.MeshStandardMaterial({ color: 0x1d2126, roughness: .8 }), 92.9, .43, bz, 12));
collide(92.9, bz, .7, .7);
collide(92.9, bz, .7, .7);
@@ -1188,21 +1186,6 @@ function entryLobby(scene) {
@@ -1188,21 +1186,6 @@ function entryLobby(scene) {
}
}
}
}
-function exitDoor(scene, x, z, ry) {
- const g = new THREE.Group();
- g.add(box(1.1, 2.4, .1, new THREE.MeshStandardMaterial({ color: 0x2e3238, roughness: .6 }), 0, 1.2, 0));
- g.add(box(.9, .5, .02, new THREE.MeshStandardMaterial({ color: 0x9aa4ae, roughness: .3, metalness: .4 }), 0, 1.55, .06)); // push bar? window
- const signTex = TX.ct(128, 96, (gg, w, h) => {
- gg.fillStyle = '#c22127'; gg.fillRect(0, 0, w, h);
- gg.fillStyle = '#fff'; gg.font = '700 17px Arial'; gg.textAlign = 'center';
- gg.fillText('EMERGENCY', w / 2, 34); gg.fillText('EXIT ONLY', w / 2, 58);
- });
- g.add(new THREE.Mesh(new THREE.PlaneGeometry(.42, .3), new THREE.MeshBasicMaterial({ map: signTex })).translateY(1.6).translateZ(.07));
- g.position.set(x, 0, z); g.rotation.y = ry;
- scene.add(g);
- collide(x, z, 1.2, 1.2);
-}
-
function emergencySignTexture() {
function emergencySignTexture() {
return TX.ct(128, 64, (g, w, h) => {
return TX.ct(128, 64, (g, w, h) => {
g.fillStyle = '#c22127'; g.fillRect(0, 0, w, h);
g.fillStyle = '#c22127'; g.fillRect(0, 0, w, h);
@@ -1280,7 +1263,7 @@ function exterior(scene) {
@@ -1280,7 +1263,7 @@ function exterior(scene) {
t.position.set(x, 0, z);
t.position.set(x, 0, z);
scene.add(t);
scene.add(t);
}
}
- // stop sign (photo 8)
+ // stop sign (from trips)
const sign = new THREE.Group();
const sign = new THREE.Group();
sign.add(cyl(.04, .04, 2.6, M.chrome, 0, 1.3, 0, 8));
sign.add(cyl(.04, .04, 2.6, M.chrome, 0, 1.3, 0, 8));
const oct = new THREE.Mesh(new THREE.CylinderGeometry(.45, .45, .04, 8), new THREE.MeshStandardMaterial({ color: 0xc22127, roughness: .5 }));
const oct = new THREE.Mesh(new THREE.CylinderGeometry(.45, .45, .04, 8), new THREE.MeshStandardMaterial({ color: 0xc22127, roughness: .5 }));
@@ -1296,7 +1279,7 @@ function exterior(scene) {
@@ -1296,7 +1279,7 @@ function exterior(scene) {
sign.add(stopF);
sign.add(stopF);
sign.position.set(-44.5, 0, -5.5); sign.rotation.y = Math.PI / 2;
sign.position.set(-44.5, 0, -5.5); sign.rotation.y = Math.PI / 2;
scene.add(sign);
scene.add(sign);
- // traffic cones (photo 2)
+ // traffic cones (from trips)
for (const [x, z] of [[-42.5, -9.5], [-42.5, -11], [-43.5, -12.5]]) {
for (const [x, z] of [[-42.5, -9.5], [-42.5, -11], [-43.5, -12.5]]) {
const cone = cyl(.03, .18, .55, new THREE.MeshStandardMaterial({ color: 0xe06a1e, roughness: .6 }), x, .28, z, 10);
const cone = cyl(.03, .18, .55, new THREE.MeshStandardMaterial({ color: 0xe06a1e, roughness: .6 }), x, .28, z, 10);
scene.add(cone);
scene.add(cone);
@@ -1311,7 +1294,7 @@ function exterior(scene) {
@@ -1311,7 +1294,7 @@ function exterior(scene) {
}
}
// ============================================================ LOUNGE
// ============================================================ LOUNGE
-// Photos 8/10: the couch corner sits in the NW glass corner (two window walls
+// From many break-room trips: the couch corner sits in the NW glass corner (two window walls
// meeting), right next to the ping pong tables. Exactly 2 couches + 1 armchair.
// meeting), right next to the ping pong tables. Exactly 2 couches + 1 armchair.
function lounge(scene) {
function lounge(scene) {
// rug centered in the corner group
// rug centered in the corner group
@@ -1336,7 +1319,7 @@ function lounge(scene) {
@@ -1336,7 +1319,7 @@ function lounge(scene) {
bookshelf(scene, -37.2, 3.6, Math.PI / 2);
bookshelf(scene, -37.2, 3.6, Math.PI / 2);
for (const [x, z] of [[-36.8, 6.4], [-25.6, 15.8], [-35.8, 8.6]]) plant(scene, x, z);
for (const [x, z] of [[-36.8, 6.4], [-25.6, 15.8], [-35.8, 8.6]]) plant(scene, x, z);
- // hazard tape square just inside the entrance (photo 4)
+ // hazard tape square just inside the entrance (from trips)
const hz = new THREE.Mesh(new THREE.PlaneGeometry(1.4, 1.4), new THREE.MeshBasicMaterial({ map: TX.hazardSquareTexture(), transparent: true }));
const hz = new THREE.Mesh(new THREE.PlaneGeometry(1.4, 1.4), new THREE.MeshBasicMaterial({ map: TX.hazardSquareTexture(), transparent: true }));
hz.rotation.x = -Math.PI / 2; hz.position.set(-30.5, .014, 1.6);
hz.rotation.x = -Math.PI / 2; hz.position.set(-30.5, .014, 1.6);
scene.add(hz);
scene.add(hz);
@@ -1455,23 +1438,23 @@ function trash(scene, x, z, color) {
@@ -1455,23 +1438,23 @@ function trash(scene, x, z, color) {
}
}
// ============================================================ GAMES CORNER
// ============================================================ GAMES CORNER
-// Photo 9: both ping pong tables sit side by side along the solid north wall,
+// From trips: both ping pong tables sit side by side along the solid north wall,
// immediately east of the couch corner; arcade + exit door further along.
// immediately east of the couch corner; arcade + exit door further along.
function gamesCorner(scene) {
function gamesCorner(scene) {
pingPongTable(scene, 'a', -28.7, -12.4);
pingPongTable(scene, 'a', -28.7, -12.4);
pingPongTable(scene, 'b', -21.3, -12.4);
pingPongTable(scene, 'b', -21.3, -12.4);
arcade(scene, -15.6, -16.2);
arcade(scene, -15.6, -16.2);
chessTable(scene, -33.2, 4.2);
chessTable(scene, -33.2, 4.2);
- connect4(scene, 'a', -36.9, -6.6, Math.PI / 2); // against west glass by the lounge (photo 8)
- connect4(scene, 'b', -27.3, 12.4, -.4); // mid-floor near the south windows (photo 10)
- // loose lime chairs by the pong tables (photo 9)
+ connect4(scene, 'a', -36.9, -6.6, Math.PI / 2); // against west glass by the lounge (from trips)
+ connect4(scene, 'b', -27.3, 12.4, -.4); // mid-floor near the south windows (from trips)
+ // loose lime chairs by the pong tables (from trips)
for (const [x, z, ry] of [[-31.9, -10.6, 2.4], [-25.2, -10.4, -2.8], [-18.2, -13.6, 1.2]]) {
for (const [x, z, ry] of [[-31.9, -10.6, 2.4], [-25.2, -10.4, -2.8], [-18.2, -13.6, 1.2]]) {
const c = simpleChair(M.chairGreen);
const c = simpleChair(M.chairGreen);
c.position.set(x, 0, z); c.rotation.y = ry;
c.position.set(x, 0, z); c.rotation.y = ry;
scene.add(c);
scene.add(c);
W.seats.push({ id: `loose-${x.toFixed(0)}`, x, z, y: .5, ry: ry + Math.PI, type: 'chair', exitX: x + Math.sin(ry), exitZ: z + Math.cos(ry) });
W.seats.push({ id: `loose-${x.toFixed(0)}`, x, z, y: .5, ry: ry + Math.PI, type: 'chair', exitX: x + Math.sin(ry), exitZ: z + Math.cos(ry) });
}
}
- // cornhole board leaning on wall (photo 9)
+ // cornhole board leaning on wall (from trips)
const ch = box(.62, .04, 1.2, M.wood, 0, 0, 0);
const ch = box(.62, .04, 1.2, M.wood, 0, 0, 0);
ch.position.set(-17.8, .6, -16.55); ch.rotation.x = -1.15;
ch.position.set(-17.8, .6, -16.55); ch.rotation.x = -1.15;
const hole = cyl(.09, .09, .05, M.blackMetal, -17.8, 1.0, -16.42); hole.rotation.x = -1.15;
const hole = cyl(.09, .09, .05, M.blackMetal, -17.8, 1.0, -16.42); hole.rotation.x = -1.15;
@@ -1642,7 +1625,7 @@ function connect4(scene, id, x, z, ry) {
@@ -1642,7 +1625,7 @@ function connect4(scene, id, x, z, ry) {
g.add(box(.12, .32, .7, blue, -.89, .16, 0));
g.add(box(.12, .32, .7, blue, -.89, .16, 0));
g.add(box(.12, .32, .7, blue, .89, .16, 0));
g.add(box(.12, .32, .7, blue, .89, .16, 0));
g.position.set(x, 0, z); g.rotation.y = ry;
g.position.set(x, 0, z); g.rotation.y = ry;
- g.scale.setScalar(.72); // real one is ~4 ft tall (photo 8)
+ g.scale.setScalar(.72); // real one is ~4 ft tall (from trips)
scene.add(g);
scene.add(g);
collide(x, z, 1.6, .8);
collide(x, z, 1.6, .8);
// disc pool (42) — laid out by minigames.js in board-local coords then transformed
// disc pool (42) — laid out by minigames.js in board-local coords then transformed
@@ -1678,14 +1661,14 @@ function connect4(scene, id, x, z, ry) {
@@ -1678,14 +1661,14 @@ function connect4(scene, id, x, z, ry) {
// ============================================================ DINING
// ============================================================ DINING
function dining(scene) {
function dining(scene) {
const clusters = [];
const clusters = [];
- // zone order from the photos, west→east: brown, green, orange, yellow
+ // zone order from trips, west→east: brown, green, orange, yellow
const zoneMats = [M.chairBrown, M.chairGreen, M.chairOrange, M.chairYellow];
const zoneMats = [M.chairBrown, M.chairGreen, M.chairOrange, M.chairYellow];
const xStarts = [-8.2, 2.0, 12.2, 22.4];
const xStarts = [-8.2, 2.0, 12.2, 22.4];
const rows = [-14, -11.3, -8.6, -5.9, -3.2, 3.2, 5.9, 8.6, 11.3, 14];
const rows = [-14, -11.3, -8.6, -5.9, -3.2, 3.2, 5.9, 8.6, 11.3, 14];
for (let zi = 0; zi < 4; zi++) for (const rz of rows) clusters.push({ x: xStarts[zi] + 3.6, z: rz, zone: zi });
for (let zi = 0; zi < 4; zi++) for (const rz of rows) clusters.push({ x: xStarts[zi] + 3.6, z: rz, zone: zi });
// each cluster = four 6-ft (1.78 m) folding tables butted end to end, with
// each cluster = four 6-ft (1.78 m) folding tables butted end to end, with
- // visible seams and folding legs at each table end (photos 3/5/7)
+ // visible seams and folding legs at each table end (from trips)
const TL = 1.78, GAP = .045, PER = 4;
const TL = 1.78, GAP = .045, PER = 4;
const topGeo = new THREE.BoxGeometry(TL, .05, .76);
const topGeo = new THREE.BoxGeometry(TL, .05, .76);
const edgeGeo = new THREE.BoxGeometry(TL + .02, .07, .8);
const edgeGeo = new THREE.BoxGeometry(TL + .02, .07, .8);
@@ -1715,7 +1698,7 @@ function dining(scene) {
@@ -1715,7 +1698,7 @@ function dining(scene) {
});
});
scene.add(tops, edges, legs);
scene.add(tops, edges, legs);
- // chairs packed tight, ~0.72 m pitch, 10 per side (photos show shoulder-to-shoulder)
+ // chairs packed tight, ~0.72 m pitch, 10 per side (shoulder-to-shoulder)
const seatGeo = new THREE.BoxGeometry(.46, .05, .45);
const seatGeo = new THREE.BoxGeometry(.46, .05, .45);
const backGeo = new THREE.BoxGeometry(.46, .48, .05);
const backGeo = new THREE.BoxGeometry(.46, .48, .05);
const sledGeo = new THREE.CylinderGeometry(.016, .016, .5, 6);
const sledGeo = new THREE.CylinderGeometry(.016, .016, .5, 6);
@@ -1779,7 +1762,7 @@ function dining(scene) {
@@ -1779,7 +1762,7 @@ function dining(scene) {
});
});
scene.add(disps, tents, sans);
scene.add(disps, tents, sans);
- // window-side laptop cart with orange chair + blue CABLE tape (photo 4)
+ // window-side laptop cart with orange chair + blue CABLE tape (from trips)
const cart = new THREE.Group();
const cart = new THREE.Group();
cart.add(box(.7, 1.1, .55, new THREE.MeshStandardMaterial({ color: 0x33373d, roughness: .5 }), 0, .55, 0));
cart.add(box(.7, 1.1, .55, new THREE.MeshStandardMaterial({ color: 0x33373d, roughness: .5 }), 0, .55, 0));
cart.add(box(.42, .26, .03, new THREE.MeshStandardMaterial({ color: 0x1b2731, roughness: .25 }), 0, 1.28, -.1)); // laptop screen
cart.add(box(.42, .26, .03, new THREE.MeshStandardMaterial({ color: 0x1b2731, roughness: .25 }), 0, 1.28, -.1)); // laptop screen
@@ -1806,7 +1789,7 @@ function market(scene) {
@@ -1806,7 +1789,7 @@ function market(scene) {
const signB = new THREE.Mesh(new THREE.PlaneGeometry(3.6, .58), new THREE.MeshStandardMaterial({ map: TX.avenueCTexture() }));
const signB = new THREE.Mesh(new THREE.PlaneGeometry(3.6, .58), new THREE.MeshStandardMaterial({ map: TX.avenueCTexture() }));
signB.rotation.y = -Math.PI / 2; signB.position.set(37.5, 3.3, 8);
signB.rotation.y = -Math.PI / 2; signB.position.set(37.5, 3.3, 8);
scene.add(signB);
scene.add(signB);
- // sign above north lockers too (photo 5)
+ // sign above north lockers too (from trips)
const sign2 = new THREE.Mesh(new THREE.PlaneGeometry(3.4, .55), new THREE.MeshStandardMaterial({ map: TX.avenueCTexture() }));
const sign2 = new THREE.Mesh(new THREE.PlaneGeometry(3.4, .55), new THREE.MeshStandardMaterial({ map: TX.avenueCTexture() }));
sign2.position.set(29, 3.1, -16.8);
sign2.position.set(29, 3.1, -16.8);
scene.add(sign2);
scene.add(sign2);
@@ -1827,9 +1810,9 @@ function market(scene) {
@@ -1827,9 +1810,9 @@ function market(scene) {
counterUnit(scene, 34, -13.5);
counterUnit(scene, 34, -13.5);
// ---- north wall fixtures ----
// ---- north wall fixtures ----
- // Pepsi machines (photo 3)
+ // Pepsi machines (from trips)
for (let i = 0; i < 4; i++) vending(scene, -2.5 + i * 1.15, -16.35, i < 2 ? 'pepsi' : 'snack');
for (let i = 0; i < 4; i++) vending(scene, -2.5 + i * 1.15, -16.35, i < 2 ? 'pepsi' : 'snack');
- // lockers (photo 5)
+ // lockers (from trips)
for (let i = 0; i < 5; i++) lockers(scene, 23.5 + i * 2.1, -16.5);
for (let i = 0; i < 5; i++) lockers(scene, 23.5 + i * 2.1, -16.5);
// trash + recycle near market
// trash + recycle near market
trash(scene, 20.5, -16.2, 0x2a63c9);
trash(scene, 20.5, -16.2, 0x2a63c9);
@@ -1911,7 +1894,7 @@ function lockers(scene, x, z, ry = 0) {
@@ -1911,7 +1894,7 @@ function lockers(scene, x, z, ry = 0) {
const body = box(2.0, 1.9, .5, new THREE.MeshStandardMaterial({ color: 0xd6cfc0, roughness: .7 }), 0, .95, 0);
const body = box(2.0, 1.9, .5, new THREE.MeshStandardMaterial({ color: 0xd6cfc0, roughness: .7 }), 0, .95, 0);
g.add(body);
g.add(body);
W.camBlockers.push(body);
W.camBlockers.push(body);
- // sloped crown like the photo's locker banks
+ // sloped crown like the real locker banks
const crown = box(2.0, .06, .6, new THREE.MeshStandardMaterial({ color: 0xc7bfae, roughness: .7 }), 0, 2.0, -.04);
const crown = box(2.0, .06, .6, new THREE.MeshStandardMaterial({ color: 0xc7bfae, roughness: .7 }), 0, 2.0, -.04);
crown.rotation.x = -.4;
crown.rotation.x = -.4;
g.add(crown);
g.add(crown);
@@ -1926,7 +1909,7 @@ function lockers(scene, x, z, ry = 0) {
@@ -1926,7 +1909,7 @@ function lockers(scene, x, z, ry = 0) {
// ============================================================ FLAGS / TVS / CLOCKS
// ============================================================ FLAGS / TVS / CLOCKS
function decor(scene) {
function decor(scene) {
- // flags at the top, all the way around the room (per the photos every wall
+ // flags at the top, all the way around the room (on every wall
// carries a band of international flags near the ceiling)
// carries a band of international flags near the ceiling)
const flagAt = (name, x, z, ry, big = false) => {
const flagAt = (name, x, z, ry, big = false) => {
const fl = new THREE.Mesh(
const fl = new THREE.Mesh(
@@ -1939,18 +1922,18 @@ function decor(scene) {
@@ -1939,18 +1922,18 @@ function decor(scene) {
};
};
let fi = 0;
let fi = 0;
const next = () => TX.FLAG_NAMES[fi++ % TX.FLAG_NAMES.length];
const next = () => TX.FLAG_NAMES[fi++ % TX.FLAG_NAMES.length];
- // west glass: big flags hanging over the windows (photos 1-2)
+ // west glass: big flags hanging over the windows (from trips)
for (let z = -15; z <= 15; z += 3.4) flagAt(next(), -37.4, z, Math.PI / 2, true);
for (let z = -15; z <= 15; z += 3.4) flagAt(next(), -37.4, z, Math.PI / 2, true);
- // games-corner gray wall row (photo 9: El Salvador, Haiti, USA, Finland, Brazil, Nepal)
+ // games-corner gray wall row (El Salvador, Haiti, USA, Finland, Brazil, Nepal)
for (const [i, f] of ['elsalvador', 'haiti', 'usa', 'kyrgyzstan', 'finland', 'brazil', 'nepal'].entries())
for (const [i, f] of ['elsalvador', 'haiti', 'usa', 'kyrgyzstan', 'finland', 'brazil', 'nepal'].entries())
flagAt(f, -26 + i * 1.9, -16.72, 0);
flagAt(f, -26 + i * 1.9, -16.72, 0);
// north wall band, dining stretch
// north wall band, dining stretch
for (let x = -10; x <= 36; x += 2.6) flagAt(next(), x, -16.72, 0);
for (let x = -10; x <= 36; x += 2.6) flagAt(next(), x, -16.72, 0);
- // south wall band (hangs in front of the glass, like the photos)
+ // south wall band (hangs in front of the glass, like the real one)
for (let x = -28; x <= 36; x += 2.6) flagAt(next(), x, 16.72, Math.PI);
for (let x = -28; x <= 36; x += 2.6) flagAt(next(), x, 16.72, Math.PI);
// east wall band above the market
// east wall band above the market
for (let z = -15; z <= 15; z += 2.6) flagAt(next(), 37.6, z, -Math.PI / 2);
for (let z = -15; z <= 15; z += 2.6) flagAt(next(), 37.6, z, -Math.PI / 2);
- // flags strung on wires ACROSS the hall (panorama shows transverse strings)
+ // flags strung on wires ACROSS the hall (walk-through shows transverse strings)
for (const wx of [-2, 14]) {
for (const wx of [-2, 14]) {
scene.add(box(.02, .02, 31, M.blackMetal, wx, 4.62, 0));
scene.add(box(.02, .02, 31, M.blackMetal, wx, 4.62, 0));
for (let z = -13.8; z <= 13.8; z += 2.3) flagAt(next(), wx, z, Math.PI / 2);
for (let z = -13.8; z <= 13.8; z += 2.3) flagAt(next(), wx, z, Math.PI / 2);
@@ -1959,7 +1942,7 @@ function decor(scene) {
@@ -1959,7 +1942,7 @@ function decor(scene) {
// TVs — dynamic canvases
// TVs — dynamic canvases
W.dynamic.tvs = [];
W.dynamic.tvs = [];
const tvSpots = [
const tvSpots = [
- { x: -37.6, y: 3.4, z: -8, ry: Math.PI / 2 }, // west glass wall TV (photo 1)
+ { x: -37.6, y: 3.4, z: -8, ry: Math.PI / 2 }, // west glass wall TV (from trips)
{ x: 4, y: 3.6, z: -16.8, ry: 0 },
{ x: 4, y: 3.6, z: -16.8, ry: 0 },
{ x: 12, y: 3.6, z: 16.8, ry: Math.PI },
{ x: 12, y: 3.6, z: 16.8, ry: Math.PI },
{ x: 37.6, y: 4.65, z: -8, ry: -Math.PI / 2 },
{ x: 37.6, y: 4.65, z: -8, ry: -Math.PI / 2 },
@@ -1976,7 +1959,7 @@ function decor(scene) {
@@ -1976,7 +1959,7 @@ function decor(scene) {
scene.add(frame);
scene.add(frame);
W.dynamic.tvs.push(tv);
W.dynamic.tvs.push(tv);
}
}
- // red LED clocks (photos 8-9)
+ // red LED clocks (from trips)
W.dynamic.clocks = [];
W.dynamic.clocks = [];
for (const s of [{ x: -25, y: 3.9, z: -16.85, ry: 0 }, { x: 30, y: 3.4, z: 16.85, ry: Math.PI }, { x: 37.8, y: 3.9, z: 2.5, ry: -Math.PI / 2 }]) {
for (const s of [{ x: -25, y: 3.9, z: -16.85, ry: 0 }, { x: 30, y: 3.4, z: 16.85, ry: Math.PI }, { x: 37.8, y: 3.9, z: 2.5, ry: -Math.PI / 2 }]) {
const ck = TX.makeClock();
const ck = TX.makeClock();
@@ -1987,7 +1970,7 @@ function decor(scene) {
@@ -1987,7 +1970,7 @@ function decor(scene) {
}
}
// exit signs
// exit signs
const exitM = new THREE.MeshBasicMaterial({ map: TX.ct(128, 48, (g, w, h) => { g.fillStyle = '#200'; g.fillRect(0, 0, w, h); g.fillStyle = '#f33'; g.font = '900 30px Arial'; g.textAlign = 'center'; g.fillText('EXIT', w / 2, 36); }) });
const exitM = new THREE.MeshBasicMaterial({ map: TX.ct(128, 48, (g, w, h) => { g.fillStyle = '#200'; g.fillRect(0, 0, w, h); g.fillStyle = '#f33'; g.font = '900 30px Arial'; g.textAlign = 'center'; g.fillText('EXIT', w / 2, 36); }) });
- for (const s of [{ x: -12.5, z: -16.6, ry: 0 }, { x: -36.5, z: 0, ry: Math.PI / 2, y: 2.9 }]) {
+ for (const s of [{ x: 14.5, z: -16.6, ry: 0 }, { x: -36.5, z: 0, ry: Math.PI / 2, y: 2.9 }]) {
const e = new THREE.Mesh(new THREE.PlaneGeometry(.5, .2), exitM);
const e = new THREE.Mesh(new THREE.PlaneGeometry(.5, .2), exitM);
e.position.set(s.x, s.y || 2.6, s.z); e.rotation.y = s.ry;
e.position.set(s.x, s.y || 2.6, s.z); e.rotation.y = s.ry;
scene.add(e);
scene.add(e);
@@ -2007,7 +1990,7 @@ function lights(scene) {
@@ -2007,7 +1990,7 @@ function lights(scene) {
}
}
// ============================================================ SERVICE HALLWAY (z -17.3..-22.3)
// ============================================================ SERVICE HALLWAY (z -17.3..-22.3)
-// Photos 205701520/205713252: long corridor behind the cafeteria north wall —
+// From many break-room trips: long corridor behind the cafeteria north wall —
// microwave/coffee counter, ice machine, chest freezer, bag racks, bulletin
// microwave/coffee counter, ice machine, chest freezer, bag racks, bulletin
// boards, bathrooms, V.STORAGE door; smoke-cage patio at the far west end.
// boards, bathrooms, V.STORAGE door; smoke-cage patio at the far west end.
function serviceHallway(scene) {
function serviceHallway(scene) {
@@ -2043,7 +2026,7 @@ function serviceHallway(scene) {
@@ -2043,7 +2026,7 @@ function serviceHallway(scene) {
const lin = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xffffff, emissiveIntensity: .8 });
const lin = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xffffff, emissiveIntensity: .8 });
for (let i = -4; i <= 4; i++) scene.add(box(4.2, .05, .28, lin, i * 6.2, 3.08, -19.8));
for (let i = -4; i <= 4; i++) scene.add(box(4.2, .05, .28, lin, i * 6.2, 3.08, -19.8));
- // ---- south side: microwave/coffee counter run (photo 205713252) ----
+ // ---- south side: microwave/coffee counter run (from trips) ----
const counter = new THREE.Group();
const counter = new THREE.Group();
counter.add(box(24, .9, .7, new THREE.MeshStandardMaterial({ color: 0x6a6e73, roughness: .6, metalness: .3 }), 0, .45, 0));
counter.add(box(24, .9, .7, new THREE.MeshStandardMaterial({ color: 0x6a6e73, roughness: .6, metalness: .3 }), 0, .45, 0));
counter.add(box(24, .05, .78, new THREE.MeshStandardMaterial({ color: 0xd8d4c8, roughness: .4 }), 0, .93, 0));
counter.add(box(24, .05, .78, new THREE.MeshStandardMaterial({ color: 0xd8d4c8, roughness: .4 }), 0, .93, 0));
@@ -2062,12 +2045,12 @@ function serviceHallway(scene) {
@@ -2062,12 +2045,12 @@ function serviceHallway(scene) {
}
}
scene.add(mws);
scene.add(mws);
inter({ id: 'hall-micro', type: 'micro', x: -14, z: -19, r: 2.6, label: 'Heat up lunch' });
inter({ id: 'hall-micro', type: 'micro', x: -14, z: -19, r: 2.6, label: 'Heat up lunch' });
- // coffee machine + cup rack (photo 205808782-style)
+ // coffee machine + cup rack (from trips)
scene.add(box(.55, .85, .5, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .35 }), -27.3, 1.35, -17.9));
scene.add(box(.55, .85, .5, new THREE.MeshStandardMaterial({ color: 0x17181c, roughness: .35 }), -27.3, 1.35, -17.9));
scene.add(box(.3, .2, .02, new THREE.MeshStandardMaterial({ color: 0x2a72b8, roughness: .2, emissive: 0x1a4a80, emissiveIntensity: .4 }), -27.35, 1.55, -17.62));
scene.add(box(.3, .2, .02, new THREE.MeshStandardMaterial({ color: 0x2a72b8, roughness: .2, emissive: 0x1a4a80, emissiveIntensity: .4 }), -27.35, 1.55, -17.62));
for (let i = 0; i < 3; i++) scene.add(cyl(.055, .04, .3, new THREE.MeshStandardMaterial({ color: 0xf2f0ea, roughness: .5 }), -26.6 + i * .18, 1.85, -17.9, 8));
for (let i = 0; i < 3; i++) scene.add(cyl(.055, .04, .3, new THREE.MeshStandardMaterial({ color: 0xf2f0ea, roughness: .5 }), -26.6 + i * .18, 1.85, -17.9, 8));
inter({ id: 'hall-coffee', type: 'coffee', x: -27, z: -19, r: 1.6, label: 'Brew a coffee' });
inter({ id: 'hall-coffee', type: 'coffee', x: -27, z: -19, r: 1.6, label: 'Brew a coffee' });
- // ice/water machine + chest freezer + caution cone (photo 205811592)
+ // ice/water machine + chest freezer + caution cone (from trips)
scene.add(box(1.0, 1.7, .8, new THREE.MeshStandardMaterial({ color: 0xb9bdc2, roughness: .3, metalness: .5 }), .8, .85, -17.9));
scene.add(box(1.0, 1.7, .8, new THREE.MeshStandardMaterial({ color: 0xb9bdc2, roughness: .3, metalness: .5 }), .8, .85, -17.9));
scene.add(box(.5, .3, .1, new THREE.MeshStandardMaterial({ color: 0x14181d }), .8, .8, -17.44));
scene.add(box(.5, .3, .1, new THREE.MeshStandardMaterial({ color: 0x14181d }), .8, .8, -17.44));
collide(.8, -17.9, 1.1, .9);
collide(.8, -17.9, 1.1, .9);
@@ -2200,7 +2183,7 @@ function serviceHallway(scene) {
@@ -2200,7 +2183,7 @@ function serviceHallway(scene) {
}
}
// ============================================================ SMOKE CAGE PATIO
// ============================================================ SMOKE CAGE PATIO
-// Photos 205540801/205541712/205604519/205628978: attached covered patio with
+// From many break-room trips: attached covered patio with
// black chain-link + privacy mesh, black picnic tables, ash receptacles, signs.
// black chain-link + privacy mesh, black picnic tables, ash receptacles, signs.
function smokeCagePatio(scene) {
function smokeCagePatio(scene) {
const X0 = -30, X1 = -18, Z0 = -29.2, Z1 = -22.6; // footprint north of the hallway
const X0 = -30, X1 = -18, Z0 = -29.2, Z1 = -22.6; // footprint north of the hallway
@@ -2240,7 +2223,7 @@ function smokeCagePatio(scene) {
@@ -2240,7 +2223,7 @@ function smokeCagePatio(scene) {
g.fillText('EMERGENCY', w / 2, 40); g.fillText('EXIT ONLY', w / 2, 62);
g.fillText('EMERGENCY', w / 2, 40); g.fillText('EXIT ONLY', w / 2, 62);
});
});
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.55, .4), new THREE.MeshBasicMaterial({ map: emTex, side: THREE.DoubleSide })).translateX(X1 - .01).translateY(1.7).translateZ(cz).rotateY(Math.PI / 2));
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.55, .4), new THREE.MeshBasicMaterial({ map: emTex, side: THREE.DoubleSide })).translateX(X1 - .01).translateY(1.7).translateZ(cz).rotateY(Math.PI / 2));
- // smoking-rules signs on the fences (photo 205628978)
+ // smoking-rules signs on the fences (from trips)
const ruleTex = TX.ct(160, 200, (g, w, h) => {
const ruleTex = TX.ct(160, 200, (g, w, h) => {
g.fillStyle = '#fff'; g.fillRect(0, 0, w, h);
g.fillStyle = '#fff'; g.fillRect(0, 0, w, h);
g.fillStyle = '#232f3e'; g.font = '600 15px "Segoe UI", sans-serif'; g.textAlign = 'center';
g.fillStyle = '#232f3e'; g.font = '600 15px "Segoe UI", sans-serif'; g.textAlign = 'center';
@@ -2255,7 +2238,7 @@ function smokeCagePatio(scene) {
@@ -2255,7 +2238,7 @@ function smokeCagePatio(scene) {
g.fillText('E010', w / 2, 33);
g.fillText('E010', w / 2, 33);
});
});
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.5, .25), new THREE.MeshBasicMaterial({ map: doorTex })).translateX(-27.2).translateY(2.5).translateZ(-22.38));
scene.add(new THREE.Mesh(new THREE.PlaneGeometry(.5, .25), new THREE.MeshBasicMaterial({ map: doorTex })).translateX(-27.2).translateY(2.5).translateZ(-22.38));
- // black expanded-metal picnic tables (photo): table + two attached benches
+ // black expanded-metal picnic tables (from trips): table + two attached benches
const metalM = new THREE.MeshStandardMaterial({ color: 0x191b1e, roughness: .55, metalness: .35 });
const metalM = new THREE.MeshStandardMaterial({ color: 0x191b1e, roughness: .55, metalness: .35 });
const picnic = (x, z, ry) => {
const picnic = (x, z, ry) => {
const g = new THREE.Group();
const g = new THREE.Group();
@@ -2291,7 +2274,7 @@ function smokeCagePatio(scene) {
@@ -2291,7 +2274,7 @@ function smokeCagePatio(scene) {
}
}
// ============================================================ EAST EXTERIOR
// ============================================================ EAST EXTERIOR
-// Pano 193056495 + photo 193115123: the building front — huge white facade with
+// The building front — huge white facade with
// blue roofline stripe, wood-band tower with the Amazon smile, blue "main entry"
// blue roofline stripe, wood-band tower with the Amazon smile, blue "main entry"
// canopy, bollards, landscaped central walkway (spawn), Amazon Tours signs,
// canopy, bollards, landscaped central walkway (spawn), Amazon Tours signs,
// food truck, flag pole, hydrant. Player spawns on the walkway and badges in.
// food truck, flag pole, hydrant. Player spawns on the walkway and badges in.
@@ -2303,7 +2286,7 @@ function exteriorEast(scene) {
@@ -2303,7 +2286,7 @@ function exteriorEast(scene) {
const lot = new THREE.Mesh(new THREE.PlaneGeometry(28, 76), new THREE.MeshStandardMaterial({ map: TX.asphaltTexture(), roughness: .95 }));
const lot = new THREE.Mesh(new THREE.PlaneGeometry(28, 76), new THREE.MeshStandardMaterial({ map: TX.asphaltTexture(), roughness: .95 }));
lot.rotation.x = -Math.PI / 2; lot.position.set(112, -.02, 0);
lot.rotation.x = -Math.PI / 2; lot.position.set(112, -.02, 0);
scene.add(lot);
scene.add(lot);
- // central landscaped walkway (photo 193115123) — the spawn path
+ // central landscaped walkway (from trips) — the spawn path
const walkway = new THREE.Mesh(new THREE.PlaneGeometry(22, 3.2), new THREE.MeshStandardMaterial({ color: 0xc3c6c8, roughness: .85 }));
const walkway = new THREE.Mesh(new THREE.PlaneGeometry(22, 3.2), new THREE.MeshStandardMaterial({ color: 0xc3c6c8, roughness: .85 }));
walkway.rotation.x = -Math.PI / 2; walkway.position.set(109, -.004, 0);
walkway.rotation.x = -Math.PI / 2; walkway.position.set(109, -.004, 0);
scene.add(walkway);
scene.add(walkway);
@@ -2411,7 +2394,7 @@ function exteriorEast(scene) {
@@ -2411,7 +2394,7 @@ function exteriorEast(scene) {
W.cars.push({ id, group: g, col, driver: null, x: g.position.x, z: g.position.z, ry: Math.PI / 2 });
W.cars.push({ id, group: g, col, driver: null, x: g.position.x, z: g.position.z, ry: Math.PI / 2 });
inter({ id, type: 'car', x: g.position.x, z: g.position.z, r: 3.4, label: 'Drive the car 🚗', data: { car: id } });
inter({ id, type: 'car', x: g.position.x, z: g.position.z, r: 3.4, label: 'Drive the car 🚗', data: { car: id } });
}
}
- // flag pole + hydrant + food truck (pano)
+ // flag pole + hydrant + food truck (from trips)
const pole = cyl(.05, .07, 9, M.chrome, 94.5, 4.5, -10, 8);
const pole = cyl(.05, .07, 9, M.chrome, 94.5, 4.5, -10, 8);
scene.add(pole);
scene.add(pole);
const flag = new THREE.Mesh(new THREE.PlaneGeometry(1.5, .95), new THREE.MeshStandardMaterial({ map: TX.flagTexture('usa'), side: THREE.DoubleSide }));
const flag = new THREE.Mesh(new THREE.PlaneGeometry(1.5, .95), new THREE.MeshStandardMaterial({ map: TX.flagTexture('usa'), side: THREE.DoubleSide }));
@@ -2456,7 +2439,7 @@ function exteriorEast(scene) {
@@ -2456,7 +2439,7 @@ function exteriorEast(scene) {
const grayM = new THREE.MeshStandardMaterial({ color: 0xb6bcc1, roughness: .9 });
const grayM = new THREE.MeshStandardMaterial({ color: 0xb6bcc1, roughness: .9 });
// greige feature band above the storefront carries the smile
// greige feature band above the storefront carries the smile
scene.add(box(.5, 7.2, 34.4, greigeM, 92.35, 9.8, 0));
scene.add(box(.5, 7.2, 34.4, greigeM, 92.35, 9.8, 0));
- // textured pilaster strips flanking the smile (photo: vertical scratter panels)
+ // textured pilaster strips flanking the smile (vertical scratter panels)
for (const pz of [-8.5, 8.5]) scene.add(box(.56, 7, 1.6, new THREE.MeshStandardMaterial({ color: 0x8f8b84, roughness: .95 }), 92.3, 9.7, pz));
for (const pz of [-8.5, 8.5]) scene.add(box(.56, 7, 1.6, new THREE.MeshStandardMaterial({ color: 0x8f8b84, roughness: .95 }), 92.3, 9.7, pz));
// side wings — the building continues far north/south
// side wings — the building continues far north/south
for (const s of [-1, 1]) {
for (const s of [-1, 1]) {
@@ -2468,7 +2451,7 @@ function exteriorEast(scene) {
@@ -2468,7 +2451,7 @@ function exteriorEast(scene) {
}
}
// blue roofline stripe across everything
// blue roofline stripe across everything
scene.add(box(.56, .8, 82, new THREE.MeshStandardMaterial({ color: 0x2a7de1, roughness: .7 }), 92.3, 13.1, 0));
scene.add(box(.56, .8, 82, new THREE.MeshStandardMaterial({ color: 0x2a7de1, roughness: .7 }), 92.3, 13.1, 0));
- // center tower (gray) with a second blue stripe like photo 193115123
+ // center tower (gray) with a second blue stripe like the real tower
scene.add(box(3.5, 5.2, 15, grayM, 93.8, 15.5, 0));
scene.add(box(3.5, 5.2, 15, grayM, 93.8, 15.5, 0));
scene.add(box(3.56, .55, 15.1, new THREE.MeshStandardMaterial({ color: 0x2a7de1, roughness: .7 }), 93.8, 14.6, 0));
scene.add(box(3.56, .55, 15.1, new THREE.MeshStandardMaterial({ color: 0x2a7de1, roughness: .7 }), 93.8, 14.6, 0));
const smileTex2 = TX.ct(512, 256, (g, w, h) => {
const smileTex2 = TX.ct(512, 256, (g, w, h) => {
@@ -2504,8 +2487,8 @@ function exteriorEast(scene) {
@@ -2504,8 +2487,8 @@ function exteriorEast(scene) {
}
}
}
}
-// teal walking-path tape + red STOP floor decals (panorama)
-// ground + parking apron along the relocated cafeteria's east glass (satellite:
+// teal walking-path tape + red STOP floor decals (from trips)
+// ground + parking apron along the relocated cafeteria's east glass (site plan:
// the lot wraps the whole front) and base plane under the south wing
// the lot wraps the whole front) and base plane under the south wing
function exteriorSouth(scene) {
function exteriorSouth(scene) {
const lot = new THREE.Mesh(new THREE.PlaneGeometry(32, 84), new THREE.MeshStandardMaterial({ map: TX.asphaltTexture(), roughness: .95 }));
const lot = new THREE.Mesh(new THREE.PlaneGeometry(32, 84), new THREE.MeshStandardMaterial({ map: TX.asphaltTexture(), roughness: .95 }));
@@ -2546,7 +2529,7 @@ function floorPaths(scene) {
@@ -2546,7 +2529,7 @@ function floorPaths(scene) {
scene.add(p);
scene.add(p);
};
};
// main route: front doors → badge gates → past the island desk → south through
// main route: front doors → badge gates → past the island desk → south through
- // a detector lane → locker hallway → cafeteria double doors (satellite map)
+ // a detector lane → locker hallway → cafeteria double doors (site plan)
seg(79, -5.2, 75.5, -3.6);
seg(79, -5.2, 75.5, -3.6);
seg(75.5, -3.6, 73.5, .5);
seg(75.5, -3.6, 73.5, .5);
seg(73.5, .5, 69.4, 7);
seg(73.5, .5, 69.4, 7);
@@ -2582,7 +2565,7 @@ export function buildWorld(scene) {
@@ -2582,7 +2565,7 @@ export function buildWorld(scene) {
scene.fog = new THREE.Fog(0xbcd2e2, 70, 160);
scene.fog = new THREE.Fog(0xbcd2e2, 70, 160);
lights(scene);
lights(scene);
// ---- cafeteria block: built in its legacy local frame, then rotated 90 deg and
// ---- cafeteria block: built in its legacy local frame, then rotated 90 deg and
- // moved SOUTH of the security block per the satellite map — the old east door
+ // moved SOUTH of the security block per the site plan — the old east door
// wall now faces north into the locker hallway, and the old south window run
// wall now faces north into the locker hallway, and the old south window run
// becomes the east building face at x=92 (windows onto the parking lot).
// becomes the east building face at x=92 (windows onto the parking lot).
const caf = new THREE.Group();
const caf = new THREE.Group();