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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/production_theme.scss +4 -1
- data/app/assets/stylesheets/wco/main.scss +4 -0
- data/app/assets/stylesheets/wco/studio.scss +12 -4
- data/app/controllers/wco/api/newspartials_controller.rb +1 -1
- data/app/controllers/wco/api/newsproducer_controller.rb +5 -0
- data/app/controllers/wco/newspartials_controller.rb +1 -0
- data/app/models/iro/datapoint.rb +1 -1
- data/app/models/iro/option.rb +16 -2
- data/app/models/iro/position.rb +4 -6
- data/app/models/iro/priceitem.rb +42 -4
- data/app/models/iro/purse.rb +2 -0
- data/app/models/iro/stock.rb +1 -2
- data/app/models/tda/option.rb +4 -9
- data/app/models/wco/newspartial.rb +12 -4
- data/app/views/wco/api/newsproducer/README_newsproducer.txt +12 -0
- data/app/views/wco/api/newsproducer/_scenes.js-bk +20 -0
- data/app/views/wco/api/newsproducer/_scenes.js.erb +15 -0
- data/app/views/wco/api/newsproducer/{_studio_blue.js → _studio_blue1.js} +4 -1
- data/app/views/wco/api/newsproducer/_studio_blue2.js +912 -0
- data/app/views/wco/api/newsproducer/_studio_coc.js +769 -0
- data/app/views/wco/api/newsproducer/_studio_coc.js-bk +389 -0
- data/app/views/wco/api/newsproducer/_studio_coc.js-cannon +677 -0
- data/app/views/wco/api/newsproducer/_studio_fps.js +1018 -0
- data/app/views/wco/api/newsproducer/_studio_ra2.js +255 -0
- data/app/views/wco/api/newsproducer/_touch_controls.js +708 -0
- data/app/views/wco/api/newsproducer/_touch_pad.css +200 -0
- data/app/views/wco/api/newsproducer/games_fps.html +504 -0
- data/app/views/wco/api/newsproducer/{studio_green.html.erb → studio_blue0.html.erb} +1 -1
- data/app/views/wco/api/newsproducer/{studio_blue.html.erb → studio_blue1.html.erb} +3 -3
- data/app/views/wco/api/newsproducer/studio_blue2.html.erb +146 -0
- data/app/views/wco/api/newsproducer/studio_coc.html.erb +95 -0
- data/app/views/wco/api/newsproducer/studio_fps.html.erb +199 -0
- data/app/views/wco/api/newsproducer/studio_ra2.html.erb +95 -0
- data/app/views/wco/newspartials/_show_in_newsvideo.haml +6 -7
- data/app/views/wco/newspartials/index.haml-trash +6 -0
- data/config/initializers/08_integrations.rb +14 -1
- data/config/initializers/08_integrations.rb-example +0 -2
- data/config/routes.rb +3 -0
- data/lib/shortcuts.rb +1 -1
- metadata +23 -7
- data/app/views/wco/api/newsproducer/README.txt +0 -2
- /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
|
+
}
|