jbilcke-hf HF staff commited on
Commit
45931a3
·
verified ·
1 Parent(s): 294d1f9

Upload 9 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ public/models/laser-gun.glb filter=lfs diff=lfs merge=lfs -text
37
+ public/models/pond-large.glb filter=lfs diff=lfs merge=lfs -text
38
+ public/models/pond-sparse.glb filter=lfs diff=lfs merge=lfs -text
index.html CHANGED
@@ -1,19 +1,544 @@
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>
3
+ <head>
4
+ <title>First Person Terrain Walker with Sky</title>
5
+ <style>
6
+ body { margin: 0; padding: 0; background: black; }
7
+ canvas { display: block; }
8
+ #instructions {
9
+ position: fixed;
10
+ top: 12px;
11
+ left: 12px;
12
+ background: rgba(0,0,0,0.4);
13
+ color: white;
14
+ padding: 10px;
15
+ font-family: Arial, sans-serif;
16
+ border-radius: 8px;
17
+ }
18
+ #downloadTexture {
19
+ display: block;
20
+ margin-top: 10px;
21
+ padding: 5px 10px;
22
+ background: #4CAF50;
23
+ color: white;
24
+ border: none;
25
+ border-radius: 4px;
26
+ cursor: pointer;
27
+ font-family: Arial, sans-serif;
28
+ }
29
+ #downloadTexture:hover {
30
+ background: #45a049;
31
+ }
32
+ #downloadTexture:disabled {
33
+ background: #cccccc;
34
+ cursor: not-allowed;
35
+ }
36
+ </style>
37
+ </head>
38
+ <body>
39
+ <div id="instructions">
40
+ WASD or Arrow Keys - Move<br>
41
+ Space - Jump<br>
42
+ Mouse - Look around<br>
43
+ Click to start<br>
44
+ <button id="downloadTexture" disabled>Download Texture</button>
45
+ </div>
46
+ <script type="importmap">
47
+ {
48
+ "imports": {
49
+ "three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.module.min.js",
50
+ "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/",
51
+ "three/examples/": "https://unpkg.com/[email protected]/examples/jsm/"
52
+ }
53
+ }
54
+ </script>
55
+ <script type="module">
56
+ import * as THREE from 'three';
57
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
58
+ import { PointerLockControls } from 'three/examples/controls/PointerLockControls.js';
59
+ import { Sky } from 'three/addons/objects/Sky.js';
60
+ import { Lensflare, LensflareElement } from 'three/addons/objects/Lensflare.js';
61
+
62
+ const USE_SPARSE = true;
63
+
64
+ // Scene setup
65
+ const scene = new THREE.Scene();
66
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
67
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
68
+ renderer.shadowMap.enabled = true;
69
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
70
+ renderer.setSize(window.innerWidth, window.innerHeight);
71
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
72
+ renderer.toneMappingExposure = 0.5;
73
+ document.body.appendChild(renderer.domElement);
74
+
75
+
76
+ // Weapon sway parameters
77
+ let currentWeaponSway = { x: 0, y: 0, z: 0 };
78
+ let targetWeaponSway = { x: 0, y: 0, z: 0 };
79
+ const maxSwayAmount = 0.03;
80
+ const swaySpeed = 0.1;
81
+ const swayLerpFactor = 0.1;
82
+ let lastSwayUpdate = 0;
83
+ const swayUpdateInterval = 150; // milliseconds
84
+
85
+
86
+ // Download button setup
87
+ const downloadButton = document.getElementById('downloadTexture');
88
+ let extractedTexture = null;
89
+
90
+ // Function to extract and download texture
91
+ function extractAndSaveTexture(mesh) {
92
+ if (!mesh.material || !mesh.material.map) {
93
+ console.warn('No texture found on this mesh');
94
+ return null;
95
+ }
96
+
97
+ const texture = mesh.material.map;
98
+ const canvas = document.createElement('canvas');
99
+ canvas.width = texture.image.width;
100
+ canvas.height = texture.image.height;
101
+
102
+ const ctx = canvas.getContext('2d');
103
+ ctx.drawImage(texture.image, 0, 0);
104
+
105
+ return canvas.toDataURL('image/png');
106
+ }
107
+
108
+ // Download button click handler
109
+ downloadButton.addEventListener('click', () => {
110
+ if (extractedTexture) {
111
+ const link = document.createElement('a');
112
+ link.href = extractedTexture;
113
+ link.download = 'terrain_texture.png';
114
+ document.body.appendChild(link);
115
+ link.click();
116
+ document.body.removeChild(link);
117
+ }
118
+ });
119
+
120
+ // Sky setup
121
+ const sky = new Sky();
122
+ sky.scale.setScalar(450000);
123
+ scene.add(sky);
124
+
125
+ const skyUniforms = sky.material.uniforms;
126
+ skyUniforms['turbidity'].value = 10;
127
+ skyUniforms['rayleigh'].value = 3;
128
+ skyUniforms['mieCoefficient'].value = 0.005;
129
+ skyUniforms['mieDirectionalG'].value = 0.7;
130
+
131
+ const sun = new THREE.Vector3();
132
+ const phi = THREE.MathUtils.degToRad(90 - 2);
133
+ const theta = THREE.MathUtils.degToRad(180);
134
+ sun.setFromSphericalCoords(1, phi, theta);
135
+ skyUniforms['sunPosition'].value.copy(sun);
136
+
137
+ // Lighting Setup
138
+ const ambientLight = new THREE.AmbientLight(0xfffdfd, 0.6);
139
+ scene.add(ambientLight);
140
+
141
+ const sunLight = new THREE.DirectionalLight(0xfffdfd, USE_SPARSE ? 0.9 : 3.0);
142
+ sunLight.position.set(50, 500, 50);
143
+ sunLight.castShadow = true;
144
+ sunLight.shadow.mapSize.width = 4096;
145
+ sunLight.shadow.mapSize.height = 4096;
146
+ sunLight.shadow.camera.near = 0.1;
147
+ sunLight.shadow.camera.far = 2000;
148
+ sunLight.shadow.camera.left = -500;
149
+ sunLight.shadow.camera.right = 500;
150
+ sunLight.shadow.camera.top = 500;
151
+ sunLight.shadow.camera.bottom = -500;
152
+ sunLight.shadow.bias = -0.0001;
153
+ scene.add(sunLight);
154
+
155
+ // Add Lensflare
156
+ const textureLoader = new THREE.TextureLoader();
157
+ const textureFlare0 = textureLoader.load('/public/textures/lensflare/lensflare0_alpha.png');
158
+ const textureFlare1 = textureLoader.load('/public/textures/lensflare/lensflare3.png');
159
+ const textureFlare2 = textureLoader.load('/public/textures/lensflare/lensflare3.png');
160
+
161
+ const lensflare = new Lensflare();
162
+ lensflare.addElement(new LensflareElement(textureFlare0, 700, 0));
163
+ lensflare.addElement(new LensflareElement(textureFlare1, 512, 0.6));
164
+ lensflare.addElement(new LensflareElement(textureFlare2, 170, 0.7));
165
+ lensflare.addElement(new LensflareElement(textureFlare2, 120, 0.9));
166
+ sunLight.add(lensflare);
167
+
168
+ const fillLight = new THREE.DirectionalLight(0xffffff, 1.2);
169
+ fillLight.position.set(-50, 50, -50);
170
+ scene.add(fillLight);
171
+
172
+ const rimLight = new THREE.DirectionalLight(0xffffff, 0.8);
173
+ rimLight.position.set(0, 20, -100);
174
+ scene.add(rimLight);
175
+
176
+ // Controls setup
177
+ const controls = new PointerLockControls(camera, document.body);
178
+
179
+ document.addEventListener('click', function() {
180
+ controls.lock();
181
+ });
182
+
183
+ // Movement
184
+ let moveForward = false;
185
+ let moveBackward = false;
186
+ let moveLeft = false;
187
+ let moveRight = false;
188
+ let canJump = false;
189
+
190
+ const onKeyDown = function(event) {
191
+ switch(event.code) {
192
+ case 'KeyW':
193
+ case 'ArrowUp':
194
+ moveForward = true;
195
+ break;
196
+ case 'KeyA':
197
+ case 'ArrowLeft':
198
+ moveLeft = true;
199
+ break;
200
+ case 'KeyS':
201
+ case 'ArrowDown':
202
+ moveBackward = true;
203
+ break;
204
+ case 'KeyD':
205
+ case 'ArrowRight':
206
+ moveRight = true;
207
+ break;
208
+ case 'Space':
209
+ if (canJump) {
210
+ verticalVelocity = jumpForce;
211
+ canJump = false;
212
+ }
213
+ break;
214
+ }
215
+ };
216
+
217
+ const onKeyUp = function(event) {
218
+ switch(event.code) {
219
+ case 'KeyW':
220
+ case 'ArrowUp':
221
+ moveForward = false;
222
+ break;
223
+ case 'KeyA':
224
+ case 'ArrowLeft':
225
+ moveLeft = false;
226
+ break;
227
+ case 'KeyS':
228
+ case 'ArrowDown':
229
+ moveBackward = false;
230
+ break;
231
+ case 'KeyD':
232
+ case 'ArrowRight':
233
+ moveRight = false;
234
+ break;
235
+ }
236
+ };
237
+
238
+ document.addEventListener('keydown', onKeyDown);
239
+ document.addEventListener('keyup', onKeyUp);
240
+
241
+ // Physics variables
242
+ const gravity = -35;
243
+ let verticalVelocity = 0;
244
+ const playerHeight = 2;
245
+
246
+ const jumpForce = 14;
247
+ const groundErrorMargin = 0.2; // Added error margin for ground detection
248
+ let isGrounded = false;
249
+ const bounceCoefficient = 0.4;
250
+
251
+ // Raycaster for ground detection
252
+ const raycaster = new THREE.Raycaster();
253
+
254
+ // Modified terrain loading with texture extraction
255
+ const loader = new GLTFLoader();
256
+ let terrain;
257
+
258
+ // Gun model setup
259
+ let gunModel;
260
+ const gunLoader = new GLTFLoader();
261
+ gunLoader.load('/public/models/laser-gun.glb', (gltf) => {
262
+ // Scale and position the gun relative to camera
263
+ gunModel = gltf.scene;
264
+ gunModel.scale.set(0.3, 0.3, 0.3);
265
+ gunModel.position.set(0.35, -0.23, -0.54);
266
+ gunModel.rotation.y = Math.PI * 1.6;
267
+ gunModel.rotation.z = Math.PI * 0.2;
268
+ gunModel.rotation.x = Math.PI * 0.1 + currentWeaponSway.x;
269
+ gunModel.rotation.y = Math.PI * 1.6 + currentWeaponSway.y;
270
+ gunModel.rotation.z = Math.PI * 0.2 + currentWeaponSway.z;
271
+
272
+ // Add the gun to the camera
273
+ camera.add(gunModel);
274
+ scene.add(camera); // Need to add camera to scene for gun to be visible
275
+ });
276
+
277
+ loader.load('/public/models/pond-sparse.glb', (gltf) => {
278
+ console.log("model loaded! preparing it..");
279
+ terrain = gltf.scene;
280
+ terrain.scale.set(100, 100, 100);
281
+ terrain.rotation.y = Math.PI;
282
+
283
+ if (USE_SPARSE) {
284
+ terrain.rotation.x = -Math.PI * 2.15;
285
+ }
286
+
287
+ let textureFound = false;
288
+ terrain.traverse((node) => {
289
+ if (node.isMesh) {
290
+ node.castShadow = true;
291
+ node.receiveShadow = true;
292
+
293
+ if (node.material) {
294
+ node.material.roughness = 0.8;
295
+ node.material.metalness = 0.2;
296
+
297
+ // Extract texture if available
298
+ if (node.material.map && !textureFound) {
299
+ extractedTexture = extractAndSaveTexture(node);
300
+ if (extractedTexture) {
301
+ downloadButton.disabled = false;
302
+ textureFound = true;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ });
308
+
309
+ scene.add(terrain);
310
+
311
+ const box = new THREE.Box3().setFromObject(terrain);
312
+ const center = box.getCenter(new THREE.Vector3());
313
+ terrain.position.x -= center.x;
314
+ terrain.position.z -= center.z;
315
+ terrain.position.y = 0;
316
+
317
+ raycaster.ray.origin.set(0, 100, 10);
318
+ raycaster.ray.direction.set(0, -1, 0);
319
+ const intersects = raycaster.intersectObjects(scene.children, true);
320
+ if (intersects.length > 0) {
321
+ camera.position.set(0, intersects[0].point.y + 2.5, 10);
322
+ } else {
323
+ camera.position.set(0, 20, 10);
324
+ }
325
+ camera.lookAt(new THREE.Vector3(0, 0, 0));
326
+ },
327
+ undefined,
328
+ (error) => {
329
+ console.error('An error happened:', error);
330
+ const geometry = new THREE.PlaneGeometry(100, 100, 20, 20);
331
+ const material = new THREE.MeshStandardMaterial({
332
+ color: 0x808080,
333
+ roughness: 0.8,
334
+ metalness: 0.2,
335
+ wireframe: true
336
+ });
337
+ terrain = new THREE.Mesh(geometry, material);
338
+ terrain.castShadow = true;
339
+ terrain.receiveShadow = true;
340
+ scene.add(terrain);
341
+ });
342
+
343
+ // Movement speed and physics
344
+ const velocity = new THREE.Vector3();
345
+ const direction = new THREE.Vector3();
346
+ const speed = 0.5;
347
+ const delta = 1/60;
348
+
349
+ function checkGround() {
350
+ if (!terrain) return {
351
+ distance: Infinity,
352
+ normal: new THREE.Vector3(0, 1, 0),
353
+ hasHeadCollision: false
354
+ };
355
+
356
+ // Ground check ray
357
+ raycaster.ray.origin.copy(camera.position);
358
+ raycaster.ray.direction.set(0, -1, 0);
359
+ const groundIntersects = raycaster.intersectObjects(scene.children, true);
360
+
361
+ // Head collision check ray
362
+ raycaster.ray.origin.copy(camera.position);
363
+ raycaster.ray.direction.set(0, 1, 0);
364
+ const headIntersects = raycaster.intersectObjects(scene.children, true);
365
+
366
+ const hasHeadCollision = headIntersects.length > 0 && headIntersects[0].distance < 1.0;
367
+
368
+ if (groundIntersects.length > 0) {
369
+ return {
370
+ distance: groundIntersects[0].distance,
371
+ normal: groundIntersects[0].face.normal.clone(),
372
+ hasHeadCollision: hasHeadCollision
373
+ };
374
+ }
375
+ return {
376
+ distance: Infinity,
377
+ normal: new THREE.Vector3(0, 1, 0),
378
+ hasHeadCollision: hasHeadCollision
379
+ };
380
+ }
381
+ function animate() {
382
+ requestAnimationFrame(animate);
383
+
384
+ if(controls.isLocked) {
385
+ const groundInfo = checkGround();
386
+ const distanceToGround = groundInfo.distance;
387
+
388
+ // Calculate current movement speed
389
+ const currentSpeed = Math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z);
390
+
391
+ // Update weapon sway based on movement
392
+ updateWeaponSway(currentSpeed);
393
+ updateGunPosition();
394
+
395
+ // Check if we're below terrain or if there's no terrain below us
396
+ raycaster.ray.origin.copy(camera.position);
397
+ raycaster.ray.direction.set(0, -1, 0);
398
+ const belowIntersects = raycaster.intersectObjects(scene.children, true);
399
+
400
+ // Check if we're below terrain by casting a ray upward
401
+ raycaster.ray.origin.copy(camera.position);
402
+ raycaster.ray.direction.set(0, 1, 0);
403
+ const aboveIntersects = raycaster.intersectObjects(scene.children, true);
404
+
405
+ // If we're below terrain (ray hits something above us) or if there's no ground below us
406
+ if ((aboveIntersects.length > 0 && aboveIntersects[0].distance < playerHeight) ||
407
+ belowIntersects.length === 0) {
408
+ // Find a safe position above terrain
409
+ raycaster.ray.origin.set(camera.position.x, 200, camera.position.z);
410
+ raycaster.ray.direction.set(0, -1, 0);
411
+ const rescueIntersects = raycaster.intersectObjects(scene.children, true);
412
+
413
+ if (rescueIntersects.length > 0) {
414
+ // Teleport player to safety
415
+ camera.position.y = rescueIntersects[0].point.y + playerHeight;
416
+ verticalVelocity = 0;
417
+ isGrounded = true;
418
+ canJump = true;
419
+ } else {
420
+ // If no safe position found, reset to initial position
421
+ camera.position.set(0, 20, 10);
422
+ verticalVelocity = 0;
423
+ }
424
+ }
425
+
426
+ // Handle head collisions
427
+ if (groundInfo.hasHeadCollision && verticalVelocity > 0) {
428
+ verticalVelocity = 0;
429
+ }
430
+
431
+ const slopeAngle = Math.acos(groundInfo.normal.dot(new THREE.Vector3(0, 1, 0)));
432
+ const maxClimbableAngle = Math.PI / 4;
433
+
434
+ // Improved ground detection with error margin
435
+ if (distanceToGround > playerHeight + groundErrorMargin) {
436
+ verticalVelocity += gravity * delta;
437
+ isGrounded = false;
438
+ } else if (distanceToGround < playerHeight - groundErrorMargin) {
439
+ if (verticalVelocity < 0) {
440
+ verticalVelocity = Math.abs(verticalVelocity) * bounceCoefficient;
441
+ camera.position.y = camera.position.y + (playerHeight - distanceToGround);
442
+ }
443
+ isGrounded = true;
444
+ canJump = true;
445
+ } else {
446
+ if (Math.abs(verticalVelocity) < 0.1) {
447
+ verticalVelocity = 0;
448
+ isGrounded = true;
449
+ canJump = true;
450
+ } else {
451
+ verticalVelocity *= 0.8;
452
+ isGrounded = false;
453
+ }
454
+ }
455
+
456
+ verticalVelocity = Math.max(verticalVelocity, -20);
457
+ camera.position.y += verticalVelocity * delta;
458
+
459
+ direction.z = Number(moveForward) - Number(moveBackward);
460
+ direction.x = Number(moveRight) - Number(moveLeft);
461
+ direction.normalize();
462
+
463
+ if(moveForward || moveBackward) velocity.z = -direction.z * speed;
464
+ if(moveLeft || moveRight) velocity.x = -direction.x * speed;
465
+
466
+ const moveDirection = new THREE.Vector3(-velocity.x, 0, -velocity.z).normalize();
467
+ const slopeDirection = groundInfo.normal.clone().projectOnPlane(new THREE.Vector3(0, 1, 0)).normalize();
468
+ const slopeFactor = 1.0 - (moveDirection.dot(slopeDirection) * (slopeAngle / (Math.PI / 2)));
469
+
470
+ const slopeSpeedMultiplier = slopeFactor > 0
471
+ ? 1.0 - (Math.min(slopeFactor, 1.0) * 0.5)
472
+ : 1.0 + (Math.min(Math.abs(slopeFactor), 1.0) * 0.3);
473
+
474
+ const movementSpeed = isGrounded ? speed * slopeSpeedMultiplier : speed * 0.8;
475
+
476
+ if (moveForward || moveBackward || moveLeft || moveRight) {
477
+ controls.moveRight(-velocity.x * movementSpeed);
478
+ controls.moveForward(-velocity.z * movementSpeed);
479
+ } else {
480
+ controls.moveRight(-velocity.x * movementSpeed);
481
+ controls.moveForward(-velocity.z * movementSpeed);
482
+ }
483
+
484
+ velocity.x *= 0.9;
485
+ velocity.z *= 0.9;
486
+ }
487
+
488
+ // Update sky and render
489
+ const time = performance.now() * 0.0001;
490
+ const distance = 400000;
491
+ sun.x = distance * Math.cos(time);
492
+ sun.y = distance * Math.sin(time) * 1.25;
493
+ sun.z = distance * Math.sin(time) * 0.25;
494
+
495
+ sky.material.uniforms['sunPosition'].value.copy(sun);
496
+ sunLight.position.copy(sun).normalize().multiplyScalar(500);
497
+
498
+ renderer.render(scene, camera);
499
+ }
500
+
501
+ function updateWeaponSway(currentSpeed) {
502
+ const now = performance.now();
503
+ if (now - lastSwayUpdate < swayUpdateInterval) return;
504
+
505
+ lastSwayUpdate = now;
506
+
507
+ // Calculate sway amount based on movement speed
508
+ const movementFactor = Math.min(currentSpeed / speed, 1);
509
+ const swayAmount = maxSwayAmount * movementFactor;
510
+
511
+ // Generate random sway targets
512
+ targetWeaponSway.x = (Math.random() * 2 - 1) * swayAmount;
513
+ targetWeaponSway.y = (Math.random() * 2 - 1) * swayAmount;
514
+ targetWeaponSway.z = (Math.random() * 2 - 1) * swayAmount * 0.5;
515
+ }
516
+
517
+
518
+ function updateGunPosition() {
519
+ if (!gunModel) return;
520
+
521
+ // Interpolate current sway towards target
522
+ currentWeaponSway.x += (targetWeaponSway.x - currentWeaponSway.x) * swayLerpFactor;
523
+ currentWeaponSway.y += (targetWeaponSway.y - currentWeaponSway.y) * swayLerpFactor;
524
+ currentWeaponSway.z += (targetWeaponSway.z - currentWeaponSway.z) * swayLerpFactor;
525
+
526
+ // Apply sway to gun model's rotation
527
+ gunModel.rotation.x = Math.PI * 0.1 + currentWeaponSway.x;
528
+ gunModel.rotation.y = Math.PI * 1.6 + currentWeaponSway.y;
529
+ gunModel.rotation.z = Math.PI * 0.2 + currentWeaponSway.z;
530
+ }
531
+
532
+ // Handle window resize
533
+ window.addEventListener('resize', onWindowResize, false);
534
+
535
+ function onWindowResize() {
536
+ camera.aspect = window.innerWidth / window.innerHeight;
537
+ camera.updateProjectionMatrix();
538
+ renderer.setSize(window.innerWidth, window.innerHeight);
539
+ }
540
+
541
+ animate();
542
+ </script>
543
+ </body>
544
  </html>
public/models/laser-gun.glb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:40f9660eaadd257df6316ce3bd5bef1fc44164e7366f01dee4d359cd734309d0
3
+ size 1730372
public/models/pond-large.glb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c55bdd74b3b53813f9e720a10ab54986b033399e5ffb1eb7dfb67ae4f2876987
3
+ size 6399760
public/models/pond-sparse.glb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:eec8fabfb89b315e5817fcc9551cfb9b96ef17958dae385276069a10e17eca79
3
+ size 1238904
public/textures/lensflare/lensflare0.png ADDED
public/textures/lensflare/lensflare0_alpha.png ADDED
public/textures/lensflare/lensflare1.png ADDED
public/textures/lensflare/lensflare2.png ADDED
public/textures/lensflare/lensflare3.png ADDED