wco_models 3.1.0.325 → 3.4.0.3

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/production_theme.scss +4 -1
  3. data/app/assets/stylesheets/wco/main.scss +4 -0
  4. data/app/assets/stylesheets/wco/studio.scss +12 -4
  5. data/app/controllers/wco/api/newspartials_controller.rb +1 -1
  6. data/app/controllers/wco/api/newsproducer_controller.rb +5 -0
  7. data/app/controllers/wco/newspartials_controller.rb +1 -0
  8. data/app/models/iro/datapoint.rb +1 -1
  9. data/app/models/iro/option.rb +16 -2
  10. data/app/models/iro/position.rb +4 -6
  11. data/app/models/iro/priceitem.rb +42 -4
  12. data/app/models/iro/purse.rb +2 -0
  13. data/app/models/iro/stock.rb +1 -2
  14. data/app/models/tda/option.rb +4 -9
  15. data/app/models/wco/newspartial.rb +12 -4
  16. data/app/views/wco/api/newsproducer/README_newsproducer.txt +12 -0
  17. data/app/views/wco/api/newsproducer/_scenes.js-bk +20 -0
  18. data/app/views/wco/api/newsproducer/_scenes.js.erb +15 -0
  19. data/app/views/wco/api/newsproducer/{_studio_blue.js → _studio_blue1.js} +4 -1
  20. data/app/views/wco/api/newsproducer/_studio_blue2.js +912 -0
  21. data/app/views/wco/api/newsproducer/_studio_coc.js +769 -0
  22. data/app/views/wco/api/newsproducer/_studio_coc.js-bk +389 -0
  23. data/app/views/wco/api/newsproducer/_studio_coc.js-cannon +677 -0
  24. data/app/views/wco/api/newsproducer/_studio_fps.js +1018 -0
  25. data/app/views/wco/api/newsproducer/_studio_ra2.js +255 -0
  26. data/app/views/wco/api/newsproducer/_touch_controls.js +708 -0
  27. data/app/views/wco/api/newsproducer/_touch_pad.css +200 -0
  28. data/app/views/wco/api/newsproducer/games_fps.html +504 -0
  29. data/app/views/wco/api/newsproducer/{studio_green.html.erb → studio_blue0.html.erb} +1 -1
  30. data/app/views/wco/api/newsproducer/{studio_blue.html.erb → studio_blue1.html.erb} +3 -3
  31. data/app/views/wco/api/newsproducer/studio_blue2.html.erb +146 -0
  32. data/app/views/wco/api/newsproducer/studio_coc.html.erb +95 -0
  33. data/app/views/wco/api/newsproducer/studio_fps.html.erb +199 -0
  34. data/app/views/wco/api/newsproducer/studio_ra2.html.erb +95 -0
  35. data/app/views/wco/newspartials/_show_in_newsvideo.haml +6 -7
  36. data/app/views/wco/newspartials/index.haml-trash +6 -0
  37. data/config/initializers/08_integrations.rb +14 -1
  38. data/config/initializers/08_integrations.rb-example +0 -2
  39. data/config/routes.rb +3 -0
  40. data/lib/shortcuts.rb +1 -1
  41. metadata +23 -7
  42. data/app/views/wco/api/newsproducer/README.txt +0 -2
  43. /data/app/views/wco/api/newsproducer/{_studio_green.js → _studio_blue0.js} +0 -0
@@ -0,0 +1,769 @@
1
+
2
+ import * as THREE from 'three';
3
+
4
+ import Stats from 'three/addons/libs/stats.module.js';
5
+
6
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
7
+
8
+ import { Octree } from 'three/addons/math/Octree.js';
9
+ import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js';
10
+
11
+ import { Capsule } from 'three/addons/math/Capsule.js';
12
+
13
+ import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
14
+
15
+ const timer = new THREE.Timer();
16
+ timer.connect( document );
17
+
18
+ const scene = new THREE.Scene();
19
+ scene.background = new THREE.Color( 0x88ccee );
20
+ scene.fog = new THREE.Fog( 0x88ccee, 0, 50 );
21
+
22
+ const frustumSize = 10;
23
+ const camera = new THREE.OrthographicCamera(
24
+ frustumSize * window.innerWidth / window.innerHeight / - 2,
25
+ frustumSize * window.innerWidth / window.innerHeight / 2,
26
+ frustumSize / 2,
27
+ frustumSize / - 2,
28
+ -100,
29
+ 1000
30
+ );
31
+ camera.rotation.order = 'YXZ';
32
+
33
+ const fillLight1 = new THREE.HemisphereLight( 0x8dc1de, 0x00668d, 1.5 );
34
+ fillLight1.position.set( 2, 1, 1 );
35
+ scene.add( fillLight1 );
36
+
37
+ const directionalLight = new THREE.DirectionalLight( 0xffffff, 2.5 );
38
+ directionalLight.position.set( - 5, 25, - 1 );
39
+ directionalLight.castShadow = true;
40
+ directionalLight.shadow.camera.near = 0.01;
41
+ directionalLight.shadow.camera.far = 500;
42
+ directionalLight.shadow.camera.right = 30;
43
+ directionalLight.shadow.camera.left = - 30;
44
+ directionalLight.shadow.camera.top = 30;
45
+ directionalLight.shadow.camera.bottom = - 30;
46
+ directionalLight.shadow.mapSize.width = 1024;
47
+ directionalLight.shadow.mapSize.height = 1024;
48
+ directionalLight.shadow.radius = 4;
49
+ directionalLight.shadow.bias = - 0.00006;
50
+ scene.add( directionalLight );
51
+
52
+ const container = document.getElementById( 'rotatingC' );
53
+
54
+ const renderer = new THREE.WebGLRenderer( { antialias: true } );
55
+ renderer.setPixelRatio( window.devicePixelRatio );
56
+ renderer.setSize( window.innerWidth, window.innerHeight );
57
+ renderer.setAnimationLoop( animate );
58
+ renderer.shadowMap.enabled = true;
59
+ renderer.shadowMap.type = THREE.VSMShadowMap;
60
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
61
+ container.appendChild( renderer.domElement );
62
+
63
+ const stats = new Stats();
64
+ stats.domElement.style.position = 'absolute';
65
+ stats.domElement.style.top = '0px';
66
+ container.appendChild( stats.domElement );
67
+
68
+ const GRAVITY = 30;
69
+
70
+ const VEHICLE_SPEED = 4
71
+ const VEHICLE_REVERSE_SPEED = 3
72
+ const VEHICLE_ACCELERATION = 2.5
73
+ const VEHICLE_BRAKE = 5
74
+ const VEHICLE_TURN_SPEED = 2.2
75
+
76
+ const NUM_SPHERES = 100;
77
+ const SPHERE_RADIUS = 0.2;
78
+
79
+ const STEPS_PER_FRAME = 5;
80
+
81
+ const sphereGeometry = new THREE.IcosahedronGeometry( SPHERE_RADIUS, 5 );
82
+ const sphereMaterial = new THREE.MeshLambertMaterial( { color: 0xdede8d } );
83
+
84
+ const spheres = [];
85
+ let sphereIdx = 0;
86
+
87
+ for ( let i = 0; i < NUM_SPHERES; i ++ ) {
88
+
89
+ const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
90
+ sphere.castShadow = true;
91
+ sphere.receiveShadow = true;
92
+
93
+ scene.add( sphere );
94
+
95
+ spheres.push( {
96
+ mesh: sphere,
97
+ collider: new THREE.Sphere( new THREE.Vector3( 0, - 100, 0 ), SPHERE_RADIUS ),
98
+ velocity: new THREE.Vector3()
99
+ } );
100
+
101
+ }
102
+
103
+ const worldOctree = new Octree();
104
+ let worldReady = false
105
+ let octreeHelper = null
106
+
107
+ let vehicle = null
108
+ const vehicleVelocity = new THREE.Vector3()
109
+ const vehicleForward = new THREE.Vector3()
110
+ const vehicleSize = new THREE.Vector3()
111
+ const worldUp = new THREE.Vector3( 0, 1, 0 )
112
+ const vehicleRay = new THREE.Ray()
113
+ const vehicleDownRay = new THREE.Ray()
114
+ const vehicleBaseQuaternion = new THREE.Quaternion()
115
+ const vehicleTargetQuat = new THREE.Quaternion()
116
+ const vehicleAlignQuat = new THREE.Quaternion()
117
+ const vehicleYawQuat = new THREE.Quaternion()
118
+ const vehicleGroundNormal = new THREE.Vector3( 0, 1, 0 )
119
+ const vehicleSlopeForward = new THREE.Vector3()
120
+ const vehicleProbeRight = new THREE.Vector3()
121
+ const vehicleNormalAvg = new THREE.Vector3()
122
+ const vehicleSampleNormal = new THREE.Vector3()
123
+ let vehicleHeading = 0
124
+ let vehicleOnFloor = false
125
+ let vehicleAgainstWall = false
126
+ let vehicleSpeed = 0
127
+
128
+ const playerCollider = new Capsule( new THREE.Vector3( 0, 0.35, 0 ), new THREE.Vector3( 0, 1, 0 ), 0.35 );
129
+
130
+ const playerVelocity = new THREE.Vector3();
131
+ const playerDirection = new THREE.Vector3();
132
+
133
+ let playerOnFloor = false;
134
+ let mouseTime = 0;
135
+
136
+ const keyStates = {};
137
+
138
+ const vector1 = new THREE.Vector3();
139
+ const vector2 = new THREE.Vector3();
140
+ const vector3 = new THREE.Vector3();
141
+
142
+
143
+
144
+ const vehicle_url = 'https://cdn.jsdelivr.net/gh/wasya-co/ishlib3js@0.3.0/public/vendor/models/vehicles/000mb car-mazda-miata/model.glb'
145
+ const vehicle_config = {
146
+ height: 0.5,
147
+ // height: 1.235,
148
+ // length: 3.970,
149
+ // width: 1.675,
150
+ }
151
+
152
+
153
+ document.addEventListener( 'keydown', ( event ) => {
154
+
155
+ keyStates[ event.code ] = true;
156
+
157
+ } );
158
+
159
+ document.addEventListener( 'keyup', ( event ) => {
160
+
161
+ keyStates[ event.code ] = false;
162
+
163
+ } );
164
+
165
+ container.addEventListener( 'mousedown', () => {
166
+
167
+ document.body.requestPointerLock();
168
+
169
+ mouseTime = performance.now();
170
+
171
+ } );
172
+
173
+ document.addEventListener( 'mouseup', () => {
174
+
175
+ if ( document.pointerLockElement !== null ) throwBall();
176
+
177
+ } );
178
+
179
+ document.body.addEventListener( 'mousemove', ( event ) => {
180
+
181
+ if ( document.pointerLockElement === document.body ) {
182
+
183
+ camera.rotation.y -= event.movementX / 500;
184
+ camera.rotation.x -= event.movementY / 500;
185
+
186
+ }
187
+
188
+ } );
189
+
190
+ window.addEventListener( 'resize', onWindowResize );
191
+
192
+ function onWindowResize() {
193
+
194
+ const aspect = window.innerWidth / window.innerHeight;
195
+ camera.left = frustumSize * aspect / - 2;
196
+ camera.right = frustumSize * aspect / 2;
197
+ camera.top = frustumSize / 2;
198
+ camera.bottom = frustumSize / - 2;
199
+ camera.updateProjectionMatrix();
200
+
201
+ renderer.setSize( window.innerWidth, window.innerHeight );
202
+
203
+ }
204
+
205
+ function throwBall() {
206
+
207
+ const sphere = spheres[ sphereIdx ];
208
+
209
+ camera.getWorldDirection( playerDirection );
210
+
211
+ sphere.collider.center.copy( playerCollider.end ).addScaledVector( playerDirection, playerCollider.radius * 1.5 );
212
+
213
+ // throw the ball with more force if we hold the button longer, and if we move forward
214
+
215
+ const impulse = 15 + 30 * ( 1 - Math.exp( ( mouseTime - performance.now() ) * 0.001 ) );
216
+
217
+ sphere.velocity.copy( playerDirection ).multiplyScalar( impulse );
218
+ sphere.velocity.addScaledVector( playerVelocity, 2 );
219
+
220
+ sphereIdx = ( sphereIdx + 1 ) % spheres.length;
221
+
222
+ }
223
+
224
+ function playerCollisions() {
225
+
226
+ const result = worldOctree.capsuleIntersect( playerCollider );
227
+
228
+ playerOnFloor = false;
229
+
230
+ if ( result ) {
231
+
232
+ // determine if the surface we bumped into is something we can stand on
233
+
234
+ playerOnFloor = result.normal.y >= 0.15; // allow slopes up to ~81° but ignore sheer vertical walls
235
+
236
+ if ( ! playerOnFloor ) {
237
+
238
+ playerVelocity.addScaledVector( result.normal, - result.normal.dot( playerVelocity ) );
239
+
240
+ }
241
+
242
+ if ( result.depth >= 1e-10 ) {
243
+
244
+ playerCollider.translate( result.normal.multiplyScalar( result.depth ) );
245
+
246
+ }
247
+
248
+ }
249
+
250
+ }
251
+
252
+ function updatePlayer( deltaTime ) {
253
+
254
+ let damping = Math.exp( - 4 * deltaTime ) - 1;
255
+
256
+ if ( ! playerOnFloor ) {
257
+
258
+ playerVelocity.y -= GRAVITY * deltaTime;
259
+
260
+ // small air resistance
261
+ damping *= 0.1;
262
+
263
+ }
264
+
265
+ playerVelocity.addScaledVector( playerVelocity, damping );
266
+
267
+ const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime );
268
+ playerCollider.translate( deltaPosition );
269
+
270
+ playerCollisions();
271
+
272
+ if ( ! vehicle ) {
273
+
274
+ camera.position.copy( playerCollider.end );
275
+
276
+ }
277
+
278
+ }
279
+
280
+ function playerSphereCollision( sphere ) {
281
+
282
+ const center = vector1.addVectors( playerCollider.start, playerCollider.end ).multiplyScalar( 0.5 );
283
+
284
+ const sphere_center = sphere.collider.center;
285
+
286
+ const r = playerCollider.radius + sphere.collider.radius;
287
+ const r2 = r * r;
288
+
289
+ // approximation: player = 3 spheres
290
+
291
+ for ( const point of [ playerCollider.start, playerCollider.end, center ] ) {
292
+
293
+ const d2 = point.distanceToSquared( sphere_center );
294
+
295
+ if ( d2 < r2 ) {
296
+
297
+ const normal = vector1.subVectors( point, sphere_center ).normalize();
298
+ const v1 = vector2.copy( normal ).multiplyScalar( normal.dot( playerVelocity ) );
299
+ const v2 = vector3.copy( normal ).multiplyScalar( normal.dot( sphere.velocity ) );
300
+
301
+ playerVelocity.add( v2 ).sub( v1 );
302
+ sphere.velocity.add( v1 ).sub( v2 );
303
+
304
+ const d = ( r - Math.sqrt( d2 ) ) / 2;
305
+ sphere_center.addScaledVector( normal, - d );
306
+
307
+ }
308
+
309
+ }
310
+
311
+ }
312
+
313
+ function spheresCollisions() {
314
+
315
+ for ( let i = 0, length = spheres.length; i < length; i ++ ) {
316
+
317
+ const s1 = spheres[ i ];
318
+
319
+ for ( let j = i + 1; j < length; j ++ ) {
320
+
321
+ const s2 = spheres[ j ];
322
+
323
+ const d2 = s1.collider.center.distanceToSquared( s2.collider.center );
324
+ const r = s1.collider.radius + s2.collider.radius;
325
+ const r2 = r * r;
326
+
327
+ if ( d2 < r2 ) {
328
+
329
+ const normal = vector1.subVectors( s1.collider.center, s2.collider.center ).normalize();
330
+ const v1 = vector2.copy( normal ).multiplyScalar( normal.dot( s1.velocity ) );
331
+ const v2 = vector3.copy( normal ).multiplyScalar( normal.dot( s2.velocity ) );
332
+
333
+ s1.velocity.add( v2 ).sub( v1 );
334
+ s2.velocity.add( v1 ).sub( v2 );
335
+
336
+ const d = ( r - Math.sqrt( d2 ) ) / 2;
337
+
338
+ s1.collider.center.addScaledVector( normal, d );
339
+ s2.collider.center.addScaledVector( normal, - d );
340
+
341
+ }
342
+
343
+ }
344
+
345
+ }
346
+
347
+ }
348
+
349
+ function updateSpheres( deltaTime ) {
350
+
351
+ spheres.forEach( sphere => {
352
+
353
+ sphere.collider.center.addScaledVector( sphere.velocity, deltaTime );
354
+
355
+ const result = worldOctree.sphereIntersect( sphere.collider );
356
+
357
+ if ( result ) {
358
+
359
+ sphere.velocity.addScaledVector( result.normal, - result.normal.dot( sphere.velocity ) * 1.5 );
360
+ sphere.collider.center.add( result.normal.multiplyScalar( result.depth ) );
361
+
362
+ } else {
363
+
364
+ sphere.velocity.y -= GRAVITY * deltaTime;
365
+
366
+ }
367
+
368
+ const damping = Math.exp( - 1.5 * deltaTime ) - 1;
369
+ sphere.velocity.addScaledVector( sphere.velocity, damping );
370
+
371
+ playerSphereCollision( sphere );
372
+
373
+ } );
374
+
375
+ spheresCollisions();
376
+
377
+ for ( const sphere of spheres ) {
378
+
379
+ sphere.mesh.position.copy( sphere.collider.center );
380
+
381
+ }
382
+
383
+ }
384
+
385
+ function getForwardVector() {
386
+
387
+ camera.getWorldDirection( playerDirection );
388
+ playerDirection.y = 0;
389
+ playerDirection.normalize();
390
+
391
+ return playerDirection;
392
+
393
+ }
394
+
395
+ function getSideVector() {
396
+
397
+ camera.getWorldDirection( playerDirection );
398
+ playerDirection.y = 0;
399
+ playerDirection.normalize();
400
+ playerDirection.cross( camera.up );
401
+
402
+ return playerDirection;
403
+
404
+ }
405
+
406
+ function controls( deltaTime ) {
407
+
408
+ // gives a bit of air control
409
+ const speedDelta = deltaTime * ( playerOnFloor ? 25 : 8 );
410
+
411
+ if ( keyStates[ 'KeyW' ] ) {
412
+
413
+ playerVelocity.add( getForwardVector().multiplyScalar( speedDelta ) );
414
+
415
+ }
416
+
417
+ if ( keyStates[ 'KeyS' ] ) {
418
+
419
+ playerVelocity.add( getForwardVector().multiplyScalar( - speedDelta ) );
420
+
421
+ }
422
+
423
+ if ( playerOnFloor ) {
424
+
425
+ if ( keyStates[ 'Space' ] ) {
426
+
427
+ playerVelocity.y = 15;
428
+
429
+ }
430
+
431
+ }
432
+
433
+ }
434
+
435
+ function rescale(model, config) {
436
+ const box = new THREE.Box3().setFromObject(model)
437
+ const size = box.getSize(new THREE.Vector3())
438
+ const scale = config.height / size.y
439
+ model.scale.setScalar(scale)
440
+ }
441
+
442
+ const loader = new GLTFLoader(); // .setPath( './models/gltf/' );
443
+ const scene_url = 'https://cdn.jsdelivr.net/gh/wasya-co/ishlib3js@0.3.0/public/vendor/models/scenes/000mb%20collision-world/collision-world.glb'
444
+
445
+ loader.load( vehicle_url, ( gltf ) => {
446
+
447
+ vehicle = gltf.scene
448
+ rescale( vehicle, vehicle_config )
449
+
450
+ vehicle.traverse( child => {
451
+
452
+ if ( child.isMesh ) {
453
+
454
+ child.castShadow = true
455
+ child.receiveShadow = true
456
+
457
+ }
458
+
459
+ } )
460
+
461
+ const box = new THREE.Box3().setFromObject( vehicle )
462
+ box.getSize( vehicleSize )
463
+ vehicleBaseQuaternion.copy( vehicle.quaternion )
464
+ vehicle.position.y += 5 - box.min.y
465
+ scene.add( vehicle )
466
+
467
+ } )
468
+
469
+ loader.load( scene_url, ( gltf ) => {
470
+
471
+ scene.add( gltf.scene );
472
+
473
+ worldOctree.fromGraphNode( gltf.scene );
474
+ worldReady = true
475
+
476
+ gltf.scene.traverse( child => {
477
+
478
+ if ( child.isMesh ) {
479
+
480
+ child.castShadow = true;
481
+ child.receiveShadow = true;
482
+
483
+ if ( child.material.map ) {
484
+
485
+ child.material.map.anisotropy = 4;
486
+
487
+ }
488
+
489
+ }
490
+
491
+ } );
492
+
493
+ octreeHelper = new OctreeHelper( worldOctree );
494
+ octreeHelper.visible = false;
495
+ scene.add( octreeHelper );
496
+
497
+ const gui = new GUI( { width: 200 } );
498
+ gui.add( { debug: false }, 'debug' )
499
+ .onChange( function ( value ) {
500
+
501
+ octreeHelper.visible = value;
502
+
503
+ } );
504
+
505
+ } );
506
+
507
+ function getVehicleForward() {
508
+
509
+ vehicleForward.set( - Math.sin( vehicleHeading ), 0, - Math.cos( vehicleHeading ) )
510
+ return vehicleForward
511
+
512
+ }
513
+
514
+ function probeGround( x, z, from_y ) {
515
+
516
+ vehicleDownRay.origin.set( x, from_y, z )
517
+ vehicleDownRay.direction.set( 0, -1, 0 )
518
+ return worldOctree.rayIntersect( vehicleDownRay )
519
+
520
+ }
521
+
522
+ function groundNormalFromHit( hit, target ) {
523
+
524
+ hit.triangle.getNormal( target )
525
+ if ( target.y < 0 ) target.negate()
526
+ return target
527
+
528
+ }
529
+
530
+ function sampleVehicleGround( from_y ) {
531
+
532
+ const forward = getVehicleForward()
533
+ vehicleProbeRight.set( - forward.z, 0, forward.x )
534
+ const half_l = Math.max( vehicleSize.x, vehicleSize.z ) * 0.35
535
+ const half_w = Math.min( vehicleSize.x, vehicleSize.z ) * 0.3
536
+ const px = vehicle.position.x
537
+ const pz = vehicle.position.z
538
+
539
+ const probes = [
540
+ [ px, pz ],
541
+ [ px + forward.x * half_l, pz + forward.z * half_l ],
542
+ [ px - forward.x * half_l, pz - forward.z * half_l ],
543
+ [ px + vehicleProbeRight.x * half_w, pz + vehicleProbeRight.z * half_w ],
544
+ [ px - vehicleProbeRight.x * half_w, pz - vehicleProbeRight.z * half_w ]
545
+ ]
546
+
547
+ vehicleNormalAvg.set( 0, 0, 0 )
548
+ let ground_y = 0
549
+ let count = 0
550
+
551
+ for ( let i = 0; i < probes.length; i ++ ) {
552
+
553
+ const hit = probeGround( probes[ i ][ 0 ], probes[ i ][ 1 ], from_y )
554
+ if ( ! hit || hit.distance > 4 ) continue
555
+
556
+ groundNormalFromHit( hit, vehicleSampleNormal )
557
+ if ( vehicleSampleNormal.y < 0.25 ) continue
558
+
559
+ vehicleNormalAvg.add( vehicleSampleNormal )
560
+ ground_y += hit.position.y
561
+ count ++
562
+
563
+ }
564
+
565
+ if ( count === 0 ) return null
566
+
567
+ vehicleNormalAvg.normalize()
568
+ return { normal: vehicleNormalAvg, y: ground_y / count }
569
+
570
+ }
571
+
572
+ function updateVehicleOrientation( deltaTime ) {
573
+
574
+ vehicleYawQuat.setFromAxisAngle( worldUp, vehicleHeading )
575
+ vehicleAlignQuat.setFromUnitVectors( worldUp, vehicleGroundNormal )
576
+ vehicleTargetQuat.copy( vehicleBaseQuaternion )
577
+ vehicleTargetQuat.premultiply( vehicleYawQuat )
578
+ vehicleTargetQuat.premultiply( vehicleAlignQuat )
579
+ vehicle.quaternion.slerp( vehicleTargetQuat, 1 - Math.exp( - 12 * deltaTime ) )
580
+
581
+ }
582
+
583
+ function sitVehicleOnGround( ground_y ) {
584
+
585
+ vehicle.updateMatrixWorld( true )
586
+ const box = new THREE.Box3().setFromObject( vehicle )
587
+ vehicle.position.y += ground_y - box.min.y
588
+
589
+ }
590
+
591
+ function updateVehicle( deltaTime ) {
592
+
593
+ if ( ! vehicle || ! worldReady ) return
594
+
595
+ const holding_a = !! keyStates[ 'KeyA' ]
596
+ const holding_d = !! keyStates[ 'KeyD' ]
597
+ const braking = holding_a && holding_d
598
+
599
+ if ( holding_a && ! holding_d ) {
600
+
601
+ vehicleHeading += VEHICLE_TURN_SPEED * deltaTime
602
+ vehicleAgainstWall = false
603
+
604
+ }
605
+
606
+ if ( holding_d && ! holding_a ) {
607
+
608
+ vehicleHeading -= VEHICLE_TURN_SPEED * deltaTime
609
+ vehicleAgainstWall = false
610
+
611
+ }
612
+
613
+ if ( braking ) {
614
+
615
+ vehicleAgainstWall = false
616
+ vehicleSpeed -= VEHICLE_BRAKE * deltaTime
617
+
618
+ if ( vehicleSpeed < - VEHICLE_REVERSE_SPEED ) {
619
+
620
+ vehicleSpeed = - VEHICLE_REVERSE_SPEED
621
+
622
+ }
623
+
624
+ } else if ( ! vehicleAgainstWall ) {
625
+
626
+ vehicleSpeed += VEHICLE_ACCELERATION * deltaTime
627
+
628
+ if ( vehicleSpeed > VEHICLE_SPEED ) {
629
+
630
+ vehicleSpeed = VEHICLE_SPEED
631
+
632
+ }
633
+
634
+ } else {
635
+
636
+ vehicleSpeed = 0
637
+
638
+ }
639
+
640
+ const ground = sampleVehicleGround( vehicle.position.y + 2 )
641
+ vehicleOnFloor = !! ground
642
+
643
+ if ( ground ) {
644
+
645
+ vehicleGroundNormal.lerp( ground.normal, 1 - Math.exp( - 8 * deltaTime ) ).normalize()
646
+
647
+ } else {
648
+
649
+ vehicleGroundNormal.lerp( worldUp, 1 - Math.exp( - 2 * deltaTime ) ).normalize()
650
+
651
+ }
652
+
653
+ updateVehicleOrientation( deltaTime )
654
+
655
+ const forward = getVehicleForward()
656
+ vehicleSlopeForward.copy( forward ).addScaledVector( vehicleGroundNormal, - forward.dot( vehicleGroundNormal ) )
657
+
658
+ if ( vehicleSlopeForward.lengthSq() > 1e-8 ) {
659
+
660
+ vehicleSlopeForward.normalize()
661
+
662
+ } else {
663
+
664
+ vehicleSlopeForward.copy( forward )
665
+
666
+ }
667
+
668
+ if ( vehicleOnFloor ) {
669
+
670
+ vehicle.position.addScaledVector( vehicleSlopeForward, vehicleSpeed * deltaTime )
671
+ sitVehicleOnGround( ground.y )
672
+ vehicleVelocity.y = 0
673
+
674
+ } else {
675
+
676
+ vehicle.position.x += forward.x * vehicleSpeed * deltaTime
677
+ vehicle.position.z += forward.z * vehicleSpeed * deltaTime
678
+ vehicleVelocity.y -= GRAVITY * deltaTime
679
+ vehicle.position.y += vehicleVelocity.y * deltaTime
680
+
681
+ }
682
+
683
+ if ( vehicleSpeed !== 0 ) {
684
+
685
+ vehicle.updateMatrixWorld( true )
686
+ const box = new THREE.Box3().setFromObject( vehicle )
687
+ const center = box.getCenter( vector1 )
688
+ const hit_distance = Math.max( vehicleSize.x, vehicleSize.z ) * 0.55
689
+ vehicleRay.origin.copy( center )
690
+ vehicleRay.direction.copy( vehicleSlopeForward )
691
+ if ( vehicleSpeed < 0 ) vehicleRay.direction.negate()
692
+ const wall_hit = worldOctree.rayIntersect( vehicleRay )
693
+
694
+ if ( wall_hit && wall_hit.distance < hit_distance ) {
695
+
696
+ groundNormalFromHit( wall_hit, vehicleSampleNormal )
697
+
698
+ if ( vehicleSampleNormal.y < 0.4 ) {
699
+
700
+ const push = vehicleSpeed < 0 ? -1 : 1
701
+ vehicle.position.addScaledVector( vehicleSlopeForward, ( wall_hit.distance - hit_distance ) * push )
702
+ vehicleAgainstWall = true
703
+ vehicleSpeed = 0
704
+
705
+ }
706
+
707
+ }
708
+
709
+ }
710
+
711
+ }
712
+
713
+ function followVehiclePosition() {
714
+
715
+ if ( ! vehicle ) return
716
+
717
+ camera.position.copy( vehicle.position )
718
+
719
+ }
720
+
721
+ function teleportPlayerIfOob() {
722
+
723
+ if ( camera.position.y <= - 25 ) {
724
+
725
+ playerCollider.start.set( 0, 0.35, 0 );
726
+ playerCollider.end.set( 0, 1, 0 );
727
+ playerCollider.radius = 0.35;
728
+ if ( ! vehicle ) {
729
+
730
+ camera.position.copy( playerCollider.end );
731
+ camera.rotation.set( 0, 0, 0 );
732
+
733
+ }
734
+
735
+ }
736
+
737
+ }
738
+
739
+
740
+ function animate() {
741
+
742
+ timer.update();
743
+
744
+ const deltaTime = Math.min( 0.05, timer.getDelta() ) / STEPS_PER_FRAME;
745
+
746
+ // we look for collisions in substeps to mitigate the risk of
747
+ // an object traversing another too quickly for detection.
748
+
749
+ for ( let i = 0; i < STEPS_PER_FRAME; i ++ ) {
750
+
751
+ controls( deltaTime );
752
+
753
+ updatePlayer( deltaTime );
754
+
755
+ updateSpheres( deltaTime );
756
+
757
+ updateVehicle( deltaTime );
758
+
759
+ followVehiclePosition();
760
+
761
+ teleportPlayerIfOob();
762
+
763
+ }
764
+
765
+ renderer.render( scene, camera );
766
+
767
+ stats.update();
768
+
769
+ }