let-them-talk 4.2.0 → 5.2.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 +640 -540
- package/README.md +592 -415
- package/cli.js +1089 -589
- package/conversation-templates/autonomous-feature.json +22 -0
- package/conversation-templates/code-review.json +21 -11
- package/conversation-templates/debug-squad.json +21 -11
- package/conversation-templates/feature-build.json +21 -11
- package/conversation-templates/research-write.json +21 -11
- package/dashboard.html +9250 -7771
- package/dashboard.js +1232 -29
- package/office/agents.js +148 -4
- package/office/animation.js +68 -0
- package/office/assets.js +431 -0
- package/office/builder.js +355 -0
- package/office/building-interior.js +261 -0
- package/office/campus-env.js +119 -23
- package/office/car-hud.js +368 -0
- package/office/daynight.js +221 -0
- package/office/economy-hud.js +432 -0
- package/office/economy-ui.js +238 -0
- package/office/environment.js +818 -808
- package/office/face.js +65 -0
- package/office/fast-travel.js +215 -0
- package/office/hq-building.js +295 -0
- package/office/index.js +1095 -423
- package/office/instancing.js +160 -0
- package/office/lod-manager.js +165 -0
- package/office/multiplayer-hud.js +428 -0
- package/office/net-client.js +299 -0
- package/office/particles.js +172 -0
- package/office/player.js +658 -436
- package/office/post-processing.js +82 -0
- package/office/sky.js +319 -0
- package/office/street-furniture.js +308 -0
- package/office/vehicle.js +455 -0
- package/office/world-save.js +91 -0
- package/package.json +59 -59
- package/server.js +7190 -4685
- package/conversation-templates/managed-team.json +0 -12
package/office/environment.js
CHANGED
|
@@ -1,808 +1,818 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
|
3
|
-
import { S } from './state.js';
|
|
4
|
-
import { FLOOR_W, FLOOR_D, DESK_POSITIONS, RECEPTION_POS, ENVS, DRESSING_ROOM_POS, REST_AREA_POS } from './constants.js';
|
|
5
|
-
import { buildCampusEnvironment, getCampusDeskPositions } from './campus-env.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (child.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
child.material.dispose();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
S.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
S.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
var
|
|
70
|
-
|
|
71
|
-
var
|
|
72
|
-
var
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
var
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
var
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
var
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
var
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
var
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
var
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
var
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
chairGroup.
|
|
193
|
-
|
|
194
|
-
var
|
|
195
|
-
var
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
var
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
var
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
var
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
var
|
|
267
|
-
var
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
var
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
group.
|
|
288
|
-
var
|
|
289
|
-
var
|
|
290
|
-
var
|
|
291
|
-
|
|
292
|
-
group.add(
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
var
|
|
300
|
-
var
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
group.
|
|
309
|
-
var
|
|
310
|
-
var
|
|
311
|
-
var
|
|
312
|
-
|
|
313
|
-
group.add(
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
var
|
|
321
|
-
var
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
group.
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
var
|
|
337
|
-
var
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
group.
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
var
|
|
352
|
-
|
|
353
|
-
var
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
});
|
|
364
|
-
//
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
group.
|
|
404
|
-
//
|
|
405
|
-
var
|
|
406
|
-
var
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
var
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
screen
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
var
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
ctx.
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
ctx.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
var
|
|
449
|
-
var
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
ctx.
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
ctx.
|
|
472
|
-
ctx.
|
|
473
|
-
y +=
|
|
474
|
-
|
|
475
|
-
//
|
|
476
|
-
ctx.fillStyle = '#546178'; ctx.
|
|
477
|
-
ctx.fillText(
|
|
478
|
-
y +=
|
|
479
|
-
|
|
480
|
-
//
|
|
481
|
-
ctx.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
ctx.fillStyle = '#
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
group.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
var
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
var
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
var
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
group.add(
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
var
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
var
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
group.
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
var
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
var
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
//
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
S.furnitureGroup.add(
|
|
662
|
-
//
|
|
663
|
-
var
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
var
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
var
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
var
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
var
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
var
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
var
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
var
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
//
|
|
788
|
-
var
|
|
789
|
-
var
|
|
790
|
-
var
|
|
791
|
-
|
|
792
|
-
group.add(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
var
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
|
3
|
+
import { S } from './state.js';
|
|
4
|
+
import { FLOOR_W, FLOOR_D, DESK_POSITIONS, RECEPTION_POS, ENVS, DRESSING_ROOM_POS, REST_AREA_POS } from './constants.js';
|
|
5
|
+
import { buildCampusEnvironment, getCampusDeskPositions } from './campus-env.js';
|
|
6
|
+
// city-env loaded dynamically to avoid killing campus FPS
|
|
7
|
+
|
|
8
|
+
export function buildEnvironment() {
|
|
9
|
+
if (S.furnitureGroup) {
|
|
10
|
+
var css2dElements = [];
|
|
11
|
+
S.furnitureGroup.traverse(function(child) {
|
|
12
|
+
if (child.isCSS2DObject) css2dElements.push(child);
|
|
13
|
+
});
|
|
14
|
+
S.scene.remove(S.furnitureGroup);
|
|
15
|
+
if (S.cssRenderer) S.cssRenderer.render(S.scene, S.camera);
|
|
16
|
+
css2dElements.forEach(function(obj) {
|
|
17
|
+
if (obj.element && obj.element.parentElement) obj.element.remove();
|
|
18
|
+
});
|
|
19
|
+
S.furnitureGroup.traverse(function(child) {
|
|
20
|
+
if (child.geometry) child.geometry.dispose();
|
|
21
|
+
if (child.material) {
|
|
22
|
+
if (Array.isArray(child.material)) child.material.forEach(function(m) { m.dispose(); });
|
|
23
|
+
else {
|
|
24
|
+
if (child.material.map) child.material.map.dispose();
|
|
25
|
+
child.material.dispose();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
S.furnitureGroup = new THREE.Group();
|
|
31
|
+
S.deskMeshes = [];
|
|
32
|
+
|
|
33
|
+
if (S.currentEnv === 'campus') {
|
|
34
|
+
buildCampusEnvironment();
|
|
35
|
+
// Store campus desk positions for agent assignment
|
|
36
|
+
S._campusDeskPositions = getCampusDeskPositions();
|
|
37
|
+
S.scene.add(S.furnitureGroup);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (S.currentEnv === 'city') {
|
|
42
|
+
import('./city-env.js').then(function(mod) {
|
|
43
|
+
mod.buildCityEnvironment();
|
|
44
|
+
S._cityDeskPositions = mod.getCityDeskPositions();
|
|
45
|
+
S.scene.add(S.furnitureGroup);
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var env = ENVS[S.currentEnv] || ENVS.modern;
|
|
51
|
+
|
|
52
|
+
buildFloor(env);
|
|
53
|
+
buildWalls(env);
|
|
54
|
+
buildReception(env);
|
|
55
|
+
DESK_POSITIONS.forEach(function(pos, i) { buildDesk(pos.x, pos.z, i, env); });
|
|
56
|
+
buildDecorations(env);
|
|
57
|
+
buildDressingRoom(env);
|
|
58
|
+
buildRestArea(env);
|
|
59
|
+
buildWingDivider(env);
|
|
60
|
+
|
|
61
|
+
S.scene.add(S.furnitureGroup);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildFloor(env) {
|
|
65
|
+
var size = 512;
|
|
66
|
+
var canvas = document.createElement('canvas');
|
|
67
|
+
canvas.width = size; canvas.height = size;
|
|
68
|
+
var ctx = canvas.getContext('2d');
|
|
69
|
+
var tiles = 16;
|
|
70
|
+
var ts = size / tiles;
|
|
71
|
+
var c1 = '#' + env.floor1.toString(16).padStart(6, '0');
|
|
72
|
+
var c2 = '#' + env.floor2.toString(16).padStart(6, '0');
|
|
73
|
+
for (var i = 0; i < tiles; i++) {
|
|
74
|
+
for (var j = 0; j < tiles; j++) {
|
|
75
|
+
ctx.fillStyle = (i + j) % 2 === 0 ? c1 : c2;
|
|
76
|
+
ctx.fillRect(i * ts, j * ts, ts, ts);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
var tex = new THREE.CanvasTexture(canvas);
|
|
80
|
+
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
|
|
81
|
+
var geo = new THREE.PlaneGeometry(FLOOR_W, FLOOR_D);
|
|
82
|
+
var mat = new THREE.MeshStandardMaterial({ map: tex, roughness: 0.8 });
|
|
83
|
+
var floor = new THREE.Mesh(geo, mat);
|
|
84
|
+
floor.rotation.x = -Math.PI / 2;
|
|
85
|
+
floor.receiveShadow = true;
|
|
86
|
+
S.furnitureGroup.add(floor);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function buildWalls(env) {
|
|
90
|
+
var wallMat = new THREE.MeshStandardMaterial({ color: env.wall, roughness: 0.9, side: THREE.DoubleSide });
|
|
91
|
+
|
|
92
|
+
var backWall = new THREE.Mesh(new THREE.PlaneGeometry(FLOOR_W, 5), wallMat);
|
|
93
|
+
backWall.position.set(0, 2.5, -FLOOR_D / 2);
|
|
94
|
+
backWall.receiveShadow = true;
|
|
95
|
+
S.furnitureGroup.add(backWall);
|
|
96
|
+
|
|
97
|
+
var leftWall = new THREE.Mesh(new THREE.PlaneGeometry(FLOOR_D, 5), wallMat);
|
|
98
|
+
leftWall.position.set(-FLOOR_W / 2, 2.5, 0);
|
|
99
|
+
leftWall.rotation.y = Math.PI / 2;
|
|
100
|
+
leftWall.receiveShadow = true;
|
|
101
|
+
S.furnitureGroup.add(leftWall);
|
|
102
|
+
|
|
103
|
+
var rightWall = new THREE.Mesh(new THREE.PlaneGeometry(FLOOR_D, 5), wallMat);
|
|
104
|
+
rightWall.position.set(FLOOR_W / 2, 2.5, 0);
|
|
105
|
+
rightWall.rotation.y = -Math.PI / 2;
|
|
106
|
+
rightWall.receiveShadow = true;
|
|
107
|
+
S.furnitureGroup.add(rightWall);
|
|
108
|
+
|
|
109
|
+
var windowMat = new THREE.MeshStandardMaterial({
|
|
110
|
+
color: 0x87CEEB, emissive: 0x87CEEB, emissiveIntensity: 0.3, roughness: 0.1
|
|
111
|
+
});
|
|
112
|
+
[-2, -1, 1, 2].forEach(function(i) {
|
|
113
|
+
var win = new THREE.Mesh(new THREE.PlaneGeometry(1.8, 2), windowMat);
|
|
114
|
+
win.position.set(i * 3.5, 3, -FLOOR_D / 2 + 0.01);
|
|
115
|
+
S.furnitureGroup.add(win);
|
|
116
|
+
});
|
|
117
|
+
[-1, 0, 1].forEach(function(i) {
|
|
118
|
+
var win = new THREE.Mesh(new THREE.PlaneGeometry(2, 1.8), windowMat);
|
|
119
|
+
win.position.set(-FLOOR_W / 2 + 0.01, 3, i * 3.5);
|
|
120
|
+
win.rotation.y = Math.PI / 2;
|
|
121
|
+
S.furnitureGroup.add(win);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildReception(env) {
|
|
126
|
+
var rx = RECEPTION_POS.x, rz = RECEPTION_POS.z;
|
|
127
|
+
var deskGeo = new THREE.BoxGeometry(3, 1, 1.2);
|
|
128
|
+
var deskMat = new THREE.MeshStandardMaterial({ color: 0x5a3e28, roughness: 0.6 });
|
|
129
|
+
var desk = new THREE.Mesh(deskGeo, deskMat);
|
|
130
|
+
desk.position.set(rx, 0.5, rz);
|
|
131
|
+
desk.castShadow = true; desk.receiveShadow = true;
|
|
132
|
+
S.furnitureGroup.add(desk);
|
|
133
|
+
|
|
134
|
+
var topGeo = new THREE.BoxGeometry(3.2, 0.08, 1.4);
|
|
135
|
+
var topMat = new THREE.MeshStandardMaterial({ color: 0x7a5a3e, roughness: 0.4 });
|
|
136
|
+
var top = new THREE.Mesh(topGeo, topMat);
|
|
137
|
+
top.position.set(rx, 1.04, rz); top.castShadow = true;
|
|
138
|
+
S.furnitureGroup.add(top);
|
|
139
|
+
|
|
140
|
+
var bellGeo = new THREE.SphereGeometry(0.08, 16, 12);
|
|
141
|
+
var bellMat = new THREE.MeshStandardMaterial({ color: 0xd4af37, metalness: 0.8, roughness: 0.2 });
|
|
142
|
+
var bell = new THREE.Mesh(bellGeo, bellMat);
|
|
143
|
+
bell.position.set(rx + 0.8, 1.12, rz);
|
|
144
|
+
S.furnitureGroup.add(bell);
|
|
145
|
+
|
|
146
|
+
var signDiv = document.createElement('div');
|
|
147
|
+
signDiv.textContent = 'RECEPTION';
|
|
148
|
+
signDiv.style.cssText = 'color:#d4af37;font-size:11px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:1px;';
|
|
149
|
+
var signLabel = new CSS2DObject(signDiv);
|
|
150
|
+
signLabel.position.set(rx, 1.5, rz);
|
|
151
|
+
S.furnitureGroup.add(signLabel);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildDesk(x, z, index, env) {
|
|
155
|
+
var group = new THREE.Group();
|
|
156
|
+
group.position.set(x, 0, z);
|
|
157
|
+
|
|
158
|
+
var topGeo = new THREE.BoxGeometry(1.8, 0.08, 0.9);
|
|
159
|
+
var topMat = new THREE.MeshStandardMaterial({ color: env.desk, roughness: 0.5 });
|
|
160
|
+
var top = new THREE.Mesh(topGeo, topMat);
|
|
161
|
+
top.position.y = 0.75; top.castShadow = true; top.receiveShadow = true;
|
|
162
|
+
group.add(top);
|
|
163
|
+
|
|
164
|
+
var legGeo = new THREE.BoxGeometry(0.06, 0.75, 0.06);
|
|
165
|
+
var legMat = new THREE.MeshStandardMaterial({ color: env.deskLegs, roughness: 0.7 });
|
|
166
|
+
[[-0.8, -0.35], [-0.8, 0.35], [0.8, -0.35], [0.8, 0.35]].forEach(function(pos) {
|
|
167
|
+
var leg = new THREE.Mesh(legGeo, legMat);
|
|
168
|
+
leg.position.set(pos[0], 0.375, pos[1]); leg.castShadow = true;
|
|
169
|
+
group.add(leg);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
var monGeo = new THREE.BoxGeometry(0.5, 0.35, 0.04);
|
|
173
|
+
var monMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.3 });
|
|
174
|
+
var monitor = new THREE.Mesh(monGeo, monMat);
|
|
175
|
+
monitor.position.set(0, 1.1, -0.2); monitor.castShadow = true;
|
|
176
|
+
group.add(monitor);
|
|
177
|
+
|
|
178
|
+
var screenGeo = new THREE.PlaneGeometry(0.44, 0.28);
|
|
179
|
+
var screenMat = new THREE.MeshStandardMaterial({
|
|
180
|
+
color: 0x333333, emissive: 0x333333, emissiveIntensity: 0.1, roughness: 0.2
|
|
181
|
+
});
|
|
182
|
+
var screen = new THREE.Mesh(screenGeo, screenMat);
|
|
183
|
+
screen.position.set(0, 1.1, -0.179);
|
|
184
|
+
group.add(screen);
|
|
185
|
+
|
|
186
|
+
var standGeo = new THREE.BoxGeometry(0.06, 0.2, 0.06);
|
|
187
|
+
var stand = new THREE.Mesh(standGeo, legMat);
|
|
188
|
+
stand.position.set(0, 0.88, -0.2);
|
|
189
|
+
group.add(stand);
|
|
190
|
+
|
|
191
|
+
// Chair
|
|
192
|
+
var chairGroup = new THREE.Group();
|
|
193
|
+
chairGroup.position.set(0, 0, 0.7);
|
|
194
|
+
var seatGeo = new THREE.CylinderGeometry(0.25, 0.25, 0.06, 16);
|
|
195
|
+
var seatMat = new THREE.MeshStandardMaterial({ color: env.chairSeat, roughness: 0.7 });
|
|
196
|
+
var seat = new THREE.Mesh(seatGeo, seatMat);
|
|
197
|
+
seat.position.y = 0.45; seat.castShadow = true;
|
|
198
|
+
chairGroup.add(seat);
|
|
199
|
+
var postGeo = new THREE.CylinderGeometry(0.03, 0.03, 0.45, 8);
|
|
200
|
+
var post = new THREE.Mesh(postGeo, legMat);
|
|
201
|
+
post.position.y = 0.225;
|
|
202
|
+
chairGroup.add(post);
|
|
203
|
+
var backGeo = new THREE.BoxGeometry(0.45, 0.35, 0.04);
|
|
204
|
+
var backMat = new THREE.MeshStandardMaterial({ color: env.chair, roughness: 0.6 });
|
|
205
|
+
var back = new THREE.Mesh(backGeo, backMat);
|
|
206
|
+
back.position.set(0, 0.7, 0.2); back.castShadow = true;
|
|
207
|
+
chairGroup.add(back);
|
|
208
|
+
group.add(chairGroup);
|
|
209
|
+
|
|
210
|
+
S.furnitureGroup.add(group);
|
|
211
|
+
S.deskMeshes.push({ group: group, screen: screen, screenMat: screenMat, index: index, x: x, z: z });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function buildDecorations(env) {
|
|
215
|
+
var isStartup = S.currentEnv === 'startup';
|
|
216
|
+
buildPlant(-9, -6.5);
|
|
217
|
+
buildPlant(9, -6.5);
|
|
218
|
+
buildPlant(-9, -2);
|
|
219
|
+
buildPlant(9, -2);
|
|
220
|
+
buildWhiteboard(-9.5, 1);
|
|
221
|
+
buildBookshelf(-9.5, -4.5);
|
|
222
|
+
buildFloorLamp(-8.5, 4.5);
|
|
223
|
+
buildFloorLamp(6, 5);
|
|
224
|
+
buildAreaRug(0, -1);
|
|
225
|
+
if (isStartup) {
|
|
226
|
+
buildPizzaBox(9, 1);
|
|
227
|
+
buildBeanbag(9, -6);
|
|
228
|
+
buildBeanbag(-9, 3);
|
|
229
|
+
buildArcadeMachine(-8.5, -6);
|
|
230
|
+
buildTV(0, -7.5);
|
|
231
|
+
} else {
|
|
232
|
+
buildCoffeeMachine(9, 1);
|
|
233
|
+
buildWatercooler(9, -4);
|
|
234
|
+
buildTV(0, -7.5);
|
|
235
|
+
buildBookshelf(-9.5, 3);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function buildPlant(x, z) {
|
|
240
|
+
var group = new THREE.Group();
|
|
241
|
+
group.position.set(x, 0, z);
|
|
242
|
+
var potGeo = new THREE.CylinderGeometry(0.2, 0.15, 0.4, 12);
|
|
243
|
+
var potMat = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.8 });
|
|
244
|
+
var pot = new THREE.Mesh(potGeo, potMat);
|
|
245
|
+
pot.position.y = 0.2; pot.castShadow = true;
|
|
246
|
+
group.add(pot);
|
|
247
|
+
var leafColors = [0x2d8a4e, 0x34a853, 0x28a745];
|
|
248
|
+
for (var i = 0; i < 5; i++) {
|
|
249
|
+
var angle = (i / 5) * Math.PI * 2;
|
|
250
|
+
var leafGeo = new THREE.SphereGeometry(0.15, 8, 6);
|
|
251
|
+
var leafMat = new THREE.MeshStandardMaterial({ color: leafColors[i % 3], roughness: 0.8 });
|
|
252
|
+
var leaf = new THREE.Mesh(leafGeo, leafMat);
|
|
253
|
+
leaf.position.set(Math.cos(angle) * 0.15, 0.55, Math.sin(angle) * 0.15);
|
|
254
|
+
leaf.scale.set(1, 0.7, 1); leaf.castShadow = true;
|
|
255
|
+
group.add(leaf);
|
|
256
|
+
}
|
|
257
|
+
var topLeaf = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), new THREE.MeshStandardMaterial({ color: 0x34a853, roughness: 0.8 }));
|
|
258
|
+
topLeaf.position.y = 0.7; topLeaf.castShadow = true;
|
|
259
|
+
group.add(topLeaf);
|
|
260
|
+
S.furnitureGroup.add(group);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function buildWhiteboard(x, z) {
|
|
264
|
+
var group = new THREE.Group();
|
|
265
|
+
group.position.set(x, 0, z);
|
|
266
|
+
var boardGeo = new THREE.BoxGeometry(0.08, 1.5, 2);
|
|
267
|
+
var boardMat = new THREE.MeshStandardMaterial({ color: 0xe8e8e8, roughness: 0.3 });
|
|
268
|
+
var board = new THREE.Mesh(boardGeo, boardMat);
|
|
269
|
+
board.position.y = 1.8; board.castShadow = true;
|
|
270
|
+
group.add(board);
|
|
271
|
+
var frameMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.5 });
|
|
272
|
+
[2.55, 1.05].forEach(function(y) {
|
|
273
|
+
var frame = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.05, 2.1), frameMat);
|
|
274
|
+
frame.position.y = y; group.add(frame);
|
|
275
|
+
});
|
|
276
|
+
var legGeo = new THREE.CylinderGeometry(0.03, 0.03, 1, 8);
|
|
277
|
+
var legMat2 = new THREE.MeshStandardMaterial({ color: 0x666666, roughness: 0.5 });
|
|
278
|
+
[-0.8, 0.8].forEach(function(lz) {
|
|
279
|
+
var leg = new THREE.Mesh(legGeo, legMat2);
|
|
280
|
+
leg.position.set(0, 0.5, lz); group.add(leg);
|
|
281
|
+
});
|
|
282
|
+
S.furnitureGroup.add(group);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function buildCoffeeMachine(x, z) {
|
|
286
|
+
var group = new THREE.Group();
|
|
287
|
+
group.position.set(x, 0, z);
|
|
288
|
+
var bodyGeo = new THREE.BoxGeometry(0.5, 0.8, 0.4);
|
|
289
|
+
var bodyMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5 });
|
|
290
|
+
var body = new THREE.Mesh(bodyGeo, bodyMat);
|
|
291
|
+
body.position.y = 0.8; body.castShadow = true;
|
|
292
|
+
group.add(body);
|
|
293
|
+
var tableGeo = new THREE.BoxGeometry(0.7, 0.04, 0.5);
|
|
294
|
+
var tableMat = new THREE.MeshStandardMaterial({ color: 0x5a3e28, roughness: 0.6 });
|
|
295
|
+
var table = new THREE.Mesh(tableGeo, tableMat);
|
|
296
|
+
table.position.y = 0.4; table.castShadow = true;
|
|
297
|
+
group.add(table);
|
|
298
|
+
var cupGeo = new THREE.CylinderGeometry(0.04, 0.03, 0.08, 12);
|
|
299
|
+
var cupMat = new THREE.MeshStandardMaterial({ color: 0xf5f5f5, roughness: 0.3 });
|
|
300
|
+
var cup = new THREE.Mesh(cupGeo, cupMat);
|
|
301
|
+
cup.position.set(0.2, 0.46, 0);
|
|
302
|
+
group.add(cup);
|
|
303
|
+
S.furnitureGroup.add(group);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function buildPizzaBox(x, z) {
|
|
307
|
+
var group = new THREE.Group();
|
|
308
|
+
group.position.set(x, 0, z);
|
|
309
|
+
var tableGeo = new THREE.BoxGeometry(0.7, 0.04, 0.5);
|
|
310
|
+
var tableMat = new THREE.MeshStandardMaterial({ color: 0x5a3e28, roughness: 0.6 });
|
|
311
|
+
var table = new THREE.Mesh(tableGeo, tableMat);
|
|
312
|
+
table.position.y = 0.4; table.castShadow = true;
|
|
313
|
+
group.add(table);
|
|
314
|
+
var boxGeo = new THREE.BoxGeometry(0.5, 0.06, 0.5);
|
|
315
|
+
var boxMat = new THREE.MeshStandardMaterial({ color: 0xd4a24e, roughness: 0.8 });
|
|
316
|
+
var box = new THREE.Mesh(boxGeo, boxMat);
|
|
317
|
+
box.position.y = 0.46; box.castShadow = true;
|
|
318
|
+
group.add(box);
|
|
319
|
+
var lidGeo = new THREE.BoxGeometry(0.5, 0.03, 0.5);
|
|
320
|
+
var lidMat = new THREE.MeshStandardMaterial({ color: 0xe8c06a, roughness: 0.8 });
|
|
321
|
+
var lid = new THREE.Mesh(lidGeo, lidMat);
|
|
322
|
+
lid.position.set(0, 0.5, -0.22); lid.rotation.x = -0.6;
|
|
323
|
+
group.add(lid);
|
|
324
|
+
S.furnitureGroup.add(group);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function buildBeanbag(x, z) {
|
|
328
|
+
var group = new THREE.Group();
|
|
329
|
+
group.position.set(x, 0, z);
|
|
330
|
+
var botGeo = new THREE.SphereGeometry(0.4, 16, 12);
|
|
331
|
+
var botMat = new THREE.MeshStandardMaterial({ color: 0xe53e3e, roughness: 0.9 });
|
|
332
|
+
var bottom = new THREE.Mesh(botGeo, botMat);
|
|
333
|
+
bottom.position.y = 0.2; bottom.scale.set(1, 0.5, 1); bottom.castShadow = true;
|
|
334
|
+
group.add(bottom);
|
|
335
|
+
var topGeo = new THREE.SphereGeometry(0.35, 16, 12);
|
|
336
|
+
var topMat = new THREE.MeshStandardMaterial({ color: 0xc53030, roughness: 0.9 });
|
|
337
|
+
var topPart = new THREE.Mesh(topGeo, topMat);
|
|
338
|
+
topPart.position.set(-0.05, 0.4, 0); topPart.scale.set(1, 0.6, 1); topPart.castShadow = true;
|
|
339
|
+
group.add(topPart);
|
|
340
|
+
S.furnitureGroup.add(group);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function buildWatercooler(x, z) {
|
|
344
|
+
var group = new THREE.Group();
|
|
345
|
+
group.position.set(x, 0, z);
|
|
346
|
+
var baseGeo = new THREE.BoxGeometry(0.3, 0.6, 0.3);
|
|
347
|
+
var baseMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.5 });
|
|
348
|
+
var base = new THREE.Mesh(baseGeo, baseMat);
|
|
349
|
+
base.position.y = 0.3; base.castShadow = true;
|
|
350
|
+
group.add(base);
|
|
351
|
+
var bottleGeo = new THREE.CylinderGeometry(0.12, 0.12, 0.4, 16);
|
|
352
|
+
var bottleMat = new THREE.MeshStandardMaterial({ color: 0x64b4ff, transparent: true, opacity: 0.5, roughness: 0.1 });
|
|
353
|
+
var bottle = new THREE.Mesh(bottleGeo, bottleMat);
|
|
354
|
+
bottle.position.y = 0.8;
|
|
355
|
+
group.add(bottle);
|
|
356
|
+
S.furnitureGroup.add(group);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ===================== BOOKSHELF =====================
|
|
360
|
+
function buildBookshelf(x, z) {
|
|
361
|
+
var group = new THREE.Group();
|
|
362
|
+
group.position.set(x, 0, z);
|
|
363
|
+
var frameMat = new THREE.MeshStandardMaterial({ color: 0x5a3e28, roughness: 0.7 });
|
|
364
|
+
// Main frame
|
|
365
|
+
var back = new THREE.Mesh(new THREE.BoxGeometry(0.08, 2.2, 1.2), frameMat);
|
|
366
|
+
back.position.y = 1.1; back.castShadow = true;
|
|
367
|
+
group.add(back);
|
|
368
|
+
// Shelves (4 levels)
|
|
369
|
+
[0.05, 0.55, 1.1, 1.65, 2.15].forEach(function(sy) {
|
|
370
|
+
var shelf = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.04, 1.2), frameMat);
|
|
371
|
+
shelf.position.set(0.13, sy, 0); shelf.castShadow = true; shelf.receiveShadow = true;
|
|
372
|
+
group.add(shelf);
|
|
373
|
+
});
|
|
374
|
+
// Side panels
|
|
375
|
+
[-0.58, 0.58].forEach(function(sz) {
|
|
376
|
+
var side = new THREE.Mesh(new THREE.BoxGeometry(0.35, 2.2, 0.04), frameMat);
|
|
377
|
+
side.position.set(0.13, 1.1, sz); side.castShadow = true;
|
|
378
|
+
group.add(side);
|
|
379
|
+
});
|
|
380
|
+
// Books (colored blocks on shelves)
|
|
381
|
+
var bookColors = [0xc0392b, 0x2980b9, 0x27ae60, 0x8e44ad, 0xe67e22, 0x2c3e50, 0xd4a24e, 0x1abc9c];
|
|
382
|
+
var shelfYs = [0.09, 0.59, 1.14, 1.69];
|
|
383
|
+
shelfYs.forEach(function(sy, si) {
|
|
384
|
+
var numBooks = 4 + Math.floor(Math.random() * 4);
|
|
385
|
+
var startZ = -0.45;
|
|
386
|
+
for (var bi = 0; bi < numBooks; bi++) {
|
|
387
|
+
var bh = 0.3 + Math.random() * 0.15;
|
|
388
|
+
var bw = 0.04 + Math.random() * 0.03;
|
|
389
|
+
var bookMat = new THREE.MeshStandardMaterial({ color: bookColors[(si * 5 + bi) % bookColors.length], roughness: 0.8 });
|
|
390
|
+
var book = new THREE.Mesh(new THREE.BoxGeometry(0.2, bh, bw), bookMat);
|
|
391
|
+
book.position.set(0.18, sy + bh / 2, startZ);
|
|
392
|
+
book.castShadow = true;
|
|
393
|
+
group.add(book);
|
|
394
|
+
startZ += bw + 0.02;
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
S.furnitureGroup.add(group);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ===================== TV / MONITOR =====================
|
|
401
|
+
function buildTV(x, z) {
|
|
402
|
+
var group = new THREE.Group();
|
|
403
|
+
group.position.set(x, 0, z);
|
|
404
|
+
// Wall mount bracket
|
|
405
|
+
var bracketMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5, metalness: 0.3 });
|
|
406
|
+
var bracket = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.3, 0.06), bracketMat);
|
|
407
|
+
bracket.position.y = 2.2;
|
|
408
|
+
group.add(bracket);
|
|
409
|
+
// TV body — wide on X, thin on Z (mounted on back wall, facing +Z into room)
|
|
410
|
+
var tvMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.3 });
|
|
411
|
+
var tvBody = new THREE.Mesh(new THREE.BoxGeometry(1.6, 1, 0.08), tvMat);
|
|
412
|
+
tvBody.position.y = 2.2; tvBody.castShadow = true;
|
|
413
|
+
group.add(tvBody);
|
|
414
|
+
// Animated screen canvas
|
|
415
|
+
var W = 320, H = 200;
|
|
416
|
+
var cvs = document.createElement('canvas');
|
|
417
|
+
cvs.width = W; cvs.height = H;
|
|
418
|
+
var tex = new THREE.CanvasTexture(cvs);
|
|
419
|
+
tex.minFilter = THREE.LinearFilter;
|
|
420
|
+
var screenMat = new THREE.MeshStandardMaterial({
|
|
421
|
+
map: tex, emissive: 0x58a6ff, emissiveIntensity: 0.25, roughness: 0.1
|
|
422
|
+
});
|
|
423
|
+
var screen = new THREE.Mesh(new THREE.PlaneGeometry(1.4, 0.9), screenMat);
|
|
424
|
+
screen.position.set(0, 2.2, 0.045);
|
|
425
|
+
group.add(screen);
|
|
426
|
+
// Store for animation updates
|
|
427
|
+
S._tvScreen = { canvas: cvs, texture: tex, tickerOffset: 0 };
|
|
428
|
+
S.furnitureGroup.add(group);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Called every ~1s from the sync interval to update the TV screen
|
|
432
|
+
export function updateTVScreen(time) {
|
|
433
|
+
var tv = S._tvScreen;
|
|
434
|
+
if (!tv) return;
|
|
435
|
+
var cvs = tv.canvas, ctx = cvs.getContext('2d');
|
|
436
|
+
var W = cvs.width, H = cvs.height;
|
|
437
|
+
|
|
438
|
+
// Clear
|
|
439
|
+
ctx.fillStyle = '#0a0e1a';
|
|
440
|
+
ctx.fillRect(0, 0, W, H);
|
|
441
|
+
|
|
442
|
+
// Top bar
|
|
443
|
+
ctx.fillStyle = '#111830';
|
|
444
|
+
ctx.fillRect(0, 0, W, 40);
|
|
445
|
+
ctx.fillStyle = '#58a6ff';
|
|
446
|
+
ctx.font = 'bold 20px monospace';
|
|
447
|
+
ctx.fillText('OFFICE DASHBOARD', 16, 27);
|
|
448
|
+
var now = new Date();
|
|
449
|
+
var timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
|
|
450
|
+
ctx.fillStyle = '#7ee787';
|
|
451
|
+
ctx.textAlign = 'right';
|
|
452
|
+
ctx.fillText(timeStr, W - 16, 27);
|
|
453
|
+
ctx.textAlign = 'left';
|
|
454
|
+
|
|
455
|
+
// Data
|
|
456
|
+
var agents = window.cachedAgents || {};
|
|
457
|
+
var history = window.cachedHistory || [];
|
|
458
|
+
var agentNames = Object.keys(agents);
|
|
459
|
+
var activeCount = 0, sleepCount = 0;
|
|
460
|
+
agentNames.forEach(function(n) {
|
|
461
|
+
if (agents[n].status === 'active') activeCount++;
|
|
462
|
+
else if (agents[n].status === 'sleeping') sleepCount++;
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
var y = 68;
|
|
466
|
+
ctx.font = '16px monospace';
|
|
467
|
+
|
|
468
|
+
// Stats row 1
|
|
469
|
+
ctx.fillStyle = '#546178'; ctx.fillText('AGENTS', 16, y);
|
|
470
|
+
ctx.fillStyle = '#d2a8ff'; ctx.fillText(String(agentNames.length), 110, y);
|
|
471
|
+
ctx.fillStyle = '#4ade80'; ctx.fillText(activeCount + ' active', 140, y);
|
|
472
|
+
ctx.fillStyle = '#facc15'; ctx.fillText(sleepCount + ' idle', 280, y);
|
|
473
|
+
y += 26;
|
|
474
|
+
|
|
475
|
+
// Stats row 2
|
|
476
|
+
ctx.fillStyle = '#546178'; ctx.fillText('MESSAGES', 16, y);
|
|
477
|
+
ctx.fillStyle = '#79c0ff'; ctx.fillText(String(history.length), 130, y);
|
|
478
|
+
y += 26;
|
|
479
|
+
|
|
480
|
+
// Separator
|
|
481
|
+
ctx.strokeStyle = '#1a2744'; ctx.lineWidth = 1;
|
|
482
|
+
ctx.beginPath(); ctx.moveTo(16, y); ctx.lineTo(W - 16, y); ctx.stroke();
|
|
483
|
+
y += 20;
|
|
484
|
+
|
|
485
|
+
// Activity header
|
|
486
|
+
ctx.fillStyle = '#546178'; ctx.font = '14px monospace';
|
|
487
|
+
ctx.fillText('RECENT ACTIVITY', 16, y);
|
|
488
|
+
y += 22;
|
|
489
|
+
|
|
490
|
+
// Messages
|
|
491
|
+
ctx.font = '13px monospace';
|
|
492
|
+
var recentMsgs = history.slice(-7);
|
|
493
|
+
for (var i = 0; i < recentMsgs.length; i++) {
|
|
494
|
+
if (y > H - 50) break;
|
|
495
|
+
var msg = recentMsgs[i];
|
|
496
|
+
var from = msg.from || '?';
|
|
497
|
+
var to2 = msg.to || 'all';
|
|
498
|
+
var content = msg.content || msg.message || '';
|
|
499
|
+
var maxLen = Math.floor((W - 40) / 7.5);
|
|
500
|
+
var snippet = content.length > maxLen ? content.substring(0, maxLen - 2) + '..' : content;
|
|
501
|
+
|
|
502
|
+
// From > To
|
|
503
|
+
ctx.fillStyle = '#7ee787'; ctx.fillText(from, 16, y);
|
|
504
|
+
var fromW = ctx.measureText(from).width;
|
|
505
|
+
ctx.fillStyle = '#546178'; ctx.fillText(' > ', 16 + fromW, y);
|
|
506
|
+
ctx.fillStyle = '#d2a8ff'; ctx.fillText(to2, 16 + fromW + ctx.measureText(' > ').width, y);
|
|
507
|
+
y += 18;
|
|
508
|
+
// Snippet
|
|
509
|
+
ctx.fillStyle = '#8892b0'; ctx.fillText(' ' + snippet, 16, y);
|
|
510
|
+
y += 22;
|
|
511
|
+
}
|
|
512
|
+
if (recentMsgs.length === 0) {
|
|
513
|
+
ctx.fillStyle = '#3d4663'; ctx.fillText(' Waiting for messages...', 16, y);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Bottom ticker
|
|
517
|
+
ctx.fillStyle = '#111830';
|
|
518
|
+
ctx.fillRect(0, H - 32, W, 32);
|
|
519
|
+
var tickerParts = [];
|
|
520
|
+
agentNames.forEach(function(n) {
|
|
521
|
+
var info = agents[n];
|
|
522
|
+
var st = info.status === 'active' ? '\u25CF' : '\u25CB';
|
|
523
|
+
tickerParts.push(st + ' ' + (info.display_name || n));
|
|
524
|
+
});
|
|
525
|
+
var tickerText = tickerParts.length > 0 ? tickerParts.join(' \u2022 ') + ' \u2022 ' : 'No agents online';
|
|
526
|
+
ctx.font = '15px monospace';
|
|
527
|
+
var charW = 9;
|
|
528
|
+
tv.tickerOffset = (tv.tickerOffset + 1.5) % (tickerText.length * charW);
|
|
529
|
+
ctx.fillStyle = '#58a6ff';
|
|
530
|
+
var fullTW = tickerText.length * charW;
|
|
531
|
+
ctx.fillText(tickerText, -tv.tickerOffset, H - 10);
|
|
532
|
+
ctx.fillText(tickerText, -tv.tickerOffset + fullTW, H - 10);
|
|
533
|
+
|
|
534
|
+
// Scanline overlay
|
|
535
|
+
ctx.fillStyle = 'rgba(0,0,0,0.04)';
|
|
536
|
+
for (var sl = 0; sl < H; sl += 2) {
|
|
537
|
+
ctx.fillRect(0, sl, W, 1);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
tv.texture.needsUpdate = true;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ===================== ARCADE MACHINE =====================
|
|
544
|
+
function buildArcadeMachine(x, z) {
|
|
545
|
+
var group = new THREE.Group();
|
|
546
|
+
group.position.set(x, 0, z);
|
|
547
|
+
var cabinetMat = new THREE.MeshStandardMaterial({ color: 0x2c1654, roughness: 0.7 });
|
|
548
|
+
// Main cabinet body
|
|
549
|
+
var cabinet = new THREE.Mesh(new THREE.BoxGeometry(0.6, 1.8, 0.7), cabinetMat);
|
|
550
|
+
cabinet.position.y = 0.9; cabinet.castShadow = true;
|
|
551
|
+
group.add(cabinet);
|
|
552
|
+
// Top section (angled screen housing)
|
|
553
|
+
var topMat = new THREE.MeshStandardMaterial({ color: 0x3a1f6e, roughness: 0.6 });
|
|
554
|
+
var top = new THREE.Mesh(new THREE.BoxGeometry(0.62, 0.5, 0.6), topMat);
|
|
555
|
+
top.position.set(0, 2.05, -0.05); top.castShadow = true;
|
|
556
|
+
group.add(top);
|
|
557
|
+
// Screen
|
|
558
|
+
var scrMat = new THREE.MeshStandardMaterial({ color: 0x000000, emissive: 0x00ff88, emissiveIntensity: 0.5, roughness: 0.1 });
|
|
559
|
+
var scr = new THREE.Mesh(new THREE.PlaneGeometry(0.35, 0.35), scrMat);
|
|
560
|
+
scr.position.set(0.31, 2.0, -0.05);
|
|
561
|
+
scr.rotation.y = Math.PI / 2;
|
|
562
|
+
group.add(scr);
|
|
563
|
+
// Control panel
|
|
564
|
+
var panelMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.5 });
|
|
565
|
+
var panel = new THREE.Mesh(new THREE.BoxGeometry(0.62, 0.1, 0.4), panelMat);
|
|
566
|
+
panel.position.set(0, 1.5, 0.2); panel.rotation.x = -0.3;
|
|
567
|
+
group.add(panel);
|
|
568
|
+
// Joystick
|
|
569
|
+
var joyMat = new THREE.MeshStandardMaterial({ color: 0xff0000, roughness: 0.4 });
|
|
570
|
+
var joyBase = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.02, 8), panelMat);
|
|
571
|
+
joyBase.position.set(0.1, 1.56, 0.15);
|
|
572
|
+
group.add(joyBase);
|
|
573
|
+
var joyStick = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.08, 6), joyMat);
|
|
574
|
+
joyStick.position.set(0.1, 1.6, 0.15);
|
|
575
|
+
group.add(joyStick);
|
|
576
|
+
var joyBall = new THREE.Mesh(new THREE.SphereGeometry(0.02, 8, 6), joyMat);
|
|
577
|
+
joyBall.position.set(0.1, 1.65, 0.15);
|
|
578
|
+
group.add(joyBall);
|
|
579
|
+
// Buttons
|
|
580
|
+
var btnColors = [0xff4444, 0x44ff44, 0x4444ff];
|
|
581
|
+
btnColors.forEach(function(col, bi) {
|
|
582
|
+
var btn = new THREE.Mesh(new THREE.CylinderGeometry(0.018, 0.018, 0.015, 8), new THREE.MeshStandardMaterial({ color: col, roughness: 0.3 }));
|
|
583
|
+
btn.position.set(-0.05 - bi * 0.06, 1.56, 0.15);
|
|
584
|
+
group.add(btn);
|
|
585
|
+
});
|
|
586
|
+
// Marquee sign
|
|
587
|
+
var marqueeDiv = document.createElement('div');
|
|
588
|
+
marqueeDiv.textContent = 'ARCADE';
|
|
589
|
+
marqueeDiv.style.cssText = 'color:#ff00ff;font-size:8px;font-weight:bold;font-family:monospace;letter-spacing:2px;text-shadow:0 0 4px #ff00ff;';
|
|
590
|
+
var marquee = new CSS2DObject(marqueeDiv);
|
|
591
|
+
marquee.position.set(0, 2.45, 0);
|
|
592
|
+
group.add(marquee);
|
|
593
|
+
S.furnitureGroup.add(group);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// ===================== FLOOR LAMP =====================
|
|
597
|
+
function buildFloorLamp(x, z) {
|
|
598
|
+
var group = new THREE.Group();
|
|
599
|
+
group.position.set(x, 0, z);
|
|
600
|
+
// Base
|
|
601
|
+
var baseMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5, metalness: 0.3 });
|
|
602
|
+
var base = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.18, 0.04, 12), baseMat);
|
|
603
|
+
base.position.y = 0.02; base.castShadow = true;
|
|
604
|
+
group.add(base);
|
|
605
|
+
// Pole
|
|
606
|
+
var pole = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 1.6, 8), baseMat);
|
|
607
|
+
pole.position.y = 0.84;
|
|
608
|
+
group.add(pole);
|
|
609
|
+
// Shade (cone)
|
|
610
|
+
var shadeMat = new THREE.MeshStandardMaterial({ color: 0xddd5c0, roughness: 0.8, side: THREE.DoubleSide });
|
|
611
|
+
var shade = new THREE.Mesh(new THREE.ConeGeometry(0.18, 0.25, 12, 1, true), shadeMat);
|
|
612
|
+
shade.position.y = 1.72; shade.castShadow = true;
|
|
613
|
+
group.add(shade);
|
|
614
|
+
// Light inside
|
|
615
|
+
var light = new THREE.PointLight(0xffeedd, 0.3, 4);
|
|
616
|
+
light.position.set(0, 1.6, 0);
|
|
617
|
+
group.add(light);
|
|
618
|
+
S.furnitureGroup.add(group);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ===================== AREA RUG =====================
|
|
622
|
+
function buildAreaRug(x, z) {
|
|
623
|
+
var group = new THREE.Group();
|
|
624
|
+
group.position.set(x, 0.005, z);
|
|
625
|
+
var isStartup = S.currentEnv === 'startup';
|
|
626
|
+
var rugColor = isStartup ? 0x3d2b1f : 0x232a35;
|
|
627
|
+
var borderColor = isStartup ? 0x4a3525 : 0x2a3340;
|
|
628
|
+
// Main rug
|
|
629
|
+
var rugMat = new THREE.MeshStandardMaterial({ color: rugColor, roughness: 0.95 });
|
|
630
|
+
var rug = new THREE.Mesh(new THREE.PlaneGeometry(6, 4), rugMat);
|
|
631
|
+
rug.rotation.x = -Math.PI / 2; rug.receiveShadow = true;
|
|
632
|
+
group.add(rug);
|
|
633
|
+
// Subtle border stripe (slightly lighter than rug, not bright)
|
|
634
|
+
var borderMat = new THREE.MeshStandardMaterial({ color: borderColor, roughness: 0.9 });
|
|
635
|
+
// Top/bottom borders
|
|
636
|
+
[-1.9, 1.9].forEach(function(bz) {
|
|
637
|
+
var stripe = new THREE.Mesh(new THREE.PlaneGeometry(5.8, 0.08), borderMat);
|
|
638
|
+
stripe.rotation.x = -Math.PI / 2;
|
|
639
|
+
stripe.position.set(0, 0.001, bz);
|
|
640
|
+
group.add(stripe);
|
|
641
|
+
});
|
|
642
|
+
// Left/right borders
|
|
643
|
+
[-2.9, 2.9].forEach(function(bx) {
|
|
644
|
+
var stripe = new THREE.Mesh(new THREE.PlaneGeometry(0.08, 3.8), borderMat);
|
|
645
|
+
stripe.rotation.x = -Math.PI / 2;
|
|
646
|
+
stripe.position.set(bx, 0.001, 0);
|
|
647
|
+
group.add(stripe);
|
|
648
|
+
});
|
|
649
|
+
S.furnitureGroup.add(group);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ===================== WING DIVIDER =====================
|
|
653
|
+
function buildWingDivider(env) {
|
|
654
|
+
var wallMat = new THREE.MeshStandardMaterial({ color: (ENVS[S.currentEnv] || ENVS.modern).wall, roughness: 0.9, side: THREE.DoubleSide });
|
|
655
|
+
// Partial wall separating main office from right wing (gap in middle for walking through)
|
|
656
|
+
// Upper section (z: -8 to -3.5)
|
|
657
|
+
var upper = new THREE.Mesh(new THREE.PlaneGeometry(4.5, 5), wallMat);
|
|
658
|
+
upper.position.set(7, 2.5, -5.75);
|
|
659
|
+
upper.rotation.y = Math.PI / 2;
|
|
660
|
+
upper.receiveShadow = true;
|
|
661
|
+
S.furnitureGroup.add(upper);
|
|
662
|
+
// Lower section (z: 0.5 to 4)
|
|
663
|
+
var lower = new THREE.Mesh(new THREE.PlaneGeometry(3.5, 5), wallMat);
|
|
664
|
+
lower.position.set(7, 2.5, 2.25);
|
|
665
|
+
lower.rotation.y = Math.PI / 2;
|
|
666
|
+
lower.receiveShadow = true;
|
|
667
|
+
S.furnitureGroup.add(lower);
|
|
668
|
+
// Archway header (connecting the two sections above the gap)
|
|
669
|
+
var header = new THREE.Mesh(new THREE.BoxGeometry(0.1, 1.5, 4), wallMat);
|
|
670
|
+
header.position.set(7, 4.25, -1.5);
|
|
671
|
+
S.furnitureGroup.add(header);
|
|
672
|
+
// "LOUNGE" sign above archway
|
|
673
|
+
var signDiv = document.createElement('div');
|
|
674
|
+
signDiv.textContent = 'LOUNGE';
|
|
675
|
+
signDiv.style.cssText = 'color:#6c8aff;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;opacity:0.7;';
|
|
676
|
+
var signLabel = new CSS2DObject(signDiv);
|
|
677
|
+
signLabel.position.set(7, 4.2, -1.5);
|
|
678
|
+
S.furnitureGroup.add(signLabel);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ===================== DRESSING ROOM =====================
|
|
682
|
+
function buildDressingRoom(env) {
|
|
683
|
+
var rx = DRESSING_ROOM_POS.x, rz = DRESSING_ROOM_POS.z;
|
|
684
|
+
var group = new THREE.Group();
|
|
685
|
+
group.position.set(rx, 0, rz);
|
|
686
|
+
|
|
687
|
+
// Floor platform (raised circular disc)
|
|
688
|
+
var platformGeo = new THREE.CylinderGeometry(0.6, 0.65, 0.06, 24);
|
|
689
|
+
var platformMat = new THREE.MeshStandardMaterial({ color: 0x4a4a5a, roughness: 0.4, metalness: 0.2 });
|
|
690
|
+
var platform = new THREE.Mesh(platformGeo, platformMat);
|
|
691
|
+
platform.position.y = 0.03; platform.receiveShadow = true; platform.castShadow = true;
|
|
692
|
+
group.add(platform);
|
|
693
|
+
// Platform rim (subtle glow ring)
|
|
694
|
+
var rimGeo = new THREE.TorusGeometry(0.62, 0.02, 8, 32);
|
|
695
|
+
var rimMat = new THREE.MeshStandardMaterial({ color: 0x6c8aff, emissive: 0x6c8aff, emissiveIntensity: 0.4, roughness: 0.3 });
|
|
696
|
+
var rim = new THREE.Mesh(rimGeo, rimMat);
|
|
697
|
+
rim.rotation.x = -Math.PI / 2; rim.position.y = 0.07;
|
|
698
|
+
group.add(rim);
|
|
699
|
+
|
|
700
|
+
// Mirror on the right wall (tall reflective rectangle)
|
|
701
|
+
var mirrorGeo = new THREE.PlaneGeometry(1.2, 2);
|
|
702
|
+
var mirrorMat = new THREE.MeshStandardMaterial({ color: 0xd0d8e8, emissive: 0x8899bb, emissiveIntensity: 0.15, roughness: 0.05, metalness: 0.8 });
|
|
703
|
+
var mirror = new THREE.Mesh(mirrorGeo, mirrorMat);
|
|
704
|
+
mirror.position.set(1.5, 1.2, 0); mirror.rotation.y = -Math.PI / 2;
|
|
705
|
+
group.add(mirror);
|
|
706
|
+
// Mirror frame
|
|
707
|
+
var frameMat = new THREE.MeshStandardMaterial({ color: 0x8B6914, roughness: 0.4, metalness: 0.3 });
|
|
708
|
+
var frameTop = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, 1.3), frameMat);
|
|
709
|
+
frameTop.position.set(1.52, 2.22, 0); group.add(frameTop);
|
|
710
|
+
var frameBot = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, 1.3), frameMat);
|
|
711
|
+
frameBot.position.set(1.52, 0.18, 0); group.add(frameBot);
|
|
712
|
+
var frameL = new THREE.Mesh(new THREE.BoxGeometry(0.06, 2.1, 0.06), frameMat);
|
|
713
|
+
frameL.position.set(1.52, 1.2, -0.65); group.add(frameL);
|
|
714
|
+
var frameR = new THREE.Mesh(new THREE.BoxGeometry(0.06, 2.1, 0.06), frameMat);
|
|
715
|
+
frameR.position.set(1.52, 1.2, 0.65); group.add(frameR);
|
|
716
|
+
|
|
717
|
+
// Left partition wall (half-height privacy screen)
|
|
718
|
+
var partMat = new THREE.MeshStandardMaterial({ color: 0x3a4050, roughness: 0.8, side: THREE.DoubleSide });
|
|
719
|
+
var partL = new THREE.Mesh(new THREE.BoxGeometry(0.06, 2.2, 2.5), partMat);
|
|
720
|
+
partL.position.set(-1.3, 1.1, 0);
|
|
721
|
+
partL.castShadow = true;
|
|
722
|
+
group.add(partL);
|
|
723
|
+
// Back partition
|
|
724
|
+
var partB = new THREE.Mesh(new THREE.BoxGeometry(2.6, 2.2, 0.06), partMat);
|
|
725
|
+
partB.position.set(0.1, 1.1, -1.3);
|
|
726
|
+
partB.castShadow = true;
|
|
727
|
+
group.add(partB);
|
|
728
|
+
|
|
729
|
+
// Coat hooks on back partition
|
|
730
|
+
var hookMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.3, metalness: 0.5 });
|
|
731
|
+
[-0.4, 0.2, 0.8].forEach(function(hx) {
|
|
732
|
+
var hook = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.12, 8), hookMat);
|
|
733
|
+
hook.position.set(hx, 1.6, -1.25); hook.rotation.x = Math.PI / 3;
|
|
734
|
+
group.add(hook);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
// Warm spotlight
|
|
738
|
+
var spotLight = new THREE.PointLight(0xffeedd, 0.6, 5);
|
|
739
|
+
spotLight.position.set(0, 3, 0);
|
|
740
|
+
group.add(spotLight);
|
|
741
|
+
|
|
742
|
+
// Sign
|
|
743
|
+
var signDiv = document.createElement('div');
|
|
744
|
+
signDiv.textContent = 'DRESSING ROOM';
|
|
745
|
+
signDiv.style.cssText = 'color:#d4af37;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:1.5px;';
|
|
746
|
+
var signLabel = new CSS2DObject(signDiv);
|
|
747
|
+
signLabel.position.set(0, 2.6, 0);
|
|
748
|
+
group.add(signLabel);
|
|
749
|
+
|
|
750
|
+
S.furnitureGroup.add(group);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// ===================== REST AREA =====================
|
|
754
|
+
function buildRestArea(env) {
|
|
755
|
+
var rx = REST_AREA_POS.x, rz = REST_AREA_POS.z;
|
|
756
|
+
var group = new THREE.Group();
|
|
757
|
+
group.position.set(rx, 0, rz);
|
|
758
|
+
|
|
759
|
+
// Soft rug (circular, textured)
|
|
760
|
+
var rugGeo = new THREE.CircleGeometry(1.8, 24);
|
|
761
|
+
var rugMat = new THREE.MeshStandardMaterial({ color: 0x4a3828, roughness: 0.95 });
|
|
762
|
+
var rug = new THREE.Mesh(rugGeo, rugMat);
|
|
763
|
+
rug.rotation.x = -Math.PI / 2; rug.position.y = 0.01; rug.receiveShadow = true;
|
|
764
|
+
group.add(rug);
|
|
765
|
+
|
|
766
|
+
// Beanbags (3, arranged in a cozy cluster)
|
|
767
|
+
var bbColors = [0xe53e3e, 0x3b82f6, 0x22c55e];
|
|
768
|
+
var bbPositions = [{ x: -0.6, z: 0.3 }, { x: 0.6, z: 0.4 }, { x: 0, z: -0.5 }];
|
|
769
|
+
bbPositions.forEach(function(pos, i) {
|
|
770
|
+
var bbGroup = new THREE.Group();
|
|
771
|
+
bbGroup.position.set(pos.x, 0, pos.z);
|
|
772
|
+
var botGeo = new THREE.SphereGeometry(0.4, 16, 12);
|
|
773
|
+
var botMat = new THREE.MeshStandardMaterial({ color: bbColors[i], roughness: 0.9 });
|
|
774
|
+
var bottom = new THREE.Mesh(botGeo, botMat);
|
|
775
|
+
bottom.position.y = 0.2; bottom.scale.set(1, 0.5, 1); bottom.castShadow = true;
|
|
776
|
+
bbGroup.add(bottom);
|
|
777
|
+
var topGeo = new THREE.SphereGeometry(0.35, 16, 12);
|
|
778
|
+
var topMat = new THREE.MeshStandardMaterial({ color: bbColors[i], roughness: 0.9 });
|
|
779
|
+
topMat.color.multiplyScalar(0.8);
|
|
780
|
+
var topPart = new THREE.Mesh(topGeo, topMat);
|
|
781
|
+
topPart.position.set(-0.05, 0.4, 0); topPart.scale.set(1, 0.6, 1); topPart.castShadow = true;
|
|
782
|
+
bbGroup.add(topPart);
|
|
783
|
+
bbGroup.rotation.y = (i / 3) * Math.PI * 2;
|
|
784
|
+
group.add(bbGroup);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Small side table
|
|
788
|
+
var tableGeo = new THREE.CylinderGeometry(0.2, 0.2, 0.04, 16);
|
|
789
|
+
var tableMat = new THREE.MeshStandardMaterial({ color: 0x5a3e28, roughness: 0.6 });
|
|
790
|
+
var table = new THREE.Mesh(tableGeo, tableMat);
|
|
791
|
+
table.position.set(1.2, 0.35, 0); table.castShadow = true;
|
|
792
|
+
group.add(table);
|
|
793
|
+
var tableLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.35, 8), tableMat);
|
|
794
|
+
tableLeg.position.set(1.2, 0.175, 0);
|
|
795
|
+
group.add(tableLeg);
|
|
796
|
+
|
|
797
|
+
// Coffee mug on table
|
|
798
|
+
var mugGeo = new THREE.CylinderGeometry(0.04, 0.03, 0.07, 12);
|
|
799
|
+
var mugMat = new THREE.MeshStandardMaterial({ color: 0xf5f5f5, roughness: 0.3 });
|
|
800
|
+
var mug = new THREE.Mesh(mugGeo, mugMat);
|
|
801
|
+
mug.position.set(1.2, 0.405, 0);
|
|
802
|
+
group.add(mug);
|
|
803
|
+
|
|
804
|
+
// Warm dim point light (cozy orange glow)
|
|
805
|
+
var warmLight = new THREE.PointLight(0xffaa55, 0.4, 6);
|
|
806
|
+
warmLight.position.set(0, 2.5, 0);
|
|
807
|
+
group.add(warmLight);
|
|
808
|
+
|
|
809
|
+
// Sign
|
|
810
|
+
var signDiv = document.createElement('div');
|
|
811
|
+
signDiv.textContent = 'REST AREA';
|
|
812
|
+
signDiv.style.cssText = 'color:#facc15;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:1.5px;';
|
|
813
|
+
var signLabel = new CSS2DObject(signDiv);
|
|
814
|
+
signLabel.position.set(0, 2.2, 0);
|
|
815
|
+
group.add(signLabel);
|
|
816
|
+
|
|
817
|
+
S.furnitureGroup.add(group);
|
|
818
|
+
}
|