let-them-talk 5.2.5 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -1
- package/README.md +158 -592
- package/SECURITY.md +3 -3
- package/USAGE.md +151 -0
- package/agent-contracts.js +447 -0
- package/api-agents.js +760 -0
- package/autonomy/decision-v2.js +380 -0
- package/autonomy/watchdog-policy.js +572 -0
- package/cli.js +454 -298
- package/conversation-templates/autonomous-feature.json +83 -22
- package/conversation-templates/code-review.json +69 -21
- package/conversation-templates/debug-squad.json +69 -21
- package/conversation-templates/feature-build.json +69 -21
- package/conversation-templates/research-write.json +69 -21
- package/dashboard.html +3148 -174
- package/dashboard.js +823 -786
- package/data-dir.js +58 -0
- package/docs/architecture/branch-semantics.md +157 -0
- package/docs/architecture/canonical-event-schema.md +88 -0
- package/docs/architecture/markdown-workspace.md +183 -0
- package/docs/architecture/runtime-contract.md +459 -0
- package/docs/architecture/runtime-migration-hardening.md +64 -0
- package/events/hooks.js +154 -0
- package/events/log.js +457 -0
- package/events/replay.js +33 -0
- package/events/schema.js +432 -0
- package/managed-team-integration.js +261 -0
- package/office/agents.js +704 -597
- package/office/animation.js +1 -1
- package/office/assets/arcade-cabinet.js +141 -0
- package/office/assets/archway.js +77 -0
- package/office/assets/bar-counter.js +91 -0
- package/office/assets/bar-stool.js +71 -0
- package/office/assets/beanbag.js +64 -0
- package/office/assets/bench.js +99 -0
- package/office/assets/bollard.js +87 -0
- package/office/assets/cactus.js +100 -0
- package/office/assets/carpet-tile.js +46 -0
- package/office/assets/chair.js +123 -0
- package/office/assets/chandelier.js +107 -0
- package/office/assets/coffee-machine.js +95 -0
- package/office/assets/coffee-table.js +81 -0
- package/office/assets/column.js +95 -0
- package/office/assets/desk-lamp.js +102 -0
- package/office/assets/desk.js +76 -0
- package/office/assets/dining-table.js +105 -0
- package/office/assets/door.js +70 -0
- package/office/assets/dual-monitor.js +72 -0
- package/office/assets/fence.js +76 -0
- package/office/assets/filing-cabinet.js +111 -0
- package/office/assets/floor-lamp.js +69 -0
- package/office/assets/floor-tile.js +54 -0
- package/office/assets/flower-pot.js +76 -0
- package/office/assets/foosball.js +95 -0
- package/office/assets/fridge.js +99 -0
- package/office/assets/gaming-chair.js +154 -0
- package/office/assets/gaming-desk.js +105 -0
- package/office/assets/glass-door.js +72 -0
- package/office/assets/glass-wall.js +64 -0
- package/office/assets/half-wall.js +49 -0
- package/office/assets/hanging-plant.js +112 -0
- package/office/assets/index.js +151 -0
- package/office/assets/indoor-tree.js +90 -0
- package/office/assets/l-sofa.js +153 -0
- package/office/assets/marble-floor.js +64 -0
- package/office/assets/materials.js +40 -0
- package/office/assets/meeting-table.js +88 -0
- package/office/assets/microwave.js +94 -0
- package/office/assets/monitor.js +67 -0
- package/office/assets/neon-strip.js +73 -0
- package/office/assets/painting.js +84 -0
- package/office/assets/palm-tree.js +108 -0
- package/office/assets/pc-tower.js +91 -0
- package/office/assets/pendant-light.js +67 -0
- package/office/assets/ping-pong.js +114 -0
- package/office/assets/plant.js +72 -0
- package/office/assets/planter-box.js +95 -0
- package/office/assets/pool-table.js +94 -0
- package/office/assets/printer.js +113 -0
- package/office/assets/reception-desk.js +133 -0
- package/office/assets/rug.js +78 -0
- package/office/assets/sculpture.js +85 -0
- package/office/assets/server-rack.js +98 -0
- package/office/assets/sink.js +109 -0
- package/office/assets/sofa.js +106 -0
- package/office/assets/speaker.js +83 -0
- package/office/assets/spotlight.js +83 -0
- package/office/assets/street-lamp.js +97 -0
- package/office/assets/trash-can.js +83 -0
- package/office/assets/treadmill.js +126 -0
- package/office/assets/trophy.js +89 -0
- package/office/assets/tv-screen.js +79 -0
- package/office/assets/vase.js +84 -0
- package/office/assets/wall-clock.js +84 -0
- package/office/assets/wall.js +53 -0
- package/office/assets/water-cooler.js +146 -0
- package/office/assets/whiteboard.js +115 -0
- package/office/assets.js +3 -431
- package/office/builder.js +791 -355
- package/office/campus-env.js +1012 -1119
- package/office/environment.js +2 -0
- package/office/gallery.js +997 -0
- package/office/index.js +165 -61
- package/office/navigation.js +173 -152
- package/office/player.js +178 -68
- package/office/robot-character.js +272 -0
- package/office/spectator-camera.js +33 -10
- package/office/state.js +2 -0
- package/office/world-save.js +35 -4
- package/package.json +57 -3
- package/providers/comfyui.js +383 -0
- package/providers/dalle.js +79 -0
- package/providers/gemini.js +181 -0
- package/providers/ollama.js +184 -0
- package/providers/replicate.js +115 -0
- package/providers/zai.js +183 -0
- package/runtime-descriptor.js +270 -0
- package/scripts/check-agent-contract-advisory.js +132 -0
- package/scripts/check-api-agent-parity.js +277 -0
- package/scripts/check-autonomy-v2-decision.js +207 -0
- package/scripts/check-autonomy-v2-execution.js +588 -0
- package/scripts/check-autonomy-v2-watchdog.js +224 -0
- package/scripts/check-branch-fork-snapshot.js +337 -0
- package/scripts/check-branch-isolation.js +787 -0
- package/scripts/check-branch-semantics.js +139 -0
- package/scripts/check-dashboard-control-plane.js +1304 -0
- package/scripts/check-docs-onboarding.js +490 -0
- package/scripts/check-event-schema.js +276 -0
- package/scripts/check-evidence-completion.js +239 -0
- package/scripts/check-invariants.js +992 -0
- package/scripts/check-lifecycle-hooks.js +525 -0
- package/scripts/check-managed-team-integration.js +166 -0
- package/scripts/check-markdown-workspace-export.js +548 -0
- package/scripts/check-markdown-workspace-safety.js +347 -0
- package/scripts/check-markdown-workspace.js +136 -0
- package/scripts/check-message-replay.js +429 -0
- package/scripts/check-migration-hardening.js +300 -0
- package/scripts/check-performance-indexing.js +272 -0
- package/scripts/check-provider-capabilities.js +316 -0
- package/scripts/check-runtime-contract.js +109 -0
- package/scripts/check-session-aware-context.js +172 -0
- package/scripts/check-session-lifecycle.js +210 -0
- package/scripts/export-markdown-workspace.js +84 -0
- package/scripts/fixtures/message-replay/clean.jsonl +2 -0
- package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
- package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
- package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
- package/scripts/migrate-legacy-to-canonical.js +201 -0
- package/scripts/run-verification-suite.js +242 -0
- package/scripts/sync-packaged-docs.js +69 -0
- package/server.js +9546 -7214
- package/state/agents.js +161 -0
- package/state/canonical.js +3068 -0
- package/state/dashboard-queries.js +441 -0
- package/state/evidence.js +56 -0
- package/state/io.js +69 -0
- package/state/markdown-workspace.js +951 -0
- package/state/messages.js +669 -0
- package/state/sessions.js +683 -0
- package/state/tasks-workflows.js +92 -0
- package/templates/debate.json +2 -2
- package/templates/managed.json +4 -4
- package/templates/pair.json +2 -2
- package/templates/review.json +2 -2
- package/templates/team.json +3 -3
package/office/campus-env.js
CHANGED
|
@@ -3,123 +3,105 @@ import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
|
|
3
3
|
import { S } from './state.js';
|
|
4
4
|
|
|
5
5
|
// ============================================================
|
|
6
|
-
// TECH
|
|
7
|
-
// Inspired by
|
|
6
|
+
// TECH HQ — Premium 2-floor environment
|
|
7
|
+
// Inspired by Apple Park / Bloomberg London
|
|
8
8
|
// ============================================================
|
|
9
9
|
|
|
10
|
-
var CAMPUS_W
|
|
11
|
-
var CAMPUS_D
|
|
12
|
-
var WALL_H
|
|
13
|
-
var MEZZ_H
|
|
14
|
-
var MEZZ_DEPTH = 12;
|
|
10
|
+
var CAMPUS_W = 90; // total interior width (X axis)
|
|
11
|
+
var CAMPUS_D = 60; // total interior depth (Z axis)
|
|
12
|
+
var WALL_H = 6; // interior wall height
|
|
13
|
+
var MEZZ_H = 3.2; // mezzanine floor height
|
|
14
|
+
var MEZZ_DEPTH = 12; // mezzanine depth from back wall
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// ── 5×4 grid: X in [-8,-4,0,4,8], Z in [6,10,14,18]
|
|
17
|
+
// ── Manager office at world (30, 10)
|
|
17
18
|
var CAMPUS_DESKS = [
|
|
18
|
-
//
|
|
19
|
-
{ x: -
|
|
20
|
-
|
|
21
|
-
{ x: -
|
|
22
|
-
//
|
|
23
|
-
{ x: -
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
{ x: 12, z: 6.7 },
|
|
19
|
+
// Row Z=6
|
|
20
|
+
{ x: -8, z: 6 }, { x: -4, z: 6 }, { x: 0, z: 6 }, { x: 4, z: 6 }, { x: 8, z: 6 },
|
|
21
|
+
// Row Z=10
|
|
22
|
+
{ x: -8, z: 10 }, { x: -4, z: 10 }, { x: 0, z: 10 }, { x: 4, z: 10 }, { x: 8, z: 10 },
|
|
23
|
+
// Row Z=14
|
|
24
|
+
{ x: -8, z: 14 }, { x: -4, z: 14 }, { x: 0, z: 14 }, { x: 4, z: 14 }, { x: 8, z: 14 },
|
|
25
|
+
// Row Z=18
|
|
26
|
+
{ x: -8, z: 18 }, { x: -4, z: 18 }, { x: 0, z: 18 }, { x: 4, z: 18 }, { x: 8, z: 18 },
|
|
27
|
+
// Manager office desk (index 20) — chair at world Z=12.7, agent walks to z+0.7
|
|
28
|
+
{ x: 30, z: 12.0 },
|
|
29
29
|
];
|
|
30
30
|
|
|
31
31
|
export function getCampusDeskPositions() {
|
|
32
32
|
return CAMPUS_DESKS;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// ============================================================
|
|
36
|
+
// MAIN BUILD ENTRY
|
|
37
|
+
// ============================================================
|
|
35
38
|
export function buildCampusEnvironment() {
|
|
36
39
|
S.deskMeshes = [];
|
|
37
40
|
|
|
38
|
-
//
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
43
|
-
var
|
|
44
|
-
var
|
|
45
|
-
var
|
|
46
|
-
var
|
|
47
|
-
var
|
|
48
|
-
var
|
|
49
|
-
var
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
var
|
|
53
|
-
var
|
|
54
|
-
var
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
buildCampusFloor(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
buildStaircase(marbleMat, chromeMat, glassMat);
|
|
70
|
-
|
|
71
|
-
// ========== GRAND LOBBY ==========
|
|
72
|
-
buildLobby(marbleMat, chromeMat, goldMat, walnutMat);
|
|
73
|
-
|
|
74
|
-
// ========== GAMING DESKS (main workspace, skip last = manager office) ==========
|
|
75
|
-
var managerDeskIdx = CAMPUS_DESKS.length - 1;
|
|
41
|
+
// ── Material palette (defined once, reused everywhere) ──────────
|
|
42
|
+
var matBlack = new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.12, metalness: 0.05 });
|
|
43
|
+
var matWhite = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.15, metalness: 0.05 });
|
|
44
|
+
var matWalnutDk = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.55 });
|
|
45
|
+
var matWalnutLt = new THREE.MeshStandardMaterial({ color: 0x8B5E3C, roughness: 0.50 });
|
|
46
|
+
var matChrPol = new THREE.MeshStandardMaterial({ color: 0xd0d0d0, roughness: 0.08, metalness: 0.85 });
|
|
47
|
+
var matChrBr = new THREE.MeshStandardMaterial({ color: 0x999999, roughness: 0.25, metalness: 0.70 });
|
|
48
|
+
var matGlassCl = new THREE.MeshStandardMaterial({ color: 0xaaccee, roughness: 0.05, metalness: 0.10, transparent: true, opacity: 0.20, side: THREE.DoubleSide });
|
|
49
|
+
var matGlassFr = new THREE.MeshStandardMaterial({ color: 0xd0d8e8, roughness: 0.40, transparent: true, opacity: 0.50, side: THREE.DoubleSide });
|
|
50
|
+
var matConcrete = new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.85 });
|
|
51
|
+
var matLeathBk = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.70 });
|
|
52
|
+
var matLeathCog = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.65 });
|
|
53
|
+
var matGold = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.30, metalness: 0.70 });
|
|
54
|
+
var matNeonBl = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.60, roughness: 0.20 });
|
|
55
|
+
var matNeonPu = new THREE.MeshStandardMaterial({ color: 0xa855f7, emissive: new THREE.Color(0xa855f7), emissiveIntensity: 0.50, roughness: 0.20 });
|
|
56
|
+
var matNeonGr = new THREE.MeshStandardMaterial({ color: 0x22c55e, emissive: new THREE.Color(0x22c55e), emissiveIntensity: 0.50, roughness: 0.20 });
|
|
57
|
+
var matFabric = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.95 });
|
|
58
|
+
|
|
59
|
+
// ── Sub-builders ────────────────────────────────────────────────
|
|
60
|
+
buildCampusFloor(matBlack, matWhite, matFabric);
|
|
61
|
+
buildCampusWalls(matConcrete, matGlassCl, matChrBr, matGold, matWhite);
|
|
62
|
+
buildCampusCeiling(matConcrete, matGlassCl);
|
|
63
|
+
buildStructuralColumns(matWhite, matGold, matChrPol);
|
|
64
|
+
buildMezzanine(matConcrete, matChrPol, matGlassCl, matWalnutDk, matFabric);
|
|
65
|
+
buildStaircase(matWhite, matChrPol, matGlassCl);
|
|
66
|
+
buildLobby(matWhite, matChrPol, matGold, matWalnutDk, matNeonBl);
|
|
67
|
+
buildMainCorridor(matBlack, matGold);
|
|
68
|
+
buildCrossCorridor(matBlack, matGold);
|
|
69
|
+
|
|
70
|
+
// ── Gaming desks (skip manager desk at index 20) ────────────────
|
|
71
|
+
var mgrIdx = CAMPUS_DESKS.length - 1;
|
|
76
72
|
CAMPUS_DESKS.forEach(function(pos, i) {
|
|
77
|
-
if (i ===
|
|
73
|
+
if (i === mgrIdx) return;
|
|
78
74
|
buildGamingDesk(pos.x, pos.z, i);
|
|
79
75
|
});
|
|
80
76
|
|
|
81
|
-
//
|
|
82
|
-
buildManagerOffice(
|
|
83
|
-
|
|
84
|
-
// ========== DESIGNER STUDIO (left wing) ==========
|
|
85
|
-
buildDesignerStudio(-12.5, 0, walnutLightMat, chromeMat);
|
|
86
|
-
|
|
87
|
-
// ========== BAR & CAFÉ (back left) ==========
|
|
88
|
-
buildBar(-14, -12, walnutMat, chromeMat, neonBlueMat, neonPurpleMat);
|
|
89
|
-
buildJukebox(-10, -13.5); // Right side of bar area, against the back wall
|
|
77
|
+
// ── Manager's glass office ──────────────────────────────────────
|
|
78
|
+
buildManagerOffice(30, 10, matGlassCl, matGlassFr, matChrBr, matWalnutDk, matLeathCog, matChrPol);
|
|
90
79
|
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// ========== PLANTS & GREENERY ==========
|
|
80
|
+
// ── Zones ───────────────────────────────────────────────────────
|
|
81
|
+
buildDesignerStudio(-28, 0, matWalnutLt, matChrPol);
|
|
82
|
+
buildBar(-28, -18, matWalnutDk, matChrPol, matNeonBl, matNeonPu);
|
|
83
|
+
buildJukebox(-18, -22);
|
|
84
|
+
buildRecCenter(0, -18, matWalnutDk, matChrPol, matFabric);
|
|
85
|
+
buildGym(22, -18, matChrPol, matConcrete);
|
|
98
86
|
buildCampusPlants();
|
|
99
|
-
|
|
100
|
-
// ========== PENDANT LIGHTS ==========
|
|
101
87
|
buildPendantLights();
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// ========== NEON SIGNS ==========
|
|
107
|
-
buildNeonSign('INNOVATE', -7, 4.5, -CAMPUS_D / 2 + 0.2, neonBlueMat);
|
|
108
|
-
buildNeonSign('CREATE', 7, 4.5, -CAMPUS_D / 2 + 0.2, neonPurpleMat);
|
|
109
|
-
buildNeonSign('BUILD', 0, MEZZ_H + 2, -CAMPUS_D / 2 + MEZZ_DEPTH + 0.2, neonGreenMat);
|
|
88
|
+
buildGlassPartitions(matGlassCl, matChrBr);
|
|
89
|
+
buildNeonSign('INNOVATE', -12, 4.5, -CAMPUS_D / 2 + 0.3, matNeonBl);
|
|
90
|
+
buildNeonSign('CREATE', 12, 4.5, -CAMPUS_D / 2 + 0.3, matNeonPu);
|
|
91
|
+
buildNeonSign('BUILD', 0, MEZZ_H + 2.2, -CAMPUS_D / 2 + MEZZ_DEPTH + 0.3, matNeonGr);
|
|
110
92
|
}
|
|
111
93
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
94
|
+
// ============================================================
|
|
95
|
+
// FLOOR — FBM dark marble, 90×60
|
|
96
|
+
// ============================================================
|
|
97
|
+
function buildCampusFloor(matBlack, matWhite, matFabric) {
|
|
115
98
|
var size = 1024;
|
|
116
99
|
var cvs = document.createElement('canvas');
|
|
117
100
|
cvs.width = size; cvs.height = size;
|
|
118
101
|
var ctx = cvs.getContext('2d');
|
|
119
|
-
var tiles =
|
|
120
|
-
var ts = size / tiles;
|
|
102
|
+
var tiles = 18;
|
|
103
|
+
var ts = Math.floor(size / tiles);
|
|
121
104
|
|
|
122
|
-
// Simple 2D noise function for marble veining
|
|
123
105
|
function noise(x, y) {
|
|
124
106
|
var n = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453;
|
|
125
107
|
return n - Math.floor(n);
|
|
@@ -127,7 +109,7 @@ function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
|
|
|
127
109
|
function smoothNoise(x, y) {
|
|
128
110
|
var ix = Math.floor(x), iy = Math.floor(y);
|
|
129
111
|
var fx = x - ix, fy = y - iy;
|
|
130
|
-
var a = noise(ix, iy),
|
|
112
|
+
var a = noise(ix, iy), b = noise(ix + 1, iy);
|
|
131
113
|
var c = noise(ix, iy + 1), d = noise(ix + 1, iy + 1);
|
|
132
114
|
var u = fx * fx * (3 - 2 * fx), v = fy * fy * (3 - 2 * fy);
|
|
133
115
|
return a + (b - a) * u + (c - a) * v + (a - b - c + d) * u * v;
|
|
@@ -144,46 +126,30 @@ function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
|
|
|
144
126
|
for (var ti = 0; ti < tiles; ti++) {
|
|
145
127
|
for (var tj = 0; tj < tiles; tj++) {
|
|
146
128
|
var tx = ti * ts, ty = tj * ts;
|
|
147
|
-
|
|
148
|
-
// Alternating dark/darker marble tiles
|
|
149
129
|
var isDark = (ti + tj) % 2 === 0;
|
|
150
|
-
var
|
|
151
|
-
|
|
152
|
-
var baseB = isDark ? 38 : 30;
|
|
153
|
-
|
|
154
|
-
// Fill base tile color
|
|
155
|
-
ctx.fillStyle = 'rgb(' + baseR + ',' + baseG + ',' + baseB + ')';
|
|
130
|
+
var bR = isDark ? 26 : 20, bG = isDark ? 28 : 22, bB = isDark ? 36 : 28;
|
|
131
|
+
ctx.fillStyle = 'rgb(' + bR + ',' + bG + ',' + bB + ')';
|
|
156
132
|
ctx.fillRect(tx, ty, ts, ts);
|
|
157
|
-
|
|
158
|
-
// Marble veining (per-pixel noise)
|
|
159
133
|
var imgData = ctx.getImageData(tx, ty, ts, ts);
|
|
160
134
|
var data = imgData.data;
|
|
161
135
|
for (var py = 0; py < ts; py++) {
|
|
162
136
|
for (var px = 0; px < ts; px++) {
|
|
163
|
-
var wx = (ti * ts + px) / size *
|
|
164
|
-
var wy = (tj * ts + py) / size *
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
var vein = Math.sin(wx * 3 + fbm(wx * 2, wy * 2) * 4) * 0.5 + 0.5;
|
|
168
|
-
var vein2 = Math.sin(wy * 2.5 + fbm(wx * 1.5 + 5, wy * 1.5 + 3) * 3.5) * 0.5 + 0.5;
|
|
137
|
+
var wx = (ti * ts + px) / size * 7;
|
|
138
|
+
var wy = (tj * ts + py) / size * 7;
|
|
139
|
+
var vein = Math.sin(wx * 3 + fbm(wx * 2, wy * 2) * 4) * 0.5 + 0.5;
|
|
140
|
+
var vein2 = Math.sin(wy * 2.5 + fbm(wx * 1.5+5, wy * 1.5+3) * 3.5) * 0.5 + 0.5;
|
|
169
141
|
var combined = vein * 0.6 + vein2 * 0.4;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
var veinStrength = Math.pow(combined, 3) * 0.35;
|
|
173
|
-
var nv = smoothNoise(wx * 4, wy * 4) * 0.08;
|
|
174
|
-
|
|
142
|
+
var vs = Math.pow(combined, 3) * 0.38;
|
|
143
|
+
var nv = smoothNoise(wx * 4, wy * 4) * 0.09;
|
|
175
144
|
var idx = (py * ts + px) * 4;
|
|
176
|
-
|
|
177
|
-
data[idx] = Math.min(255,
|
|
178
|
-
data[idx +
|
|
179
|
-
data[idx + 2] = Math.min(255, baseB + veinStrength * 60 + nv * 30); // B
|
|
145
|
+
data[idx] = Math.min(255, bR + vs * 130 + nv * 45);
|
|
146
|
+
data[idx + 1] = Math.min(255, bG + vs * 110 + nv * 38);
|
|
147
|
+
data[idx + 2] = Math.min(255, bB + vs * 65 + nv * 30);
|
|
180
148
|
data[idx + 3] = 255;
|
|
181
149
|
}
|
|
182
150
|
}
|
|
183
151
|
ctx.putImageData(imgData, tx, ty);
|
|
184
|
-
|
|
185
|
-
// Tile grout line (very thin, slightly lighter)
|
|
186
|
-
ctx.strokeStyle = 'rgba(50,52,60,0.8)';
|
|
152
|
+
ctx.strokeStyle = 'rgba(48,50,58,0.75)';
|
|
187
153
|
ctx.lineWidth = 1.5;
|
|
188
154
|
ctx.strokeRect(tx + 0.5, ty + 0.5, ts - 1, ts - 1);
|
|
189
155
|
}
|
|
@@ -191,1186 +157,1135 @@ function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
|
|
|
191
157
|
|
|
192
158
|
var floorTex = new THREE.CanvasTexture(cvs);
|
|
193
159
|
floorTex.wrapS = floorTex.wrapT = THREE.RepeatWrapping;
|
|
194
|
-
floorTex.anisotropy =
|
|
195
|
-
var
|
|
196
|
-
|
|
197
|
-
map: floorTex, roughness: 0.
|
|
198
|
-
|
|
199
|
-
var floor = new THREE.Mesh(floorGeo, floorMeshMat);
|
|
160
|
+
floorTex.anisotropy = 8;
|
|
161
|
+
var floor = new THREE.Mesh(
|
|
162
|
+
new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D),
|
|
163
|
+
new THREE.MeshStandardMaterial({ map: floorTex, roughness: 0.10, metalness: 0.10 })
|
|
164
|
+
);
|
|
200
165
|
floor.rotation.x = -Math.PI / 2;
|
|
201
166
|
floor.receiveShadow = true;
|
|
202
167
|
S.furnitureGroup.add(floor);
|
|
203
168
|
|
|
204
|
-
// Carpet runner in workspace zone
|
|
205
|
-
var carpet = new THREE.Mesh(new THREE.PlaneGeometry(
|
|
169
|
+
// Carpet runner in workspace zone
|
|
170
|
+
var carpet = new THREE.Mesh(new THREE.PlaneGeometry(26, 18), matFabric);
|
|
206
171
|
carpet.rotation.x = -Math.PI / 2;
|
|
207
|
-
carpet.position.set(0, 0.01,
|
|
172
|
+
carpet.position.set(0, 0.01, 12);
|
|
208
173
|
carpet.receiveShadow = true;
|
|
209
174
|
S.furnitureGroup.add(carpet);
|
|
210
175
|
}
|
|
211
176
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
var
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
177
|
+
// ============================================================
|
|
178
|
+
// WALLS — thick (0.4) BoxGeometry, recessed windows + frames
|
|
179
|
+
// ============================================================
|
|
180
|
+
function buildCampusWalls(matConcrete, matGlass, matFrame, matGold, matMarble) {
|
|
181
|
+
var wallT = 0.4; // wall thickness
|
|
182
|
+
var wallMat = new THREE.MeshStandardMaterial({ color: 0x22252e, roughness: 0.80 });
|
|
183
|
+
var wSill = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.18, metalness: 0.06 });
|
|
184
|
+
|
|
185
|
+
// Helper: build a recessed window into a wall segment
|
|
186
|
+
function addWindow(wx, wy, wz, rotY, winW, winH) {
|
|
187
|
+
var depth = wallT + 0.02;
|
|
188
|
+
// Recess box (cut-out fill — dark interior)
|
|
189
|
+
var recessMat = new THREE.MeshStandardMaterial({ color: 0x15171e, roughness: 0.9 });
|
|
190
|
+
var recess = new THREE.Mesh(new THREE.BoxGeometry(winW + 0.08, winH + 0.08, depth), recessMat);
|
|
191
|
+
recess.position.set(wx, wy, wz);
|
|
192
|
+
recess.rotation.y = rotY;
|
|
193
|
+
S.furnitureGroup.add(recess);
|
|
194
|
+
// Glass pane
|
|
195
|
+
var glass = new THREE.Mesh(new THREE.PlaneGeometry(winW, winH), matGlass);
|
|
196
|
+
glass.rotation.y = rotY;
|
|
197
|
+
// Offset glass flush with inner face
|
|
198
|
+
var nx = Math.sin(rotY) * (depth / 2 - 0.01);
|
|
199
|
+
var nz = Math.cos(rotY) * (depth / 2 - 0.01);
|
|
200
|
+
glass.position.set(wx - nx, wy, wz - nz);
|
|
201
|
+
S.furnitureGroup.add(glass);
|
|
202
|
+
// Chrome frame (4 sides)
|
|
203
|
+
var ft = 0.05; // frame thickness
|
|
204
|
+
var frameMat2 = matFrame;
|
|
205
|
+
// horizontal top/bottom bars
|
|
206
|
+
[wy + winH / 2 + ft / 2, wy - winH / 2 - ft / 2].forEach(function(fy) {
|
|
207
|
+
var bar = new THREE.Mesh(new THREE.BoxGeometry(winW + ft * 2, ft, ft), frameMat2);
|
|
208
|
+
bar.rotation.y = rotY;
|
|
209
|
+
bar.position.set(wx - nx, fy, wz - nz);
|
|
210
|
+
S.furnitureGroup.add(bar);
|
|
211
|
+
});
|
|
212
|
+
// vertical left/right bars
|
|
213
|
+
[-winW / 2 - ft / 2, winW / 2 + ft / 2].forEach(function(dx) {
|
|
214
|
+
var cos = Math.cos(rotY), sin = Math.sin(rotY);
|
|
215
|
+
var vx = wx - nx + cos * dx;
|
|
216
|
+
var vz = wz - nz - sin * dx;
|
|
217
|
+
var bar = new THREE.Mesh(new THREE.BoxGeometry(ft, winH + ft * 2, ft), frameMat2);
|
|
218
|
+
bar.position.set(vx, wy, vz);
|
|
219
|
+
S.furnitureGroup.add(bar);
|
|
220
|
+
});
|
|
221
|
+
// Marble sill
|
|
222
|
+
var sill = new THREE.Mesh(new THREE.BoxGeometry(winW + 0.12, 0.06, 0.20), wSill);
|
|
223
|
+
sill.rotation.y = rotY;
|
|
224
|
+
var sx = wx - nx + Math.sin(rotY) * 0.08;
|
|
225
|
+
var sz = wz - nz + Math.cos(rotY) * 0.08;
|
|
226
|
+
sill.position.set(sx, wy - winH / 2 - 0.01, sz);
|
|
227
|
+
S.furnitureGroup.add(sill);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── BACK WALL (Z = -CAMPUS_D/2) ─────────────────────────────────
|
|
231
|
+
var bwZ = -CAMPUS_D / 2;
|
|
232
|
+
var bw = new THREE.Mesh(new THREE.BoxGeometry(CAMPUS_W, WALL_H, wallT), wallMat);
|
|
233
|
+
bw.position.set(0, WALL_H / 2, bwZ);
|
|
234
|
+
S.furnitureGroup.add(bw);
|
|
235
|
+
// 5 back-wall windows above mezzanine
|
|
236
|
+
[-32, -16, 0, 16, 32].forEach(function(wx) {
|
|
237
|
+
addWindow(wx, MEZZ_H + 1.2, bwZ, 0, 5, 1.8);
|
|
259
238
|
});
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
239
|
+
|
|
240
|
+
// ── LEFT WALL (X = -CAMPUS_W/2) ─────────────────────────────────
|
|
241
|
+
var lwX = -CAMPUS_W / 2;
|
|
242
|
+
var lw = new THREE.Mesh(new THREE.BoxGeometry(wallT, WALL_H, CAMPUS_D), wallMat);
|
|
243
|
+
lw.position.set(lwX, WALL_H / 2, 0);
|
|
244
|
+
S.furnitureGroup.add(lw);
|
|
245
|
+
// 5 windows at Z = -20,-10,0,10,20
|
|
246
|
+
[-20, -10, 0, 10, 20].forEach(function(wz) {
|
|
247
|
+
addWindow(lwX, 2.8, wz, Math.PI / 2, 4, 3.2);
|
|
266
248
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
249
|
+
|
|
250
|
+
// ── RIGHT WALL (X = +CAMPUS_W/2) — solid (gallery is now inside campus)
|
|
251
|
+
var rwX = CAMPUS_W / 2;
|
|
252
|
+
var rwFull = new THREE.Mesh(new THREE.BoxGeometry(wallT, WALL_H, CAMPUS_D), wallMat);
|
|
253
|
+
rwFull.position.set(rwX, WALL_H / 2, 0);
|
|
254
|
+
S.furnitureGroup.add(rwFull);
|
|
255
|
+
|
|
256
|
+
// Right-wall windows
|
|
257
|
+
[-20, -10, 0, 10, 20].forEach(function(wz) {
|
|
258
|
+
addWindow(rwX, 2.8, wz, -Math.PI / 2, 4, 3.2);
|
|
272
259
|
});
|
|
260
|
+
|
|
261
|
+
// ── FRONT WALL (Z = +CAMPUS_D/2) — split with 6-unit entrance at center
|
|
262
|
+
var fwZ = CAMPUS_D / 2;
|
|
263
|
+
var entHalf = 3; // entrance half-width
|
|
264
|
+
var fwSideLen = (CAMPUS_W - entHalf * 2) / 2;
|
|
265
|
+
|
|
266
|
+
var fwLeft = new THREE.Mesh(new THREE.BoxGeometry(fwSideLen, WALL_H, wallT), wallMat);
|
|
267
|
+
fwLeft.position.set(-(entHalf + fwSideLen / 2), WALL_H / 2, fwZ);
|
|
268
|
+
S.furnitureGroup.add(fwLeft);
|
|
269
|
+
|
|
270
|
+
var fwRight = new THREE.Mesh(new THREE.BoxGeometry(fwSideLen, WALL_H, wallT), wallMat);
|
|
271
|
+
fwRight.position.set(entHalf + fwSideLen / 2, WALL_H / 2, fwZ);
|
|
272
|
+
S.furnitureGroup.add(fwRight);
|
|
273
|
+
|
|
274
|
+
// Gold entrance lintel above opening
|
|
275
|
+
var lintel = new THREE.Mesh(new THREE.BoxGeometry(entHalf * 2 + 0.4, 0.18, wallT + 0.02), matGold);
|
|
276
|
+
lintel.position.set(0, WALL_H - 0.09, fwZ);
|
|
277
|
+
S.furnitureGroup.add(lintel);
|
|
278
|
+
|
|
279
|
+
// Gold threshold strip at floor
|
|
280
|
+
var thresh = new THREE.Mesh(new THREE.BoxGeometry(entHalf * 2, 0.04, 0.30), matGold);
|
|
281
|
+
thresh.position.set(0, 0.02, fwZ - 0.15);
|
|
282
|
+
S.furnitureGroup.add(thresh);
|
|
273
283
|
}
|
|
274
284
|
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
285
|
+
// ============================================================
|
|
286
|
+
// CEILING — coffered grid (6 X-beams + 10 Z-beams), 5 skylights
|
|
287
|
+
// ============================================================
|
|
288
|
+
function buildCampusCeiling(matConcrete, matGlass) {
|
|
278
289
|
S._roofGroup = new THREE.Group();
|
|
279
290
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
var ceiling = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D),
|
|
291
|
+
var ceilMat = new THREE.MeshStandardMaterial({ color: 0x1c1f28, roughness: 0.88, side: THREE.DoubleSide });
|
|
292
|
+
// Main ceiling slab
|
|
293
|
+
var ceiling = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D), ceilMat);
|
|
283
294
|
ceiling.rotation.x = Math.PI / 2;
|
|
284
295
|
ceiling.position.y = WALL_H;
|
|
285
296
|
S._roofGroup.add(ceiling);
|
|
286
297
|
|
|
287
|
-
//
|
|
298
|
+
// Coffered beams — X-direction (run along X axis)
|
|
299
|
+
var beamMat = new THREE.MeshStandardMaterial({ color: 0x1a1c24, roughness: 0.82 });
|
|
300
|
+
var xBeamZPositions = [-25, -15, -5, 5, 15, 25];
|
|
301
|
+
xBeamZPositions.forEach(function(bz) {
|
|
302
|
+
var bm = new THREE.Mesh(new THREE.BoxGeometry(CAMPUS_W, 0.22, 0.30), beamMat);
|
|
303
|
+
bm.position.set(0, WALL_H - 0.11, bz);
|
|
304
|
+
S._roofGroup.add(bm);
|
|
305
|
+
});
|
|
306
|
+
// Z-direction beams (run along Z axis)
|
|
307
|
+
var zBeamXPositions = [-40, -32, -24, -16, -8, 0, 8, 16, 24, 32, 40];
|
|
308
|
+
zBeamXPositions.forEach(function(bx) {
|
|
309
|
+
var bm = new THREE.Mesh(new THREE.BoxGeometry(0.30, 0.22, CAMPUS_D), beamMat);
|
|
310
|
+
bm.position.set(bx, WALL_H - 0.11, 0);
|
|
311
|
+
S._roofGroup.add(bm);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// 5 skylights in cross pattern
|
|
288
315
|
var skylightMat = new THREE.MeshStandardMaterial({
|
|
289
|
-
color: 0xaaddff, emissive: 0xaaddff, emissiveIntensity: 0.
|
|
316
|
+
color: 0xaaddff, emissive: new THREE.Color(0xaaddff), emissiveIntensity: 0.35,
|
|
317
|
+
transparent: true, opacity: 0.45, side: THREE.DoubleSide
|
|
290
318
|
});
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
319
|
+
var skylightPositions = [
|
|
320
|
+
[0, 0],
|
|
321
|
+
[-16, -12], [16, -12],
|
|
322
|
+
[-16, 12], [16, 12],
|
|
323
|
+
];
|
|
324
|
+
skylightPositions.forEach(function(pos) {
|
|
325
|
+
var sk = new THREE.Mesh(new THREE.PlaneGeometry(7, 4.5), skylightMat);
|
|
326
|
+
sk.rotation.x = Math.PI / 2;
|
|
327
|
+
sk.position.set(pos[0], WALL_H - 0.04, pos[1]);
|
|
328
|
+
S._roofGroup.add(sk);
|
|
329
|
+
// Chrome skylight frame
|
|
330
|
+
var sfMat = new THREE.MeshStandardMaterial({ color: 0xd0d0d0, roughness: 0.10, metalness: 0.85 });
|
|
331
|
+
var sfH = new THREE.Mesh(new THREE.BoxGeometry(7.2, 0.06, 0.06), sfMat);
|
|
332
|
+
sfH.position.set(pos[0], WALL_H - 0.01, pos[1] - 2.28);
|
|
333
|
+
S._roofGroup.add(sfH.clone());
|
|
334
|
+
sfH.position.z = pos[1] + 2.28;
|
|
335
|
+
S._roofGroup.add(sfH);
|
|
336
|
+
var sfV = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, 4.62), sfMat);
|
|
337
|
+
sfV.position.set(pos[0] - 3.6, WALL_H - 0.01, pos[1]);
|
|
338
|
+
S._roofGroup.add(sfV.clone());
|
|
339
|
+
sfV.position.x = pos[0] + 3.6;
|
|
340
|
+
S._roofGroup.add(sfV);
|
|
296
341
|
});
|
|
297
342
|
|
|
298
343
|
S.furnitureGroup.add(S._roofGroup);
|
|
299
344
|
}
|
|
300
345
|
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
346
|
+
// ============================================================
|
|
347
|
+
// STRUCTURAL COLUMNS — 2 rows at X=±20, every ~10 units on Z
|
|
348
|
+
// Marble base shaft + gold TorusGeometry capital ring
|
|
349
|
+
// ============================================================
|
|
350
|
+
function buildStructuralColumns(matMarble, matGold, matChrome) {
|
|
351
|
+
var colZPositions = [-25, -15, -5, 5, 15, 25];
|
|
352
|
+
var colXPositions = [-20, 20];
|
|
353
|
+
|
|
354
|
+
colXPositions.forEach(function(cx) {
|
|
355
|
+
colZPositions.forEach(function(cz) {
|
|
356
|
+
// Base plinth
|
|
357
|
+
var plinth = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.18, 0.70), matMarble);
|
|
358
|
+
plinth.position.set(cx, 0.09, cz);
|
|
359
|
+
S.furnitureGroup.add(plinth);
|
|
360
|
+
// Shaft (octagonal — approximated with CylinderGeometry, 8 sides)
|
|
361
|
+
var shaft = new THREE.Mesh(new THREE.CylinderGeometry(0.22, 0.24, WALL_H - 0.28, 8), matMarble);
|
|
362
|
+
shaft.position.set(cx, WALL_H / 2 + 0.09, cz);
|
|
363
|
+
S.furnitureGroup.add(shaft);
|
|
364
|
+
// Gold capital ring (torus)
|
|
365
|
+
var capital = new THREE.Mesh(new THREE.TorusGeometry(0.26, 0.055, 8, 24), matGold);
|
|
366
|
+
capital.rotation.x = Math.PI / 2;
|
|
367
|
+
capital.position.set(cx, WALL_H - 0.22, cz);
|
|
368
|
+
S.furnitureGroup.add(capital);
|
|
369
|
+
// Capital top plate
|
|
370
|
+
var capTop = new THREE.Mesh(new THREE.BoxGeometry(0.60, 0.12, 0.60), matMarble);
|
|
371
|
+
capTop.position.set(cx, WALL_H - 0.06, cz);
|
|
372
|
+
S.furnitureGroup.add(capTop);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ============================================================
|
|
378
|
+
// MEZZANINE — 86W × 12D platform at y=MEZZ_H, glass railing
|
|
379
|
+
// ============================================================
|
|
380
|
+
function buildMezzanine(matConcrete, matChrome, matGlass, matWalnut, matFabric) {
|
|
381
|
+
var mw = CAMPUS_W - 4; // 86
|
|
382
|
+
var md = MEZZ_DEPTH; // 12
|
|
383
|
+
var mz = -CAMPUS_D / 2 + md / 2;
|
|
384
|
+
|
|
385
|
+
// Platform slab
|
|
386
|
+
var slab = new THREE.Mesh(new THREE.BoxGeometry(mw, 0.24, md), matConcrete);
|
|
387
|
+
slab.position.set(0, MEZZ_H - 0.12, mz);
|
|
388
|
+
slab.receiveShadow = true;
|
|
389
|
+
S.furnitureGroup.add(slab);
|
|
390
|
+
|
|
391
|
+
// Walnut surface
|
|
392
|
+
var surface = new THREE.Mesh(new THREE.PlaneGeometry(mw, md), matWalnut);
|
|
393
|
+
surface.rotation.x = -Math.PI / 2;
|
|
394
|
+
surface.position.set(0, MEZZ_H + 0.01, mz);
|
|
395
|
+
surface.receiveShadow = true;
|
|
396
|
+
S.furnitureGroup.add(surface);
|
|
397
|
+
|
|
398
|
+
// Support columns (every 14.3 units along X)
|
|
399
|
+
var supportXs = [-36, -25, -14, -3, 3, 14, 25, 36];
|
|
400
|
+
supportXs.forEach(function(sx) {
|
|
401
|
+
var col = new THREE.Mesh(new THREE.CylinderGeometry(0.14, 0.14, MEZZ_H, 8), matChrome);
|
|
402
|
+
col.position.set(sx, MEZZ_H / 2, -CAMPUS_D / 2 + md);
|
|
331
403
|
S.furnitureGroup.add(col);
|
|
332
404
|
});
|
|
333
405
|
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
406
|
+
// Glass railing front edge
|
|
407
|
+
var railGlass = new THREE.Mesh(new THREE.PlaneGeometry(mw - 4, 1.1), matGlass);
|
|
408
|
+
railGlass.position.set(0, MEZZ_H + 0.66, -CAMPUS_D / 2 + md + 0.02);
|
|
409
|
+
S.furnitureGroup.add(railGlass);
|
|
410
|
+
|
|
411
|
+
// Chrome top handrail bar
|
|
412
|
+
var topBar = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, mw - 4, 8), matChrome);
|
|
413
|
+
topBar.rotation.z = Math.PI / 2;
|
|
414
|
+
topBar.position.set(0, MEZZ_H + 1.22, -CAMPUS_D / 2 + md);
|
|
415
|
+
S.furnitureGroup.add(topBar);
|
|
416
|
+
|
|
417
|
+
// Railing vertical posts (every ~7 units)
|
|
418
|
+
for (var rx = -(mw / 2 - 3); rx <= mw / 2 - 3; rx += 7) {
|
|
419
|
+
var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 1.22, 6), matChrome);
|
|
420
|
+
post.position.set(rx, MEZZ_H + 0.61, -CAMPUS_D / 2 + md);
|
|
421
|
+
S.furnitureGroup.add(post);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Meeting pods (2 round tables with chairs)
|
|
425
|
+
[-22, 22].forEach(function(mx2) {
|
|
426
|
+
var table = new THREE.Mesh(new THREE.CylinderGeometry(0.9, 0.9, 0.06, 24), matWalnut);
|
|
427
|
+
table.position.set(mx2, MEZZ_H + 0.78, -CAMPUS_D / 2 + 5);
|
|
340
428
|
S.furnitureGroup.add(table);
|
|
341
|
-
var tableLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.
|
|
342
|
-
tableLeg.position.set(
|
|
429
|
+
var tableLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.07, 0.14, 0.72, 8), matChrome);
|
|
430
|
+
tableLeg.position.set(mx2, MEZZ_H + 0.40, -CAMPUS_D / 2 + 5);
|
|
343
431
|
S.furnitureGroup.add(tableLeg);
|
|
344
|
-
// 4 chairs
|
|
432
|
+
// 4 chairs
|
|
345
433
|
for (var ci = 0; ci < 4; ci++) {
|
|
346
434
|
var ca = (ci / 4) * Math.PI * 2;
|
|
347
|
-
|
|
348
|
-
var cz2 = -CAMPUS_D / 2 + 5 + Math.sin(ca) * 1.5;
|
|
349
|
-
buildModernChair(cx2, MEZZ_H + 0.11, cz2, ca + Math.PI, chromeMat);
|
|
435
|
+
buildModernChair(mx2 + Math.cos(ca) * 1.4, MEZZ_H + 0.01, -CAMPUS_D / 2 + 5 + Math.sin(ca) * 1.4, ca + Math.PI, matChrome);
|
|
350
436
|
}
|
|
351
437
|
});
|
|
352
438
|
|
|
353
|
-
// Lounge
|
|
354
|
-
buildSofa(0, MEZZ_H + 0.
|
|
439
|
+
// Lounge area
|
|
440
|
+
buildSofa(0, MEZZ_H + 0.01, -CAMPUS_D / 2 + 3);
|
|
355
441
|
|
|
356
|
-
//
|
|
357
|
-
var
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
var
|
|
361
|
-
|
|
362
|
-
S.furnitureGroup.add(
|
|
442
|
+
// Upper Deck label
|
|
443
|
+
var udDiv = document.createElement('div');
|
|
444
|
+
udDiv.textContent = 'UPPER DECK';
|
|
445
|
+
udDiv.style.cssText = 'color:#d4af37;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
|
|
446
|
+
var udLabel = new CSS2DObject(udDiv);
|
|
447
|
+
udLabel.position.set(0, MEZZ_H + 1.8, -CAMPUS_D / 2 + md);
|
|
448
|
+
S.furnitureGroup.add(udLabel);
|
|
363
449
|
}
|
|
364
450
|
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
var
|
|
371
|
-
var
|
|
372
|
-
var
|
|
373
|
-
|
|
451
|
+
// ============================================================
|
|
452
|
+
// STAIRCASE — switchback at X=35
|
|
453
|
+
// Lower 8 steps going -Z, landing at half height, upper 8 steps -Z
|
|
454
|
+
// ============================================================
|
|
455
|
+
function buildStaircase(matMarble, matChrome, matGlass) {
|
|
456
|
+
var stairX = 35;
|
|
457
|
+
var stairW = 3.0;
|
|
458
|
+
var steps = 8;
|
|
459
|
+
var stepH = MEZZ_H / (steps * 2);
|
|
460
|
+
var stepD = 0.55;
|
|
461
|
+
var startZ = -14; // ground level bottom of stairs, going -Z toward mezzanine at Z=-18
|
|
462
|
+
|
|
463
|
+
// ── Lower flight (going in -Z direction) ────────────────────────
|
|
374
464
|
for (var i = 0; i < steps; i++) {
|
|
375
|
-
var step = new THREE.Mesh(new THREE.BoxGeometry(
|
|
376
|
-
step.position.set(stairX, stepH / 2 + i * stepH,
|
|
377
|
-
step.
|
|
465
|
+
var step = new THREE.Mesh(new THREE.BoxGeometry(stairW, stepH, stepD), matMarble);
|
|
466
|
+
step.position.set(stairX, stepH / 2 + i * stepH, startZ - i * stepD);
|
|
467
|
+
step.receiveShadow = true;
|
|
378
468
|
S.furnitureGroup.add(step);
|
|
379
469
|
}
|
|
380
470
|
|
|
381
|
-
//
|
|
382
|
-
var
|
|
383
|
-
var
|
|
384
|
-
var
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
S.furnitureGroup.add(
|
|
388
|
-
|
|
389
|
-
//
|
|
390
|
-
var
|
|
391
|
-
var
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
471
|
+
// ── Landing platform at mid-height ──────────────────────────────
|
|
472
|
+
var landingY = steps * stepH;
|
|
473
|
+
var landingZ = startZ - steps * stepD;
|
|
474
|
+
var landing = new THREE.Mesh(new THREE.BoxGeometry(stairW, 0.10, stairW), matMarble);
|
|
475
|
+
landing.position.set(stairX, landingY + 0.05, landingZ - stairW / 2);
|
|
476
|
+
landing.receiveShadow = true;
|
|
477
|
+
S.furnitureGroup.add(landing);
|
|
478
|
+
|
|
479
|
+
// ── Upper flight (continuing -Z from landing) ────────────────────
|
|
480
|
+
var upperStartZ = landingZ - stairW;
|
|
481
|
+
for (var j = 0; j < steps; j++) {
|
|
482
|
+
var stepU = new THREE.Mesh(new THREE.BoxGeometry(stairW, stepH, stepD), matMarble);
|
|
483
|
+
stepU.position.set(stairX, landingY + stepH / 2 + j * stepH, upperStartZ - j * stepD);
|
|
484
|
+
stepU.receiveShadow = true;
|
|
485
|
+
S.furnitureGroup.add(stepU);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ── Glass side panels ────────────────────────────────────────────
|
|
489
|
+
var totalFlightD = steps * stepD;
|
|
490
|
+
var panelH = MEZZ_H + 0.8;
|
|
491
|
+
|
|
492
|
+
// Lower flight panel (right side)
|
|
493
|
+
var lpLower = new THREE.Mesh(new THREE.PlaneGeometry(totalFlightD + 0.2, panelH), matGlass);
|
|
494
|
+
lpLower.position.set(stairX + stairW / 2 + 0.04, panelH / 2, startZ - totalFlightD / 2);
|
|
495
|
+
lpLower.rotation.y = Math.PI / 2;
|
|
496
|
+
S.furnitureGroup.add(lpLower);
|
|
497
|
+
|
|
498
|
+
// Upper flight panel (right side)
|
|
499
|
+
var lpUpper = new THREE.Mesh(new THREE.PlaneGeometry(totalFlightD + 0.2, panelH), matGlass);
|
|
500
|
+
lpUpper.position.set(stairX + stairW / 2 + 0.04, panelH / 2, upperStartZ - totalFlightD / 2);
|
|
501
|
+
lpUpper.rotation.y = Math.PI / 2;
|
|
502
|
+
S.furnitureGroup.add(lpUpper);
|
|
503
|
+
|
|
504
|
+
// ── Chrome handrails (diagonal) ──────────────────────────────────
|
|
505
|
+
function addHandrail(sx, sy, sz, len, slope) {
|
|
506
|
+
var railLen = Math.sqrt(len * len + (slope * len) * (slope * len));
|
|
507
|
+
var rail = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, railLen, 6), matChrome);
|
|
508
|
+
rail.position.set(sx, sy, sz);
|
|
509
|
+
rail.rotation.x = Math.atan2(slope * len, len);
|
|
510
|
+
S.furnitureGroup.add(rail);
|
|
511
|
+
}
|
|
512
|
+
addHandrail(stairX + stairW / 2 + 0.08, panelH * 0.6, startZ - totalFlightD / 2, totalFlightD, -stepH / stepD);
|
|
513
|
+
addHandrail(stairX + stairW / 2 + 0.08, panelH * 0.6, upperStartZ - totalFlightD / 2, totalFlightD, -stepH / stepD);
|
|
395
514
|
}
|
|
396
515
|
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
516
|
+
// ============================================================
|
|
517
|
+
// GRAND LOBBY — Z = 22 to 30 (inside front zone)
|
|
518
|
+
// ============================================================
|
|
519
|
+
function buildLobby(matMarble, matChrome, matGold, matWalnut, matNeonBlue) {
|
|
520
|
+
var lz = CAMPUS_D / 2 - 5; // approx Z=25 from center
|
|
400
521
|
var group = new THREE.Group();
|
|
401
522
|
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
523
|
+
// ── Entrance archway ─────────────────────────────────────────────
|
|
524
|
+
// Two thick columns flanking entrance (outside main walls, decorative)
|
|
525
|
+
[-6, 6].forEach(function(ax) {
|
|
526
|
+
var archCol = new THREE.Mesh(new THREE.BoxGeometry(1.0, 5.0, 0.8), matMarble);
|
|
527
|
+
archCol.position.set(ax, 2.5, lz + 4.0);
|
|
528
|
+
group.add(archCol);
|
|
529
|
+
// Gold capital
|
|
530
|
+
var cap = new THREE.Mesh(new THREE.TorusGeometry(0.58, 0.07, 8, 20), matGold);
|
|
531
|
+
cap.rotation.x = Math.PI / 2;
|
|
532
|
+
cap.position.set(ax, 5.1, lz + 4.0);
|
|
533
|
+
group.add(cap);
|
|
534
|
+
});
|
|
535
|
+
// Header beam across arch
|
|
536
|
+
var headerBeam = new THREE.Mesh(new THREE.BoxGeometry(13.2, 0.35, 0.80), matMarble);
|
|
537
|
+
headerBeam.position.set(0, 5.18, lz + 4.0);
|
|
538
|
+
group.add(headerBeam);
|
|
539
|
+
// Gold trim strip on beam underside
|
|
540
|
+
var beamTrim = new THREE.Mesh(new THREE.BoxGeometry(13.0, 0.06, 0.75), matGold);
|
|
541
|
+
beamTrim.position.set(0, 5.00, lz + 4.0);
|
|
542
|
+
group.add(beamTrim);
|
|
543
|
+
|
|
544
|
+
// ── Lower lobby ceiling (4.5 units) ─────────────────────────────
|
|
545
|
+
var lobbyCeilMat = new THREE.MeshStandardMaterial({ color: 0x1e2128, roughness: 0.80 });
|
|
546
|
+
var lobbyCeil = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, 10), lobbyCeilMat);
|
|
547
|
+
lobbyCeil.rotation.x = Math.PI / 2;
|
|
548
|
+
lobbyCeil.position.set(0, 4.5, CAMPUS_D / 2 - 5);
|
|
549
|
+
group.add(lobbyCeil);
|
|
550
|
+
|
|
551
|
+
// ── Column arcade — 4 pairs of decorative columns ────────────────
|
|
552
|
+
[-18, -9, 9, 18].forEach(function(ax) {
|
|
553
|
+
[-2, 2].forEach(function(az) {
|
|
554
|
+
var dcol = new THREE.Mesh(new THREE.CylinderGeometry(0.20, 0.22, 4.5, 12), matMarble);
|
|
555
|
+
dcol.position.set(ax, 2.25, lz + az);
|
|
556
|
+
group.add(dcol);
|
|
557
|
+
var dring = new THREE.Mesh(new THREE.TorusGeometry(0.24, 0.04, 6, 18), matGold);
|
|
558
|
+
dring.rotation.x = Math.PI / 2;
|
|
559
|
+
dring.position.set(ax, 4.44, lz + az);
|
|
560
|
+
group.add(dring);
|
|
561
|
+
});
|
|
415
562
|
});
|
|
416
|
-
// Marble countertop
|
|
417
|
-
var counterTop = new THREE.Mesh(new THREE.BoxGeometry(4.2, 0.06, 1.4), marbleMat);
|
|
418
|
-
counterTop.position.set(0, 1.17, lz - 0.05);
|
|
419
|
-
counterTop.castShadow = true;
|
|
420
|
-
group.add(counterTop);
|
|
421
|
-
// Gold accent strip on front
|
|
422
|
-
var accentStrip = new THREE.Mesh(new THREE.BoxGeometry(3.9, 0.04, 0.005), goldMat);
|
|
423
|
-
accentStrip.position.set(0, 1.0, lz + 0.57);
|
|
424
|
-
group.add(accentStrip);
|
|
425
|
-
// LED underglow (blue)
|
|
426
|
-
var ledMat = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: 0x58a6ff, emissiveIntensity: 0.6, roughness: 0.2 });
|
|
427
|
-
var ledStrip = new THREE.Mesh(new THREE.BoxGeometry(3.8, 0.02, 0.02), ledMat);
|
|
428
|
-
ledStrip.position.set(0, 0.03, lz + 0.55);
|
|
429
|
-
group.add(ledStrip);
|
|
430
563
|
|
|
431
|
-
// Reception
|
|
432
|
-
|
|
433
|
-
var
|
|
434
|
-
|
|
564
|
+
// ── Reception desk — symmetric L-shaped (marble top, walnut body) ─
|
|
565
|
+
// Left wing
|
|
566
|
+
var deskBodyL = new THREE.Mesh(new THREE.BoxGeometry(5.5, 1.10, 1.0), matWalnut);
|
|
567
|
+
deskBodyL.position.set(-4, 0.55, lz + 0.5);
|
|
568
|
+
group.add(deskBodyL);
|
|
569
|
+
// Right wing
|
|
570
|
+
var deskBodyR = new THREE.Mesh(new THREE.BoxGeometry(5.5, 1.10, 1.0), matWalnut);
|
|
571
|
+
deskBodyR.position.set(4, 0.55, lz + 0.5);
|
|
572
|
+
group.add(deskBodyR);
|
|
573
|
+
// Center connector (back panel)
|
|
574
|
+
var deskCenter = new THREE.Mesh(new THREE.BoxGeometry(3.2, 1.10, 0.90), matWalnut);
|
|
575
|
+
deskCenter.position.set(0, 0.55, lz - 0.65);
|
|
576
|
+
group.add(deskCenter);
|
|
577
|
+
// Marble countertop spanning whole desk
|
|
578
|
+
var counterL = new THREE.Mesh(new THREE.BoxGeometry(5.7, 0.06, 1.22), matMarble);
|
|
579
|
+
counterL.position.set(-4, 1.16, lz + 0.5);
|
|
580
|
+
group.add(counterL);
|
|
581
|
+
var counterR = new THREE.Mesh(new THREE.BoxGeometry(5.7, 0.06, 1.22), matMarble);
|
|
582
|
+
counterR.position.set(4, 1.16, lz + 0.5);
|
|
583
|
+
group.add(counterR);
|
|
584
|
+
var counterC = new THREE.Mesh(new THREE.BoxGeometry(3.4, 0.06, 1.12), matMarble);
|
|
585
|
+
counterC.position.set(0, 1.16, lz - 0.65);
|
|
586
|
+
group.add(counterC);
|
|
587
|
+
// Blue LED strip under countertop
|
|
588
|
+
var ledStrip = new THREE.Mesh(new THREE.BoxGeometry(13.8, 0.02, 0.02), matNeonBlue);
|
|
589
|
+
ledStrip.position.set(0, 1.04, lz + 1.07);
|
|
590
|
+
group.add(ledStrip);
|
|
591
|
+
// Gold accent strip on front panel
|
|
592
|
+
var goldAccent = new THREE.Mesh(new THREE.BoxGeometry(13.8, 0.04, 0.005), matGold);
|
|
593
|
+
goldAccent.position.set(0, 0.92, lz + 1.0);
|
|
594
|
+
group.add(goldAccent);
|
|
595
|
+
|
|
596
|
+
// Reception monitor + keyboard
|
|
597
|
+
var monBez = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
598
|
+
var mon = new THREE.Mesh(new THREE.BoxGeometry(0.55, 0.38, 0.025), monBez);
|
|
599
|
+
mon.position.set(-2.5, 1.47, lz + 0.0);
|
|
435
600
|
group.add(mon);
|
|
436
|
-
var
|
|
437
|
-
new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: 0x58a6ff, emissiveIntensity: 0.
|
|
438
|
-
|
|
439
|
-
group.add(
|
|
440
|
-
var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.
|
|
441
|
-
monStand.position.set(-
|
|
601
|
+
var monScr = new THREE.Mesh(new THREE.PlaneGeometry(0.50, 0.34),
|
|
602
|
+
new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.28, roughness: 0.1 }));
|
|
603
|
+
monScr.position.set(-2.5, 1.47, lz - 0.01);
|
|
604
|
+
group.add(monScr);
|
|
605
|
+
var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.22, 6), matChrome);
|
|
606
|
+
monStand.position.set(-2.5, 1.30, lz + 0.0);
|
|
442
607
|
group.add(monStand);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.01, 0.1), kbMat);
|
|
447
|
-
kb.position.set(-0.8, 1.2, lz - 0.4);
|
|
608
|
+
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.012, 0.11),
|
|
609
|
+
new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 }));
|
|
610
|
+
kb.position.set(-2.5, 1.185, lz + 0.45);
|
|
448
611
|
group.add(kb);
|
|
449
612
|
|
|
450
|
-
//
|
|
451
|
-
var
|
|
452
|
-
var
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
tvFrame
|
|
613
|
+
// ── Feature wall (10W, walnut paneled, Z behind desk) ────────────
|
|
614
|
+
var fwMat = new THREE.MeshStandardMaterial({ color: 0x2a1e10, roughness: 0.65 });
|
|
615
|
+
var featureWall = new THREE.Mesh(new THREE.BoxGeometry(10, 4, 0.18), fwMat);
|
|
616
|
+
featureWall.position.set(0, 2.2, lz + 2.0);
|
|
617
|
+
group.add(featureWall);
|
|
618
|
+
// Walnut vertical panel strips
|
|
619
|
+
var stripMat = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.58 });
|
|
620
|
+
for (var pi = -4; pi <= 4; pi++) {
|
|
621
|
+
var strip = new THREE.Mesh(new THREE.BoxGeometry(0.06, 4.0, 0.02), stripMat);
|
|
622
|
+
strip.position.set(pi * 1.1, 2.2, lz + 2.10);
|
|
623
|
+
group.add(strip);
|
|
624
|
+
}
|
|
625
|
+
// Gold divider line at top of feature wall
|
|
626
|
+
var fwGold = new THREE.Mesh(new THREE.BoxGeometry(10.2, 0.07, 0.02), matGold);
|
|
627
|
+
fwGold.position.set(0, 4.23, lz + 2.10);
|
|
628
|
+
group.add(fwGold);
|
|
629
|
+
|
|
630
|
+
// ── TV canvas (PRESERVED: S._tvScreen contract) ───────────────────
|
|
631
|
+
var tvW = 960, tvH = 560;
|
|
632
|
+
var tvFrame = new THREE.Mesh(new THREE.BoxGeometry(5.2, 2.9, 0.07),
|
|
633
|
+
new THREE.MeshStandardMaterial({ color: 0x080808, roughness: 0.2 }));
|
|
634
|
+
tvFrame.position.set(0, 2.1, lz + 1.9);
|
|
470
635
|
group.add(tvFrame);
|
|
471
|
-
// Animated canvas
|
|
472
|
-
var tvW = 960, tvH = 600;
|
|
473
636
|
var tvCvs = document.createElement('canvas');
|
|
474
637
|
tvCvs.width = tvW; tvCvs.height = tvH;
|
|
475
638
|
var tvTex = new THREE.CanvasTexture(tvCvs);
|
|
476
639
|
tvTex.minFilter = THREE.LinearFilter;
|
|
477
640
|
var tvScreenMat = new THREE.MeshStandardMaterial({
|
|
478
|
-
map: tvTex, emissive: 0x58a6ff, emissiveIntensity: 0.
|
|
641
|
+
map: tvTex, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.18, roughness: 0.08
|
|
479
642
|
});
|
|
480
|
-
var tvScreen = new THREE.Mesh(new THREE.PlaneGeometry(4.
|
|
481
|
-
tvScreen.position.set(0, 2.
|
|
643
|
+
var tvScreen = new THREE.Mesh(new THREE.PlaneGeometry(4.9, 2.65), tvScreenMat);
|
|
644
|
+
tvScreen.position.set(0, 2.1, lz + 1.86);
|
|
482
645
|
tvScreen.rotation.y = Math.PI;
|
|
483
646
|
group.add(tvScreen);
|
|
484
647
|
S._tvScreen = { canvas: tvCvs, texture: tvTex, tickerOffset: 0 };
|
|
648
|
+
// TV accent light
|
|
649
|
+
var tvLight = new THREE.PointLight(0x58a6ff, 0.45, 7);
|
|
650
|
+
tvLight.castShadow = false;
|
|
651
|
+
tvLight.position.set(0, 4.0, lz + 1.5);
|
|
652
|
+
group.add(tvLight);
|
|
485
653
|
|
|
486
|
-
//
|
|
487
|
-
var
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
654
|
+
// ── LET THEM TALK logo above feature wall ────────────────────────
|
|
655
|
+
var logoDiv = document.createElement('div');
|
|
656
|
+
logoDiv.textContent = 'LET THEM TALK';
|
|
657
|
+
logoDiv.style.cssText = 'color:#ffffff;font-size:14px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:6px;text-shadow:0 0 20px rgba(88,166,255,0.6),0 0 40px rgba(88,166,255,0.3);';
|
|
658
|
+
var logoLabel = new CSS2DObject(logoDiv);
|
|
659
|
+
logoLabel.position.set(0, 4.6, lz + 2.1);
|
|
660
|
+
group.add(logoLabel);
|
|
661
|
+
|
|
662
|
+
// ── Water feature (5×2 pool, center lobby) ───────────────────────
|
|
663
|
+
var poolRimMat = new THREE.MeshStandardMaterial({ color: 0x22252e, roughness: 0.42 });
|
|
664
|
+
var poolRim = new THREE.Mesh(new THREE.BoxGeometry(5.2, 0.22, 2.2), poolRimMat);
|
|
665
|
+
poolRim.position.set(0, 0.11, lz - 5.0);
|
|
666
|
+
group.add(poolRim);
|
|
667
|
+
var waterMat = new THREE.MeshStandardMaterial({
|
|
668
|
+
color: 0x1e5a8a, roughness: 0.04, metalness: 0.28, transparent: true, opacity: 0.75
|
|
669
|
+
});
|
|
670
|
+
var water = new THREE.Mesh(new THREE.PlaneGeometry(4.8, 1.8), waterMat);
|
|
499
671
|
water.rotation.x = -Math.PI / 2;
|
|
500
|
-
water.position.set(0, 0.
|
|
672
|
+
water.position.set(0, 0.23, lz - 5.0);
|
|
501
673
|
group.add(water);
|
|
502
|
-
// Decorative stones
|
|
674
|
+
// Decorative smooth stones
|
|
503
675
|
var stoneMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.7 });
|
|
504
|
-
[[-
|
|
505
|
-
var stone = new THREE.Mesh(new THREE.SphereGeometry(0.
|
|
506
|
-
stone.position.set(sp[0], 0.
|
|
676
|
+
[[-1.2, -0.4], [0.6, 0.3], [-0.3, 0.2], [1.3, -0.3], [-0.8, -0.1], [0.0, 0.5]].forEach(function(sp) {
|
|
677
|
+
var stone = new THREE.Mesh(new THREE.SphereGeometry(0.055 + Math.random() * 0.04, 6, 4), stoneMat);
|
|
678
|
+
stone.position.set(sp[0], 0.21, lz - 5.0 + sp[1]);
|
|
507
679
|
stone.scale.y = 0.5;
|
|
508
680
|
group.add(stone);
|
|
509
681
|
});
|
|
510
682
|
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
benchSeat.castShadow = true;
|
|
517
|
-
group.add(benchSeat);
|
|
518
|
-
// Chrome legs
|
|
519
|
-
[-1, 1].forEach(function(lx) {
|
|
520
|
-
var benchLeg = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.42, 0.5), chromeMat);
|
|
521
|
-
benchLeg.position.set(bx + lx, 0.22, lz - 2);
|
|
522
|
-
group.add(benchLeg);
|
|
523
|
-
});
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// --- Pendant lights above reception ---
|
|
527
|
-
[-1.2, 0, 1.2].forEach(function(px) {
|
|
528
|
-
var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.005, 0.005, 2.5, 4),
|
|
529
|
-
new THREE.MeshStandardMaterial({ color: 0x333333 }));
|
|
530
|
-
wire.position.set(px, WALL_H - 1.25, lz);
|
|
683
|
+
// ── Pendant lights above reception (3 clusters) ──────────────────
|
|
684
|
+
[-5, 0, 5].forEach(function(px) {
|
|
685
|
+
var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.006, 0.006, 2.2, 4),
|
|
686
|
+
new THREE.MeshStandardMaterial({ color: 0x2a2a2a }));
|
|
687
|
+
wire.position.set(px, 4.5 - 1.1, lz + 0.3);
|
|
531
688
|
group.add(wire);
|
|
532
|
-
var shade = new THREE.Mesh(new THREE.SphereGeometry(0.
|
|
533
|
-
new THREE.MeshStandardMaterial({ color: 0xffeedd, emissive: 0xffeedd, emissiveIntensity: 0.
|
|
534
|
-
shade.position.set(px,
|
|
689
|
+
var shade = new THREE.Mesh(new THREE.SphereGeometry(0.13, 14, 10),
|
|
690
|
+
new THREE.MeshStandardMaterial({ color: 0xffeedd, emissive: new THREE.Color(0xffeedd), emissiveIntensity: 0.40, transparent: true, opacity: 0.80 }));
|
|
691
|
+
shade.position.set(px, 4.5 - 2.55, lz + 0.3);
|
|
535
692
|
group.add(shade);
|
|
693
|
+
// Gold ring
|
|
694
|
+
var ring = new THREE.Mesh(new THREE.TorusGeometry(0.135, 0.012, 6, 18), matGold);
|
|
695
|
+
ring.rotation.x = Math.PI / 2;
|
|
696
|
+
ring.position.set(px, 4.5 - 2.68, lz + 0.3);
|
|
697
|
+
group.add(ring);
|
|
536
698
|
});
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
group.add(
|
|
699
|
+
var recLight = new THREE.PointLight(0xffeedd, 0.38, 10);
|
|
700
|
+
recLight.castShadow = false;
|
|
701
|
+
recLight.position.set(0, 3.8, lz + 0.3);
|
|
702
|
+
group.add(recLight);
|
|
541
703
|
|
|
542
|
-
// RECEPTION
|
|
704
|
+
// ── RECEPTION gold sign ──────────────────────────────────────────
|
|
543
705
|
var signDiv = document.createElement('div');
|
|
544
706
|
signDiv.textContent = 'RECEPTION';
|
|
545
707
|
signDiv.style.cssText = 'color:#d4af37;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:3px;';
|
|
546
708
|
var sign = new CSS2DObject(signDiv);
|
|
547
|
-
sign.position.set(0, 4.
|
|
709
|
+
sign.position.set(0, 4.8, lz + 0.3);
|
|
548
710
|
group.add(sign);
|
|
549
711
|
|
|
550
712
|
S.furnitureGroup.add(group);
|
|
551
713
|
}
|
|
552
714
|
|
|
553
|
-
//
|
|
715
|
+
// ============================================================
|
|
716
|
+
// MAIN CORRIDOR — Z=0, 4 units wide (-2 to +2)
|
|
717
|
+
// Polished dark floor + gold inlay lines
|
|
718
|
+
// ============================================================
|
|
719
|
+
function buildMainCorridor(matDark, matGold) {
|
|
720
|
+
// Dark polished floor strip
|
|
721
|
+
var corrFloor = new THREE.Mesh(
|
|
722
|
+
new THREE.PlaneGeometry(CAMPUS_W, 4),
|
|
723
|
+
new THREE.MeshStandardMaterial({ color: 0x12141a, roughness: 0.08, metalness: 0.12 })
|
|
724
|
+
);
|
|
725
|
+
corrFloor.rotation.x = -Math.PI / 2;
|
|
726
|
+
corrFloor.position.set(0, 0.015, 0);
|
|
727
|
+
S.furnitureGroup.add(corrFloor);
|
|
728
|
+
|
|
729
|
+
// Gold inlay centre line
|
|
730
|
+
var inlayCtr = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, 0.06), matGold);
|
|
731
|
+
inlayCtr.rotation.x = -Math.PI / 2;
|
|
732
|
+
inlayCtr.position.set(0, 0.018, 0);
|
|
733
|
+
S.furnitureGroup.add(inlayCtr);
|
|
734
|
+
|
|
735
|
+
// Gold inlay edge lines
|
|
736
|
+
[-1.8, 1.8].forEach(function(ez) {
|
|
737
|
+
var inlay = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, 0.03), matGold);
|
|
738
|
+
inlay.rotation.x = -Math.PI / 2;
|
|
739
|
+
inlay.position.set(0, 0.018, ez);
|
|
740
|
+
S.furnitureGroup.add(inlay);
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// ============================================================
|
|
745
|
+
// CROSS CORRIDORS — at X=±20, 3 units wide
|
|
746
|
+
// ============================================================
|
|
747
|
+
function buildCrossCorridor(matDark, matGold) {
|
|
748
|
+
[-20, 20].forEach(function(cx) {
|
|
749
|
+
var cFloor = new THREE.Mesh(
|
|
750
|
+
new THREE.PlaneGeometry(3, CAMPUS_D),
|
|
751
|
+
new THREE.MeshStandardMaterial({ color: 0x13151c, roughness: 0.10, metalness: 0.10 })
|
|
752
|
+
);
|
|
753
|
+
cFloor.rotation.x = -Math.PI / 2;
|
|
754
|
+
cFloor.position.set(cx, 0.012, 0);
|
|
755
|
+
S.furnitureGroup.add(cFloor);
|
|
756
|
+
|
|
757
|
+
// Gold inlay centre
|
|
758
|
+
var inlay = new THREE.Mesh(new THREE.PlaneGeometry(0.04, CAMPUS_D), matGold);
|
|
759
|
+
inlay.rotation.x = -Math.PI / 2;
|
|
760
|
+
inlay.position.set(cx, 0.016, 0);
|
|
761
|
+
S.furnitureGroup.add(inlay);
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// === PHASE 3-5 FUNCTIONS FOLLOW ===
|
|
766
|
+
|
|
767
|
+
// ============================================================
|
|
768
|
+
// GAMING DESK
|
|
769
|
+
// ============================================================
|
|
554
770
|
function buildGamingDesk(x, z, index) {
|
|
555
771
|
var group = new THREE.Group();
|
|
556
772
|
group.position.set(x, 0, z);
|
|
557
773
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
var
|
|
561
|
-
|
|
562
|
-
// Main desktop
|
|
563
|
-
var mainTop = new THREE.Mesh(new THREE.BoxGeometry(2, 0.05, 0.9), deskMat);
|
|
774
|
+
var deskMat = new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.28, metalness: 0.12 });
|
|
775
|
+
// Main top
|
|
776
|
+
var mainTop = new THREE.Mesh(new THREE.BoxGeometry(2.0, 0.05, 0.90), deskMat);
|
|
564
777
|
mainTop.position.y = 0.76; mainTop.castShadow = true; mainTop.receiveShadow = true;
|
|
565
778
|
group.add(mainTop);
|
|
566
|
-
|
|
567
|
-
// RGB LED strip under desk edge (front)
|
|
779
|
+
// RGB LED strip
|
|
568
780
|
var rgbColors = [0x58a6ff, 0xa855f7, 0x22c55e, 0xef4444, 0x06b6d4, 0xec4899];
|
|
569
781
|
var rgbColor = rgbColors[index % rgbColors.length];
|
|
570
|
-
var rgbMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: rgbColor, emissiveIntensity: 0.8, roughness: 0.2 });
|
|
782
|
+
var rgbMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: new THREE.Color(rgbColor), emissiveIntensity: 0.8, roughness: 0.2 });
|
|
571
783
|
var rgbStrip = new THREE.Mesh(new THREE.BoxGeometry(1.9, 0.015, 0.015), rgbMat);
|
|
572
784
|
rgbStrip.position.set(0, 0.74, 0.44);
|
|
573
785
|
group.add(rgbStrip);
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
var legMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.2 });
|
|
786
|
+
// Legs
|
|
787
|
+
var legMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.40, metalness: 0.22 });
|
|
577
788
|
[[-0.85, -0.35], [-0.85, 0.35], [0.85, -0.35], [0.85, 0.35]].forEach(function(p) {
|
|
578
789
|
var leg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.76, 0.06), legMat);
|
|
579
790
|
leg.position.set(p[0], 0.38, p[1]);
|
|
580
|
-
leg.castShadow = true;
|
|
581
791
|
group.add(leg);
|
|
582
792
|
});
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
var monBody = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
586
|
-
monBody.position.set(0, 1.
|
|
587
|
-
monBody.castShadow = true;
|
|
793
|
+
// Monitor body
|
|
794
|
+
var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
795
|
+
var monBody = new THREE.Mesh(new THREE.BoxGeometry(0.72, 0.38, 0.03), monMat);
|
|
796
|
+
monBody.position.set(0, 1.14, -0.25);
|
|
588
797
|
group.add(monBody);
|
|
589
|
-
|
|
590
798
|
// Monitor screen
|
|
591
|
-
var
|
|
592
|
-
var
|
|
593
|
-
|
|
594
|
-
});
|
|
595
|
-
var screen = new THREE.Mesh(screenGeo, screenMat);
|
|
596
|
-
screen.position.set(0, 1.15, -0.234);
|
|
799
|
+
var screenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: new THREE.Color(0x333333), emissiveIntensity: 0.1, roughness: 0.2 });
|
|
800
|
+
var screen = new THREE.Mesh(new THREE.PlaneGeometry(0.65, 0.32), screenMat);
|
|
801
|
+
screen.position.set(0, 1.14, -0.234);
|
|
597
802
|
group.add(screen);
|
|
598
|
-
|
|
599
|
-
// Monitor stand (V-shaped, chrome — positioned BEHIND the screen to avoid clipping)
|
|
803
|
+
// Monitor stand
|
|
600
804
|
var standMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.15, metalness: 0.7 });
|
|
601
|
-
var standArm = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.
|
|
602
|
-
standArm.position.set(0, 0.
|
|
805
|
+
var standArm = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.24, 0.04), standMat);
|
|
806
|
+
standArm.position.set(0, 0.91, -0.27);
|
|
603
807
|
group.add(standArm);
|
|
604
|
-
var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
808
|
+
var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.02, 0.15), standMat);
|
|
605
809
|
standBase.position.set(0, 0.78, -0.27);
|
|
606
810
|
group.add(standBase);
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
var pcMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.3 });
|
|
811
|
+
// PC tower with RGB glow
|
|
812
|
+
var pcMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.30 });
|
|
610
813
|
var pcCase = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.45, 0.45), pcMat);
|
|
611
|
-
pcCase.position.set(0.
|
|
612
|
-
pcCase.castShadow = true;
|
|
814
|
+
pcCase.position.set(0.72, 0.23, 0);
|
|
613
815
|
group.add(pcCase);
|
|
614
|
-
|
|
615
|
-
var
|
|
616
|
-
|
|
617
|
-
pcGlow.position.set(0.7 + 0.115, 0.23, 0);
|
|
816
|
+
var pcGlowMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: new THREE.Color(rgbColor), emissiveIntensity: 0.4, transparent: true, opacity: 0.5 });
|
|
817
|
+
var pcGlow = new THREE.Mesh(new THREE.PlaneGeometry(0.18, 0.40), pcGlowMat);
|
|
818
|
+
pcGlow.position.set(0.72 + 0.115, 0.23, 0);
|
|
618
819
|
pcGlow.rotation.y = Math.PI / 2;
|
|
619
820
|
group.add(pcGlow);
|
|
620
|
-
|
|
621
|
-
// Keyboard
|
|
821
|
+
// Keyboard + mousepad
|
|
622
822
|
var kbMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
|
|
623
823
|
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.02, 0.12), kbMat);
|
|
624
824
|
kb.position.set(-0.1, 0.78, 0.15);
|
|
625
825
|
group.add(kb);
|
|
626
|
-
|
|
627
|
-
// Mouse + mousepad
|
|
628
826
|
var padMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.8 });
|
|
629
|
-
var pad = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.005, 0.
|
|
630
|
-
pad.position.set(0.
|
|
827
|
+
var pad = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.005, 0.20), padMat);
|
|
828
|
+
pad.position.set(0.30, 0.765, 0.15);
|
|
631
829
|
group.add(pad);
|
|
632
|
-
var
|
|
633
|
-
|
|
634
|
-
mouse.position.set(0.3, 0.78, 0.15);
|
|
830
|
+
var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.02, 0.07), kbMat);
|
|
831
|
+
mouse.position.set(0.30, 0.78, 0.15);
|
|
635
832
|
group.add(mouse);
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
buildGamingChair(group, 0, 0.7, rgbColor);
|
|
833
|
+
// Chair
|
|
834
|
+
buildGamingChair(group, 0, 0.70, rgbColor);
|
|
639
835
|
|
|
640
836
|
S.furnitureGroup.add(group);
|
|
641
837
|
S.deskMeshes.push({ group: group, screen: screen, screenMat: screenMat, index: index, x: x, z: z });
|
|
642
838
|
}
|
|
643
839
|
|
|
644
|
-
//
|
|
840
|
+
// ============================================================
|
|
841
|
+
// GAMING CHAIR
|
|
842
|
+
// ============================================================
|
|
645
843
|
function buildGamingChair(parent, cx, cz, accentColor) {
|
|
646
844
|
var chairGroup = new THREE.Group();
|
|
647
845
|
chairGroup.position.set(cx, 0, cz);
|
|
648
|
-
|
|
649
|
-
var
|
|
650
|
-
var seatMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.65 });
|
|
846
|
+
var baseMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.3 });
|
|
847
|
+
var seatMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.65 });
|
|
651
848
|
var accentMat = new THREE.MeshStandardMaterial({ color: accentColor, roughness: 0.5 });
|
|
652
|
-
|
|
653
849
|
// 5-star base
|
|
654
|
-
var
|
|
655
|
-
|
|
656
|
-
chairGroup.add(baseHub);
|
|
850
|
+
var hub = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.04, 12), baseMat);
|
|
851
|
+
hub.position.y = 0.05; chairGroup.add(hub);
|
|
657
852
|
for (var i = 0; i < 5; i++) {
|
|
658
853
|
var a = (i / 5) * Math.PI * 2;
|
|
659
|
-
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
854
|
+
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.30, 0.02, 0.03), baseMat);
|
|
660
855
|
arm.position.set(Math.cos(a) * 0.15, 0.04, Math.sin(a) * 0.15);
|
|
661
|
-
arm.rotation.y = -a;
|
|
662
|
-
chairGroup.add(arm);
|
|
663
|
-
// Wheel
|
|
856
|
+
arm.rotation.y = -a; chairGroup.add(arm);
|
|
664
857
|
var wheel = new THREE.Mesh(new THREE.SphereGeometry(0.025, 6, 4), baseMat);
|
|
665
858
|
wheel.position.set(Math.cos(a) * 0.28, 0.025, Math.sin(a) * 0.28);
|
|
666
859
|
chairGroup.add(wheel);
|
|
667
860
|
}
|
|
668
|
-
|
|
669
|
-
// Gas cylinder
|
|
670
861
|
var cyl = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.35, 8), baseMat);
|
|
671
|
-
cyl.position.y = 0.25;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// Seat
|
|
675
|
-
var seat = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.08, 0.42), seatMat);
|
|
676
|
-
seat.position.y = 0.46;
|
|
677
|
-
seat.castShadow = true;
|
|
678
|
-
chairGroup.add(seat);
|
|
679
|
-
|
|
680
|
-
// Backrest (tall, racing-style with wings)
|
|
862
|
+
cyl.position.y = 0.25; chairGroup.add(cyl);
|
|
863
|
+
var seat = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.08, 0.42), seatMat);
|
|
864
|
+
seat.position.y = 0.46; chairGroup.add(seat);
|
|
681
865
|
var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.55, 0.06), seatMat);
|
|
682
|
-
back.position.set(0, 0.78, 0.
|
|
683
|
-
|
|
684
|
-
chairGroup.add(
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
var
|
|
688
|
-
|
|
689
|
-
chairGroup.add(headrest);
|
|
690
|
-
|
|
691
|
-
// Accent stripes on backrest
|
|
692
|
-
var stripe1 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.5, 0.005), accentMat);
|
|
693
|
-
stripe1.position.set(-0.12, 0.78, 0.17);
|
|
694
|
-
chairGroup.add(stripe1);
|
|
695
|
-
var stripe2 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.5, 0.005), accentMat);
|
|
696
|
-
stripe2.position.set(0.12, 0.78, 0.17);
|
|
697
|
-
chairGroup.add(stripe2);
|
|
698
|
-
|
|
699
|
-
// Armrests
|
|
866
|
+
back.position.set(0, 0.78, 0.20); chairGroup.add(back);
|
|
867
|
+
var headrest = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.10, 0.06), seatMat);
|
|
868
|
+
headrest.position.set(0, 1.10, 0.20); chairGroup.add(headrest);
|
|
869
|
+
var s1 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.50, 0.005), accentMat);
|
|
870
|
+
s1.position.set(-0.12, 0.78, 0.17); chairGroup.add(s1);
|
|
871
|
+
var s2 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.50, 0.005), accentMat);
|
|
872
|
+
s2.position.set(0.12, 0.78, 0.17); chairGroup.add(s2);
|
|
700
873
|
[-0.22, 0.22].forEach(function(ax) {
|
|
701
|
-
var
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
armPad.position.set(ax, 0.66, 0.05);
|
|
706
|
-
chairGroup.add(armPad);
|
|
874
|
+
var ap = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.20, 0.03), baseMat);
|
|
875
|
+
ap.position.set(ax, 0.55, 0.05); chairGroup.add(ap);
|
|
876
|
+
var apd = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.02, 0.20), seatMat);
|
|
877
|
+
apd.position.set(ax, 0.66, 0.05); chairGroup.add(apd);
|
|
707
878
|
});
|
|
708
|
-
|
|
709
879
|
parent.add(chairGroup);
|
|
710
880
|
}
|
|
711
881
|
|
|
712
|
-
//
|
|
713
|
-
|
|
882
|
+
// ============================================================
|
|
883
|
+
// MODERN CHAIR (meeting rooms / mezzanine)
|
|
884
|
+
// ============================================================
|
|
885
|
+
function buildModernChair(x, y, z, rotation, matChrome) {
|
|
714
886
|
var group = new THREE.Group();
|
|
715
887
|
group.position.set(x, y, z);
|
|
716
888
|
group.rotation.y = rotation;
|
|
717
|
-
|
|
718
|
-
var
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
group.add(
|
|
722
|
-
var
|
|
723
|
-
|
|
724
|
-
group.add(back);
|
|
725
|
-
var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.4, 6), chromeMat);
|
|
726
|
-
post.position.y = 0.22;
|
|
727
|
-
group.add(post);
|
|
728
|
-
|
|
889
|
+
var seatMat = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.75 });
|
|
890
|
+
var seat = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.05, 0.40), seatMat);
|
|
891
|
+
seat.position.y = 0.45; group.add(seat);
|
|
892
|
+
var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.40, 0.04), seatMat);
|
|
893
|
+
back.position.set(0, 0.70, 0.18); group.add(back);
|
|
894
|
+
var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.40, 6), matChrome);
|
|
895
|
+
post.position.y = 0.22; group.add(post);
|
|
729
896
|
S.furnitureGroup.add(group);
|
|
730
897
|
}
|
|
731
898
|
|
|
732
|
-
//
|
|
899
|
+
// ============================================================
|
|
900
|
+
// SOFA
|
|
901
|
+
// ============================================================
|
|
733
902
|
function buildSofa(x, y, z) {
|
|
734
903
|
var group = new THREE.Group();
|
|
735
904
|
group.position.set(x, y, z);
|
|
736
|
-
|
|
737
|
-
var
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
group.add(
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
group.add(backrest);
|
|
746
|
-
// Armrests
|
|
747
|
-
[-1.4, 1.4].forEach(function(ax) {
|
|
748
|
-
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.3, 0.9), sofaMat);
|
|
749
|
-
arm.position.set(ax, 0.4, 0); arm.castShadow = true;
|
|
750
|
-
group.add(arm);
|
|
905
|
+
var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.78 });
|
|
906
|
+
var cushionMat = new THREE.MeshStandardMaterial({ color: 0x1e2030, roughness: 0.82 });
|
|
907
|
+
var base = new THREE.Mesh(new THREE.BoxGeometry(3.2, 0.35, 0.9), sofaMat);
|
|
908
|
+
base.position.y = 0.20; group.add(base);
|
|
909
|
+
var backrest = new THREE.Mesh(new THREE.BoxGeometry(3.2, 0.50, 0.20), sofaMat);
|
|
910
|
+
backrest.position.set(0, 0.56, -0.36); group.add(backrest);
|
|
911
|
+
[-1.5, 1.5].forEach(function(ax) {
|
|
912
|
+
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.30, 0.90), sofaMat);
|
|
913
|
+
arm.position.set(ax, 0.40, 0); group.add(arm);
|
|
751
914
|
});
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.1, 0.7), cushionMat);
|
|
756
|
-
cushion.position.set(cx2, 0.42, 0.05);
|
|
757
|
-
group.add(cushion);
|
|
915
|
+
[-0.9, 0, 0.9].forEach(function(cx2) {
|
|
916
|
+
var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.85, 0.10, 0.72), cushionMat);
|
|
917
|
+
cushion.position.set(cx2, 0.43, 0.05); group.add(cushion);
|
|
758
918
|
});
|
|
759
|
-
|
|
760
919
|
S.furnitureGroup.add(group);
|
|
761
920
|
}
|
|
762
921
|
|
|
763
|
-
//
|
|
764
|
-
|
|
765
|
-
|
|
922
|
+
// ============================================================
|
|
923
|
+
// MANAGER'S GLASS OFFICE
|
|
924
|
+
// ============================================================
|
|
925
|
+
function buildManagerOffice(x, z, matGlassCl, matGlassFr, matFrame, matWalnut, matLeather, matChrome) {
|
|
926
|
+
var offW = 14, offD = 10, wallH = 4.5;
|
|
766
927
|
var group = new THREE.Group();
|
|
767
928
|
group.position.set(x, 0, z);
|
|
768
929
|
|
|
769
|
-
//
|
|
770
|
-
var floorMat = new THREE.MeshStandardMaterial({ color:
|
|
771
|
-
var
|
|
772
|
-
|
|
773
|
-
group.add(floor);
|
|
930
|
+
// Raised walnut floor
|
|
931
|
+
var floorMat = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.52 });
|
|
932
|
+
var offFloor = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.07, offD), floorMat);
|
|
933
|
+
offFloor.position.y = 0.035; group.add(offFloor);
|
|
774
934
|
|
|
775
|
-
//
|
|
776
|
-
var clearGlass = new THREE.MeshStandardMaterial({ color: 0xaaccee, transparent: true, opacity: 0.2, roughness: 0.05, metalness: 0.1, side: THREE.DoubleSide });
|
|
777
|
-
var frostedGlass = new THREE.MeshStandardMaterial({ color: 0xd0d8e8, transparent: true, opacity: 0.5, roughness: 0.4, side: THREE.DoubleSide });
|
|
778
|
-
|
|
779
|
-
// Front wall (with door gap in center)
|
|
935
|
+
// Glass walls (front with door, sides, back)
|
|
780
936
|
var doorW = 1.2;
|
|
781
|
-
|
|
782
|
-
var fwLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), clearGlass);
|
|
937
|
+
var fwLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), matGlassCl);
|
|
783
938
|
fwLeft.position.set(-(offW + doorW) / 4, wallH / 2, -offD / 2);
|
|
784
939
|
group.add(fwLeft);
|
|
785
|
-
|
|
786
|
-
var fwRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), clearGlass);
|
|
940
|
+
var fwRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), matGlassCl);
|
|
787
941
|
fwRight.position.set((offW + doorW) / 4, wallH / 2, -offD / 2);
|
|
788
942
|
group.add(fwRight);
|
|
789
|
-
// Frosted
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
var doorGlass = new THREE.MeshStandardMaterial({ color: 0xbbddff, transparent: true, opacity: 0.3, roughness: 0.05, side: THREE.DoubleSide });
|
|
799
|
-
var door = new THREE.Mesh(new THREE.PlaneGeometry(doorW, wallH - 0.2), doorGlass);
|
|
943
|
+
// Frosted privacy strips
|
|
944
|
+
[-(offW + doorW) / 4, (offW + doorW) / 4].forEach(function(fx) {
|
|
945
|
+
var frost = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, 0.85), matGlassFr);
|
|
946
|
+
frost.position.set(fx, 1.25, -offD / 2 + 0.01);
|
|
947
|
+
group.add(frost);
|
|
948
|
+
});
|
|
949
|
+
// Sliding door
|
|
950
|
+
var doorGlassMat = new THREE.MeshStandardMaterial({ color: 0xbbddff, transparent: true, opacity: 0.28, roughness: 0.05, side: THREE.DoubleSide });
|
|
951
|
+
var door = new THREE.Mesh(new THREE.PlaneGeometry(doorW, wallH - 0.2), doorGlassMat);
|
|
800
952
|
door.position.set(0, wallH / 2, -offD / 2);
|
|
801
953
|
group.add(door);
|
|
802
|
-
|
|
803
|
-
var handleMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
|
|
804
|
-
var handle = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.3, 0.04), handleMat);
|
|
954
|
+
var handle = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.30, 0.04), matChrome);
|
|
805
955
|
handle.position.set(doorW / 2 - 0.1, 1.1, -offD / 2 + 0.03);
|
|
806
956
|
group.add(handle);
|
|
807
|
-
|
|
808
|
-
S.
|
|
809
|
-
S._managerDoorOpen = 0; // 0=closed, 1=open (lerp target)
|
|
957
|
+
S._managerDoor = door;
|
|
958
|
+
S._managerDoorOpen = 0;
|
|
810
959
|
S._managerDoorLerp = 0;
|
|
811
960
|
S._managerDoorClosedZ = -offD / 2;
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
group.add(
|
|
822
|
-
|
|
823
|
-
//
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
var frostRightW = new THREE.Mesh(new THREE.PlaneGeometry(offD, 0.8), frostedGlass);
|
|
829
|
-
frostRightW.position.set(offW / 2 - 0.01, 1.2, 0);
|
|
830
|
-
frostRightW.rotation.y = -Math.PI / 2;
|
|
831
|
-
group.add(frostRightW);
|
|
832
|
-
|
|
833
|
-
// Back glass wall
|
|
834
|
-
var backWall = new THREE.Mesh(new THREE.PlaneGeometry(offW, wallH), clearGlass);
|
|
835
|
-
backWall.position.set(0, wallH / 2, offD / 2);
|
|
836
|
-
backWall.rotation.y = Math.PI;
|
|
837
|
-
group.add(backWall);
|
|
838
|
-
var frostBackW = new THREE.Mesh(new THREE.PlaneGeometry(offW, 0.8), frostedGlass);
|
|
839
|
-
frostBackW.position.set(0, 1.2, offD / 2 - 0.01);
|
|
840
|
-
frostBackW.rotation.y = Math.PI;
|
|
841
|
-
group.add(frostBackW);
|
|
842
|
-
|
|
843
|
-
// --- Chrome frame structure ---
|
|
844
|
-
// Top beams (all 4 sides)
|
|
845
|
-
// Front beam
|
|
846
|
-
var beamFront = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, 0.06), frameMat);
|
|
847
|
-
beamFront.position.set(0, wallH, -offD / 2); group.add(beamFront);
|
|
848
|
-
// Back beam
|
|
849
|
-
var beamBack = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, 0.06), frameMat);
|
|
850
|
-
beamBack.position.set(0, wallH, offD / 2); group.add(beamBack);
|
|
851
|
-
// Left beam
|
|
852
|
-
var beamLeft = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, offD), frameMat);
|
|
853
|
-
beamLeft.position.set(-offW / 2, wallH, 0); group.add(beamLeft);
|
|
854
|
-
// Right beam
|
|
855
|
-
var beamRight = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, offD), frameMat);
|
|
856
|
-
beamRight.position.set(offW / 2, wallH, 0); group.add(beamRight);
|
|
857
|
-
// Vertical corner posts (all 4 corners)
|
|
858
|
-
[[-offW / 2, -offD / 2], [-offW / 2, offD / 2], [offW / 2, -offD / 2], [offW / 2, offD / 2]].forEach(function(p) {
|
|
859
|
-
var post = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), frameMat);
|
|
860
|
-
post.position.set(p[0], wallH / 2, p[1]);
|
|
861
|
-
group.add(post);
|
|
961
|
+
// Side walls
|
|
962
|
+
var leftW = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), matGlassCl);
|
|
963
|
+
leftW.position.set(-offW / 2, wallH / 2, 0); leftW.rotation.y = Math.PI / 2;
|
|
964
|
+
group.add(leftW);
|
|
965
|
+
var rightW = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), matGlassCl);
|
|
966
|
+
rightW.position.set(offW / 2, wallH / 2, 0); rightW.rotation.y = -Math.PI / 2;
|
|
967
|
+
group.add(rightW);
|
|
968
|
+
var backW = new THREE.Mesh(new THREE.PlaneGeometry(offW, wallH), matGlassCl);
|
|
969
|
+
backW.position.set(0, wallH / 2, offD / 2); backW.rotation.y = Math.PI;
|
|
970
|
+
group.add(backW);
|
|
971
|
+
|
|
972
|
+
// Chrome frame structure
|
|
973
|
+
[[offW, 0.06, 0.06, 0, wallH, -offD / 2], [offW, 0.06, 0.06, 0, wallH, offD / 2],
|
|
974
|
+
[0.06, 0.06, offD, -offW / 2, wallH, 0], [0.06, 0.06, offD, offW / 2, wallH, 0]].forEach(function(b) {
|
|
975
|
+
var beam = new THREE.Mesh(new THREE.BoxGeometry(b[0], b[1], b[2]), matFrame);
|
|
976
|
+
beam.position.set(b[3], b[4], b[5]); group.add(beam);
|
|
862
977
|
});
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
doorPost.position.set(dx, wallH / 2, -offD / 2);
|
|
867
|
-
group.add(doorPost);
|
|
978
|
+
[[-offW/2,-offD/2],[-offW/2,offD/2],[offW/2,-offD/2],[offW/2,offD/2]].forEach(function(p) {
|
|
979
|
+
var post = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), matFrame);
|
|
980
|
+
post.position.set(p[0], wallH / 2, p[1]); group.add(post);
|
|
868
981
|
});
|
|
869
|
-
// Door top beam
|
|
870
|
-
var doorTopBeam = new THREE.Mesh(new THREE.BoxGeometry(doorW + 0.12, 0.06, 0.06), frameMat);
|
|
871
|
-
doorTopBeam.position.set(0, wallH, -offD / 2);
|
|
872
|
-
group.add(doorTopBeam);
|
|
873
982
|
|
|
874
|
-
//
|
|
983
|
+
// Executive L-desk
|
|
875
984
|
var marbleTopMat = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.12, metalness: 0.05 });
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
group.add(
|
|
880
|
-
var
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
marbleTop2.position.set(1.6, 0.82, 0.7);
|
|
889
|
-
group.add(marbleTop2);
|
|
890
|
-
// Desk legs (chrome, elegant)
|
|
891
|
-
[[-1.2, 1], [-1.2, 2], [1.2, 2], [1.2, 1], [2.1, 0.4], [2.1, 1]].forEach(function(p) {
|
|
892
|
-
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.78, 8), chromeMat);
|
|
893
|
-
leg.position.set(p[0], 0.39, p[1]);
|
|
894
|
-
group.add(leg);
|
|
985
|
+
var deskMain = new THREE.Mesh(new THREE.BoxGeometry(3.2, 0.07, 1.3), matWalnut);
|
|
986
|
+
deskMain.position.set(0, 0.79, 1.8); group.add(deskMain);
|
|
987
|
+
var marbMain = new THREE.Mesh(new THREE.BoxGeometry(3.22, 0.016, 1.32), marbleTopMat);
|
|
988
|
+
marbMain.position.set(0, 0.835, 1.8); group.add(marbMain);
|
|
989
|
+
var deskWing = new THREE.Mesh(new THREE.BoxGeometry(1.3, 0.07, 1.0), matWalnut);
|
|
990
|
+
deskWing.position.set(1.8, 0.79, 0.9); group.add(deskWing);
|
|
991
|
+
var marbWing = new THREE.Mesh(new THREE.BoxGeometry(1.32, 0.016, 1.02), marbleTopMat);
|
|
992
|
+
marbWing.position.set(1.8, 0.835, 0.9); group.add(marbWing);
|
|
993
|
+
// Desk legs
|
|
994
|
+
[[-1.45,1.1],[-1.45,2.5],[1.45,2.5],[1.45,1.1],[2.35,0.5],[2.35,1.3]].forEach(function(p) {
|
|
995
|
+
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.79, 8), matChrome);
|
|
996
|
+
leg.position.set(p[0], 0.395, p[1]); group.add(leg);
|
|
895
997
|
});
|
|
896
|
-
// Cable management panel (dark, under desk back)
|
|
897
|
-
var cablePanelMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.5 });
|
|
898
|
-
var cablePanel = new THREE.Mesh(new THREE.BoxGeometry(2.6, 0.5, 0.04), cablePanelMat);
|
|
899
|
-
cablePanel.position.set(0, 0.5, 0.9);
|
|
900
|
-
group.add(cablePanel);
|
|
901
998
|
|
|
902
|
-
//
|
|
999
|
+
// 47" monitor
|
|
903
1000
|
var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
904
|
-
// 47" = ~1.2m wide x 0.67m tall in world units (16:9 aspect ratio, scaled to desk)
|
|
905
1001
|
var bigMon = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.67, 0.025), monMat);
|
|
906
|
-
bigMon.position.set(0, 1.
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
var
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
group.add(stand);
|
|
917
|
-
// Stand base (BEHIND screen)
|
|
918
|
-
var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.02, 0.2), chromeMat);
|
|
919
|
-
standBase.position.set(0, 0.78, 1.07);
|
|
920
|
-
group.add(standBase);
|
|
921
|
-
|
|
922
|
-
// --- Keyboard + mouse ---
|
|
923
|
-
var kbMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
|
|
924
|
-
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.015, 0.12), kbMat);
|
|
925
|
-
kb.position.set(-0.15, 0.835, 1.8);
|
|
926
|
-
group.add(kb);
|
|
927
|
-
var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.015, 0.06), kbMat);
|
|
928
|
-
mouse.position.set(0.35, 0.835, 1.8);
|
|
929
|
-
group.add(mouse);
|
|
930
|
-
|
|
931
|
-
// --- Premium leather executive chair ---
|
|
1002
|
+
bigMon.position.set(0, 1.22, 1.25); group.add(bigMon);
|
|
1003
|
+
var scrMat2 = new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.3, roughness: 0.1 });
|
|
1004
|
+
var bigScr = new THREE.Mesh(new THREE.PlaneGeometry(1.14, 0.61), scrMat2);
|
|
1005
|
+
bigScr.position.set(0, 1.22, 1.263); group.add(bigScr);
|
|
1006
|
+
var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.29, 8), matChrome);
|
|
1007
|
+
monStand.position.set(0, 0.94, 1.27); group.add(monStand);
|
|
1008
|
+
var monBase = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.02, 0.20), matChrome);
|
|
1009
|
+
monBase.position.set(0, 0.795, 1.27); group.add(monBase);
|
|
1010
|
+
|
|
1011
|
+
// Executive chair (leather)
|
|
932
1012
|
var chairG = new THREE.Group();
|
|
933
|
-
chairG.position.set(0, 0, 2.
|
|
934
|
-
|
|
935
|
-
var baseMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.4 });
|
|
1013
|
+
chairG.position.set(0, 0, 2.7);
|
|
1014
|
+
var baseMat2 = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.4 });
|
|
936
1015
|
for (var ci = 0; ci < 5; ci++) {
|
|
937
1016
|
var ca = (ci / 5) * Math.PI * 2;
|
|
938
|
-
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.02, 0.035),
|
|
1017
|
+
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.02, 0.035), baseMat2);
|
|
939
1018
|
arm.position.set(Math.cos(ca) * 0.16, 0.04, Math.sin(ca) * 0.16);
|
|
940
|
-
arm.rotation.y = -ca;
|
|
941
|
-
chairG.add(arm);
|
|
1019
|
+
arm.rotation.y = -ca; chairG.add(arm);
|
|
942
1020
|
}
|
|
943
|
-
var cylM = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.
|
|
1021
|
+
var cylM = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.40, 8), matChrome);
|
|
944
1022
|
cylM.position.y = 0.26; chairG.add(cylM);
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
var
|
|
950
|
-
|
|
951
|
-
// Headrest
|
|
952
|
-
var headM = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.12, 0.08), leatherMat);
|
|
953
|
-
headM.position.set(0, 1.3, 0.24); chairG.add(headM);
|
|
954
|
-
// Armrests
|
|
1023
|
+
var seatM = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.10, 0.50), matLeather);
|
|
1024
|
+
seatM.position.y = 0.50; chairG.add(seatM);
|
|
1025
|
+
var backM = new THREE.Mesh(new THREE.BoxGeometry(0.48, 0.70, 0.08), matLeather);
|
|
1026
|
+
backM.position.set(0, 0.92, 0.25); chairG.add(backM);
|
|
1027
|
+
var headM = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.12, 0.08), matLeather);
|
|
1028
|
+
headM.position.set(0, 1.32, 0.25); chairG.add(headM);
|
|
955
1029
|
[-0.27, 0.27].forEach(function(ax) {
|
|
956
|
-
var
|
|
957
|
-
|
|
958
|
-
var
|
|
959
|
-
|
|
1030
|
+
var ap = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.04), baseMat2);
|
|
1031
|
+
ap.position.set(ax, 0.60, 0.08); chairG.add(ap);
|
|
1032
|
+
var apd = new THREE.Mesh(new THREE.BoxGeometry(0.09, 0.03, 0.25), matLeather);
|
|
1033
|
+
apd.position.set(ax, 0.73, 0.08); chairG.add(apd);
|
|
960
1034
|
});
|
|
961
1035
|
group.add(chairG);
|
|
962
1036
|
|
|
963
|
-
//
|
|
964
|
-
var
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
var
|
|
971
|
-
|
|
972
|
-
shelfGroup.add(shelf);
|
|
973
|
-
});
|
|
974
|
-
// Books
|
|
975
|
-
var bookColors = [0xc0392b, 0x2980b9, 0x8e44ad, 0xd4a24e, 0x1abc9c, 0x2c3e50];
|
|
976
|
-
[0.09, 0.59, 1.14, 1.69].forEach(function(sy, si) {
|
|
977
|
-
var startZ = -0.55;
|
|
978
|
-
for (var bi2 = 0; bi2 < 5; bi2++) {
|
|
979
|
-
var bh = 0.32 + Math.sin(si + bi2) * 0.08;
|
|
980
|
-
var bw = 0.04 + Math.sin(si * 3 + bi2) * 0.015;
|
|
981
|
-
var bMat = new THREE.MeshStandardMaterial({ color: bookColors[(si * 3 + bi2) % bookColors.length], roughness: 0.8 });
|
|
982
|
-
var book = new THREE.Mesh(new THREE.BoxGeometry(0.18, bh, bw), bMat);
|
|
983
|
-
book.position.set(0.16, sy + bh / 2, startZ);
|
|
984
|
-
shelfGroup.add(book);
|
|
985
|
-
startZ += bw + 0.02;
|
|
986
|
-
}
|
|
987
|
-
});
|
|
988
|
-
group.add(shelfGroup);
|
|
989
|
-
|
|
990
|
-
// --- Small sofa + coffee table ---
|
|
991
|
-
var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a1a0a, roughness: 0.7 });
|
|
992
|
-
var sofaBase = new THREE.Mesh(new THREE.BoxGeometry(2, 0.3, 0.7), sofaMat);
|
|
993
|
-
sofaBase.position.set(-2.5, 0.18, -0.5); sofaBase.castShadow = true;
|
|
994
|
-
group.add(sofaBase);
|
|
995
|
-
var sofaBack = new THREE.Mesh(new THREE.BoxGeometry(2, 0.4, 0.15), sofaMat);
|
|
996
|
-
sofaBack.position.set(-2.5, 0.45, -0.85); sofaBack.castShadow = true;
|
|
997
|
-
group.add(sofaBack);
|
|
998
|
-
// Cushions
|
|
999
|
-
var cushionMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.8 });
|
|
1000
|
-
[-3.1, -2.5, -1.9].forEach(function(cx) {
|
|
1001
|
-
var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.06, 0.55), cushionMat);
|
|
1002
|
-
cushion.position.set(cx, 0.36, -0.5);
|
|
1003
|
-
group.add(cushion);
|
|
1004
|
-
});
|
|
1005
|
-
// Coffee table (glass top, chrome legs)
|
|
1006
|
-
var coffeeGlassMat = new THREE.MeshStandardMaterial({ color: 0xccddee, transparent: true, opacity: 0.35, roughness: 0.05 });
|
|
1007
|
-
var coffeeTop = new THREE.Mesh(new THREE.BoxGeometry(1, 0.03, 0.5), coffeeGlassMat);
|
|
1008
|
-
coffeeTop.position.set(-2.5, 0.45, 0.2);
|
|
1009
|
-
group.add(coffeeTop);
|
|
1010
|
-
[-0.4, 0.4].forEach(function(lx) {
|
|
1011
|
-
[-0.18, 0.18].forEach(function(lz) {
|
|
1012
|
-
var cLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.42, 6), chromeMat);
|
|
1013
|
-
cLeg.position.set(-2.5 + lx, 0.22, 0.2 + lz);
|
|
1014
|
-
group.add(cLeg);
|
|
1015
|
-
});
|
|
1037
|
+
// Gold accent art frame on back wall
|
|
1038
|
+
var goldMat2 = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.30, metalness: 0.70 });
|
|
1039
|
+
var artW = 2.0, artH = 1.3;
|
|
1040
|
+
[[artW + 0.08, 0.06, 0.06, 0, 3.4, offD/2-0.1],
|
|
1041
|
+
[artW + 0.08, 0.06, 0.06, 0, 2.1, offD/2-0.1],
|
|
1042
|
+
[0.06, (artH+0.12), 0.06, -(artW/2+0.04), 2.75, offD/2-0.1],
|
|
1043
|
+
[0.06, (artH+0.12), 0.06, (artW/2+0.04), 2.75, offD/2-0.1]].forEach(function(b) {
|
|
1044
|
+
var bar = new THREE.Mesh(new THREE.BoxGeometry(b[0], b[1], b[2]), goldMat2);
|
|
1045
|
+
bar.position.set(b[3], b[4], b[5]); group.add(bar);
|
|
1016
1046
|
});
|
|
1017
|
-
|
|
1018
|
-
// --- Luxury plant ---
|
|
1019
|
-
var planterMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3a, roughness: 0.5 });
|
|
1020
|
-
var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.2, 0.5, 12), planterMat);
|
|
1021
|
-
planter.position.set(3, 0.25, -2.5); planter.castShadow = true;
|
|
1022
|
-
group.add(planter);
|
|
1023
|
-
var leafMat = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.8 });
|
|
1024
|
-
for (var pi = 0; pi < 6; pi++) {
|
|
1025
|
-
var pa = (pi / 6) * Math.PI * 2;
|
|
1026
|
-
var leaf = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 6), leafMat);
|
|
1027
|
-
leaf.position.set(3 + Math.cos(pa) * 0.15, 0.6, -2.5 + Math.sin(pa) * 0.15);
|
|
1028
|
-
group.add(leaf);
|
|
1029
|
-
}
|
|
1030
|
-
var topL = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), leafMat);
|
|
1031
|
-
topL.position.set(3, 0.75, -2.5); group.add(topL);
|
|
1032
|
-
|
|
1033
|
-
// --- Gold accent artwork frame on back wall ---
|
|
1034
|
-
var goldMat = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.3, metalness: 0.7 });
|
|
1035
|
-
// Frame
|
|
1036
|
-
var artFrameTop = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.06, 0.06), goldMat);
|
|
1037
|
-
artFrameTop.position.set(0, 3.2, offD / 2 - 0.08); group.add(artFrameTop);
|
|
1038
|
-
var artFrameBot = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.06, 0.06), goldMat);
|
|
1039
|
-
artFrameBot.position.set(0, 2.0, offD / 2 - 0.08); group.add(artFrameBot);
|
|
1040
|
-
var artFrameL = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.26, 0.06), goldMat);
|
|
1041
|
-
artFrameL.position.set(-0.8, 2.6, offD / 2 - 0.08); group.add(artFrameL);
|
|
1042
|
-
var artFrameR = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.26, 0.06), goldMat);
|
|
1043
|
-
artFrameR.position.set(0.8, 2.6, offD / 2 - 0.08); group.add(artFrameR);
|
|
1044
|
-
// Canvas inside frame (dark elegant)
|
|
1045
1047
|
var artMat = new THREE.MeshStandardMaterial({ color: 0x1a2a3a, roughness: 0.8 });
|
|
1046
|
-
var art = new THREE.Mesh(new THREE.PlaneGeometry(
|
|
1047
|
-
art.position.set(0, 2.
|
|
1048
|
-
art.rotation.y = Math.PI;
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
// --- Pendant light (premium, gold accent) ---
|
|
1060
|
-
var pendWire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 2), new THREE.MeshStandardMaterial({ color: 0x333333 }));
|
|
1061
|
-
pendWire.position.set(0, wallH - 1, 1.5);
|
|
1062
|
-
group.add(pendWire);
|
|
1063
|
-
var pendShade = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.3, 0.2, 12, 1, true),
|
|
1048
|
+
var art = new THREE.Mesh(new THREE.PlaneGeometry(artW, artH), artMat);
|
|
1049
|
+
art.position.set(0, 2.75, offD / 2 - 0.08);
|
|
1050
|
+
art.rotation.y = Math.PI; group.add(art);
|
|
1051
|
+
|
|
1052
|
+
// Warm lighting
|
|
1053
|
+
var wl = new THREE.PointLight(0xffeedd, 0.40, 10);
|
|
1054
|
+
wl.castShadow = false; wl.position.set(0, 3.8, 1.8); group.add(wl);
|
|
1055
|
+
// Pendant
|
|
1056
|
+
var pendWire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 2.2, 4),
|
|
1057
|
+
new THREE.MeshStandardMaterial({ color: 0x2a2a2a }));
|
|
1058
|
+
pendWire.position.set(0, wallH - 1.1, 1.8); group.add(pendWire);
|
|
1059
|
+
var pendShade = new THREE.Mesh(new THREE.CylinderGeometry(0.16, 0.32, 0.22, 12, 1, true),
|
|
1064
1060
|
new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.4, metalness: 0.3, side: THREE.DoubleSide }));
|
|
1065
|
-
pendShade.position.set(0, wallH - 2.
|
|
1066
|
-
|
|
1067
|
-
var pendRim = new THREE.Mesh(new THREE.TorusGeometry(0.3, 0.01, 6, 24), goldMat);
|
|
1068
|
-
pendRim.position.set(0, wallH - 2.2, 1.5);
|
|
1061
|
+
pendShade.position.set(0, wallH - 2.3, 1.8); group.add(pendShade);
|
|
1062
|
+
var pendRim = new THREE.Mesh(new THREE.TorusGeometry(0.32, 0.012, 6, 24), goldMat2);
|
|
1069
1063
|
pendRim.rotation.x = Math.PI / 2;
|
|
1070
|
-
group.add(pendRim);
|
|
1064
|
+
pendRim.position.set(0, wallH - 2.42, 1.8); group.add(pendRim);
|
|
1071
1065
|
|
|
1072
|
-
//
|
|
1066
|
+
// MANAGER sign
|
|
1073
1067
|
var signDiv = document.createElement('div');
|
|
1074
1068
|
signDiv.textContent = 'MANAGER';
|
|
1075
1069
|
signDiv.style.cssText = 'color:#d4af37;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:3px;text-shadow:0 0 6px rgba(212,175,55,0.4);';
|
|
1076
1070
|
var sign = new CSS2DObject(signDiv);
|
|
1077
|
-
sign.position.set(0, wallH + 0.
|
|
1071
|
+
sign.position.set(0, wallH + 0.35, -offD / 2);
|
|
1078
1072
|
group.add(sign);
|
|
1079
1073
|
|
|
1080
1074
|
S.furnitureGroup.add(group);
|
|
1081
1075
|
S._managerOfficeGroup = group;
|
|
1082
1076
|
S._managerOfficePos = { x: x, z: z };
|
|
1083
1077
|
|
|
1084
|
-
// Register manager desk in deskMeshes
|
|
1085
|
-
// bigScr is the 47" monitor screen — used for click detection + iframe overlay
|
|
1078
|
+
// Register manager desk in deskMeshes
|
|
1086
1079
|
var mgrDeskIdx = CAMPUS_DESKS.length - 1;
|
|
1087
|
-
var mgrScreenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: 0x333333, emissiveIntensity: 0.1, roughness: 0.2 });
|
|
1088
|
-
S.deskMeshes[mgrDeskIdx] = { group: group, screen: bigScr, screenMat: mgrScreenMat, index: mgrDeskIdx, x: x, z: z + 1.
|
|
1080
|
+
var mgrScreenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: new THREE.Color(0x333333), emissiveIntensity: 0.1, roughness: 0.2 });
|
|
1081
|
+
S.deskMeshes[mgrDeskIdx] = { group: group, screen: bigScr, screenMat: mgrScreenMat, index: mgrDeskIdx, x: x, z: z + 1.9 };
|
|
1089
1082
|
}
|
|
1090
1083
|
|
|
1091
|
-
//
|
|
1092
|
-
|
|
1093
|
-
|
|
1084
|
+
// ============================================================
|
|
1085
|
+
// DESIGNER STUDIO (left wing)
|
|
1086
|
+
// ============================================================
|
|
1087
|
+
function buildDesignerStudio(x, z, matWalnut, matChrome) {
|
|
1094
1088
|
var boardMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.5 });
|
|
1095
|
-
var board = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
1096
|
-
board.position.set(x -
|
|
1089
|
+
var board = new THREE.Mesh(new THREE.BoxGeometry(0.10, 2.2, 5), boardMat);
|
|
1090
|
+
board.position.set(x - 8, 1.6, z);
|
|
1097
1091
|
board.castShadow = true;
|
|
1098
1092
|
S.furnitureGroup.add(board);
|
|
1099
|
-
// Colorful sticky notes on board
|
|
1100
1093
|
var noteColors = [0xfbbf24, 0xf87171, 0x34d399, 0x60a5fa, 0xa78bfa, 0xfb923c];
|
|
1101
|
-
for (var ni = 0; ni <
|
|
1094
|
+
for (var ni = 0; ni < 16; ni++) {
|
|
1102
1095
|
var noteMat = new THREE.MeshStandardMaterial({ color: noteColors[ni % noteColors.length], roughness: 0.9 });
|
|
1103
|
-
var note = new THREE.Mesh(new THREE.PlaneGeometry(0.
|
|
1104
|
-
note.position.set(x -
|
|
1096
|
+
var note = new THREE.Mesh(new THREE.PlaneGeometry(0.30, 0.30), noteMat);
|
|
1097
|
+
note.position.set(x - 7.94, 0.8 + Math.floor(ni / 4) * 0.55, z - 2.0 + (ni % 4) * 1.0);
|
|
1105
1098
|
note.rotation.y = Math.PI / 2;
|
|
1106
1099
|
S.furnitureGroup.add(note);
|
|
1107
1100
|
}
|
|
1108
|
-
|
|
1109
1101
|
// Standing desk
|
|
1110
|
-
var standDesk = new THREE.Mesh(new THREE.BoxGeometry(
|
|
1111
|
-
standDesk.position.set(x -
|
|
1102
|
+
var standDesk = new THREE.Mesh(new THREE.BoxGeometry(2.0, 0.06, 0.9), matWalnut);
|
|
1103
|
+
standDesk.position.set(x - 3, 1.05, z + 4);
|
|
1112
1104
|
standDesk.castShadow = true;
|
|
1113
1105
|
S.furnitureGroup.add(standDesk);
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
standLeg.position.set(x - 2 + lx, 0.55, z + 3);
|
|
1106
|
+
[-0.8, 0.8].forEach(function(lx) {
|
|
1107
|
+
var standLeg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.05, 0.06), matChrome);
|
|
1108
|
+
standLeg.position.set(x - 3 + lx, 0.525, z + 4);
|
|
1118
1109
|
S.furnitureGroup.add(standLeg);
|
|
1119
1110
|
});
|
|
1120
|
-
|
|
1121
|
-
// "DESIGN LAB" sign
|
|
1122
1111
|
var signDiv = document.createElement('div');
|
|
1123
1112
|
signDiv.textContent = 'DESIGN LAB';
|
|
1124
1113
|
signDiv.style.cssText = 'color:#a855f7;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
|
|
1125
1114
|
var sign = new CSS2DObject(signDiv);
|
|
1126
|
-
sign.position.set(x,
|
|
1115
|
+
sign.position.set(x, 4.0, z);
|
|
1127
1116
|
S.furnitureGroup.add(sign);
|
|
1128
1117
|
}
|
|
1129
1118
|
|
|
1130
|
-
//
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
barTop.
|
|
1119
|
+
// ============================================================
|
|
1120
|
+
// BAR & CAFÉ
|
|
1121
|
+
// ============================================================
|
|
1122
|
+
function buildBar(x, z, matWalnut, matChrome, matNeonBlue, matNeonPurple) {
|
|
1123
|
+
var barTop = new THREE.Mesh(new THREE.BoxGeometry(8, 0.09, 1.4), matWalnut);
|
|
1124
|
+
barTop.position.set(x, 1.12, z); barTop.castShadow = true;
|
|
1135
1125
|
S.furnitureGroup.add(barTop);
|
|
1136
|
-
var barFront = new THREE.Mesh(new THREE.BoxGeometry(
|
|
1137
|
-
new THREE.MeshStandardMaterial({ color:
|
|
1138
|
-
barFront.position.set(x, 0.
|
|
1139
|
-
barFront.castShadow = true;
|
|
1126
|
+
var barFront = new THREE.Mesh(new THREE.BoxGeometry(8, 1.12, 0.12),
|
|
1127
|
+
new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.38 }));
|
|
1128
|
+
barFront.position.set(x, 0.56, z + 0.65); barFront.castShadow = true;
|
|
1140
1129
|
S.furnitureGroup.add(barFront);
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
var barLed = new THREE.Mesh(new THREE.BoxGeometry(5.8, 0.02, 0.02), neonBlueMat);
|
|
1144
|
-
barLed.position.set(x, 1.02, z + 0.58);
|
|
1130
|
+
var barLed = new THREE.Mesh(new THREE.BoxGeometry(7.8, 0.022, 0.022), matNeonBlue);
|
|
1131
|
+
barLed.position.set(x, 1.03, z + 0.68);
|
|
1145
1132
|
S.furnitureGroup.add(barLed);
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
for (var si = 0; si < 5; si++) {
|
|
1149
|
-
var sx = x - 2 + si * 1;
|
|
1133
|
+
// Stools
|
|
1134
|
+
for (var si = 0; si < 6; si++) {
|
|
1150
1135
|
var stoolGroup = new THREE.Group();
|
|
1151
|
-
stoolGroup.position.set(
|
|
1136
|
+
stoolGroup.position.set(x - 3 + si * 1.1, 0, z + 1.4);
|
|
1152
1137
|
var stoolSeat = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.18, 0.06, 12),
|
|
1153
|
-
new THREE.MeshStandardMaterial({ color:
|
|
1154
|
-
stoolSeat.position.y = 0.
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
stoolGroup.add(
|
|
1159
|
-
var stoolBase = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.2, 0.04, 12), chromeMat);
|
|
1160
|
-
stoolBase.position.y = 0.04;
|
|
1161
|
-
stoolGroup.add(stoolBase);
|
|
1138
|
+
new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.6 }));
|
|
1139
|
+
stoolSeat.position.y = 0.76; stoolGroup.add(stoolSeat);
|
|
1140
|
+
var stoolPost = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.72, 8), matChrome);
|
|
1141
|
+
stoolPost.position.y = 0.38; stoolGroup.add(stoolPost);
|
|
1142
|
+
var stoolBase = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.20, 0.04, 12), matChrome);
|
|
1143
|
+
stoolBase.position.y = 0.04; stoolGroup.add(stoolBase);
|
|
1162
1144
|
S.furnitureGroup.add(stoolGroup);
|
|
1163
1145
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
var shelfMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.6 });
|
|
1146
|
+
// Shelves + bottles
|
|
1147
|
+
var shelfMat = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.58 });
|
|
1167
1148
|
[1.5, 2.2, 2.9].forEach(function(sy) {
|
|
1168
|
-
var shelf = new THREE.Mesh(new THREE.BoxGeometry(
|
|
1169
|
-
shelf.position.set(x, sy, z - 0
|
|
1149
|
+
var shelf = new THREE.Mesh(new THREE.BoxGeometry(7.5, 0.04, 0.32), shelfMat);
|
|
1150
|
+
shelf.position.set(x, sy, z - 1.0);
|
|
1170
1151
|
S.furnitureGroup.add(shelf);
|
|
1171
1152
|
});
|
|
1172
|
-
|
|
1173
|
-
// Bottles on shelves
|
|
1174
1153
|
var bottleColors = [0x2d8a4e, 0x8B4513, 0xd4af37, 0xcc3333, 0x1a5276, 0xf0f0f0];
|
|
1175
|
-
for (var bi = 0; bi <
|
|
1176
|
-
var bx = x -
|
|
1177
|
-
var by = 1.55 + Math.floor(bi /
|
|
1154
|
+
for (var bi = 0; bi < 18; bi++) {
|
|
1155
|
+
var bx = x - 3.5 + (bi % 6) * 1.2;
|
|
1156
|
+
var by = 1.55 + Math.floor(bi / 6) * 0.7;
|
|
1178
1157
|
var bottleMat = new THREE.MeshStandardMaterial({ color: bottleColors[bi % bottleColors.length], roughness: 0.3, metalness: 0.1 });
|
|
1179
|
-
var bottle = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.
|
|
1180
|
-
bottle.position.set(bx, by + 0.
|
|
1158
|
+
var bottle = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.26, 8), bottleMat);
|
|
1159
|
+
bottle.position.set(bx, by + 0.13, z - 0.94);
|
|
1181
1160
|
S.furnitureGroup.add(bottle);
|
|
1182
|
-
var
|
|
1183
|
-
|
|
1184
|
-
S.furnitureGroup.add(
|
|
1161
|
+
var neck = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.035, 0.10, 6), bottleMat);
|
|
1162
|
+
neck.position.set(bx, by + 0.31, z - 0.94);
|
|
1163
|
+
S.furnitureGroup.add(neck);
|
|
1185
1164
|
}
|
|
1186
|
-
|
|
1187
1165
|
// Coffee machine
|
|
1188
|
-
var
|
|
1189
|
-
|
|
1190
|
-
coffee.position.set(x +
|
|
1191
|
-
coffee.castShadow = true;
|
|
1166
|
+
var coffee = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.50, 0.30),
|
|
1167
|
+
new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.28, metalness: 0.22 }));
|
|
1168
|
+
coffee.position.set(x + 3.5, 1.38, z - 0.12);
|
|
1192
1169
|
S.furnitureGroup.add(coffee);
|
|
1193
|
-
|
|
1194
|
-
// "BAR" neon sign
|
|
1170
|
+
// Sign
|
|
1195
1171
|
var signDiv = document.createElement('div');
|
|
1196
1172
|
signDiv.textContent = 'BAR & CAFÉ';
|
|
1197
1173
|
signDiv.style.cssText = 'color:#a855f7;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;text-shadow:0 0 8px #a855f7;';
|
|
1198
1174
|
var sign = new CSS2DObject(signDiv);
|
|
1199
|
-
sign.position.set(x,
|
|
1175
|
+
sign.position.set(x, 4.0, z - 1.2);
|
|
1200
1176
|
S.furnitureGroup.add(sign);
|
|
1201
1177
|
}
|
|
1202
1178
|
|
|
1203
|
-
//
|
|
1179
|
+
// ============================================================
|
|
1180
|
+
// JUKEBOX (Wurlitzer 1015 style)
|
|
1181
|
+
// ============================================================
|
|
1204
1182
|
function buildJukebox(x, z) {
|
|
1205
1183
|
var group = new THREE.Group();
|
|
1206
1184
|
group.position.set(x, 0, z);
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
var
|
|
1211
|
-
body.position.y = 0.75; body.castShadow = true;
|
|
1212
|
-
group.add(body);
|
|
1213
|
-
|
|
1214
|
-
// Chrome trim top arch
|
|
1215
|
-
var chromeMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
|
|
1185
|
+
var bodyMat = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.50, metalness: 0.05 });
|
|
1186
|
+
var body = new THREE.Mesh(new THREE.BoxGeometry(0.90, 1.50, 0.50), bodyMat);
|
|
1187
|
+
body.position.y = 0.75; body.castShadow = true; group.add(body);
|
|
1188
|
+
var chromeMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.10, metalness: 0.80 });
|
|
1216
1189
|
var topArch = new THREE.Mesh(new THREE.CylinderGeometry(0.45, 0.45, 0.06, 16, 1, false, 0, Math.PI), chromeMat);
|
|
1217
|
-
topArch.position.set(0, 1.53, 0);
|
|
1218
|
-
topArch.rotation.z = Math.PI;
|
|
1219
|
-
topArch.rotation.y = Math.PI / 2;
|
|
1190
|
+
topArch.position.set(0, 1.53, 0); topArch.rotation.z = Math.PI; topArch.rotation.y = Math.PI / 2;
|
|
1220
1191
|
group.add(topArch);
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
var glassPanel = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.4, 0.08), glassMat);
|
|
1225
|
-
glassPanel.position.set(0, 1.35, 0.22);
|
|
1226
|
-
group.add(glassPanel);
|
|
1227
|
-
|
|
1228
|
-
// Neon glow strips (sides) — animated via S._jukeboxNeon
|
|
1192
|
+
var glassMat2 = new THREE.MeshStandardMaterial({ color: 0xaaddff, transparent: true, opacity: 0.40, roughness: 0.05 });
|
|
1193
|
+
var glassPanel = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.40, 0.08), glassMat2);
|
|
1194
|
+
glassPanel.position.set(0, 1.35, 0.22); group.add(glassPanel);
|
|
1229
1195
|
var neonColors = [0xff4488, 0xff8844, 0xffdd44, 0x44ff88, 0x4488ff, 0xaa44ff];
|
|
1230
|
-
var neonMat = new THREE.MeshStandardMaterial({ color: 0xff4488, emissive: 0xff4488, emissiveIntensity: 0.
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
group.add(
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
var
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
// Bubble tube columns (sides)
|
|
1245
|
-
var bubbleMat = new THREE.MeshStandardMaterial({ color: 0x66ccff, transparent: true, opacity: 0.5, emissive: 0x66ccff, emissiveIntensity: 0.3 });
|
|
1246
|
-
var bubbleL = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.3, 8), bubbleMat);
|
|
1247
|
-
bubbleL.position.set(-0.38, 0.75, 0.18);
|
|
1248
|
-
group.add(bubbleL);
|
|
1249
|
-
var bubbleR = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.3, 8), bubbleMat);
|
|
1250
|
-
bubbleR.position.set(0.38, 0.75, 0.18);
|
|
1251
|
-
group.add(bubbleR);
|
|
1252
|
-
|
|
1253
|
-
// Chrome base
|
|
1254
|
-
var baseMat = chromeMat;
|
|
1255
|
-
var base = new THREE.Mesh(new THREE.BoxGeometry(1, 0.08, 0.55), baseMat);
|
|
1256
|
-
base.position.y = 0.04;
|
|
1257
|
-
group.add(base);
|
|
1258
|
-
|
|
1259
|
-
// Chrome grille (speaker area — lower section)
|
|
1196
|
+
var neonMat = new THREE.MeshStandardMaterial({ color: 0xff4488, emissive: new THREE.Color(0xff4488), emissiveIntensity: 0.80, roughness: 0.20 });
|
|
1197
|
+
var neonL = new THREE.Mesh(new THREE.BoxGeometry(0.04, 1.20, 0.04), neonMat);
|
|
1198
|
+
neonL.position.set(-0.42, 0.75, 0.23); group.add(neonL);
|
|
1199
|
+
var neonR = new THREE.Mesh(new THREE.BoxGeometry(0.04, 1.20, 0.04), neonMat);
|
|
1200
|
+
neonR.position.set(0.42, 0.75, 0.23); group.add(neonR);
|
|
1201
|
+
var neonTop = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.04, 0.04), neonMat);
|
|
1202
|
+
neonTop.position.set(0, 1.50, 0.23); group.add(neonTop);
|
|
1203
|
+
var bubbleMat = new THREE.MeshStandardMaterial({ color: 0x66ccff, transparent: true, opacity: 0.50, emissive: new THREE.Color(0x66ccff), emissiveIntensity: 0.30 });
|
|
1204
|
+
var bubbleL = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.30, 8), bubbleMat);
|
|
1205
|
+
bubbleL.position.set(-0.38, 0.75, 0.18); group.add(bubbleL);
|
|
1206
|
+
var bubbleR = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.30, 8), bubbleMat);
|
|
1207
|
+
bubbleR.position.set(0.38, 0.75, 0.18); group.add(bubbleR);
|
|
1208
|
+
var base = new THREE.Mesh(new THREE.BoxGeometry(1.00, 0.08, 0.55), chromeMat);
|
|
1209
|
+
base.position.y = 0.04; group.add(base);
|
|
1260
1210
|
for (var gi = 0; gi < 6; gi++) {
|
|
1261
|
-
var grille = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
1262
|
-
grille.position.set(0, 0.15 + gi * 0.06, 0.26);
|
|
1263
|
-
group.add(grille);
|
|
1211
|
+
var grille = new THREE.Mesh(new THREE.BoxGeometry(0.60, 0.01, 0.01), chromeMat);
|
|
1212
|
+
grille.position.set(0, 0.15 + gi * 0.06, 0.26); group.add(grille);
|
|
1264
1213
|
}
|
|
1265
|
-
|
|
1266
|
-
// Record selector buttons (front panel)
|
|
1267
1214
|
var buttonMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.3, metalness: 0.5 });
|
|
1268
|
-
for (var
|
|
1215
|
+
for (var bi2 = 0; bi2 < 3; bi2++) {
|
|
1269
1216
|
var btn = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.02, 8), buttonMat);
|
|
1270
|
-
btn.position.set(-0.15 +
|
|
1271
|
-
btn.rotation.x = Math.PI / 2;
|
|
1217
|
+
btn.position.set(-0.15 + bi2 * 0.15, 0.85, 0.26); btn.rotation.x = Math.PI / 2;
|
|
1272
1218
|
group.add(btn);
|
|
1273
1219
|
}
|
|
1274
|
-
|
|
1275
|
-
// "NOW PLAYING" CSS2D label (hidden by default, shown when music plays)
|
|
1276
1220
|
var labelDiv = document.createElement('div');
|
|
1277
1221
|
labelDiv.className = 'jukebox-label';
|
|
1278
1222
|
labelDiv.style.cssText = 'color:#ff4488;font-size:8px;font-weight:bold;font-family:monospace;text-shadow:0 0 6px #ff4488;text-align:center;pointer-events:none;opacity:0.9;';
|
|
1279
1223
|
labelDiv.innerHTML = '<div style="color:#ffdd44;font-size:10px">JUKEBOX</div><div style="font-size:7px;color:#aaa">Press E to play</div>';
|
|
1280
1224
|
var label = new CSS2DObject(labelDiv);
|
|
1281
|
-
label.position.set(0, 1.
|
|
1225
|
+
label.position.set(0, 1.80, 0);
|
|
1282
1226
|
group.add(label);
|
|
1283
|
-
|
|
1284
|
-
// Store references for interaction + animation
|
|
1285
|
-
S._jukebox = {
|
|
1286
|
-
group: group,
|
|
1287
|
-
neonMat: neonMat,
|
|
1288
|
-
neonColors: neonColors,
|
|
1289
|
-
neonIndex: 0,
|
|
1290
|
-
label: labelDiv,
|
|
1291
|
-
pos: { x: x, z: z },
|
|
1292
|
-
playing: false
|
|
1293
|
-
};
|
|
1294
|
-
|
|
1227
|
+
S._jukebox = { group: group, neonMat: neonMat, neonColors: neonColors, neonIndex: 0, label: labelDiv, pos: { x: x, z: z }, playing: false };
|
|
1295
1228
|
S.furnitureGroup.add(group);
|
|
1296
1229
|
}
|
|
1297
1230
|
|
|
1298
|
-
//
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1231
|
+
// ============================================================
|
|
1232
|
+
// RECREATION CENTER
|
|
1233
|
+
// ============================================================
|
|
1234
|
+
function buildRecCenter(x, z, matWalnut, matChrome, matFabric) {
|
|
1235
|
+
var recCarpet = new THREE.Mesh(new THREE.PlaneGeometry(16, 14), matFabric);
|
|
1302
1236
|
recCarpet.rotation.x = -Math.PI / 2;
|
|
1303
1237
|
recCarpet.position.set(x, 0.01, z);
|
|
1304
1238
|
recCarpet.receiveShadow = true;
|
|
1305
1239
|
S.furnitureGroup.add(recCarpet);
|
|
1306
|
-
|
|
1307
1240
|
// Pool table
|
|
1308
1241
|
var ptGroup = new THREE.Group();
|
|
1309
|
-
ptGroup.position.set(x -
|
|
1310
|
-
var ptTop = new THREE.Mesh(new THREE.BoxGeometry(2.4, 0.
|
|
1242
|
+
ptGroup.position.set(x - 3, 0, z - 1);
|
|
1243
|
+
var ptTop = new THREE.Mesh(new THREE.BoxGeometry(2.4, 0.10, 1.3),
|
|
1311
1244
|
new THREE.MeshStandardMaterial({ color: 0x006633, roughness: 0.9 }));
|
|
1312
|
-
ptTop.position.y = 0.85; ptTop
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
[[-1.1, -0.55], [-1.1, 0.55], [1.1, -0.55], [1.1, 0.55]].forEach(function(p) {
|
|
1319
|
-
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.7, 8), walnutMat);
|
|
1320
|
-
leg.position.set(p[0], 0.35, p[1]);
|
|
1321
|
-
ptGroup.add(leg);
|
|
1245
|
+
ptTop.position.y = 0.85; ptGroup.add(ptTop);
|
|
1246
|
+
var ptFrame = new THREE.Mesh(new THREE.BoxGeometry(2.55, 0.15, 1.45), matWalnut);
|
|
1247
|
+
ptFrame.position.y = 0.78; ptGroup.add(ptFrame);
|
|
1248
|
+
[[-1.1,-0.55],[-1.1,0.55],[1.1,-0.55],[1.1,0.55]].forEach(function(p) {
|
|
1249
|
+
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.70, 8), matWalnut);
|
|
1250
|
+
leg.position.set(p[0], 0.35, p[1]); ptGroup.add(leg);
|
|
1322
1251
|
});
|
|
1323
1252
|
S.furnitureGroup.add(ptGroup);
|
|
1324
|
-
|
|
1325
1253
|
// Foosball table
|
|
1326
1254
|
var fbGroup = new THREE.Group();
|
|
1327
|
-
fbGroup.position.set(x +
|
|
1328
|
-
var fbBody = new THREE.Mesh(new THREE.BoxGeometry(1.
|
|
1255
|
+
fbGroup.position.set(x + 3, 0, z - 1);
|
|
1256
|
+
var fbBody = new THREE.Mesh(new THREE.BoxGeometry(1.40, 0.20, 0.75),
|
|
1329
1257
|
new THREE.MeshStandardMaterial({ color: 0x2a1a0a, roughness: 0.6 }));
|
|
1330
|
-
fbBody.position.y = 0.85; fbBody
|
|
1331
|
-
|
|
1332
|
-
var fbField = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.02, 0.6),
|
|
1258
|
+
fbBody.position.y = 0.85; fbGroup.add(fbBody);
|
|
1259
|
+
var fbField = new THREE.Mesh(new THREE.BoxGeometry(1.20, 0.02, 0.60),
|
|
1333
1260
|
new THREE.MeshStandardMaterial({ color: 0x006633, roughness: 0.8 }));
|
|
1334
|
-
fbField.position.y = 0.96;
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.75, 6), chromeMat);
|
|
1339
|
-
leg.position.set(p[0], 0.38, p[1]);
|
|
1340
|
-
fbGroup.add(leg);
|
|
1261
|
+
fbField.position.y = 0.96; fbGroup.add(fbField);
|
|
1262
|
+
[[-0.6,-0.3],[-0.6,0.3],[0.6,-0.3],[0.6,0.3]].forEach(function(p) {
|
|
1263
|
+
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.75, 6), matChrome);
|
|
1264
|
+
leg.position.set(p[0], 0.38, p[1]); fbGroup.add(leg);
|
|
1341
1265
|
});
|
|
1342
|
-
// Rods
|
|
1343
1266
|
[-0.3, 0, 0.3].forEach(function(rz) {
|
|
1344
|
-
var rod = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.
|
|
1345
|
-
rod.position.set(0, 0.98, rz);
|
|
1346
|
-
rod.rotation.z = Math.PI / 2;
|
|
1347
|
-
fbGroup.add(rod);
|
|
1267
|
+
var rod = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.90, 6), matChrome);
|
|
1268
|
+
rod.position.set(0, 0.98, rz); rod.rotation.z = Math.PI / 2; fbGroup.add(rod);
|
|
1348
1269
|
});
|
|
1349
1270
|
S.furnitureGroup.add(fbGroup);
|
|
1350
|
-
|
|
1351
1271
|
// Beanbags
|
|
1352
1272
|
var bbColors = [0xe53e3e, 0x3b82f6, 0x22c55e, 0xa855f7];
|
|
1353
|
-
[{ x: -1, z:
|
|
1273
|
+
[{ x: -1, z: 4 }, { x: 2, z: 4.5 }, { x: 4, z: 3.5 }, { x: -3, z: 4.5 }].forEach(function(bp, bi) {
|
|
1354
1274
|
var bbMat = new THREE.MeshStandardMaterial({ color: bbColors[bi], roughness: 0.9 });
|
|
1355
1275
|
var bot = new THREE.Mesh(new THREE.SphereGeometry(0.45, 16, 12), bbMat);
|
|
1356
1276
|
bot.position.set(x + bp.x, 0.22, z + bp.z);
|
|
1357
1277
|
bot.scale.set(1, 0.5, 1);
|
|
1358
|
-
bot.castShadow = true;
|
|
1359
1278
|
S.furnitureGroup.add(bot);
|
|
1360
1279
|
});
|
|
1361
|
-
|
|
1362
|
-
// Static decorative TV (smaller, no dashboard — main TV is at reception)
|
|
1280
|
+
// Decorative TV
|
|
1363
1281
|
var tvMat2 = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
1364
1282
|
var tvBody = new THREE.Mesh(new THREE.BoxGeometry(2.5, 1.5, 0.08), tvMat2);
|
|
1365
|
-
tvBody.position.set(x, 2.3, z -
|
|
1366
|
-
tvBody.castShadow = true;
|
|
1283
|
+
tvBody.position.set(x, 2.3, z - 6.5);
|
|
1367
1284
|
S.furnitureGroup.add(tvBody);
|
|
1368
1285
|
var tvScr = new THREE.Mesh(new THREE.PlaneGeometry(2.3, 1.3),
|
|
1369
|
-
new THREE.MeshStandardMaterial({ color: 0x0a1520, emissive: 0x22c55e, emissiveIntensity: 0.15, roughness: 0.1 }));
|
|
1370
|
-
tvScr.position.set(x, 2.3, z -
|
|
1286
|
+
new THREE.MeshStandardMaterial({ color: 0x0a1520, emissive: new THREE.Color(0x22c55e), emissiveIntensity: 0.15, roughness: 0.1 }));
|
|
1287
|
+
tvScr.position.set(x, 2.3, z - 6.42);
|
|
1371
1288
|
S.furnitureGroup.add(tvScr);
|
|
1372
|
-
|
|
1373
|
-
// "REC ZONE" sign
|
|
1374
1289
|
var signDiv = document.createElement('div');
|
|
1375
1290
|
signDiv.textContent = 'REC ZONE';
|
|
1376
1291
|
signDiv.style.cssText = 'color:#22c55e;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;text-shadow:0 0 8px #22c55e;';
|
|
@@ -1379,84 +1294,73 @@ function buildRecCenter(x, z, walnutMat, chromeMat, carpetMat) {
|
|
|
1379
1294
|
S.furnitureGroup.add(sign);
|
|
1380
1295
|
}
|
|
1381
1296
|
|
|
1382
|
-
//
|
|
1383
|
-
|
|
1384
|
-
|
|
1297
|
+
// ============================================================
|
|
1298
|
+
// GYM
|
|
1299
|
+
// ============================================================
|
|
1300
|
+
function buildGym(x, z, matChrome, matConcrete) {
|
|
1385
1301
|
var rubberMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.95 });
|
|
1386
|
-
var gymFloor = new THREE.Mesh(new THREE.PlaneGeometry(
|
|
1302
|
+
var gymFloor = new THREE.Mesh(new THREE.PlaneGeometry(12, 12), rubberMat);
|
|
1387
1303
|
gymFloor.rotation.x = -Math.PI / 2;
|
|
1388
1304
|
gymFloor.position.set(x, 0.01, z);
|
|
1389
1305
|
gymFloor.receiveShadow = true;
|
|
1390
1306
|
S.furnitureGroup.add(gymFloor);
|
|
1391
|
-
|
|
1392
1307
|
// Treadmill
|
|
1393
1308
|
var tmGroup = new THREE.Group();
|
|
1394
|
-
tmGroup.position.set(x -
|
|
1395
|
-
var tmBase = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
1396
|
-
tmBase.position.y = 0.
|
|
1397
|
-
|
|
1398
|
-
var tmBelt = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.02, 1.3),
|
|
1309
|
+
tmGroup.position.set(x - 2, 0, z - 2);
|
|
1310
|
+
var tmBase = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.15, 1.60), matConcrete);
|
|
1311
|
+
tmBase.position.y = 0.10; tmGroup.add(tmBase);
|
|
1312
|
+
var tmBelt = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.02, 1.30),
|
|
1399
1313
|
new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.8 }));
|
|
1400
|
-
tmBelt.position.y = 0.19;
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
var handle = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 1, 6), chromeMat);
|
|
1405
|
-
handle.position.set(hx, 0.7, -0.6);
|
|
1406
|
-
tmGroup.add(handle);
|
|
1314
|
+
tmBelt.position.y = 0.19; tmGroup.add(tmBelt);
|
|
1315
|
+
[-0.30, 0.30].forEach(function(hx) {
|
|
1316
|
+
var handle = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 1.00, 6), matChrome);
|
|
1317
|
+
handle.position.set(hx, 0.70, -0.60); tmGroup.add(handle);
|
|
1407
1318
|
});
|
|
1408
|
-
var console2 = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
1409
|
-
console2.position.set(0, 1.
|
|
1410
|
-
tmGroup.add(console2);
|
|
1319
|
+
var console2 = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.25, 0.08), matConcrete);
|
|
1320
|
+
console2.position.set(0, 1.10, -0.65); tmGroup.add(console2);
|
|
1411
1321
|
S.furnitureGroup.add(tmGroup);
|
|
1412
|
-
|
|
1413
1322
|
// Dumbbell rack
|
|
1414
|
-
var rackBase = new THREE.Mesh(new THREE.BoxGeometry(2, 0.
|
|
1415
|
-
rackBase.position.set(x +
|
|
1416
|
-
rackBase.castShadow = true;
|
|
1323
|
+
var rackBase = new THREE.Mesh(new THREE.BoxGeometry(2.80, 0.80, 0.42), matChrome);
|
|
1324
|
+
rackBase.position.set(x + 2, 0.40, z - 4);
|
|
1417
1325
|
S.furnitureGroup.add(rackBase);
|
|
1418
|
-
|
|
1419
|
-
for (var di = 0; di < 5; di++) {
|
|
1326
|
+
for (var di = 0; di < 6; di++) {
|
|
1420
1327
|
var dbMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5, metalness: 0.4 });
|
|
1421
|
-
var db = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.
|
|
1422
|
-
db.position.set(x + 0.
|
|
1328
|
+
var db = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.30, 8), dbMat);
|
|
1329
|
+
db.position.set(x + 0.8 + di * 0.40, 0.90, z - 4);
|
|
1423
1330
|
db.rotation.z = Math.PI / 2;
|
|
1424
1331
|
S.furnitureGroup.add(db);
|
|
1425
1332
|
}
|
|
1426
|
-
|
|
1427
|
-
// Yoga mat area
|
|
1333
|
+
// Yoga mats
|
|
1428
1334
|
var yogaMat = new THREE.MeshStandardMaterial({ color: 0x7c3aed, roughness: 0.9 });
|
|
1429
|
-
var
|
|
1430
|
-
|
|
1431
|
-
S.furnitureGroup.add(
|
|
1432
|
-
var mat2 = new THREE.Mesh(new THREE.BoxGeometry(0.
|
|
1335
|
+
var mat1 = new THREE.Mesh(new THREE.BoxGeometry(0.80, 0.02, 1.80), yogaMat);
|
|
1336
|
+
mat1.position.set(x + 3, 0.02, z + 2);
|
|
1337
|
+
S.furnitureGroup.add(mat1);
|
|
1338
|
+
var mat2 = new THREE.Mesh(new THREE.BoxGeometry(0.80, 0.02, 1.80),
|
|
1433
1339
|
new THREE.MeshStandardMaterial({ color: 0x06b6d4, roughness: 0.9 }));
|
|
1434
|
-
mat2.position.set(x +
|
|
1340
|
+
mat2.position.set(x + 4.2, 0.02, z + 2);
|
|
1435
1341
|
S.furnitureGroup.add(mat2);
|
|
1436
|
-
|
|
1437
|
-
// "FITNESS" sign
|
|
1438
1342
|
var signDiv = document.createElement('div');
|
|
1439
1343
|
signDiv.textContent = 'FITNESS';
|
|
1440
1344
|
signDiv.style.cssText = 'color:#ef4444;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;text-shadow:0 0 8px #ef4444;';
|
|
1441
1345
|
var sign = new CSS2DObject(signDiv);
|
|
1442
|
-
sign.position.set(x, 4, z);
|
|
1346
|
+
sign.position.set(x, 4.0, z);
|
|
1443
1347
|
S.furnitureGroup.add(sign);
|
|
1444
1348
|
}
|
|
1445
1349
|
|
|
1446
|
-
//
|
|
1350
|
+
// ============================================================
|
|
1351
|
+
// PLANTS
|
|
1352
|
+
// ============================================================
|
|
1447
1353
|
function buildCampusPlants() {
|
|
1448
1354
|
var plantPositions = [
|
|
1449
|
-
[-
|
|
1450
|
-
[-
|
|
1451
|
-
[
|
|
1452
|
-
[-
|
|
1355
|
+
[-38, 20], [38, 20], [-38, 0], [38, 0],
|
|
1356
|
+
[-12, 22], [12, 22], [-20, 10], [20, 10],
|
|
1357
|
+
[-20, -10], [20, -10], [0, 24],
|
|
1358
|
+
[-8, -20], [8, -20], [-28, -10], [28, -10],
|
|
1453
1359
|
];
|
|
1454
1360
|
plantPositions.forEach(function(pos) {
|
|
1455
1361
|
buildLuxuryPlant(pos[0], pos[1]);
|
|
1456
1362
|
});
|
|
1457
|
-
|
|
1458
|
-
// Indoor trees (taller, premium)
|
|
1459
|
-
[[-18, 0], [18, 0], [0, -7]].forEach(function(pos) {
|
|
1363
|
+
[[-30, 5], [30, 5], [0, -12], [-15, 20], [15, 20]].forEach(function(pos) {
|
|
1460
1364
|
buildIndoorTree(pos[0], pos[1]);
|
|
1461
1365
|
});
|
|
1462
1366
|
}
|
|
@@ -1464,62 +1368,52 @@ function buildCampusPlants() {
|
|
|
1464
1368
|
function buildLuxuryPlant(x, z) {
|
|
1465
1369
|
var group = new THREE.Group();
|
|
1466
1370
|
group.position.set(x, 0, z);
|
|
1467
|
-
|
|
1468
|
-
var
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
var leafMat = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.8 });
|
|
1474
|
-
for (var i = 0; i < 6; i++) {
|
|
1475
|
-
var a = (i / 6) * Math.PI * 2;
|
|
1371
|
+
var planterMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.62 });
|
|
1372
|
+
var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.26, 0.20, 0.52, 12), planterMat);
|
|
1373
|
+
planter.position.y = 0.26; group.add(planter);
|
|
1374
|
+
var leafMat = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.80 });
|
|
1375
|
+
for (var i = 0; i < 7; i++) {
|
|
1376
|
+
var a = (i / 7) * Math.PI * 2;
|
|
1476
1377
|
var leaf = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 6), leafMat);
|
|
1477
|
-
leaf.position.set(Math.cos(a) * 0.
|
|
1478
|
-
leaf.castShadow = true;
|
|
1378
|
+
leaf.position.set(Math.cos(a) * 0.16, 0.62, Math.sin(a) * 0.16);
|
|
1479
1379
|
group.add(leaf);
|
|
1480
1380
|
}
|
|
1481
1381
|
var topLeaf = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), leafMat);
|
|
1482
|
-
topLeaf.position.y = 0.
|
|
1483
|
-
group.add(topLeaf);
|
|
1382
|
+
topLeaf.position.y = 0.77; group.add(topLeaf);
|
|
1484
1383
|
S.furnitureGroup.add(group);
|
|
1485
1384
|
}
|
|
1486
1385
|
|
|
1487
1386
|
function buildIndoorTree(x, z) {
|
|
1488
1387
|
var group = new THREE.Group();
|
|
1489
1388
|
group.position.set(x, 0, z);
|
|
1490
|
-
|
|
1491
|
-
var
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
var
|
|
1497
|
-
var
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
var canopyMat = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.85 });
|
|
1502
|
-
var canopyMat2 = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.85 });
|
|
1503
|
-
[{ y: 2.8, r: 0.6 }, { y: 3.2, r: 0.5 }, { y: 3.5, r: 0.35 }].forEach(function(c, ci) {
|
|
1504
|
-
var canopy = new THREE.Mesh(new THREE.SphereGeometry(c.r, 12, 10), ci % 2 === 0 ? canopyMat : canopyMat2);
|
|
1505
|
-
canopy.position.y = c.y;
|
|
1506
|
-
canopy.castShadow = true;
|
|
1507
|
-
group.add(canopy);
|
|
1389
|
+
var planterMat = new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.52 });
|
|
1390
|
+
var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.42, 0.36, 0.64, 12), planterMat);
|
|
1391
|
+
planter.position.y = 0.32; group.add(planter);
|
|
1392
|
+
var trunkMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e, roughness: 0.80 });
|
|
1393
|
+
var trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.11, 2.6, 8), trunkMat);
|
|
1394
|
+
trunk.position.y = 1.92; group.add(trunk);
|
|
1395
|
+
var cm1 = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.85 });
|
|
1396
|
+
var cm2 = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.85 });
|
|
1397
|
+
[{ y: 2.9, r: 0.65 }, { y: 3.30, r: 0.52 }, { y: 3.60, r: 0.36 }].forEach(function(c, ci) {
|
|
1398
|
+
var canopy = new THREE.Mesh(new THREE.SphereGeometry(c.r, 12, 10), ci % 2 === 0 ? cm1 : cm2);
|
|
1399
|
+
canopy.position.y = c.y; group.add(canopy);
|
|
1508
1400
|
});
|
|
1509
1401
|
S.furnitureGroup.add(group);
|
|
1510
1402
|
}
|
|
1511
1403
|
|
|
1512
|
-
//
|
|
1404
|
+
// ============================================================
|
|
1405
|
+
// PENDANT LIGHTS
|
|
1406
|
+
// ============================================================
|
|
1513
1407
|
function buildPendantLights() {
|
|
1514
|
-
var
|
|
1515
|
-
[0,
|
|
1516
|
-
[-
|
|
1517
|
-
[
|
|
1518
|
-
[-
|
|
1519
|
-
[
|
|
1520
|
-
[
|
|
1408
|
+
var positions = [
|
|
1409
|
+
[0, 6], [0, 10], [0, 14], [0, 18],
|
|
1410
|
+
[-8, 6], [-8, 14], [8, 6], [8, 14],
|
|
1411
|
+
[-28, 0], [28, 0],
|
|
1412
|
+
[-28, -18], [0, -18], [28, -18],
|
|
1413
|
+
[30, 10],
|
|
1414
|
+
[0, 24], [-10, 24], [10, 24],
|
|
1521
1415
|
];
|
|
1522
|
-
|
|
1416
|
+
positions.forEach(function(pos) {
|
|
1523
1417
|
buildPendantLight(pos[0], pos[1]);
|
|
1524
1418
|
});
|
|
1525
1419
|
}
|
|
@@ -1527,52 +1421,51 @@ function buildPendantLights() {
|
|
|
1527
1421
|
function buildPendantLight(x, z) {
|
|
1528
1422
|
var group = new THREE.Group();
|
|
1529
1423
|
group.position.set(x, 0, z);
|
|
1530
|
-
|
|
1531
|
-
var
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
var
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
group.add(shade);
|
|
1540
|
-
// Warm light
|
|
1541
|
-
var light = new THREE.PointLight(0xffeedd, 0.25, 6);
|
|
1542
|
-
light.position.set(0, WALL_H - 1.7, 0);
|
|
1543
|
-
light.castShadow = false; // performance
|
|
1424
|
+
var wireMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.5 });
|
|
1425
|
+
var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 1.60, 4), wireMat);
|
|
1426
|
+
wire.position.y = WALL_H - 0.80; group.add(wire);
|
|
1427
|
+
var shadeMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5, metalness: 0.3, side: THREE.DoubleSide });
|
|
1428
|
+
var shade = new THREE.Mesh(new THREE.ConeGeometry(0.26, 0.22, 12, 1, true), shadeMat);
|
|
1429
|
+
shade.position.y = WALL_H - 1.70; group.add(shade);
|
|
1430
|
+
var light = new THREE.PointLight(0xffeedd, 0.22, 6);
|
|
1431
|
+
light.position.y = WALL_H - 1.85;
|
|
1432
|
+
light.castShadow = false;
|
|
1544
1433
|
group.add(light);
|
|
1545
1434
|
S.furnitureGroup.add(group);
|
|
1546
1435
|
}
|
|
1547
1436
|
|
|
1548
|
-
//
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1437
|
+
// ============================================================
|
|
1438
|
+
// GLASS PARTITIONS
|
|
1439
|
+
// ============================================================
|
|
1440
|
+
function buildGlassPartitions(matGlass, matFrame) {
|
|
1441
|
+
// Between workspace and zone areas
|
|
1442
|
+
var p1 = new THREE.Mesh(new THREE.PlaneGeometry(26, 2.8), matGlass);
|
|
1443
|
+
p1.position.set(0, 1.40, 3.0);
|
|
1444
|
+
S.furnitureGroup.add(p1);
|
|
1445
|
+
var f1 = new THREE.Mesh(new THREE.BoxGeometry(26, 0.04, 0.04), matFrame);
|
|
1446
|
+
f1.position.set(0, 2.82, 3.0);
|
|
1447
|
+
S.furnitureGroup.add(f1);
|
|
1448
|
+
// Between manager office and workspace
|
|
1449
|
+
var p2 = new THREE.Mesh(new THREE.PlaneGeometry(14, 2.8), matGlass);
|
|
1450
|
+
p2.position.set(22, 1.40, 8);
|
|
1451
|
+
p2.rotation.y = Math.PI / 2;
|
|
1452
|
+
S.furnitureGroup.add(p2);
|
|
1453
|
+
var f2 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.04, 14), matFrame);
|
|
1454
|
+
f2.position.set(22, 2.82, 8);
|
|
1455
|
+
S.furnitureGroup.add(f2);
|
|
1563
1456
|
}
|
|
1564
1457
|
|
|
1565
|
-
//
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1458
|
+
// ============================================================
|
|
1459
|
+
// NEON SIGNS
|
|
1460
|
+
// ============================================================
|
|
1461
|
+
function buildNeonSign(text, x, y, z, matNeon) {
|
|
1462
|
+
var glowBar = new THREE.Mesh(new THREE.BoxGeometry(text.length * 0.44, 0.40, 0.04), matNeon);
|
|
1569
1463
|
glowBar.position.set(x, y, z);
|
|
1570
1464
|
S.furnitureGroup.add(glowBar);
|
|
1571
|
-
|
|
1572
|
-
var color = '#' + neonMat.color.getHexString();
|
|
1465
|
+
var color = '#' + matNeon.color.getHexString();
|
|
1573
1466
|
var div = document.createElement('div');
|
|
1574
1467
|
div.textContent = text;
|
|
1575
|
-
div.style.cssText = 'color:' + color + ';font-size:
|
|
1468
|
+
div.style.cssText = 'color:' + color + ';font-size:13px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:4px;text-shadow:0 0 14px ' + color + ',0 0 28px ' + color + ';';
|
|
1576
1469
|
var label = new CSS2DObject(div);
|
|
1577
1470
|
label.position.set(x, y, z + 0.05);
|
|
1578
1471
|
S.furnitureGroup.add(label);
|