let-them-talk 3.6.0 → 3.6.2
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 +22 -0
- package/cli.js +1 -1
- package/dashboard.html +10 -0
- package/office/agents.js +89 -15
- package/office/animation.js +34 -0
- package/office/campus-env.js +1461 -0
- package/office/environment.js +10 -0
- package/office/index.js +52 -16
- package/office/navigation.js +263 -0
- package/office/scene.js +3 -3
- package/office/spectator-camera.js +1 -1
- package/office/state.js +1 -1
- package/package.json +1 -1
- package/server.js +63 -10
|
@@ -0,0 +1,1461 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
|
3
|
+
import { S } from './state.js';
|
|
4
|
+
|
|
5
|
+
// ============================================================
|
|
6
|
+
// TECH CAMPUS — Premium 2-floor environment
|
|
7
|
+
// Inspired by Google/Apple HQ: marble, glass, wood, RGB gaming
|
|
8
|
+
// ============================================================
|
|
9
|
+
|
|
10
|
+
var CAMPUS_W = 50;
|
|
11
|
+
var CAMPUS_D = 35;
|
|
12
|
+
var WALL_H = 6;
|
|
13
|
+
var MEZZ_H = 3.2;
|
|
14
|
+
var MEZZ_DEPTH = 12; // how far mezzanine extends from back wall
|
|
15
|
+
|
|
16
|
+
// Campus desk positions (gaming desk layout)
|
|
17
|
+
var CAMPUS_DESKS = [
|
|
18
|
+
// Main coder zone (center, 3 rows of 4)
|
|
19
|
+
{ x: -4.5, z: 2 }, { x: -1.5, z: 2 }, { x: 1.5, z: 2 }, { x: 4.5, z: 2 },
|
|
20
|
+
{ x: -4.5, z: -1 }, { x: -1.5, z: -1 }, { x: 1.5, z: -1 }, { x: 4.5, z: -1 },
|
|
21
|
+
{ x: -4.5, z: -4 }, { x: -1.5, z: -4 }, { x: 1.5, z: -4 }, { x: 4.5, z: -4 },
|
|
22
|
+
// Designer wing (left, 2 rows of 2)
|
|
23
|
+
{ x: -14, z: 1 }, { x: -11, z: 1 },
|
|
24
|
+
{ x: -14, z: -2 }, { x: -11, z: -2 },
|
|
25
|
+
// Manager's private office desk (last position — assigned to first "Manager" role agent)
|
|
26
|
+
// Office at (12,5), desk at relative (0,1.5)=world(12,6.5), chair at relative (0,2.4)=world(12,7.4)
|
|
27
|
+
// Agent sits at deskPos.z + 0.7, so set z to 6.7 → agent at 7.4 (chair pos)
|
|
28
|
+
{ x: 12, z: 6.7 },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export function getCampusDeskPositions() {
|
|
32
|
+
return CAMPUS_DESKS;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function buildCampusEnvironment() {
|
|
36
|
+
S.deskMeshes = [];
|
|
37
|
+
|
|
38
|
+
// Materials palette
|
|
39
|
+
var marbleMat = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.15, metalness: 0.05 });
|
|
40
|
+
var marbleDarkMat = new THREE.MeshStandardMaterial({ color: 0xd4cfc7, roughness: 0.2 });
|
|
41
|
+
var walnutMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e, roughness: 0.6 });
|
|
42
|
+
var walnutLightMat = new THREE.MeshStandardMaterial({ color: 0x8B5E3C, roughness: 0.55 });
|
|
43
|
+
var chromeMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
|
|
44
|
+
var glassMat = new THREE.MeshStandardMaterial({ color: 0xaaccee, transparent: true, opacity: 0.25, roughness: 0.05, metalness: 0.1 });
|
|
45
|
+
var glassFrameMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.2, metalness: 0.6 });
|
|
46
|
+
var darkMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.4 });
|
|
47
|
+
var carpetMat = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.95 });
|
|
48
|
+
var neonBlueMat = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: 0x58a6ff, emissiveIntensity: 0.6, roughness: 0.2 });
|
|
49
|
+
var neonPurpleMat = new THREE.MeshStandardMaterial({ color: 0xa855f7, emissive: 0xa855f7, emissiveIntensity: 0.5, roughness: 0.2 });
|
|
50
|
+
var neonGreenMat = new THREE.MeshStandardMaterial({ color: 0x22c55e, emissive: 0x22c55e, emissiveIntensity: 0.5, roughness: 0.2 });
|
|
51
|
+
var concreteMat = new THREE.MeshStandardMaterial({ color: 0x3a3d45, roughness: 0.85 });
|
|
52
|
+
var leatherBlackMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.7 });
|
|
53
|
+
var leatherBrownMat = new THREE.MeshStandardMaterial({ color: 0x6b3e26, roughness: 0.65 });
|
|
54
|
+
var goldMat = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.3, metalness: 0.7 });
|
|
55
|
+
|
|
56
|
+
// ========== FLOOR ==========
|
|
57
|
+
buildCampusFloor(marbleMat, marbleDarkMat, carpetMat);
|
|
58
|
+
|
|
59
|
+
// ========== WALLS & WINDOWS ==========
|
|
60
|
+
buildCampusWalls(concreteMat, glassMat, glassFrameMat);
|
|
61
|
+
|
|
62
|
+
// ========== CEILING & SKYLIGHTS ==========
|
|
63
|
+
buildCampusCeiling(concreteMat, glassMat);
|
|
64
|
+
|
|
65
|
+
// ========== MEZZANINE (2nd floor) ==========
|
|
66
|
+
buildMezzanine(concreteMat, chromeMat, glassMat, walnutMat);
|
|
67
|
+
|
|
68
|
+
// ========== STAIRCASE ==========
|
|
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;
|
|
76
|
+
CAMPUS_DESKS.forEach(function(pos, i) {
|
|
77
|
+
if (i === managerDeskIdx) return; // manager office has its own built-in desk
|
|
78
|
+
buildGamingDesk(pos.x, pos.z, i);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ========== MANAGER'S OFFICE (glass room, front right) ==========
|
|
82
|
+
buildManagerOffice(12, 5, glassMat, glassFrameMat, walnutMat, leatherBrownMat, chromeMat);
|
|
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
|
+
|
|
90
|
+
// ========== RECREATION CENTER (back center) ==========
|
|
91
|
+
buildRecCenter(0, -12, walnutMat, chromeMat, carpetMat);
|
|
92
|
+
|
|
93
|
+
// ========== GYM (back right) ==========
|
|
94
|
+
buildGym(14, -12, chromeMat, darkMat);
|
|
95
|
+
|
|
96
|
+
// ========== PLANTS & GREENERY ==========
|
|
97
|
+
buildCampusPlants();
|
|
98
|
+
|
|
99
|
+
// ========== PENDANT LIGHTS ==========
|
|
100
|
+
buildPendantLights();
|
|
101
|
+
|
|
102
|
+
// ========== GLASS PARTITIONS ==========
|
|
103
|
+
buildGlassPartitions(glassMat, glassFrameMat);
|
|
104
|
+
|
|
105
|
+
// ========== NEON SIGNS ==========
|
|
106
|
+
buildNeonSign('INNOVATE', -7, 4.5, -CAMPUS_D / 2 + 0.2, neonBlueMat);
|
|
107
|
+
buildNeonSign('CREATE', 7, 4.5, -CAMPUS_D / 2 + 0.2, neonPurpleMat);
|
|
108
|
+
buildNeonSign('BUILD', 0, MEZZ_H + 2, -CAMPUS_D / 2 + MEZZ_DEPTH + 0.2, neonGreenMat);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ==================== FLOOR ====================
|
|
112
|
+
function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
|
|
113
|
+
// High-quality procedural dark marble tile floor
|
|
114
|
+
var size = 1024;
|
|
115
|
+
var cvs = document.createElement('canvas');
|
|
116
|
+
cvs.width = size; cvs.height = size;
|
|
117
|
+
var ctx = cvs.getContext('2d');
|
|
118
|
+
var tiles = 12;
|
|
119
|
+
var ts = size / tiles;
|
|
120
|
+
|
|
121
|
+
// Simple 2D noise function for marble veining
|
|
122
|
+
function noise(x, y) {
|
|
123
|
+
var n = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453;
|
|
124
|
+
return n - Math.floor(n);
|
|
125
|
+
}
|
|
126
|
+
function smoothNoise(x, y) {
|
|
127
|
+
var ix = Math.floor(x), iy = Math.floor(y);
|
|
128
|
+
var fx = x - ix, fy = y - iy;
|
|
129
|
+
var a = noise(ix, iy), b = noise(ix + 1, iy);
|
|
130
|
+
var c = noise(ix, iy + 1), d = noise(ix + 1, iy + 1);
|
|
131
|
+
var u = fx * fx * (3 - 2 * fx), v = fy * fy * (3 - 2 * fy);
|
|
132
|
+
return a + (b - a) * u + (c - a) * v + (a - b - c + d) * u * v;
|
|
133
|
+
}
|
|
134
|
+
function fbm(x, y) {
|
|
135
|
+
var val = 0, amp = 0.5;
|
|
136
|
+
for (var o = 0; o < 5; o++) {
|
|
137
|
+
val += smoothNoise(x, y) * amp;
|
|
138
|
+
x *= 2.1; y *= 2.1; amp *= 0.48;
|
|
139
|
+
}
|
|
140
|
+
return val;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (var ti = 0; ti < tiles; ti++) {
|
|
144
|
+
for (var tj = 0; tj < tiles; tj++) {
|
|
145
|
+
var tx = ti * ts, ty = tj * ts;
|
|
146
|
+
|
|
147
|
+
// Alternating dark/darker marble tiles
|
|
148
|
+
var isDark = (ti + tj) % 2 === 0;
|
|
149
|
+
var baseR = isDark ? 28 : 22;
|
|
150
|
+
var baseG = isDark ? 30 : 24;
|
|
151
|
+
var baseB = isDark ? 38 : 30;
|
|
152
|
+
|
|
153
|
+
// Fill base tile color
|
|
154
|
+
ctx.fillStyle = 'rgb(' + baseR + ',' + baseG + ',' + baseB + ')';
|
|
155
|
+
ctx.fillRect(tx, ty, ts, ts);
|
|
156
|
+
|
|
157
|
+
// Marble veining (per-pixel noise)
|
|
158
|
+
var imgData = ctx.getImageData(tx, ty, ts, ts);
|
|
159
|
+
var data = imgData.data;
|
|
160
|
+
for (var py = 0; py < ts; py++) {
|
|
161
|
+
for (var px = 0; px < ts; px++) {
|
|
162
|
+
var wx = (ti * ts + px) / size * 6;
|
|
163
|
+
var wy = (tj * ts + py) / size * 6;
|
|
164
|
+
|
|
165
|
+
// Marble pattern: distorted sine wave + noise
|
|
166
|
+
var vein = Math.sin(wx * 3 + fbm(wx * 2, wy * 2) * 4) * 0.5 + 0.5;
|
|
167
|
+
var vein2 = Math.sin(wy * 2.5 + fbm(wx * 1.5 + 5, wy * 1.5 + 3) * 3.5) * 0.5 + 0.5;
|
|
168
|
+
var combined = vein * 0.6 + vein2 * 0.4;
|
|
169
|
+
|
|
170
|
+
// Color the veins — gold/white streaks on dark base
|
|
171
|
+
var veinStrength = Math.pow(combined, 3) * 0.35;
|
|
172
|
+
var nv = smoothNoise(wx * 4, wy * 4) * 0.08;
|
|
173
|
+
|
|
174
|
+
var idx = (py * ts + px) * 4;
|
|
175
|
+
// Base dark marble + gold/gray veins
|
|
176
|
+
data[idx] = Math.min(255, baseR + veinStrength * 120 + nv * 40); // R
|
|
177
|
+
data[idx + 1] = Math.min(255, baseG + veinStrength * 100 + nv * 35); // G
|
|
178
|
+
data[idx + 2] = Math.min(255, baseB + veinStrength * 60 + nv * 30); // B
|
|
179
|
+
data[idx + 3] = 255;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
ctx.putImageData(imgData, tx, ty);
|
|
183
|
+
|
|
184
|
+
// Tile grout line (very thin, slightly lighter)
|
|
185
|
+
ctx.strokeStyle = 'rgba(50,52,60,0.8)';
|
|
186
|
+
ctx.lineWidth = 1.5;
|
|
187
|
+
ctx.strokeRect(tx + 0.5, ty + 0.5, ts - 1, ts - 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var floorTex = new THREE.CanvasTexture(cvs);
|
|
192
|
+
floorTex.wrapS = floorTex.wrapT = THREE.RepeatWrapping;
|
|
193
|
+
floorTex.anisotropy = 4;
|
|
194
|
+
var floorGeo = new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D);
|
|
195
|
+
var floorMeshMat = new THREE.MeshStandardMaterial({
|
|
196
|
+
map: floorTex, roughness: 0.12, metalness: 0.08
|
|
197
|
+
});
|
|
198
|
+
var floor = new THREE.Mesh(floorGeo, floorMeshMat);
|
|
199
|
+
floor.rotation.x = -Math.PI / 2;
|
|
200
|
+
floor.receiveShadow = true;
|
|
201
|
+
S.furnitureGroup.add(floor);
|
|
202
|
+
|
|
203
|
+
// Carpet runner in workspace zone (dark charcoal)
|
|
204
|
+
var carpet = new THREE.Mesh(new THREE.PlaneGeometry(14, 10), carpetMat);
|
|
205
|
+
carpet.rotation.x = -Math.PI / 2;
|
|
206
|
+
carpet.position.set(0, 0.01, -1);
|
|
207
|
+
carpet.receiveShadow = true;
|
|
208
|
+
S.furnitureGroup.add(carpet);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ==================== WALLS & WINDOWS ====================
|
|
212
|
+
function buildCampusWalls(concreteMat, glassMat, frameMat) {
|
|
213
|
+
// Back wall (concrete with large windows)
|
|
214
|
+
var wallMat = new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.8, side: THREE.DoubleSide });
|
|
215
|
+
|
|
216
|
+
var backWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, WALL_H), wallMat);
|
|
217
|
+
backWall.position.set(0, WALL_H / 2, -CAMPUS_D / 2);
|
|
218
|
+
backWall.receiveShadow = true;
|
|
219
|
+
S.furnitureGroup.add(backWall);
|
|
220
|
+
|
|
221
|
+
var leftWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_D, WALL_H), wallMat);
|
|
222
|
+
leftWall.position.set(-CAMPUS_W / 2, WALL_H / 2, 0);
|
|
223
|
+
leftWall.rotation.y = Math.PI / 2;
|
|
224
|
+
leftWall.receiveShadow = true;
|
|
225
|
+
S.furnitureGroup.add(leftWall);
|
|
226
|
+
|
|
227
|
+
var rightWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_D, WALL_H), wallMat);
|
|
228
|
+
rightWall.position.set(CAMPUS_W / 2, WALL_H / 2, 0);
|
|
229
|
+
rightWall.rotation.y = -Math.PI / 2;
|
|
230
|
+
rightWall.receiveShadow = true;
|
|
231
|
+
S.furnitureGroup.add(rightWall);
|
|
232
|
+
|
|
233
|
+
// Front wall with entrance gap
|
|
234
|
+
var frontLeftWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W / 2 - 4, WALL_H), wallMat);
|
|
235
|
+
frontLeftWall.position.set(-CAMPUS_W / 4 - 2, WALL_H / 2, CAMPUS_D / 2);
|
|
236
|
+
frontLeftWall.rotation.y = Math.PI;
|
|
237
|
+
S.furnitureGroup.add(frontLeftWall);
|
|
238
|
+
|
|
239
|
+
var frontRightWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W / 2 - 4, WALL_H), wallMat);
|
|
240
|
+
frontRightWall.position.set(CAMPUS_W / 4 + 2, WALL_H / 2, CAMPUS_D / 2);
|
|
241
|
+
frontRightWall.rotation.y = Math.PI;
|
|
242
|
+
S.furnitureGroup.add(frontRightWall);
|
|
243
|
+
|
|
244
|
+
// Floor-to-ceiling windows (left and right walls)
|
|
245
|
+
var windowMat = new THREE.MeshStandardMaterial({
|
|
246
|
+
color: 0x87CEEB, emissive: 0x87CEEB, emissiveIntensity: 0.15, roughness: 0.05, transparent: true, opacity: 0.6
|
|
247
|
+
});
|
|
248
|
+
// Left wall windows
|
|
249
|
+
[-10, -5, 0, 5, 10].forEach(function(wz) {
|
|
250
|
+
var win = new THREE.Mesh(new THREE.PlaneGeometry(3, 4.5), windowMat);
|
|
251
|
+
win.position.set(-CAMPUS_W / 2 + 0.05, 3, wz);
|
|
252
|
+
win.rotation.y = Math.PI / 2;
|
|
253
|
+
S.furnitureGroup.add(win);
|
|
254
|
+
// Chrome frame
|
|
255
|
+
var frame = new THREE.Mesh(new THREE.BoxGeometry(0.04, 4.6, 3.1), frameMat);
|
|
256
|
+
frame.position.set(-CAMPUS_W / 2 + 0.02, 3, wz);
|
|
257
|
+
S.furnitureGroup.add(frame);
|
|
258
|
+
});
|
|
259
|
+
// Right wall windows
|
|
260
|
+
[-10, -5, 0, 5, 10].forEach(function(wz) {
|
|
261
|
+
var win = new THREE.Mesh(new THREE.PlaneGeometry(3, 4.5), windowMat);
|
|
262
|
+
win.position.set(CAMPUS_W / 2 - 0.05, 3, wz);
|
|
263
|
+
win.rotation.y = -Math.PI / 2;
|
|
264
|
+
S.furnitureGroup.add(win);
|
|
265
|
+
});
|
|
266
|
+
// Back wall windows (above mezzanine level)
|
|
267
|
+
[-15, -8, 0, 8, 15].forEach(function(wx) {
|
|
268
|
+
var win = new THREE.Mesh(new THREE.PlaneGeometry(4, 2), windowMat);
|
|
269
|
+
win.position.set(wx, 4.5, -CAMPUS_D / 2 + 0.05);
|
|
270
|
+
S.furnitureGroup.add(win);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ==================== CEILING & SKYLIGHTS ====================
|
|
275
|
+
function buildCampusCeiling(concreteMat, glassMat) {
|
|
276
|
+
// Group for everything that should hide when camera is above roof
|
|
277
|
+
S._roofGroup = new THREE.Group();
|
|
278
|
+
|
|
279
|
+
// Main ceiling
|
|
280
|
+
var ceilingMat = new THREE.MeshStandardMaterial({ color: 0x1e2028, roughness: 0.9, side: THREE.DoubleSide });
|
|
281
|
+
var ceiling = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D), ceilingMat);
|
|
282
|
+
ceiling.rotation.x = Math.PI / 2;
|
|
283
|
+
ceiling.position.y = WALL_H;
|
|
284
|
+
S._roofGroup.add(ceiling);
|
|
285
|
+
|
|
286
|
+
// Skylights (glass rectangles in ceiling)
|
|
287
|
+
var skylightMat = new THREE.MeshStandardMaterial({
|
|
288
|
+
color: 0xaaddff, emissive: 0xaaddff, emissiveIntensity: 0.3, transparent: true, opacity: 0.4, side: THREE.DoubleSide
|
|
289
|
+
});
|
|
290
|
+
[[-6, 4], [6, 4], [-6, -4], [6, -4], [0, 0]].forEach(function(pos) {
|
|
291
|
+
var skylight = new THREE.Mesh(new THREE.PlaneGeometry(5, 3), skylightMat);
|
|
292
|
+
skylight.rotation.x = Math.PI / 2;
|
|
293
|
+
skylight.position.set(pos[0], WALL_H - 0.05, pos[1]);
|
|
294
|
+
S._roofGroup.add(skylight);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
S.furnitureGroup.add(S._roofGroup);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ==================== MEZZANINE ====================
|
|
301
|
+
function buildMezzanine(concreteMat, chromeMat, glassMat, walnutMat) {
|
|
302
|
+
// Platform
|
|
303
|
+
var mezzFloor = new THREE.Mesh(new THREE.BoxGeometry(CAMPUS_W - 2, 0.2, MEZZ_DEPTH), concreteMat);
|
|
304
|
+
mezzFloor.position.set(0, MEZZ_H, -CAMPUS_D / 2 + MEZZ_DEPTH / 2);
|
|
305
|
+
mezzFloor.castShadow = true; mezzFloor.receiveShadow = true;
|
|
306
|
+
S.furnitureGroup.add(mezzFloor);
|
|
307
|
+
|
|
308
|
+
// Floor surface (walnut)
|
|
309
|
+
var mezzTop = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W - 2, MEZZ_DEPTH), walnutMat);
|
|
310
|
+
mezzTop.rotation.x = -Math.PI / 2;
|
|
311
|
+
mezzTop.position.set(0, MEZZ_H + 0.11, -CAMPUS_D / 2 + MEZZ_DEPTH / 2);
|
|
312
|
+
mezzTop.receiveShadow = true;
|
|
313
|
+
S.furnitureGroup.add(mezzTop);
|
|
314
|
+
|
|
315
|
+
// Glass railing along front edge
|
|
316
|
+
var railGlass = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W - 6, 1.1), glassMat);
|
|
317
|
+
railGlass.position.set(0, MEZZ_H + 0.65, -CAMPUS_D / 2 + MEZZ_DEPTH);
|
|
318
|
+
S.furnitureGroup.add(railGlass);
|
|
319
|
+
// Chrome rail bar on top
|
|
320
|
+
var railBar = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, CAMPUS_W - 6, 8), chromeMat);
|
|
321
|
+
railBar.rotation.z = Math.PI / 2;
|
|
322
|
+
railBar.position.set(0, MEZZ_H + 1.2, -CAMPUS_D / 2 + MEZZ_DEPTH);
|
|
323
|
+
S.furnitureGroup.add(railBar);
|
|
324
|
+
|
|
325
|
+
// Support columns
|
|
326
|
+
[-18, -9, 0, 9, 18].forEach(function(cx) {
|
|
327
|
+
var col = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.15, MEZZ_H, 12), chromeMat);
|
|
328
|
+
col.position.set(cx, MEZZ_H / 2, -CAMPUS_D / 2 + MEZZ_DEPTH);
|
|
329
|
+
col.castShadow = true;
|
|
330
|
+
S.furnitureGroup.add(col);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Meeting pods on mezzanine (2 round tables with chairs)
|
|
334
|
+
[-10, 10].forEach(function(mx) {
|
|
335
|
+
// Round table
|
|
336
|
+
var table = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.06, 24), walnutMat);
|
|
337
|
+
table.position.set(mx, MEZZ_H + 0.85, -CAMPUS_D / 2 + 5);
|
|
338
|
+
table.castShadow = true;
|
|
339
|
+
S.furnitureGroup.add(table);
|
|
340
|
+
var tableLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.15, 0.7, 8), chromeMat);
|
|
341
|
+
tableLeg.position.set(mx, MEZZ_H + 0.46, -CAMPUS_D / 2 + 5);
|
|
342
|
+
S.furnitureGroup.add(tableLeg);
|
|
343
|
+
// 4 chairs around
|
|
344
|
+
for (var ci = 0; ci < 4; ci++) {
|
|
345
|
+
var ca = (ci / 4) * Math.PI * 2;
|
|
346
|
+
var cx2 = mx + Math.cos(ca) * 1.5;
|
|
347
|
+
var cz2 = -CAMPUS_D / 2 + 5 + Math.sin(ca) * 1.5;
|
|
348
|
+
buildModernChair(cx2, MEZZ_H + 0.11, cz2, ca + Math.PI, chromeMat);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Lounge sofa on mezzanine
|
|
353
|
+
buildSofa(0, MEZZ_H + 0.11, -CAMPUS_D / 2 + 3);
|
|
354
|
+
|
|
355
|
+
// "UPPER DECK" sign
|
|
356
|
+
var signDiv = document.createElement('div');
|
|
357
|
+
signDiv.textContent = 'UPPER DECK';
|
|
358
|
+
signDiv.style.cssText = 'color:#d4af37;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
|
|
359
|
+
var sign = new CSS2DObject(signDiv);
|
|
360
|
+
sign.position.set(0, MEZZ_H + 2, -CAMPUS_D / 2 + MEZZ_DEPTH);
|
|
361
|
+
S.furnitureGroup.add(sign);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ==================== STAIRCASE ====================
|
|
365
|
+
function buildStaircase(marbleMat, chromeMat, glassMat) {
|
|
366
|
+
var stairX = 20;
|
|
367
|
+
var stairZ = -CAMPUS_D / 2 + MEZZ_DEPTH + 2;
|
|
368
|
+
var steps = 12;
|
|
369
|
+
var stepW = 2.5;
|
|
370
|
+
var stepH = MEZZ_H / steps;
|
|
371
|
+
var stepD = 0.5;
|
|
372
|
+
|
|
373
|
+
for (var i = 0; i < steps; i++) {
|
|
374
|
+
var step = new THREE.Mesh(new THREE.BoxGeometry(stepW, stepH, stepD), marbleMat);
|
|
375
|
+
step.position.set(stairX, stepH / 2 + i * stepH, stairZ - i * stepD);
|
|
376
|
+
step.castShadow = true; step.receiveShadow = true;
|
|
377
|
+
S.furnitureGroup.add(step);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Glass side panels
|
|
381
|
+
var panelH = MEZZ_H + 1;
|
|
382
|
+
var panelD = steps * stepD;
|
|
383
|
+
var sidePanel = new THREE.Mesh(new THREE.PlaneGeometry(panelD, panelH), glassMat);
|
|
384
|
+
sidePanel.position.set(stairX + stepW / 2 + 0.05, panelH / 2, stairZ - panelD / 2);
|
|
385
|
+
sidePanel.rotation.y = Math.PI / 2;
|
|
386
|
+
S.furnitureGroup.add(sidePanel);
|
|
387
|
+
|
|
388
|
+
// Chrome handrail
|
|
389
|
+
var railLen = Math.sqrt(panelD * panelD + MEZZ_H * MEZZ_H);
|
|
390
|
+
var rail = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, railLen, 6), chromeMat);
|
|
391
|
+
rail.position.set(stairX + stepW / 2 + 0.08, MEZZ_H / 2 + 0.5, stairZ - panelD / 2);
|
|
392
|
+
rail.rotation.x = Math.atan2(MEZZ_H, panelD);
|
|
393
|
+
S.furnitureGroup.add(rail);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ==================== GRAND LOBBY ====================
|
|
397
|
+
function buildLobby(marbleMat, chromeMat, goldMat, walnutMat) {
|
|
398
|
+
var lz = CAMPUS_D / 2 - 3;
|
|
399
|
+
var group = new THREE.Group();
|
|
400
|
+
|
|
401
|
+
// --- Modern reception desk (sleek angular shape) ---
|
|
402
|
+
var deskBodyMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.3, metalness: 0.15 });
|
|
403
|
+
// Front panel (angled, facing visitors)
|
|
404
|
+
var frontPanel = new THREE.Mesh(new THREE.BoxGeometry(4, 1.15, 0.12), deskBodyMat);
|
|
405
|
+
frontPanel.position.set(0, 0.58, lz + 0.5);
|
|
406
|
+
frontPanel.castShadow = true;
|
|
407
|
+
group.add(frontPanel);
|
|
408
|
+
// Side panels
|
|
409
|
+
[-2, 2].forEach(function(sx) {
|
|
410
|
+
var side = new THREE.Mesh(new THREE.BoxGeometry(0.12, 1.15, 1.2), deskBodyMat);
|
|
411
|
+
side.position.set(sx, 0.58, lz - 0.05);
|
|
412
|
+
side.castShadow = true;
|
|
413
|
+
group.add(side);
|
|
414
|
+
});
|
|
415
|
+
// Marble countertop
|
|
416
|
+
var counterTop = new THREE.Mesh(new THREE.BoxGeometry(4.2, 0.06, 1.4), marbleMat);
|
|
417
|
+
counterTop.position.set(0, 1.17, lz - 0.05);
|
|
418
|
+
counterTop.castShadow = true;
|
|
419
|
+
group.add(counterTop);
|
|
420
|
+
// Gold accent strip on front
|
|
421
|
+
var accentStrip = new THREE.Mesh(new THREE.BoxGeometry(3.9, 0.04, 0.005), goldMat);
|
|
422
|
+
accentStrip.position.set(0, 1.0, lz + 0.57);
|
|
423
|
+
group.add(accentStrip);
|
|
424
|
+
// LED underglow (blue)
|
|
425
|
+
var ledMat = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: 0x58a6ff, emissiveIntensity: 0.6, roughness: 0.2 });
|
|
426
|
+
var ledStrip = new THREE.Mesh(new THREE.BoxGeometry(3.8, 0.02, 0.02), ledMat);
|
|
427
|
+
ledStrip.position.set(0, 0.03, lz + 0.55);
|
|
428
|
+
group.add(ledStrip);
|
|
429
|
+
|
|
430
|
+
// Reception monitor (thin, on desk)
|
|
431
|
+
var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
432
|
+
var mon = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.35, 0.02), monMat);
|
|
433
|
+
mon.position.set(-0.8, 1.45, lz - 0.1);
|
|
434
|
+
group.add(mon);
|
|
435
|
+
var monScreen = new THREE.Mesh(new THREE.PlaneGeometry(0.45, 0.3),
|
|
436
|
+
new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: 0x58a6ff, emissiveIntensity: 0.25, roughness: 0.1 }));
|
|
437
|
+
monScreen.position.set(-0.8, 1.45, lz - 0.088);
|
|
438
|
+
group.add(monScreen);
|
|
439
|
+
var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.2, 6), chromeMat);
|
|
440
|
+
monStand.position.set(-0.8, 1.28, lz - 0.1);
|
|
441
|
+
group.add(monStand);
|
|
442
|
+
|
|
443
|
+
// Keyboard on desk
|
|
444
|
+
var kbMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.5 });
|
|
445
|
+
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.01, 0.1), kbMat);
|
|
446
|
+
kb.position.set(-0.8, 1.2, lz - 0.4);
|
|
447
|
+
group.add(kb);
|
|
448
|
+
|
|
449
|
+
// --- Company logo wall (behind reception) ---
|
|
450
|
+
var logoWallMat = new THREE.MeshStandardMaterial({ color: 0x15181f, roughness: 0.7 });
|
|
451
|
+
var logoWall = new THREE.Mesh(new THREE.BoxGeometry(6, 3, 0.15), logoWallMat);
|
|
452
|
+
logoWall.position.set(0, 2, lz + 1.5);
|
|
453
|
+
logoWall.castShadow = true;
|
|
454
|
+
group.add(logoWall);
|
|
455
|
+
// Backlit logo text
|
|
456
|
+
var logoDiv = document.createElement('div');
|
|
457
|
+
logoDiv.textContent = 'LET THEM TALK';
|
|
458
|
+
logoDiv.style.cssText = 'color:#ffffff;font-size:16px;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);';
|
|
459
|
+
var logoLabel = new CSS2DObject(logoDiv);
|
|
460
|
+
logoLabel.position.set(0, 2.8, lz + 1.6);
|
|
461
|
+
group.add(logoLabel);
|
|
462
|
+
// Accent light behind logo wall
|
|
463
|
+
var logoLight = new THREE.RectAreaLight ? null : null; // not available without addon
|
|
464
|
+
var logoSpot = new THREE.PointLight(0x58a6ff, 0.5, 6);
|
|
465
|
+
logoSpot.position.set(0, 3.5, lz + 1);
|
|
466
|
+
group.add(logoSpot);
|
|
467
|
+
|
|
468
|
+
// --- Water feature (low rectangular pool) ---
|
|
469
|
+
var poolFrame = new THREE.Mesh(new THREE.BoxGeometry(3, 0.2, 1.5),
|
|
470
|
+
new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.4 }));
|
|
471
|
+
poolFrame.position.set(0, 0.1, lz - 4);
|
|
472
|
+
poolFrame.castShadow = true;
|
|
473
|
+
group.add(poolFrame);
|
|
474
|
+
var waterMat = new THREE.MeshStandardMaterial({ color: 0x2a6090, roughness: 0.05, metalness: 0.3, transparent: true, opacity: 0.7 });
|
|
475
|
+
var water = new THREE.Mesh(new THREE.PlaneGeometry(2.7, 1.2), waterMat);
|
|
476
|
+
water.rotation.x = -Math.PI / 2;
|
|
477
|
+
water.position.set(0, 0.22, lz - 4);
|
|
478
|
+
group.add(water);
|
|
479
|
+
// Decorative stones in water
|
|
480
|
+
var stoneMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.7 });
|
|
481
|
+
[[-0.8, -0.3], [0.5, 0.2], [-0.2, 0.1], [0.9, -0.2], [-0.5, -0.1]].forEach(function(sp) {
|
|
482
|
+
var stone = new THREE.Mesh(new THREE.SphereGeometry(0.06 + Math.random() * 0.04, 6, 5), stoneMat);
|
|
483
|
+
stone.position.set(sp[0], 0.2, lz - 4 + sp[1]);
|
|
484
|
+
stone.scale.y = 0.5;
|
|
485
|
+
group.add(stone);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// --- Waiting area (2 modern benches) ---
|
|
489
|
+
var benchMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3a, roughness: 0.6 });
|
|
490
|
+
[-5, 5].forEach(function(bx) {
|
|
491
|
+
var benchSeat = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.08, 0.6), benchMat);
|
|
492
|
+
benchSeat.position.set(bx, 0.45, lz - 2);
|
|
493
|
+
benchSeat.castShadow = true;
|
|
494
|
+
group.add(benchSeat);
|
|
495
|
+
// Chrome legs
|
|
496
|
+
[-1, 1].forEach(function(lx) {
|
|
497
|
+
var benchLeg = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.42, 0.5), chromeMat);
|
|
498
|
+
benchLeg.position.set(bx + lx, 0.22, lz - 2);
|
|
499
|
+
group.add(benchLeg);
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// --- Pendant lights above reception ---
|
|
504
|
+
[-1.2, 0, 1.2].forEach(function(px) {
|
|
505
|
+
var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.005, 0.005, 2.5, 4),
|
|
506
|
+
new THREE.MeshStandardMaterial({ color: 0x333333 }));
|
|
507
|
+
wire.position.set(px, WALL_H - 1.25, lz);
|
|
508
|
+
group.add(wire);
|
|
509
|
+
var shade = new THREE.Mesh(new THREE.SphereGeometry(0.12, 12, 10),
|
|
510
|
+
new THREE.MeshStandardMaterial({ color: 0xffeedd, emissive: 0xffeedd, emissiveIntensity: 0.4, transparent: true, opacity: 0.8 }));
|
|
511
|
+
shade.position.set(px, WALL_H - 2.6, lz);
|
|
512
|
+
group.add(shade);
|
|
513
|
+
});
|
|
514
|
+
// Warm light for reception area
|
|
515
|
+
var receptionLight = new THREE.PointLight(0xffeedd, 0.4, 8);
|
|
516
|
+
receptionLight.position.set(0, 4, lz);
|
|
517
|
+
group.add(receptionLight);
|
|
518
|
+
|
|
519
|
+
// RECEPTION sign (gold, above logo wall)
|
|
520
|
+
var signDiv = document.createElement('div');
|
|
521
|
+
signDiv.textContent = 'RECEPTION';
|
|
522
|
+
signDiv.style.cssText = 'color:#d4af37;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:3px;';
|
|
523
|
+
var sign = new CSS2DObject(signDiv);
|
|
524
|
+
sign.position.set(0, 4.5, lz);
|
|
525
|
+
group.add(sign);
|
|
526
|
+
|
|
527
|
+
S.furnitureGroup.add(group);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ==================== GAMING DESK ====================
|
|
531
|
+
function buildGamingDesk(x, z, index) {
|
|
532
|
+
var group = new THREE.Group();
|
|
533
|
+
group.position.set(x, 0, z);
|
|
534
|
+
|
|
535
|
+
// L-shaped desk (main + side wing)
|
|
536
|
+
var deskColor = 0x1a1a2e;
|
|
537
|
+
var deskMat = new THREE.MeshStandardMaterial({ color: deskColor, roughness: 0.3, metalness: 0.1 });
|
|
538
|
+
|
|
539
|
+
// Main desktop
|
|
540
|
+
var mainTop = new THREE.Mesh(new THREE.BoxGeometry(2, 0.05, 0.9), deskMat);
|
|
541
|
+
mainTop.position.y = 0.76; mainTop.castShadow = true; mainTop.receiveShadow = true;
|
|
542
|
+
group.add(mainTop);
|
|
543
|
+
|
|
544
|
+
// RGB LED strip under desk edge (front)
|
|
545
|
+
var rgbColors = [0x58a6ff, 0xa855f7, 0x22c55e, 0xef4444, 0x06b6d4, 0xec4899];
|
|
546
|
+
var rgbColor = rgbColors[index % rgbColors.length];
|
|
547
|
+
var rgbMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: rgbColor, emissiveIntensity: 0.8, roughness: 0.2 });
|
|
548
|
+
var rgbStrip = new THREE.Mesh(new THREE.BoxGeometry(1.9, 0.015, 0.015), rgbMat);
|
|
549
|
+
rgbStrip.position.set(0, 0.74, 0.44);
|
|
550
|
+
group.add(rgbStrip);
|
|
551
|
+
|
|
552
|
+
// Carbon fiber legs (angular, gaming style)
|
|
553
|
+
var legMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.2 });
|
|
554
|
+
[[-0.85, -0.35], [-0.85, 0.35], [0.85, -0.35], [0.85, 0.35]].forEach(function(p) {
|
|
555
|
+
var leg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.76, 0.06), legMat);
|
|
556
|
+
leg.position.set(p[0], 0.38, p[1]);
|
|
557
|
+
leg.castShadow = true;
|
|
558
|
+
group.add(leg);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// Curved ultrawide monitor (wider, thinner)
|
|
562
|
+
var monBody = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.35, 0.03), new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 }));
|
|
563
|
+
monBody.position.set(0, 1.15, -0.25);
|
|
564
|
+
monBody.castShadow = true;
|
|
565
|
+
group.add(monBody);
|
|
566
|
+
|
|
567
|
+
// Monitor screen
|
|
568
|
+
var screenGeo = new THREE.PlaneGeometry(0.64, 0.3);
|
|
569
|
+
var screenMat = new THREE.MeshStandardMaterial({
|
|
570
|
+
color: 0x333333, emissive: 0x333333, emissiveIntensity: 0.1, roughness: 0.2
|
|
571
|
+
});
|
|
572
|
+
var screen = new THREE.Mesh(screenGeo, screenMat);
|
|
573
|
+
screen.position.set(0, 1.15, -0.234);
|
|
574
|
+
group.add(screen);
|
|
575
|
+
|
|
576
|
+
// Monitor stand (V-shaped, chrome)
|
|
577
|
+
var standMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.15, metalness: 0.7 });
|
|
578
|
+
var standArm = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.04), standMat);
|
|
579
|
+
standArm.position.set(0, 0.92, -0.25);
|
|
580
|
+
group.add(standArm);
|
|
581
|
+
var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.02, 0.15), standMat);
|
|
582
|
+
standBase.position.set(0, 0.78, -0.25);
|
|
583
|
+
group.add(standBase);
|
|
584
|
+
|
|
585
|
+
// PC tower under desk (with RGB glow)
|
|
586
|
+
var pcMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.3 });
|
|
587
|
+
var pcCase = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.45, 0.45), pcMat);
|
|
588
|
+
pcCase.position.set(0.7, 0.23, 0);
|
|
589
|
+
pcCase.castShadow = true;
|
|
590
|
+
group.add(pcCase);
|
|
591
|
+
// RGB glass panel on PC
|
|
592
|
+
var pcGlowMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: rgbColor, emissiveIntensity: 0.4, transparent: true, opacity: 0.5 });
|
|
593
|
+
var pcGlow = new THREE.Mesh(new THREE.PlaneGeometry(0.18, 0.4), pcGlowMat);
|
|
594
|
+
pcGlow.position.set(0.7 + 0.115, 0.23, 0);
|
|
595
|
+
pcGlow.rotation.y = Math.PI / 2;
|
|
596
|
+
group.add(pcGlow);
|
|
597
|
+
|
|
598
|
+
// Keyboard
|
|
599
|
+
var kbMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
|
|
600
|
+
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.02, 0.12), kbMat);
|
|
601
|
+
kb.position.set(-0.1, 0.78, 0.15);
|
|
602
|
+
group.add(kb);
|
|
603
|
+
|
|
604
|
+
// Mouse + mousepad
|
|
605
|
+
var padMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.8 });
|
|
606
|
+
var pad = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.005, 0.2), padMat);
|
|
607
|
+
pad.position.set(0.3, 0.765, 0.15);
|
|
608
|
+
group.add(pad);
|
|
609
|
+
var mouseMat2 = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3 });
|
|
610
|
+
var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.02, 0.07), mouseMat2);
|
|
611
|
+
mouse.position.set(0.3, 0.78, 0.15);
|
|
612
|
+
group.add(mouse);
|
|
613
|
+
|
|
614
|
+
// Gaming chair (racing style)
|
|
615
|
+
buildGamingChair(group, 0, 0.7, rgbColor);
|
|
616
|
+
|
|
617
|
+
S.furnitureGroup.add(group);
|
|
618
|
+
S.deskMeshes.push({ group: group, screen: screen, screenMat: screenMat, index: index, x: x, z: z });
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ==================== GAMING CHAIR ====================
|
|
622
|
+
function buildGamingChair(parent, cx, cz, accentColor) {
|
|
623
|
+
var chairGroup = new THREE.Group();
|
|
624
|
+
chairGroup.position.set(cx, 0, cz);
|
|
625
|
+
|
|
626
|
+
var baseMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.3 });
|
|
627
|
+
var seatMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.65 });
|
|
628
|
+
var accentMat = new THREE.MeshStandardMaterial({ color: accentColor, roughness: 0.5 });
|
|
629
|
+
|
|
630
|
+
// 5-star base
|
|
631
|
+
var baseHub = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.04, 12), baseMat);
|
|
632
|
+
baseHub.position.y = 0.05;
|
|
633
|
+
chairGroup.add(baseHub);
|
|
634
|
+
for (var i = 0; i < 5; i++) {
|
|
635
|
+
var a = (i / 5) * Math.PI * 2;
|
|
636
|
+
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.02, 0.03), baseMat);
|
|
637
|
+
arm.position.set(Math.cos(a) * 0.15, 0.04, Math.sin(a) * 0.15);
|
|
638
|
+
arm.rotation.y = -a;
|
|
639
|
+
chairGroup.add(arm);
|
|
640
|
+
// Wheel
|
|
641
|
+
var wheel = new THREE.Mesh(new THREE.SphereGeometry(0.025, 6, 4), baseMat);
|
|
642
|
+
wheel.position.set(Math.cos(a) * 0.28, 0.025, Math.sin(a) * 0.28);
|
|
643
|
+
chairGroup.add(wheel);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Gas cylinder
|
|
647
|
+
var cyl = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.35, 8), baseMat);
|
|
648
|
+
cyl.position.y = 0.25;
|
|
649
|
+
chairGroup.add(cyl);
|
|
650
|
+
|
|
651
|
+
// Seat
|
|
652
|
+
var seat = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.08, 0.42), seatMat);
|
|
653
|
+
seat.position.y = 0.46;
|
|
654
|
+
seat.castShadow = true;
|
|
655
|
+
chairGroup.add(seat);
|
|
656
|
+
|
|
657
|
+
// Backrest (tall, racing-style with wings)
|
|
658
|
+
var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.55, 0.06), seatMat);
|
|
659
|
+
back.position.set(0, 0.78, 0.2);
|
|
660
|
+
back.castShadow = true;
|
|
661
|
+
chairGroup.add(back);
|
|
662
|
+
|
|
663
|
+
// Headrest
|
|
664
|
+
var headrest = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.1, 0.06), seatMat);
|
|
665
|
+
headrest.position.set(0, 1.1, 0.2);
|
|
666
|
+
chairGroup.add(headrest);
|
|
667
|
+
|
|
668
|
+
// Accent stripes on backrest
|
|
669
|
+
var stripe1 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.5, 0.005), accentMat);
|
|
670
|
+
stripe1.position.set(-0.12, 0.78, 0.17);
|
|
671
|
+
chairGroup.add(stripe1);
|
|
672
|
+
var stripe2 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.5, 0.005), accentMat);
|
|
673
|
+
stripe2.position.set(0.12, 0.78, 0.17);
|
|
674
|
+
chairGroup.add(stripe2);
|
|
675
|
+
|
|
676
|
+
// Armrests
|
|
677
|
+
[-0.22, 0.22].forEach(function(ax) {
|
|
678
|
+
var armPost = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.2, 0.03), baseMat);
|
|
679
|
+
armPost.position.set(ax, 0.55, 0.05);
|
|
680
|
+
chairGroup.add(armPost);
|
|
681
|
+
var armPad = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.02, 0.2), seatMat);
|
|
682
|
+
armPad.position.set(ax, 0.66, 0.05);
|
|
683
|
+
chairGroup.add(armPad);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
parent.add(chairGroup);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// ==================== MODERN CHAIR (for meeting rooms) ====================
|
|
690
|
+
function buildModernChair(x, y, z, rotation, chromeMat) {
|
|
691
|
+
var group = new THREE.Group();
|
|
692
|
+
group.position.set(x, y, z);
|
|
693
|
+
group.rotation.y = rotation;
|
|
694
|
+
|
|
695
|
+
var seatMat = new THREE.MeshStandardMaterial({ color: 0x333340, roughness: 0.7 });
|
|
696
|
+
var seat = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.05, 0.4), seatMat);
|
|
697
|
+
seat.position.y = 0.45; seat.castShadow = true;
|
|
698
|
+
group.add(seat);
|
|
699
|
+
var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.4, 0.04), seatMat);
|
|
700
|
+
back.position.set(0, 0.7, 0.18); back.castShadow = true;
|
|
701
|
+
group.add(back);
|
|
702
|
+
var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.4, 6), chromeMat);
|
|
703
|
+
post.position.y = 0.22;
|
|
704
|
+
group.add(post);
|
|
705
|
+
|
|
706
|
+
S.furnitureGroup.add(group);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ==================== SOFA ====================
|
|
710
|
+
function buildSofa(x, y, z) {
|
|
711
|
+
var group = new THREE.Group();
|
|
712
|
+
group.position.set(x, y, z);
|
|
713
|
+
|
|
714
|
+
var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3e, roughness: 0.75 });
|
|
715
|
+
// Base
|
|
716
|
+
var base = new THREE.Mesh(new THREE.BoxGeometry(3, 0.35, 0.9), sofaMat);
|
|
717
|
+
base.position.y = 0.2; base.castShadow = true;
|
|
718
|
+
group.add(base);
|
|
719
|
+
// Backrest
|
|
720
|
+
var backrest = new THREE.Mesh(new THREE.BoxGeometry(3, 0.5, 0.2), sofaMat);
|
|
721
|
+
backrest.position.set(0, 0.55, -0.35); backrest.castShadow = true;
|
|
722
|
+
group.add(backrest);
|
|
723
|
+
// Armrests
|
|
724
|
+
[-1.4, 1.4].forEach(function(ax) {
|
|
725
|
+
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.3, 0.9), sofaMat);
|
|
726
|
+
arm.position.set(ax, 0.4, 0); arm.castShadow = true;
|
|
727
|
+
group.add(arm);
|
|
728
|
+
});
|
|
729
|
+
// Cushions
|
|
730
|
+
var cushionMat = new THREE.MeshStandardMaterial({ color: 0x3a3a5e, roughness: 0.8 });
|
|
731
|
+
[-0.8, 0, 0.8].forEach(function(cx2) {
|
|
732
|
+
var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.1, 0.7), cushionMat);
|
|
733
|
+
cushion.position.set(cx2, 0.42, 0.05);
|
|
734
|
+
group.add(cushion);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
S.furnitureGroup.add(group);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// ==================== MANAGER'S OFFICE ====================
|
|
741
|
+
function buildManagerOffice(x, z, glassMat, frameMat, walnutMat, leatherMat, chromeMat) {
|
|
742
|
+
var offW = 8, offD = 7, wallH = 4;
|
|
743
|
+
var group = new THREE.Group();
|
|
744
|
+
group.position.set(x, 0, z);
|
|
745
|
+
|
|
746
|
+
// --- Raised floor (dark walnut) ---
|
|
747
|
+
var floorMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.5 });
|
|
748
|
+
var floor = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, offD), floorMat);
|
|
749
|
+
floor.position.y = 0.03; floor.receiveShadow = true;
|
|
750
|
+
group.add(floor);
|
|
751
|
+
|
|
752
|
+
// --- Glass walls with frosted privacy strip ---
|
|
753
|
+
var clearGlass = new THREE.MeshStandardMaterial({ color: 0xaaccee, transparent: true, opacity: 0.2, roughness: 0.05, metalness: 0.1, side: THREE.DoubleSide });
|
|
754
|
+
var frostedGlass = new THREE.MeshStandardMaterial({ color: 0xd0d8e8, transparent: true, opacity: 0.5, roughness: 0.4, side: THREE.DoubleSide });
|
|
755
|
+
|
|
756
|
+
// Front wall (with door gap in center)
|
|
757
|
+
var doorW = 1.2;
|
|
758
|
+
// Left section of front wall
|
|
759
|
+
var fwLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), clearGlass);
|
|
760
|
+
fwLeft.position.set(-(offW + doorW) / 4, wallH / 2, -offD / 2);
|
|
761
|
+
group.add(fwLeft);
|
|
762
|
+
// Right section of front wall
|
|
763
|
+
var fwRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), clearGlass);
|
|
764
|
+
fwRight.position.set((offW + doorW) / 4, wallH / 2, -offD / 2);
|
|
765
|
+
group.add(fwRight);
|
|
766
|
+
// Frosted strip on front walls (waist-height privacy)
|
|
767
|
+
var frostLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, 0.8), frostedGlass);
|
|
768
|
+
frostLeft.position.set(-(offW + doorW) / 4, 1.2, -offD / 2 + 0.01);
|
|
769
|
+
group.add(frostLeft);
|
|
770
|
+
var frostRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, 0.8), frostedGlass);
|
|
771
|
+
frostRight.position.set((offW + doorW) / 4, 1.2, -offD / 2 + 0.01);
|
|
772
|
+
group.add(frostRight);
|
|
773
|
+
|
|
774
|
+
// Glass sliding door (animated)
|
|
775
|
+
var doorGlass = new THREE.MeshStandardMaterial({ color: 0xbbddff, transparent: true, opacity: 0.3, roughness: 0.05, side: THREE.DoubleSide });
|
|
776
|
+
var door = new THREE.Mesh(new THREE.PlaneGeometry(doorW, wallH - 0.2), doorGlass);
|
|
777
|
+
door.position.set(0, wallH / 2, -offD / 2);
|
|
778
|
+
group.add(door);
|
|
779
|
+
// Door handle (chrome bar)
|
|
780
|
+
var handleMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
|
|
781
|
+
var handle = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.3, 0.04), handleMat);
|
|
782
|
+
handle.position.set(doorW / 2 - 0.1, 1.1, -offD / 2 + 0.03);
|
|
783
|
+
group.add(handle);
|
|
784
|
+
// Store door ref for animation
|
|
785
|
+
S._managerDoor = door;
|
|
786
|
+
S._managerDoorOpen = 0; // 0=closed, 1=open (lerp target)
|
|
787
|
+
S._managerDoorLerp = 0;
|
|
788
|
+
S._managerDoorClosedZ = -offD / 2;
|
|
789
|
+
|
|
790
|
+
// Left glass wall
|
|
791
|
+
var leftWall = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), clearGlass);
|
|
792
|
+
leftWall.position.set(-offW / 2, wallH / 2, 0);
|
|
793
|
+
leftWall.rotation.y = Math.PI / 2;
|
|
794
|
+
group.add(leftWall);
|
|
795
|
+
var frostLeftW = new THREE.Mesh(new THREE.PlaneGeometry(offD, 0.8), frostedGlass);
|
|
796
|
+
frostLeftW.position.set(-offW / 2 + 0.01, 1.2, 0);
|
|
797
|
+
frostLeftW.rotation.y = Math.PI / 2;
|
|
798
|
+
group.add(frostLeftW);
|
|
799
|
+
|
|
800
|
+
// Right glass wall
|
|
801
|
+
var rightWall = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), clearGlass);
|
|
802
|
+
rightWall.position.set(offW / 2, wallH / 2, 0);
|
|
803
|
+
rightWall.rotation.y = -Math.PI / 2;
|
|
804
|
+
group.add(rightWall);
|
|
805
|
+
var frostRightW = new THREE.Mesh(new THREE.PlaneGeometry(offD, 0.8), frostedGlass);
|
|
806
|
+
frostRightW.position.set(offW / 2 - 0.01, 1.2, 0);
|
|
807
|
+
frostRightW.rotation.y = -Math.PI / 2;
|
|
808
|
+
group.add(frostRightW);
|
|
809
|
+
|
|
810
|
+
// Back glass wall
|
|
811
|
+
var backWall = new THREE.Mesh(new THREE.PlaneGeometry(offW, wallH), clearGlass);
|
|
812
|
+
backWall.position.set(0, wallH / 2, offD / 2);
|
|
813
|
+
backWall.rotation.y = Math.PI;
|
|
814
|
+
group.add(backWall);
|
|
815
|
+
var frostBackW = new THREE.Mesh(new THREE.PlaneGeometry(offW, 0.8), frostedGlass);
|
|
816
|
+
frostBackW.position.set(0, 1.2, offD / 2 - 0.01);
|
|
817
|
+
frostBackW.rotation.y = Math.PI;
|
|
818
|
+
group.add(frostBackW);
|
|
819
|
+
|
|
820
|
+
// --- Chrome frame structure ---
|
|
821
|
+
// Top beams (all 4 sides)
|
|
822
|
+
// Front beam
|
|
823
|
+
var beamFront = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, 0.06), frameMat);
|
|
824
|
+
beamFront.position.set(0, wallH, -offD / 2); group.add(beamFront);
|
|
825
|
+
// Back beam
|
|
826
|
+
var beamBack = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, 0.06), frameMat);
|
|
827
|
+
beamBack.position.set(0, wallH, offD / 2); group.add(beamBack);
|
|
828
|
+
// Left beam
|
|
829
|
+
var beamLeft = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, offD), frameMat);
|
|
830
|
+
beamLeft.position.set(-offW / 2, wallH, 0); group.add(beamLeft);
|
|
831
|
+
// Right beam
|
|
832
|
+
var beamRight = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, offD), frameMat);
|
|
833
|
+
beamRight.position.set(offW / 2, wallH, 0); group.add(beamRight);
|
|
834
|
+
// Vertical corner posts (all 4 corners)
|
|
835
|
+
[[-offW / 2, -offD / 2], [-offW / 2, offD / 2], [offW / 2, -offD / 2], [offW / 2, offD / 2]].forEach(function(p) {
|
|
836
|
+
var post = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), frameMat);
|
|
837
|
+
post.position.set(p[0], wallH / 2, p[1]);
|
|
838
|
+
group.add(post);
|
|
839
|
+
});
|
|
840
|
+
// Door frame posts
|
|
841
|
+
[-doorW / 2 - 0.03, doorW / 2 + 0.03].forEach(function(dx) {
|
|
842
|
+
var doorPost = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), frameMat);
|
|
843
|
+
doorPost.position.set(dx, wallH / 2, -offD / 2);
|
|
844
|
+
group.add(doorPost);
|
|
845
|
+
});
|
|
846
|
+
// Door top beam
|
|
847
|
+
var doorTopBeam = new THREE.Mesh(new THREE.BoxGeometry(doorW + 0.12, 0.06, 0.06), frameMat);
|
|
848
|
+
doorTopBeam.position.set(0, wallH, -offD / 2);
|
|
849
|
+
group.add(doorTopBeam);
|
|
850
|
+
|
|
851
|
+
// --- L-shaped executive desk (walnut + marble top) ---
|
|
852
|
+
var marbleTopMat = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.12, metalness: 0.05 });
|
|
853
|
+
// Main section
|
|
854
|
+
var deskMain = new THREE.Mesh(new THREE.BoxGeometry(2.8, 0.06, 1.2), walnutMat);
|
|
855
|
+
deskMain.position.set(0, 0.78, 1.5); deskMain.castShadow = true;
|
|
856
|
+
group.add(deskMain);
|
|
857
|
+
var marbleTop1 = new THREE.Mesh(new THREE.BoxGeometry(2.82, 0.015, 1.22), marbleTopMat);
|
|
858
|
+
marbleTop1.position.set(0, 0.82, 1.5);
|
|
859
|
+
group.add(marbleTop1);
|
|
860
|
+
// Side wing
|
|
861
|
+
var deskWing = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.06, 0.8), walnutMat);
|
|
862
|
+
deskWing.position.set(1.6, 0.78, 0.7); deskWing.castShadow = true;
|
|
863
|
+
group.add(deskWing);
|
|
864
|
+
var marbleTop2 = new THREE.Mesh(new THREE.BoxGeometry(1.22, 0.015, 0.82), marbleTopMat);
|
|
865
|
+
marbleTop2.position.set(1.6, 0.82, 0.7);
|
|
866
|
+
group.add(marbleTop2);
|
|
867
|
+
// Desk legs (chrome, elegant)
|
|
868
|
+
[[-1.2, 1], [-1.2, 2], [1.2, 2], [1.2, 1], [2.1, 0.4], [2.1, 1]].forEach(function(p) {
|
|
869
|
+
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.78, 8), chromeMat);
|
|
870
|
+
leg.position.set(p[0], 0.39, p[1]);
|
|
871
|
+
group.add(leg);
|
|
872
|
+
});
|
|
873
|
+
// Cable management panel (dark, under desk back)
|
|
874
|
+
var cablePanelMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.5 });
|
|
875
|
+
var cablePanel = new THREE.Mesh(new THREE.BoxGeometry(2.6, 0.5, 0.04), cablePanelMat);
|
|
876
|
+
cablePanel.position.set(0, 0.5, 0.9);
|
|
877
|
+
group.add(cablePanel);
|
|
878
|
+
|
|
879
|
+
// --- Dual ultrawide monitors ---
|
|
880
|
+
var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
881
|
+
[-0.45, 0.45].forEach(function(mx) {
|
|
882
|
+
var mon = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.35, 0.025), monMat);
|
|
883
|
+
mon.position.set(mx, 1.15, 1.05); mon.castShadow = true;
|
|
884
|
+
group.add(mon);
|
|
885
|
+
// Screen
|
|
886
|
+
var scrMat = new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: 0x58a6ff, emissiveIntensity: 0.3, roughness: 0.1 });
|
|
887
|
+
var scr = new THREE.Mesh(new THREE.PlaneGeometry(0.55, 0.3), scrMat);
|
|
888
|
+
scr.position.set(mx, 1.15, 1.037);
|
|
889
|
+
group.add(scr);
|
|
890
|
+
// Stand
|
|
891
|
+
var stand = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.22, 6), chromeMat);
|
|
892
|
+
stand.position.set(mx, 0.95, 1.05);
|
|
893
|
+
group.add(stand);
|
|
894
|
+
});
|
|
895
|
+
// Monitor arm (chrome, connecting both)
|
|
896
|
+
var monArm = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.03, 0.03), chromeMat);
|
|
897
|
+
monArm.position.set(0, 1.0, 1.05);
|
|
898
|
+
group.add(monArm);
|
|
899
|
+
|
|
900
|
+
// --- Keyboard + mouse ---
|
|
901
|
+
var kbMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
|
|
902
|
+
var kb = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.015, 0.12), kbMat);
|
|
903
|
+
kb.position.set(-0.15, 0.835, 1.8);
|
|
904
|
+
group.add(kb);
|
|
905
|
+
var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.015, 0.06), kbMat);
|
|
906
|
+
mouse.position.set(0.35, 0.835, 1.8);
|
|
907
|
+
group.add(mouse);
|
|
908
|
+
|
|
909
|
+
// --- Premium leather executive chair ---
|
|
910
|
+
var chairG = new THREE.Group();
|
|
911
|
+
chairG.position.set(0, 0, 2.4);
|
|
912
|
+
// 5-star base
|
|
913
|
+
var baseMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.4 });
|
|
914
|
+
for (var ci = 0; ci < 5; ci++) {
|
|
915
|
+
var ca = (ci / 5) * Math.PI * 2;
|
|
916
|
+
var arm = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.02, 0.035), baseMat);
|
|
917
|
+
arm.position.set(Math.cos(ca) * 0.16, 0.04, Math.sin(ca) * 0.16);
|
|
918
|
+
arm.rotation.y = -ca;
|
|
919
|
+
chairG.add(arm);
|
|
920
|
+
}
|
|
921
|
+
var cylM = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.4, 8), chromeMat);
|
|
922
|
+
cylM.position.y = 0.26; chairG.add(cylM);
|
|
923
|
+
// Wide seat
|
|
924
|
+
var seatM = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.1, 0.5), leatherMat);
|
|
925
|
+
seatM.position.y = 0.5; seatM.castShadow = true; chairG.add(seatM);
|
|
926
|
+
// Tall padded backrest
|
|
927
|
+
var backM = new THREE.Mesh(new THREE.BoxGeometry(0.48, 0.7, 0.08), leatherMat);
|
|
928
|
+
backM.position.set(0, 0.9, 0.24); backM.castShadow = true; chairG.add(backM);
|
|
929
|
+
// Headrest
|
|
930
|
+
var headM = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.12, 0.08), leatherMat);
|
|
931
|
+
headM.position.set(0, 1.3, 0.24); chairG.add(headM);
|
|
932
|
+
// Armrests
|
|
933
|
+
[-0.27, 0.27].forEach(function(ax) {
|
|
934
|
+
var armPost = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.04), baseMat);
|
|
935
|
+
armPost.position.set(ax, 0.6, 0.08); chairG.add(armPost);
|
|
936
|
+
var armPad = new THREE.Mesh(new THREE.BoxGeometry(0.09, 0.03, 0.25), leatherMat);
|
|
937
|
+
armPad.position.set(ax, 0.73, 0.08); chairG.add(armPad);
|
|
938
|
+
});
|
|
939
|
+
group.add(chairG);
|
|
940
|
+
|
|
941
|
+
// --- Bookshelf (right wall, walnut) ---
|
|
942
|
+
var shelfGroup = new THREE.Group();
|
|
943
|
+
shelfGroup.position.set(3.2, 0, 0.5);
|
|
944
|
+
var shelfBack = new THREE.Mesh(new THREE.BoxGeometry(0.06, 2.2, 1.4), walnutMat);
|
|
945
|
+
shelfBack.position.y = 1.1; shelfBack.castShadow = true;
|
|
946
|
+
shelfGroup.add(shelfBack);
|
|
947
|
+
[0.05, 0.55, 1.1, 1.65, 2.15].forEach(function(sy) {
|
|
948
|
+
var shelf = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.03, 1.4), walnutMat);
|
|
949
|
+
shelf.position.set(0.12, sy, 0); shelf.receiveShadow = true;
|
|
950
|
+
shelfGroup.add(shelf);
|
|
951
|
+
});
|
|
952
|
+
// Books
|
|
953
|
+
var bookColors = [0xc0392b, 0x2980b9, 0x8e44ad, 0xd4a24e, 0x1abc9c, 0x2c3e50];
|
|
954
|
+
[0.09, 0.59, 1.14, 1.69].forEach(function(sy, si) {
|
|
955
|
+
var startZ = -0.55;
|
|
956
|
+
for (var bi2 = 0; bi2 < 5; bi2++) {
|
|
957
|
+
var bh = 0.32 + Math.sin(si + bi2) * 0.08;
|
|
958
|
+
var bw = 0.04 + Math.sin(si * 3 + bi2) * 0.015;
|
|
959
|
+
var bMat = new THREE.MeshStandardMaterial({ color: bookColors[(si * 3 + bi2) % bookColors.length], roughness: 0.8 });
|
|
960
|
+
var book = new THREE.Mesh(new THREE.BoxGeometry(0.18, bh, bw), bMat);
|
|
961
|
+
book.position.set(0.16, sy + bh / 2, startZ);
|
|
962
|
+
shelfGroup.add(book);
|
|
963
|
+
startZ += bw + 0.02;
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
group.add(shelfGroup);
|
|
967
|
+
|
|
968
|
+
// --- Small sofa + coffee table ---
|
|
969
|
+
var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a1a0a, roughness: 0.7 });
|
|
970
|
+
var sofaBase = new THREE.Mesh(new THREE.BoxGeometry(2, 0.3, 0.7), sofaMat);
|
|
971
|
+
sofaBase.position.set(-2.5, 0.18, -0.5); sofaBase.castShadow = true;
|
|
972
|
+
group.add(sofaBase);
|
|
973
|
+
var sofaBack = new THREE.Mesh(new THREE.BoxGeometry(2, 0.4, 0.15), sofaMat);
|
|
974
|
+
sofaBack.position.set(-2.5, 0.45, -0.85); sofaBack.castShadow = true;
|
|
975
|
+
group.add(sofaBack);
|
|
976
|
+
// Cushions
|
|
977
|
+
var cushionMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.8 });
|
|
978
|
+
[-3.1, -2.5, -1.9].forEach(function(cx) {
|
|
979
|
+
var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.06, 0.55), cushionMat);
|
|
980
|
+
cushion.position.set(cx, 0.36, -0.5);
|
|
981
|
+
group.add(cushion);
|
|
982
|
+
});
|
|
983
|
+
// Coffee table (glass top, chrome legs)
|
|
984
|
+
var coffeeGlassMat = new THREE.MeshStandardMaterial({ color: 0xccddee, transparent: true, opacity: 0.35, roughness: 0.05 });
|
|
985
|
+
var coffeeTop = new THREE.Mesh(new THREE.BoxGeometry(1, 0.03, 0.5), coffeeGlassMat);
|
|
986
|
+
coffeeTop.position.set(-2.5, 0.45, 0.2);
|
|
987
|
+
group.add(coffeeTop);
|
|
988
|
+
[-0.4, 0.4].forEach(function(lx) {
|
|
989
|
+
[-0.18, 0.18].forEach(function(lz) {
|
|
990
|
+
var cLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.42, 6), chromeMat);
|
|
991
|
+
cLeg.position.set(-2.5 + lx, 0.22, 0.2 + lz);
|
|
992
|
+
group.add(cLeg);
|
|
993
|
+
});
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
// --- Luxury plant ---
|
|
997
|
+
var planterMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3a, roughness: 0.5 });
|
|
998
|
+
var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.2, 0.5, 12), planterMat);
|
|
999
|
+
planter.position.set(3, 0.25, -2.5); planter.castShadow = true;
|
|
1000
|
+
group.add(planter);
|
|
1001
|
+
var leafMat = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.8 });
|
|
1002
|
+
for (var pi = 0; pi < 6; pi++) {
|
|
1003
|
+
var pa = (pi / 6) * Math.PI * 2;
|
|
1004
|
+
var leaf = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 6), leafMat);
|
|
1005
|
+
leaf.position.set(3 + Math.cos(pa) * 0.15, 0.6, -2.5 + Math.sin(pa) * 0.15);
|
|
1006
|
+
group.add(leaf);
|
|
1007
|
+
}
|
|
1008
|
+
var topL = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), leafMat);
|
|
1009
|
+
topL.position.set(3, 0.75, -2.5); group.add(topL);
|
|
1010
|
+
|
|
1011
|
+
// --- Gold accent artwork frame on back wall ---
|
|
1012
|
+
var goldMat = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.3, metalness: 0.7 });
|
|
1013
|
+
// Frame
|
|
1014
|
+
var artFrameTop = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.06, 0.06), goldMat);
|
|
1015
|
+
artFrameTop.position.set(0, 3.2, offD / 2 - 0.08); group.add(artFrameTop);
|
|
1016
|
+
var artFrameBot = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.06, 0.06), goldMat);
|
|
1017
|
+
artFrameBot.position.set(0, 2.0, offD / 2 - 0.08); group.add(artFrameBot);
|
|
1018
|
+
var artFrameL = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.26, 0.06), goldMat);
|
|
1019
|
+
artFrameL.position.set(-0.8, 2.6, offD / 2 - 0.08); group.add(artFrameL);
|
|
1020
|
+
var artFrameR = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.26, 0.06), goldMat);
|
|
1021
|
+
artFrameR.position.set(0.8, 2.6, offD / 2 - 0.08); group.add(artFrameR);
|
|
1022
|
+
// Canvas inside frame (dark elegant)
|
|
1023
|
+
var artMat = new THREE.MeshStandardMaterial({ color: 0x1a2a3a, roughness: 0.8 });
|
|
1024
|
+
var art = new THREE.Mesh(new THREE.PlaneGeometry(1.5, 1.1), artMat);
|
|
1025
|
+
art.position.set(0, 2.6, offD / 2 - 0.06);
|
|
1026
|
+
art.rotation.y = Math.PI;
|
|
1027
|
+
group.add(art);
|
|
1028
|
+
|
|
1029
|
+
// --- Warm ambient lighting ---
|
|
1030
|
+
var warmLight1 = new THREE.PointLight(0xffeedd, 0.4, 8);
|
|
1031
|
+
warmLight1.position.set(0, 3.5, 1.5);
|
|
1032
|
+
group.add(warmLight1);
|
|
1033
|
+
var warmLight2 = new THREE.PointLight(0xffeedd, 0.2, 5);
|
|
1034
|
+
warmLight2.position.set(-2, 2, 0);
|
|
1035
|
+
group.add(warmLight2);
|
|
1036
|
+
|
|
1037
|
+
// --- Pendant light (premium, gold accent) ---
|
|
1038
|
+
var pendWire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 2), new THREE.MeshStandardMaterial({ color: 0x333333 }));
|
|
1039
|
+
pendWire.position.set(0, wallH - 1, 1.5);
|
|
1040
|
+
group.add(pendWire);
|
|
1041
|
+
var pendShade = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.3, 0.2, 12, 1, true),
|
|
1042
|
+
new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.4, metalness: 0.3, side: THREE.DoubleSide }));
|
|
1043
|
+
pendShade.position.set(0, wallH - 2.1, 1.5);
|
|
1044
|
+
group.add(pendShade);
|
|
1045
|
+
var pendRim = new THREE.Mesh(new THREE.TorusGeometry(0.3, 0.01, 6, 24), goldMat);
|
|
1046
|
+
pendRim.position.set(0, wallH - 2.2, 1.5);
|
|
1047
|
+
pendRim.rotation.x = Math.PI / 2;
|
|
1048
|
+
group.add(pendRim);
|
|
1049
|
+
|
|
1050
|
+
// --- "MANAGER" gold sign above door ---
|
|
1051
|
+
var signDiv = document.createElement('div');
|
|
1052
|
+
signDiv.textContent = 'MANAGER';
|
|
1053
|
+
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);';
|
|
1054
|
+
var sign = new CSS2DObject(signDiv);
|
|
1055
|
+
sign.position.set(0, wallH + 0.3, -offD / 2);
|
|
1056
|
+
group.add(sign);
|
|
1057
|
+
|
|
1058
|
+
S.furnitureGroup.add(group);
|
|
1059
|
+
S._managerOfficeGroup = group;
|
|
1060
|
+
S._managerOfficePos = { x: x, z: z };
|
|
1061
|
+
|
|
1062
|
+
// Register manager desk in deskMeshes so monitor screen system works
|
|
1063
|
+
var mgrDeskIdx = CAMPUS_DESKS.length - 1;
|
|
1064
|
+
var mgrScreenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: 0x333333, emissiveIntensity: 0.1, roughness: 0.2 });
|
|
1065
|
+
S.deskMeshes[mgrDeskIdx] = { group: group, screen: null, screenMat: mgrScreenMat, index: mgrDeskIdx, x: x, z: z + 1.7 };
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// ==================== DESIGNER STUDIO ====================
|
|
1069
|
+
function buildDesignerStudio(x, z, walnutMat, chromeMat) {
|
|
1070
|
+
// Mood board wall
|
|
1071
|
+
var boardMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.5 });
|
|
1072
|
+
var board = new THREE.Mesh(new THREE.BoxGeometry(0.08, 2, 4), boardMat);
|
|
1073
|
+
board.position.set(x - 5.5, 1.5, z);
|
|
1074
|
+
board.castShadow = true;
|
|
1075
|
+
S.furnitureGroup.add(board);
|
|
1076
|
+
// Colorful sticky notes on board
|
|
1077
|
+
var noteColors = [0xfbbf24, 0xf87171, 0x34d399, 0x60a5fa, 0xa78bfa, 0xfb923c];
|
|
1078
|
+
for (var ni = 0; ni < 12; ni++) {
|
|
1079
|
+
var noteMat = new THREE.MeshStandardMaterial({ color: noteColors[ni % noteColors.length], roughness: 0.9 });
|
|
1080
|
+
var note = new THREE.Mesh(new THREE.PlaneGeometry(0.3, 0.3), noteMat);
|
|
1081
|
+
note.position.set(x - 5.44, 0.8 + Math.floor(ni / 4) * 0.5, z - 1.5 + (ni % 4) * 0.8);
|
|
1082
|
+
note.rotation.y = Math.PI / 2;
|
|
1083
|
+
S.furnitureGroup.add(note);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// Standing desk
|
|
1087
|
+
var standDesk = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.06, 0.8), walnutMat);
|
|
1088
|
+
standDesk.position.set(x - 2, 1.1, z + 3);
|
|
1089
|
+
standDesk.castShadow = true;
|
|
1090
|
+
S.furnitureGroup.add(standDesk);
|
|
1091
|
+
// Adjustable legs
|
|
1092
|
+
[-0.7, 0.7].forEach(function(lx) {
|
|
1093
|
+
var standLeg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.1, 0.06), chromeMat);
|
|
1094
|
+
standLeg.position.set(x - 2 + lx, 0.55, z + 3);
|
|
1095
|
+
S.furnitureGroup.add(standLeg);
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
// "DESIGN LAB" sign
|
|
1099
|
+
var signDiv = document.createElement('div');
|
|
1100
|
+
signDiv.textContent = 'DESIGN LAB';
|
|
1101
|
+
signDiv.style.cssText = 'color:#a855f7;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
|
|
1102
|
+
var sign = new CSS2DObject(signDiv);
|
|
1103
|
+
sign.position.set(x, 3.5, z);
|
|
1104
|
+
S.furnitureGroup.add(sign);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// ==================== BAR & CAFÉ ====================
|
|
1108
|
+
function buildBar(x, z, walnutMat, chromeMat, neonBlueMat, neonPurpleMat) {
|
|
1109
|
+
// Long bar counter
|
|
1110
|
+
var barTop = new THREE.Mesh(new THREE.BoxGeometry(6, 0.08, 1.2), walnutMat);
|
|
1111
|
+
barTop.position.set(x, 1.1, z); barTop.castShadow = true;
|
|
1112
|
+
S.furnitureGroup.add(barTop);
|
|
1113
|
+
var barFront = new THREE.Mesh(new THREE.BoxGeometry(6, 1.1, 0.1),
|
|
1114
|
+
new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.4 }));
|
|
1115
|
+
barFront.position.set(x, 0.55, z + 0.55);
|
|
1116
|
+
barFront.castShadow = true;
|
|
1117
|
+
S.furnitureGroup.add(barFront);
|
|
1118
|
+
|
|
1119
|
+
// LED strip under bar counter
|
|
1120
|
+
var barLed = new THREE.Mesh(new THREE.BoxGeometry(5.8, 0.02, 0.02), neonBlueMat);
|
|
1121
|
+
barLed.position.set(x, 1.02, z + 0.58);
|
|
1122
|
+
S.furnitureGroup.add(barLed);
|
|
1123
|
+
|
|
1124
|
+
// Bar stools (5)
|
|
1125
|
+
for (var si = 0; si < 5; si++) {
|
|
1126
|
+
var sx = x - 2 + si * 1;
|
|
1127
|
+
var stoolGroup = new THREE.Group();
|
|
1128
|
+
stoolGroup.position.set(sx, 0, z + 1.2);
|
|
1129
|
+
var stoolSeat = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.18, 0.06, 12),
|
|
1130
|
+
new THREE.MeshStandardMaterial({ color: 0x333340, roughness: 0.6 }));
|
|
1131
|
+
stoolSeat.position.y = 0.75;
|
|
1132
|
+
stoolGroup.add(stoolSeat);
|
|
1133
|
+
var stoolPost = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.7, 8), chromeMat);
|
|
1134
|
+
stoolPost.position.y = 0.38;
|
|
1135
|
+
stoolGroup.add(stoolPost);
|
|
1136
|
+
var stoolBase = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.2, 0.04, 12), chromeMat);
|
|
1137
|
+
stoolBase.position.y = 0.04;
|
|
1138
|
+
stoolGroup.add(stoolBase);
|
|
1139
|
+
S.furnitureGroup.add(stoolGroup);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// Bottle shelf behind bar
|
|
1143
|
+
var shelfMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.6 });
|
|
1144
|
+
[1.5, 2.2, 2.9].forEach(function(sy) {
|
|
1145
|
+
var shelf = new THREE.Mesh(new THREE.BoxGeometry(5.5, 0.04, 0.3), shelfMat);
|
|
1146
|
+
shelf.position.set(x, sy, z - 0.9);
|
|
1147
|
+
S.furnitureGroup.add(shelf);
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
// Bottles on shelves
|
|
1151
|
+
var bottleColors = [0x2d8a4e, 0x8B4513, 0xd4af37, 0xcc3333, 0x1a5276, 0xf0f0f0];
|
|
1152
|
+
for (var bi = 0; bi < 15; bi++) {
|
|
1153
|
+
var bx = x - 2.5 + (bi % 5) * 1;
|
|
1154
|
+
var by = 1.55 + Math.floor(bi / 5) * 0.7;
|
|
1155
|
+
var bottleMat = new THREE.MeshStandardMaterial({ color: bottleColors[bi % bottleColors.length], roughness: 0.3, metalness: 0.1 });
|
|
1156
|
+
var bottle = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.25, 8), bottleMat);
|
|
1157
|
+
bottle.position.set(bx, by + 0.12, z - 0.85);
|
|
1158
|
+
S.furnitureGroup.add(bottle);
|
|
1159
|
+
var bottleNeck = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.03, 0.1, 6), bottleMat);
|
|
1160
|
+
bottleNeck.position.set(bx, by + 0.3, z - 0.85);
|
|
1161
|
+
S.furnitureGroup.add(bottleNeck);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Coffee machine
|
|
1165
|
+
var coffeeMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.2 });
|
|
1166
|
+
var coffee = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.5, 0.3), coffeeMat);
|
|
1167
|
+
coffee.position.set(x + 2.5, 1.4, z - 0.1);
|
|
1168
|
+
coffee.castShadow = true;
|
|
1169
|
+
S.furnitureGroup.add(coffee);
|
|
1170
|
+
|
|
1171
|
+
// "BAR" neon sign
|
|
1172
|
+
var signDiv = document.createElement('div');
|
|
1173
|
+
signDiv.textContent = 'BAR & CAFÉ';
|
|
1174
|
+
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;';
|
|
1175
|
+
var sign = new CSS2DObject(signDiv);
|
|
1176
|
+
sign.position.set(x, 3.5, z - 1);
|
|
1177
|
+
S.furnitureGroup.add(sign);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// ==================== RECREATION CENTER ====================
|
|
1181
|
+
function buildRecCenter(x, z, walnutMat, chromeMat, carpetMat) {
|
|
1182
|
+
// Carpet area
|
|
1183
|
+
var recCarpet = new THREE.Mesh(new THREE.PlaneGeometry(10, 8), carpetMat);
|
|
1184
|
+
recCarpet.rotation.x = -Math.PI / 2;
|
|
1185
|
+
recCarpet.position.set(x, 0.01, z);
|
|
1186
|
+
recCarpet.receiveShadow = true;
|
|
1187
|
+
S.furnitureGroup.add(recCarpet);
|
|
1188
|
+
|
|
1189
|
+
// Pool table
|
|
1190
|
+
var ptGroup = new THREE.Group();
|
|
1191
|
+
ptGroup.position.set(x - 2, 0, z);
|
|
1192
|
+
var ptTop = new THREE.Mesh(new THREE.BoxGeometry(2.4, 0.1, 1.3),
|
|
1193
|
+
new THREE.MeshStandardMaterial({ color: 0x006633, roughness: 0.9 }));
|
|
1194
|
+
ptTop.position.y = 0.85; ptTop.castShadow = true;
|
|
1195
|
+
ptGroup.add(ptTop);
|
|
1196
|
+
var ptFrame = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.15, 1.4), walnutMat);
|
|
1197
|
+
ptFrame.position.y = 0.78; ptFrame.castShadow = true;
|
|
1198
|
+
ptGroup.add(ptFrame);
|
|
1199
|
+
// Legs
|
|
1200
|
+
[[-1.1, -0.55], [-1.1, 0.55], [1.1, -0.55], [1.1, 0.55]].forEach(function(p) {
|
|
1201
|
+
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.7, 8), walnutMat);
|
|
1202
|
+
leg.position.set(p[0], 0.35, p[1]);
|
|
1203
|
+
ptGroup.add(leg);
|
|
1204
|
+
});
|
|
1205
|
+
S.furnitureGroup.add(ptGroup);
|
|
1206
|
+
|
|
1207
|
+
// Foosball table
|
|
1208
|
+
var fbGroup = new THREE.Group();
|
|
1209
|
+
fbGroup.position.set(x + 2.5, 0, z);
|
|
1210
|
+
var fbBody = new THREE.Mesh(new THREE.BoxGeometry(1.4, 0.2, 0.75),
|
|
1211
|
+
new THREE.MeshStandardMaterial({ color: 0x2a1a0a, roughness: 0.6 }));
|
|
1212
|
+
fbBody.position.y = 0.85; fbBody.castShadow = true;
|
|
1213
|
+
fbGroup.add(fbBody);
|
|
1214
|
+
var fbField = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.02, 0.6),
|
|
1215
|
+
new THREE.MeshStandardMaterial({ color: 0x006633, roughness: 0.8 }));
|
|
1216
|
+
fbField.position.y = 0.96;
|
|
1217
|
+
fbGroup.add(fbField);
|
|
1218
|
+
// Legs
|
|
1219
|
+
[[-0.6, -0.3], [-0.6, 0.3], [0.6, -0.3], [0.6, 0.3]].forEach(function(p) {
|
|
1220
|
+
var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.75, 6), chromeMat);
|
|
1221
|
+
leg.position.set(p[0], 0.38, p[1]);
|
|
1222
|
+
fbGroup.add(leg);
|
|
1223
|
+
});
|
|
1224
|
+
// Rods
|
|
1225
|
+
[-0.3, 0, 0.3].forEach(function(rz) {
|
|
1226
|
+
var rod = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.9, 6), chromeMat);
|
|
1227
|
+
rod.position.set(0, 0.98, rz);
|
|
1228
|
+
rod.rotation.z = Math.PI / 2;
|
|
1229
|
+
fbGroup.add(rod);
|
|
1230
|
+
});
|
|
1231
|
+
S.furnitureGroup.add(fbGroup);
|
|
1232
|
+
|
|
1233
|
+
// Beanbags
|
|
1234
|
+
var bbColors = [0xe53e3e, 0x3b82f6, 0x22c55e, 0xa855f7];
|
|
1235
|
+
[{ x: -1, z: 3 }, { x: 1.5, z: 3.5 }, { x: 3, z: 2.5 }, { x: -2.5, z: 3.5 }].forEach(function(bp, bi) {
|
|
1236
|
+
var bbMat = new THREE.MeshStandardMaterial({ color: bbColors[bi], roughness: 0.9 });
|
|
1237
|
+
var bot = new THREE.Mesh(new THREE.SphereGeometry(0.45, 16, 12), bbMat);
|
|
1238
|
+
bot.position.set(x + bp.x, 0.22, z + bp.z);
|
|
1239
|
+
bot.scale.set(1, 0.5, 1);
|
|
1240
|
+
bot.castShadow = true;
|
|
1241
|
+
S.furnitureGroup.add(bot);
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
// Big TV on the back
|
|
1245
|
+
var tvMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
|
|
1246
|
+
var tv = new THREE.Mesh(new THREE.BoxGeometry(3, 1.8, 0.08), tvMat);
|
|
1247
|
+
tv.position.set(x, 2.5, z - 3.8);
|
|
1248
|
+
tv.castShadow = true;
|
|
1249
|
+
S.furnitureGroup.add(tv);
|
|
1250
|
+
var tvScreen = new THREE.Mesh(new THREE.PlaneGeometry(2.8, 1.6),
|
|
1251
|
+
new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: 0x1a2a4a, emissiveIntensity: 0.3, roughness: 0.1 }));
|
|
1252
|
+
tvScreen.position.set(x, 2.5, z - 3.75);
|
|
1253
|
+
S.furnitureGroup.add(tvScreen);
|
|
1254
|
+
|
|
1255
|
+
// "REC ZONE" sign
|
|
1256
|
+
var signDiv = document.createElement('div');
|
|
1257
|
+
signDiv.textContent = 'REC ZONE';
|
|
1258
|
+
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;';
|
|
1259
|
+
var sign = new CSS2DObject(signDiv);
|
|
1260
|
+
sign.position.set(x, 4.5, z);
|
|
1261
|
+
S.furnitureGroup.add(sign);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// ==================== GYM ====================
|
|
1265
|
+
function buildGym(x, z, chromeMat, darkMat) {
|
|
1266
|
+
// Rubber floor
|
|
1267
|
+
var rubberMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.95 });
|
|
1268
|
+
var gymFloor = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), rubberMat);
|
|
1269
|
+
gymFloor.rotation.x = -Math.PI / 2;
|
|
1270
|
+
gymFloor.position.set(x, 0.01, z);
|
|
1271
|
+
gymFloor.receiveShadow = true;
|
|
1272
|
+
S.furnitureGroup.add(gymFloor);
|
|
1273
|
+
|
|
1274
|
+
// Treadmill
|
|
1275
|
+
var tmGroup = new THREE.Group();
|
|
1276
|
+
tmGroup.position.set(x - 1.5, 0, z - 2);
|
|
1277
|
+
var tmBase = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.15, 1.6), darkMat);
|
|
1278
|
+
tmBase.position.y = 0.1; tmBase.castShadow = true;
|
|
1279
|
+
tmGroup.add(tmBase);
|
|
1280
|
+
var tmBelt = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.02, 1.3),
|
|
1281
|
+
new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.8 }));
|
|
1282
|
+
tmBelt.position.y = 0.19;
|
|
1283
|
+
tmGroup.add(tmBelt);
|
|
1284
|
+
// Handles
|
|
1285
|
+
[-0.3, 0.3].forEach(function(hx) {
|
|
1286
|
+
var handle = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 1, 6), chromeMat);
|
|
1287
|
+
handle.position.set(hx, 0.7, -0.6);
|
|
1288
|
+
tmGroup.add(handle);
|
|
1289
|
+
});
|
|
1290
|
+
var console2 = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.25, 0.08), darkMat);
|
|
1291
|
+
console2.position.set(0, 1.1, -0.65);
|
|
1292
|
+
tmGroup.add(console2);
|
|
1293
|
+
S.furnitureGroup.add(tmGroup);
|
|
1294
|
+
|
|
1295
|
+
// Dumbbell rack
|
|
1296
|
+
var rackBase = new THREE.Mesh(new THREE.BoxGeometry(2, 0.8, 0.4), chromeMat);
|
|
1297
|
+
rackBase.position.set(x + 1.5, 0.4, z - 3);
|
|
1298
|
+
rackBase.castShadow = true;
|
|
1299
|
+
S.furnitureGroup.add(rackBase);
|
|
1300
|
+
// Dumbbells on rack
|
|
1301
|
+
for (var di = 0; di < 5; di++) {
|
|
1302
|
+
var dbMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5, metalness: 0.4 });
|
|
1303
|
+
var db = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.3, 8), dbMat);
|
|
1304
|
+
db.position.set(x + 0.7 + di * 0.35, 0.9, z - 3);
|
|
1305
|
+
db.rotation.z = Math.PI / 2;
|
|
1306
|
+
S.furnitureGroup.add(db);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// Yoga mat area
|
|
1310
|
+
var yogaMat = new THREE.MeshStandardMaterial({ color: 0x7c3aed, roughness: 0.9 });
|
|
1311
|
+
var mat = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.02, 1.8), yogaMat);
|
|
1312
|
+
mat.position.set(x + 2, 0.02, z + 1);
|
|
1313
|
+
S.furnitureGroup.add(mat);
|
|
1314
|
+
var mat2 = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.02, 1.8),
|
|
1315
|
+
new THREE.MeshStandardMaterial({ color: 0x06b6d4, roughness: 0.9 }));
|
|
1316
|
+
mat2.position.set(x + 3, 0.02, z + 1);
|
|
1317
|
+
S.furnitureGroup.add(mat2);
|
|
1318
|
+
|
|
1319
|
+
// "FITNESS" sign
|
|
1320
|
+
var signDiv = document.createElement('div');
|
|
1321
|
+
signDiv.textContent = 'FITNESS';
|
|
1322
|
+
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;';
|
|
1323
|
+
var sign = new CSS2DObject(signDiv);
|
|
1324
|
+
sign.position.set(x, 4, z);
|
|
1325
|
+
S.furnitureGroup.add(sign);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// ==================== PLANTS ====================
|
|
1329
|
+
function buildCampusPlants() {
|
|
1330
|
+
var plantPositions = [
|
|
1331
|
+
[-20, 8], [20, 8], [-20, -5], [20, -5],
|
|
1332
|
+
[-8, 10], [8, 10], [-8, -8], [8, -8],
|
|
1333
|
+
[0, 10], [-15, 5], [15, 5],
|
|
1334
|
+
[-6, -10], [6, -10],
|
|
1335
|
+
];
|
|
1336
|
+
plantPositions.forEach(function(pos) {
|
|
1337
|
+
buildLuxuryPlant(pos[0], pos[1]);
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
// Indoor trees (taller, premium)
|
|
1341
|
+
[[-18, 0], [18, 0], [0, -7]].forEach(function(pos) {
|
|
1342
|
+
buildIndoorTree(pos[0], pos[1]);
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
function buildLuxuryPlant(x, z) {
|
|
1347
|
+
var group = new THREE.Group();
|
|
1348
|
+
group.position.set(x, 0, z);
|
|
1349
|
+
// Concrete planter
|
|
1350
|
+
var planterMat = new THREE.MeshStandardMaterial({ color: 0x4a4a5a, roughness: 0.6 });
|
|
1351
|
+
var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.2, 0.5, 12), planterMat);
|
|
1352
|
+
planter.position.y = 0.25; planter.castShadow = true;
|
|
1353
|
+
group.add(planter);
|
|
1354
|
+
// Lush greenery
|
|
1355
|
+
var leafMat = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.8 });
|
|
1356
|
+
for (var i = 0; i < 6; i++) {
|
|
1357
|
+
var a = (i / 6) * Math.PI * 2;
|
|
1358
|
+
var leaf = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 6), leafMat);
|
|
1359
|
+
leaf.position.set(Math.cos(a) * 0.15, 0.6, Math.sin(a) * 0.15);
|
|
1360
|
+
leaf.castShadow = true;
|
|
1361
|
+
group.add(leaf);
|
|
1362
|
+
}
|
|
1363
|
+
var topLeaf = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), leafMat);
|
|
1364
|
+
topLeaf.position.y = 0.75;
|
|
1365
|
+
group.add(topLeaf);
|
|
1366
|
+
S.furnitureGroup.add(group);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function buildIndoorTree(x, z) {
|
|
1370
|
+
var group = new THREE.Group();
|
|
1371
|
+
group.position.set(x, 0, z);
|
|
1372
|
+
// Large planter
|
|
1373
|
+
var planterMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.5 });
|
|
1374
|
+
var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.4, 0.35, 0.6, 12), planterMat);
|
|
1375
|
+
planter.position.y = 0.3; planter.castShadow = true;
|
|
1376
|
+
group.add(planter);
|
|
1377
|
+
// Trunk
|
|
1378
|
+
var trunkMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e, roughness: 0.8 });
|
|
1379
|
+
var trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.1, 2.5, 8), trunkMat);
|
|
1380
|
+
trunk.position.y = 1.85; trunk.castShadow = true;
|
|
1381
|
+
group.add(trunk);
|
|
1382
|
+
// Canopy (layered spheres)
|
|
1383
|
+
var canopyMat = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.85 });
|
|
1384
|
+
var canopyMat2 = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.85 });
|
|
1385
|
+
[{ y: 2.8, r: 0.6 }, { y: 3.2, r: 0.5 }, { y: 3.5, r: 0.35 }].forEach(function(c, ci) {
|
|
1386
|
+
var canopy = new THREE.Mesh(new THREE.SphereGeometry(c.r, 12, 10), ci % 2 === 0 ? canopyMat : canopyMat2);
|
|
1387
|
+
canopy.position.y = c.y;
|
|
1388
|
+
canopy.castShadow = true;
|
|
1389
|
+
group.add(canopy);
|
|
1390
|
+
});
|
|
1391
|
+
S.furnitureGroup.add(group);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// ==================== PENDANT LIGHTS ====================
|
|
1395
|
+
function buildPendantLights() {
|
|
1396
|
+
var lightPositions = [
|
|
1397
|
+
[0, 2], [0, -1], [0, -4],
|
|
1398
|
+
[-4.5, 2], [-4.5, -1], [-4.5, -4],
|
|
1399
|
+
[4.5, 2], [4.5, -1], [4.5, -4],
|
|
1400
|
+
[-12, 1], [-12, -2],
|
|
1401
|
+
[12, 5],
|
|
1402
|
+
[-14, -12], [0, -12], [14, -12],
|
|
1403
|
+
];
|
|
1404
|
+
lightPositions.forEach(function(pos) {
|
|
1405
|
+
buildPendantLight(pos[0], pos[1]);
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
function buildPendantLight(x, z) {
|
|
1410
|
+
var group = new THREE.Group();
|
|
1411
|
+
group.position.set(x, 0, z);
|
|
1412
|
+
// Wire
|
|
1413
|
+
var wireMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5 });
|
|
1414
|
+
var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 1.5, 4), wireMat);
|
|
1415
|
+
wire.position.y = WALL_H - 0.75;
|
|
1416
|
+
group.add(wire);
|
|
1417
|
+
// Shade (industrial style)
|
|
1418
|
+
var shadeMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.5, metalness: 0.3, side: THREE.DoubleSide });
|
|
1419
|
+
var shade = new THREE.Mesh(new THREE.ConeGeometry(0.25, 0.2, 12, 1, true), shadeMat);
|
|
1420
|
+
shade.position.y = WALL_H - 1.55;
|
|
1421
|
+
group.add(shade);
|
|
1422
|
+
// Warm light
|
|
1423
|
+
var light = new THREE.PointLight(0xffeedd, 0.25, 6);
|
|
1424
|
+
light.position.set(0, WALL_H - 1.7, 0);
|
|
1425
|
+
light.castShadow = false; // performance
|
|
1426
|
+
group.add(light);
|
|
1427
|
+
S.furnitureGroup.add(group);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// ==================== GLASS PARTITIONS ====================
|
|
1431
|
+
function buildGlassPartitions(glassMat, frameMat) {
|
|
1432
|
+
// Between coder zone and rec area
|
|
1433
|
+
var partition1 = new THREE.Mesh(new THREE.PlaneGeometry(14, 2.5), glassMat);
|
|
1434
|
+
partition1.position.set(0, 1.25, -7);
|
|
1435
|
+
S.furnitureGroup.add(partition1);
|
|
1436
|
+
var frame1 = new THREE.Mesh(new THREE.BoxGeometry(14, 0.04, 0.04), frameMat);
|
|
1437
|
+
frame1.position.set(0, 2.5, -7);
|
|
1438
|
+
S.furnitureGroup.add(frame1);
|
|
1439
|
+
|
|
1440
|
+
// Between designer area and main
|
|
1441
|
+
var partition2 = new THREE.Mesh(new THREE.PlaneGeometry(10, 2.5), glassMat);
|
|
1442
|
+
partition2.position.set(-8, 1.25, 0);
|
|
1443
|
+
partition2.rotation.y = Math.PI / 2;
|
|
1444
|
+
S.furnitureGroup.add(partition2);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// ==================== NEON SIGNS ====================
|
|
1448
|
+
function buildNeonSign(text, x, y, z, neonMat) {
|
|
1449
|
+
// Glow bar behind text
|
|
1450
|
+
var glowBar = new THREE.Mesh(new THREE.BoxGeometry(text.length * 0.4, 0.4, 0.04), neonMat);
|
|
1451
|
+
glowBar.position.set(x, y, z);
|
|
1452
|
+
S.furnitureGroup.add(glowBar);
|
|
1453
|
+
// CSS label
|
|
1454
|
+
var color = '#' + neonMat.color.getHexString();
|
|
1455
|
+
var div = document.createElement('div');
|
|
1456
|
+
div.textContent = text;
|
|
1457
|
+
div.style.cssText = 'color:' + color + ';font-size:12px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:4px;text-shadow:0 0 12px ' + color + ',0 0 24px ' + color + ';';
|
|
1458
|
+
var label = new CSS2DObject(div);
|
|
1459
|
+
label.position.set(x, y, z + 0.05);
|
|
1460
|
+
S.furnitureGroup.add(label);
|
|
1461
|
+
}
|