awacke1 commited on
Commit
99001f9
·
verified ·
1 Parent(s): b1af438

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +569 -18
index.html CHANGED
@@ -1,19 +1,570 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Three.js Dynamic Simulated City - Corner Columns</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ body { margin: 0; overflow: hidden; background-color: #000000; color: #e2e8f0; font-family: 'Inter', sans-serif; }
10
+ canvas { display: block; }
11
+ #infoPanel {
12
+ position: absolute;
13
+ top: 20px;
14
+ left: 20px;
15
+ background-color: rgba(0,0,0,0.75);
16
+ padding: 15px;
17
+ border-radius: 8px;
18
+ color: white;
19
+ font-size: 0.85em;
20
+ max-width: 320px;
21
+ box-shadow: 0 4px 12px rgba(0,0,0,0.5);
22
+ max-height: 90vh;
23
+ overflow-y: auto;
24
+ }
25
+ #infoPanel h2 {
26
+ margin-top: 0;
27
+ font-size: 1.1em;
28
+ border-bottom: 1px solid #4a5568;
29
+ padding-bottom: 5px;
30
+ margin-bottom: 10px;
31
+ }
32
+ #infoPanel p, #infoPanel ul {
33
+ margin-bottom: 8px;
34
+ line-height: 1.5;
35
+ }
36
+ #infoPanel ul {
37
+ list-style: disc;
38
+ padding-left: 20px;
39
+ }
40
+ #loadingScreen {
41
+ position: fixed;
42
+ top: 0;
43
+ left: 0;
44
+ width: 100%;
45
+ height: 100%;
46
+ background-color: #111827; /* Darker initial loading */
47
+ display: flex;
48
+ flex-direction: column;
49
+ justify-content: center;
50
+ align-items: center;
51
+ z-index: 9999;
52
+ color: white;
53
+ font-size: 1.5em;
54
+ }
55
+ .spinner {
56
+ border: 4px solid rgba(255, 255, 255, 0.3);
57
+ border-radius: 50%;
58
+ border-top: 4px solid #fff;
59
+ width: 40px;
60
+ height: 40px;
61
+ animation: spin 1s linear infinite;
62
+ margin-bottom: 20px;
63
+ }
64
+ @keyframes spin {
65
+ 0% { transform: rotate(0deg); }
66
+ 100% { transform: rotate(360deg); }
67
+ }
68
+ </style>
69
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
70
+ </head>
71
+ <body>
72
+ <div id="loadingScreen">
73
+ <div class="spinner"></div>
74
+ Loading Dynamic City...
75
+ </div>
76
+ <div id="infoPanel">
77
+ <h2>Dynamic Simulated City - V4</h2>
78
+ <p>Added stone-colored cylinder columns to building corners.</p>
79
+ <p><strong>Features Added/Improved:</strong></p>
80
+ <ul>
81
+ <li>Moving cars and people.</li>
82
+ <li>Birds flying.</li>
83
+ <li>Recessed building windows with lights.</li>
84
+ <li>Solid roof caps on buildings.</li>
85
+ <li>Internal floor slabs.</li>
86
+ <li>Corner columns on buildings.</li>
87
+ <li>Day/Night cycle with Sun & Moon.</li>
88
+ </ul>
89
+ <p><em>Use mouse to orbit, scroll to zoom, right-click to pan.</em></p>
90
+ </div>
91
+ <canvas id="cityCanvas"></canvas>
92
+
93
+ <script type="importmap">
94
+ {
95
+ "imports": {
96
+ "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
97
+ "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
98
+ }
99
+ }
100
+ </script>
101
+
102
+ <script type="module">
103
+ import * as THREE from 'three';
104
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
105
+
106
+ let scene, camera, renderer, controls;
107
+ const buildings = [];
108
+ const vehicles = [];
109
+ const pedestrians = [];
110
+ const birds = [];
111
+
112
+ const citySize = 20;
113
+ const buildingSpacing = 2.0;
114
+ const roadWidth = 0.5;
115
+ const buildingMaxHeight = 8;
116
+ const buildingMinHeight = 1;
117
+ const roofHeight = 0.2;
118
+ const floorSlabThickness = 0.05;
119
+ const typicalFloorHeight = 0.5;
120
+ const columnRadius = 0.1; // Radius of the corner columns
121
+
122
+ let sunLight, moonLight, ambientLight;
123
+ const skyRadius = citySize * buildingSpacing * 1.5;
124
+
125
+ const dayClearColor = new THREE.Color(0x87CEEB);
126
+ const nightClearColor = new THREE.Color(0x000020);
127
+ const dayFogColor = new THREE.Color(0x87CEEB);
128
+ const nightFogColor = new THREE.Color(0x000010);
129
+
130
+ // --- Core Three.js Setup ---
131
+ function init() {
132
+ const canvas = document.getElementById('cityCanvas');
133
+ renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
134
+ renderer.setSize(window.innerWidth, window.innerHeight);
135
+ renderer.setPixelRatio(window.devicePixelRatio);
136
+ renderer.shadowMap.enabled = true;
137
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
138
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
139
+ renderer.toneMappingExposure = 0.8;
140
+
141
+ scene = new THREE.Scene();
142
+
143
+ camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, skyRadius * 2.5);
144
+ camera.position.set(citySize * 0.7, citySize * 0.6, citySize * 0.7);
145
+
146
+ controls = new OrbitControls(camera, renderer.domElement);
147
+ controls.enableDamping = true;
148
+ controls.dampingFactor = 0.05;
149
+ controls.screenSpacePanning = false;
150
+ controls.minDistance = 3;
151
+ controls.maxDistance = citySize * 2.5;
152
+ controls.maxPolarAngle = Math.PI / 2 - 0.01;
153
+
154
+ // --- Lighting ---
155
+ ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
156
+ scene.add(ambientLight);
157
+
158
+ sunLight = new THREE.DirectionalLight(0xffffee, 0);
159
+ sunLight.castShadow = true;
160
+ sunLight.shadow.mapSize.width = 2048;
161
+ sunLight.shadow.mapSize.height = 2048;
162
+ sunLight.shadow.camera.near = 0.5;
163
+ sunLight.shadow.camera.far = skyRadius * 0.8;
164
+ sunLight.shadow.camera.left = -citySize * 1.5;
165
+ sunLight.shadow.camera.right = citySize * 1.5;
166
+ sunLight.shadow.camera.top = citySize * 1.5;
167
+ sunLight.shadow.camera.bottom = -citySize * 1.5;
168
+ sunLight.shadow.bias = -0.0005;
169
+ scene.add(sunLight);
170
+
171
+ moonLight = new THREE.DirectionalLight(0x7788cc, 0);
172
+ moonLight.castShadow = true;
173
+ moonLight.shadow.mapSize.width = 1024;
174
+ moonLight.shadow.mapSize.height = 1024;
175
+ moonLight.shadow.camera.near = 0.5;
176
+ moonLight.shadow.camera.far = skyRadius * 0.8;
177
+ moonLight.shadow.bias = -0.0005;
178
+ scene.add(moonLight);
179
+
180
+ // --- Ground ---
181
+ const groundGeometry = new THREE.PlaneGeometry(citySize * buildingSpacing * 1.2, citySize * buildingSpacing * 1.2);
182
+ const groundMaterial = new THREE.MeshStandardMaterial({
183
+ color: 0x445544,
184
+ roughness: 0.9,
185
+ metalness: 0.1
186
+ });
187
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
188
+ ground.rotation.x = -Math.PI / 2;
189
+ ground.receiveShadow = true;
190
+ scene.add(ground);
191
+
192
+ // --- City, Entities ---
193
+ generateCity();
194
+ createVehicles(30);
195
+ createPedestrians(50);
196
+ createBirds(20);
197
+
198
+ document.getElementById('loadingScreen').style.display = 'none';
199
+ window.addEventListener('resize', onWindowResize, false);
200
+ animate();
201
+ }
202
+
203
+ // --- Procedural City Generation with Windows, Roofs, Floor Slabs & Columns ---
204
+ function generateCity() {
205
+ const buildingBaseGeometry = new THREE.BoxGeometry(1, 1, 1);
206
+ const roofGeometry = new THREE.BoxGeometry(1, roofHeight, 1);
207
+ const floorSlabGeometry = new THREE.BoxGeometry(1, floorSlabThickness, 1);
208
+ const columnGeometry = new THREE.CylinderGeometry(columnRadius, columnRadius, 1, 12); // Height will be scaled
209
+
210
+ const floorSlabMaterial = new THREE.MeshStandardMaterial({
211
+ color: 0x606060,
212
+ roughness: 0.8,
213
+ metalness: 0.1,
214
+ });
215
+ const columnMaterial = new THREE.MeshStandardMaterial({
216
+ color: 0x787878, // Stone grey
217
+ roughness: 0.7,
218
+ metalness: 0.05,
219
+ });
220
+
221
+
222
+ for (let i = 0; i < citySize; i++) {
223
+ for (let j = 0; j < citySize; j++) {
224
+ if (Math.random() > 0.25) {
225
+ const buildingBodyHeight = THREE.MathUtils.randFloat(buildingMinHeight, buildingMaxHeight - roofHeight);
226
+ // Adjust building width/depth slightly to make space for columns visually, or columns will clip into facade
227
+ const buildingWidth = THREE.MathUtils.randFloat(0.7, buildingSpacing - roadWidth * 0.8 - columnRadius * 2.5);
228
+ const buildingDepth = THREE.MathUtils.randFloat(0.7, buildingSpacing - roadWidth * 0.8 - columnRadius * 2.5);
229
+
230
+
231
+ const buildingMaterial = new THREE.MeshStandardMaterial({
232
+ color: new THREE.Color().setHSL(Math.random() * 0.1 + 0.55, 0.1, Math.random() * 0.2 + 0.3),
233
+ roughness: THREE.MathUtils.randFloat(0.6, 0.9),
234
+ metalness: THREE.MathUtils.randFloat(0.0, 0.2),
235
+ });
236
+
237
+ // Create Building Body
238
+ const building = new THREE.Mesh(buildingBaseGeometry, buildingMaterial);
239
+ building.scale.set(buildingWidth, buildingBodyHeight, buildingDepth);
240
+ const buildingCenterX = (i - citySize / 2 + 0.5) * buildingSpacing;
241
+ const buildingCenterZ = (j - citySize / 2 + 0.5) * buildingSpacing;
242
+ building.position.set(buildingCenterX, buildingBodyHeight / 2, buildingCenterZ);
243
+ building.castShadow = true;
244
+ building.receiveShadow = true;
245
+ scene.add(building);
246
+ building.userData.windows = [];
247
+
248
+ // Create Roof Cap
249
+ const roofMaterial = buildingMaterial.clone();
250
+ roofMaterial.color.offsetHSL(0, 0, -0.05);
251
+ const roof = new THREE.Mesh(roofGeometry, roofMaterial);
252
+ roof.scale.set(buildingWidth * 1.05 + columnRadius*2, 1, buildingDepth * 1.05 + columnRadius*2); // Roof overhangs columns
253
+ roof.position.set(buildingCenterX, buildingBodyHeight + roofHeight / 2, buildingCenterZ);
254
+ roof.castShadow = true;
255
+ roof.receiveShadow = true;
256
+ scene.add(roof);
257
+
258
+ // Add Corner Columns
259
+ const columnPositions = [
260
+ { x: buildingWidth / 2, z: buildingDepth / 2 },
261
+ { x: -buildingWidth / 2, z: buildingDepth / 2 },
262
+ { x: buildingWidth / 2, z: -buildingDepth / 2 },
263
+ { x: -buildingWidth / 2, z: -buildingDepth / 2 },
264
+ ];
265
+
266
+ columnPositions.forEach(pos => {
267
+ const column = new THREE.Mesh(columnGeometry, columnMaterial.clone());
268
+ column.scale.y = buildingBodyHeight; // Scale cylinder height
269
+ column.position.set(
270
+ buildingCenterX + pos.x,
271
+ buildingBodyHeight / 2, // Center of the column height
272
+ buildingCenterZ + pos.z
273
+ );
274
+ column.castShadow = true;
275
+ column.receiveShadow = true;
276
+ scene.add(column);
277
+ });
278
+
279
+
280
+ // Add Floor Slabs
281
+ for (let fh = typicalFloorHeight; fh < buildingBodyHeight - typicalFloorHeight / 2; fh += typicalFloorHeight) {
282
+ const floorSlab = new THREE.Mesh(floorSlabGeometry, floorSlabMaterial.clone());
283
+ floorSlab.scale.set(buildingWidth * 0.98, 1, buildingDepth * 0.98); // Slightly smaller than building body
284
+ const localY = fh - buildingBodyHeight / 2;
285
+ floorSlab.position.set(0, localY, 0);
286
+ floorSlab.castShadow = true;
287
+ floorSlab.receiveShadow = true;
288
+ building.add(floorSlab);
289
+ }
290
+
291
+
292
+ // Add Windows (to the building body)
293
+ const windowSize = 0.15;
294
+ const windowSpacingFactor = 0.25;
295
+ const windowRecessOffset = -0.02;
296
+
297
+ const maxWindowHeight = buildingBodyHeight - (windowSpacingFactor * buildingBodyHeight / 2) - (windowSize / 2);
298
+
299
+ // Facade Z+ (Front)
300
+ for (let bh = windowSpacingFactor * buildingBodyHeight / 2; bh < maxWindowHeight; bh += windowSpacingFactor * buildingBodyHeight) {
301
+ for (let bw = -buildingWidth / 2 + windowSpacingFactor * buildingWidth / 2; bw < buildingWidth / 2 - windowSpacingFactor * buildingWidth / 4; bw += windowSpacingFactor * buildingWidth * 0.75) {
302
+ if (Math.random() < 0.8) {
303
+ const windowMat = new THREE.MeshStandardMaterial({
304
+ color: 0x050510, emissive: 0x000000, emissiveIntensity: 0, transparent: true, opacity: 0.65
305
+ });
306
+ const windowGeo = new THREE.PlaneGeometry(windowSize, windowSize);
307
+ const win = new THREE.Mesh(windowGeo, windowMat);
308
+ win.position.set(bw, bh - buildingBodyHeight / 2, buildingDepth / 2 + windowRecessOffset);
309
+ building.add(win);
310
+ building.userData.windows.push(win);
311
+ }
312
+ }
313
+ }
314
+
315
+ // Facade Z- (Back)
316
+ for (let bh = windowSpacingFactor * buildingBodyHeight / 2; bh < maxWindowHeight; bh += windowSpacingFactor * buildingBodyHeight) {
317
+ for (let bw = -buildingWidth / 2 + windowSpacingFactor * buildingWidth / 2; bw < buildingWidth / 2 - windowSpacingFactor * buildingWidth / 4; bw += windowSpacingFactor * buildingWidth * 0.75) {
318
+ if (Math.random() < 0.8) {
319
+ const windowMat = new THREE.MeshStandardMaterial({
320
+ color: 0x050510, emissive: 0x000000, emissiveIntensity: 0, transparent: true, opacity: 0.65
321
+ });
322
+ const windowGeo = new THREE.PlaneGeometry(windowSize, windowSize);
323
+ const win = new THREE.Mesh(windowGeo, windowMat);
324
+ win.position.set(bw, bh - buildingBodyHeight / 2, -buildingDepth / 2 - windowRecessOffset);
325
+ win.rotation.y = Math.PI;
326
+ building.add(win);
327
+ building.userData.windows.push(win);
328
+ }
329
+ }
330
+ }
331
+
332
+ // Facade X+ (Right)
333
+ for (let bh = windowSpacingFactor * buildingBodyHeight / 2; bh < maxWindowHeight; bh += windowSpacingFactor * buildingBodyHeight) {
334
+ for (let bd = -buildingDepth / 2 + windowSpacingFactor * buildingDepth / 2; bd < buildingDepth / 2 - windowSpacingFactor * buildingDepth / 4; bd += windowSpacingFactor * buildingDepth * 0.75) {
335
+ if (Math.random() < 0.8) {
336
+ const windowMat = new THREE.MeshStandardMaterial({
337
+ color: 0x050510, emissive: 0x000000, emissiveIntensity: 0, transparent: true, opacity: 0.65
338
+ });
339
+ const windowGeo = new THREE.PlaneGeometry(windowSize, windowSize);
340
+ const win = new THREE.Mesh(windowGeo, windowMat);
341
+ win.position.set(buildingWidth / 2 + windowRecessOffset, bh - buildingBodyHeight / 2, bd);
342
+ win.rotation.y = Math.PI / 2;
343
+ building.add(win);
344
+ building.userData.windows.push(win);
345
+ }
346
+ }
347
+ }
348
+
349
+ // Facade X- (Left)
350
+ for (let bh = windowSpacingFactor * buildingBodyHeight / 2; bh < maxWindowHeight; bh += windowSpacingFactor * buildingBodyHeight) {
351
+ for (let bd = -buildingDepth / 2 + windowSpacingFactor * buildingDepth / 2; bd < buildingDepth / 2 - windowSpacingFactor * buildingDepth / 4; bd += windowSpacingFactor * buildingDepth * 0.75) {
352
+ if (Math.random() < 0.8) {
353
+ const windowMat = new THREE.MeshStandardMaterial({
354
+ color: 0x050510, emissive: 0x000000, emissiveIntensity: 0, transparent: true, opacity: 0.65
355
+ });
356
+ const windowGeo = new THREE.PlaneGeometry(windowSize, windowSize);
357
+ const win = new THREE.Mesh(windowGeo, windowMat);
358
+ win.position.set(-buildingWidth / 2 - windowRecessOffset, bh - buildingBodyHeight / 2, bd);
359
+ win.rotation.y = -Math.PI / 2;
360
+ building.add(win);
361
+ building.userData.windows.push(win);
362
+ }
363
+ }
364
+ }
365
+ buildings.push(building);
366
+ }
367
+ }
368
+ }
369
+ }
370
+
371
+ // --- Create Moving Entities (Unchanged) ---
372
+ function createVehicles(count) {
373
+ const carGeo = new THREE.BoxGeometry(0.6, 0.25, 0.3);
374
+ const carMat = new THREE.MeshStandardMaterial({ color: 0xaa0000, roughness: 0.3, metalness: 0.5 });
375
+ for (let i = 0; i < count; i++) {
376
+ const vehicle = new THREE.Mesh(carGeo, carMat.clone());
377
+ vehicle.material.color.setHSL(Math.random(), 0.7, 0.5);
378
+ vehicle.castShadow = true;
379
+ const onXAxis = Math.random() > 0.5;
380
+ const roadLine = Math.floor(Math.random() * citySize) - citySize / 2 + 0.5;
381
+ vehicle.position.set(
382
+ onXAxis ? (Math.random() - 0.5) * citySize * buildingSpacing : roadLine * buildingSpacing + (Math.random() > 0.5 ? roadWidth : -roadWidth),
383
+ 0.125,
384
+ onXAxis ? roadLine * buildingSpacing + (Math.random() > 0.5 ? roadWidth : -roadWidth) : (Math.random() - 0.5) * citySize * buildingSpacing
385
+ );
386
+ vehicle.userData.speed = THREE.MathUtils.randFloat(0.02, 0.05) * (Math.random() > 0.5 ? 1 : -1);
387
+ vehicle.userData.axis = onXAxis ? 'x' : 'z';
388
+ if (vehicle.userData.axis === 'x') {
389
+ vehicle.rotation.y = vehicle.userData.speed > 0 ? 0 : Math.PI;
390
+ } else {
391
+ vehicle.rotation.y = vehicle.userData.speed > 0 ? -Math.PI / 2 : Math.PI / 2;
392
+ }
393
+ scene.add(vehicle);
394
+ vehicles.push(vehicle);
395
+ }
396
+ }
397
+
398
+ function createPedestrians(count) {
399
+ const pedGeo = new THREE.CylinderGeometry(0.05, 0.05, 0.3, 8);
400
+ const pedMat = new THREE.MeshStandardMaterial({ color: 0x00aa00, roughness: 0.8, metalness: 0.1 });
401
+ for (let i = 0; i < count; i++) {
402
+ const pedestrian = new THREE.Mesh(pedGeo, pedMat.clone());
403
+ pedestrian.material.color.setHSL(Math.random(), 0.6, 0.6);
404
+ pedestrian.castShadow = true;
405
+ const buildingIndex = Math.floor(Math.random() * buildings.length);
406
+ const targetBuilding = buildings[buildingIndex];
407
+ if (!targetBuilding) continue;
408
+
409
+ const side = Math.floor(Math.random() * 4);
410
+ let offsetX = 0, offsetZ = 0;
411
+ const actualBuildingWidth = targetBuilding.scale.x; // This is the inner facade width
412
+ const actualBuildingDepth = targetBuilding.scale.z; // This is the inner facade depth
413
+ const sidewalkGap = 0.35 + columnRadius; // Place pedestrians outside columns
414
+
415
+ if (side === 0) {
416
+ offsetZ = actualBuildingDepth / 2 + sidewalkGap;
417
+ offsetX = (Math.random() - 0.5) * actualBuildingWidth;
418
+ } else if (side === 1) {
419
+ offsetZ = -actualBuildingDepth / 2 - sidewalkGap;
420
+ offsetX = (Math.random() - 0.5) * actualBuildingWidth;
421
+ } else if (side === 2) {
422
+ offsetX = actualBuildingWidth / 2 + sidewalkGap;
423
+ offsetZ = (Math.random() - 0.5) * actualBuildingDepth;
424
+ } else {
425
+ offsetX = -actualBuildingWidth / 2 - sidewalkGap;
426
+ offsetZ = (Math.random() - 0.5) * actualBuildingDepth;
427
+ }
428
+
429
+ pedestrian.position.set(
430
+ targetBuilding.position.x + offsetX, // Relative to building center
431
+ 0.15,
432
+ targetBuilding.position.z + offsetZ // Relative to building center
433
+ );
434
+ pedestrian.userData.speed = THREE.MathUtils.randFloat(0.005, 0.01);
435
+ pedestrian.userData.direction = new THREE.Vector3(Math.random()-0.5, 0, Math.random()-0.5).normalize();
436
+ scene.add(pedestrian);
437
+ pedestrians.push(pedestrian);
438
+ }
439
+ }
440
+
441
+ function createBirds(count) {
442
+ const birdGeo = new THREE.SphereGeometry(0.1, 8, 6);
443
+ const birdMat = new THREE.MeshStandardMaterial({ color: 0x555555, roughness: 0.5 });
444
+ for (let i = 0; i < count; i++) {
445
+ const bird = new THREE.Mesh(birdGeo, birdMat.clone());
446
+ bird.material.color.setHex(Math.random() * 0xffffff);
447
+ bird.position.set(
448
+ (Math.random() - 0.5) * citySize * buildingSpacing * 0.8,
449
+ THREE.MathUtils.randFloat(buildingMaxHeight + 2, buildingMaxHeight + 10),
450
+ (Math.random() - 0.5) * citySize * buildingSpacing * 0.8
451
+ );
452
+ bird.userData.speed = THREE.MathUtils.randFloat(0.02, 0.06);
453
+ bird.userData.phase = Math.random() * Math.PI * 2;
454
+ bird.userData.amplitudeY = THREE.MathUtils.randFloat(0.5, 2);
455
+ bird.userData.radius = THREE.MathUtils.randFloat(citySize * 0.2, citySize * 0.5);
456
+ bird.userData.angle = Math.random() * Math.PI * 2;
457
+ bird.userData.angleSpeed = THREE.MathUtils.randFloat(0.001, 0.005) * (Math.random() > 0.5 ? 1: -1);
458
+
459
+ scene.add(bird);
460
+ birds.push(bird);
461
+ }
462
+ }
463
+
464
+
465
+ // --- Animation Loop (Unchanged) ---
466
+ let lastWindowUpdateTime = 0;
467
+ const windowUpdateInterval = 200;
468
+
469
+ function animate(time) {
470
+ requestAnimationFrame(animate);
471
+ const currentTime = time || 0;
472
+ const delta = currentTime - (lastWindowUpdateTime || 0);
473
+
474
+ const timeOfDay = (currentTime * 0.00002) % 1;
475
+ const sunAngle = timeOfDay * Math.PI * 2;
476
+
477
+ sunLight.position.set(
478
+ Math.cos(sunAngle) * skyRadius,
479
+ Math.sin(sunAngle) * skyRadius * 0.7,
480
+ Math.sin(sunAngle - Math.PI / 4) * skyRadius
481
+ );
482
+ sunLight.intensity = Math.max(0, Math.sin(sunAngle)) * 1.8;
483
+ sunLight.visible = sunLight.intensity > 0.01;
484
+
485
+ const moonAngle = sunAngle + Math.PI;
486
+ moonLight.position.set(
487
+ Math.cos(moonAngle) * skyRadius * 0.9,
488
+ Math.sin(moonAngle) * skyRadius * 0.6,
489
+ Math.sin(moonAngle - Math.PI / 4) * skyRadius * 0.9
490
+ );
491
+ moonLight.intensity = Math.max(0, Math.sin(moonAngle)) * 0.4;
492
+ moonLight.visible = moonLight.intensity > 0.01;
493
+
494
+ const dayFactor = Math.pow(Math.max(0, Math.sin(sunAngle)), 0.7);
495
+ ambientLight.intensity = dayFactor * 0.5 + 0.1;
496
+
497
+ scene.background = nightClearColor.clone().lerp(dayClearColor, dayFactor);
498
+ if (scene.fog) {
499
+ scene.fog.color = nightFogColor.clone().lerp(dayFogColor, dayFactor);
500
+ scene.fog.near = skyRadius * 0.2 * (1 - dayFactor * 0.5);
501
+ scene.fog.far = skyRadius * (1 - dayFactor * 0.3);
502
+ } else {
503
+ scene.fog = new THREE.Fog(scene.background, skyRadius * 0.2, skyRadius);
504
+ }
505
+
506
+ if (delta > windowUpdateInterval) {
507
+ lastWindowUpdateTime = currentTime;
508
+ buildings.forEach(building => {
509
+ building.userData.windows.forEach(win => {
510
+ if (Math.random() < 0.05) {
511
+ const isNight = dayFactor < 0.3;
512
+ const lightOnProb = isNight ? 0.6 : 0.15;
513
+
514
+ if (Math.random() < lightOnProb) {
515
+ win.material.emissive.setHex(0xffffaa);
516
+ win.material.emissiveIntensity = THREE.MathUtils.randFloat(0.5, 1.2);
517
+ win.material.opacity = 0.9;
518
+ } else {
519
+ win.material.emissive.setHex(0x000000);
520
+ win.material.emissiveIntensity = 0;
521
+ win.material.opacity = 0.65;
522
+ }
523
+ }
524
+ });
525
+ });
526
+ }
527
+
528
+ const cityBoundary = citySize * buildingSpacing / 2;
529
+ vehicles.forEach(v => {
530
+ if (v.userData.axis === 'x') {
531
+ v.position.x += v.userData.speed;
532
+ if (v.position.x > cityBoundary && v.userData.speed > 0) v.position.x = -cityBoundary;
533
+ if (v.position.x < -cityBoundary && v.userData.speed < 0) v.position.x = cityBoundary;
534
+ } else {
535
+ v.position.z += v.userData.speed;
536
+ if (v.position.z > cityBoundary && v.userData.speed > 0) v.position.z = -cityBoundary;
537
+ if (v.position.z < -cityBoundary && v.userData.speed < 0) v.position.z = cityBoundary;
538
+ }
539
+ });
540
+
541
+ pedestrians.forEach(p => {
542
+ p.position.addScaledVector(p.userData.direction, p.userData.speed);
543
+ if (Math.random() < 0.01) {
544
+ p.userData.direction.set(Math.random()-0.5, 0, Math.random()-0.5).normalize();
545
+ }
546
+ p.position.x = THREE.MathUtils.clamp(p.position.x, -cityBoundary * 1.1, cityBoundary * 1.1);
547
+ p.position.z = THREE.MathUtils.clamp(p.position.z, -cityBoundary * 1.1, cityBoundary * 1.1);
548
+ });
549
+
550
+ birds.forEach(b => {
551
+ b.userData.angle += b.userData.angleSpeed;
552
+ b.position.x = Math.cos(b.userData.angle) * b.userData.radius;
553
+ b.position.z = Math.sin(b.userData.angle) * b.userData.radius;
554
+ b.position.y = (buildingMaxHeight + 5) + Math.sin(currentTime * 0.001 * b.userData.speed + b.userData.phase) * b.userData.amplitudeY;
555
+ });
556
+
557
+ controls.update();
558
+ renderer.render(scene, camera);
559
+ }
560
+
561
+ function onWindowResize() {
562
+ camera.aspect = window.innerWidth / window.innerHeight;
563
+ camera.updateProjectionMatrix();
564
+ renderer.setSize(window.innerWidth, window.innerHeight);
565
+ }
566
+
567
+ init();
568
+ </script>
569
+ </body>
570
  </html>