wco_models 3.1.0.324 → 3.1.0.326
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
- data/lib/tasks/office_tasks.rake +7 -5
- 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,677 @@
|
|
|
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 { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
|
9
|
+
|
|
10
|
+
import * as CANNON from 'cannon-es'
|
|
11
|
+
import CannonDebugger from 'cannon-es-debugger'
|
|
12
|
+
|
|
13
|
+
const timer = new THREE.Timer()
|
|
14
|
+
timer.connect( document )
|
|
15
|
+
|
|
16
|
+
const scene = new THREE.Scene()
|
|
17
|
+
scene.background = new THREE.Color( 0x88ccee )
|
|
18
|
+
scene.fog = new THREE.Fog( 0x88ccee, 0, 50 )
|
|
19
|
+
|
|
20
|
+
const frustumSize = 10
|
|
21
|
+
const camera = new THREE.OrthographicCamera(
|
|
22
|
+
frustumSize * window.innerWidth / window.innerHeight / - 2,
|
|
23
|
+
frustumSize * window.innerWidth / window.innerHeight / 2,
|
|
24
|
+
frustumSize / 2,
|
|
25
|
+
frustumSize / - 2,
|
|
26
|
+
-100,
|
|
27
|
+
1000
|
|
28
|
+
)
|
|
29
|
+
camera.rotation.order = 'YXZ'
|
|
30
|
+
|
|
31
|
+
const fillLight1 = new THREE.HemisphereLight( 0x8dc1de, 0x00668d, 1.5 )
|
|
32
|
+
fillLight1.position.set( 2, 1, 1 )
|
|
33
|
+
scene.add( fillLight1 )
|
|
34
|
+
|
|
35
|
+
const directionalLight = new THREE.DirectionalLight( 0xffffff, 2.5 )
|
|
36
|
+
directionalLight.position.set( - 5, 25, - 1 )
|
|
37
|
+
directionalLight.castShadow = true
|
|
38
|
+
directionalLight.shadow.camera.near = 0.01
|
|
39
|
+
directionalLight.shadow.camera.far = 500
|
|
40
|
+
directionalLight.shadow.camera.right = 30
|
|
41
|
+
directionalLight.shadow.camera.left = - 30
|
|
42
|
+
directionalLight.shadow.camera.top = 30
|
|
43
|
+
directionalLight.shadow.camera.bottom = - 30
|
|
44
|
+
directionalLight.shadow.mapSize.width = 1024
|
|
45
|
+
directionalLight.shadow.mapSize.height = 1024
|
|
46
|
+
directionalLight.shadow.radius = 4
|
|
47
|
+
directionalLight.shadow.bias = - 0.00006
|
|
48
|
+
scene.add( directionalLight )
|
|
49
|
+
|
|
50
|
+
const container = document.getElementById( 'rotatingC' )
|
|
51
|
+
|
|
52
|
+
const renderer = new THREE.WebGLRenderer( { antialias: true } )
|
|
53
|
+
renderer.setPixelRatio( window.devicePixelRatio )
|
|
54
|
+
renderer.setSize( window.innerWidth, window.innerHeight )
|
|
55
|
+
renderer.setAnimationLoop( animate )
|
|
56
|
+
renderer.shadowMap.enabled = true
|
|
57
|
+
renderer.shadowMap.type = THREE.VSMShadowMap
|
|
58
|
+
renderer.toneMapping = THREE.ACESFilmicToneMapping
|
|
59
|
+
container.appendChild( renderer.domElement )
|
|
60
|
+
|
|
61
|
+
const stats = new Stats()
|
|
62
|
+
stats.domElement.style.position = 'absolute'
|
|
63
|
+
stats.domElement.style.top = '0px'
|
|
64
|
+
container.appendChild( stats.domElement )
|
|
65
|
+
|
|
66
|
+
const GRAVITY = 16
|
|
67
|
+
|
|
68
|
+
const VEHICLE_SPEED = 4
|
|
69
|
+
const VEHICLE_REVERSE_SPEED = 3
|
|
70
|
+
const VEHICLE_ACCELERATION = 2.5
|
|
71
|
+
const VEHICLE_BRAKE = 5
|
|
72
|
+
const VEHICLE_TURN_SPEED = 2.2
|
|
73
|
+
const VEHICLE_MASS = 80
|
|
74
|
+
const VEHICLE_DRIVE_FORCE = VEHICLE_MASS * VEHICLE_ACCELERATION
|
|
75
|
+
const VEHICLE_BRAKE_FORCE = VEHICLE_MASS * VEHICLE_BRAKE
|
|
76
|
+
const VEHICLE_TURN_TORQUE = 55
|
|
77
|
+
const VEHICLE_UPRIGHT_STRENGTH = 14
|
|
78
|
+
|
|
79
|
+
const NUM_SPHERES = 100
|
|
80
|
+
const SPHERE_RADIUS = 0.2
|
|
81
|
+
|
|
82
|
+
const sphereGeometry = new THREE.IcosahedronGeometry( SPHERE_RADIUS, 5 )
|
|
83
|
+
const sphereMaterial = new THREE.MeshLambertMaterial( { color: 0xdede8d } )
|
|
84
|
+
|
|
85
|
+
const world = new CANNON.World()
|
|
86
|
+
world.gravity.set( 0, - GRAVITY, 0 )
|
|
87
|
+
world.broadphase = new CANNON.NaiveBroadphase()
|
|
88
|
+
world.allowSleep = true
|
|
89
|
+
world.solver.iterations = 15
|
|
90
|
+
world.defaultContactMaterial.friction = 0.4
|
|
91
|
+
world.defaultContactMaterial.restitution = 0.1
|
|
92
|
+
|
|
93
|
+
const groundMaterial = new CANNON.Material( 'ground' )
|
|
94
|
+
const vehicleMaterial = new CANNON.Material( 'vehicle' )
|
|
95
|
+
const playerMaterial = new CANNON.Material( 'player' )
|
|
96
|
+
const ballMaterial = new CANNON.Material( 'ball' )
|
|
97
|
+
|
|
98
|
+
world.addContactMaterial( new CANNON.ContactMaterial( groundMaterial, vehicleMaterial, {
|
|
99
|
+
friction: 0.3,
|
|
100
|
+
restitution: 0.05
|
|
101
|
+
} ) )
|
|
102
|
+
world.addContactMaterial( new CANNON.ContactMaterial( groundMaterial, playerMaterial, {
|
|
103
|
+
friction: 0.1,
|
|
104
|
+
restitution: 0
|
|
105
|
+
} ) )
|
|
106
|
+
world.addContactMaterial( new CANNON.ContactMaterial( groundMaterial, ballMaterial, {
|
|
107
|
+
friction: 0.4,
|
|
108
|
+
restitution: 0.6
|
|
109
|
+
} ) )
|
|
110
|
+
world.addContactMaterial( new CANNON.ContactMaterial( vehicleMaterial, ballMaterial, {
|
|
111
|
+
friction: 0.3,
|
|
112
|
+
restitution: 0.4
|
|
113
|
+
} ) )
|
|
114
|
+
|
|
115
|
+
const cannonDebugger = new CannonDebugger( scene, world )
|
|
116
|
+
let debugPhysics = false
|
|
117
|
+
|
|
118
|
+
let worldReady = false
|
|
119
|
+
|
|
120
|
+
let vehicle = null
|
|
121
|
+
let vehicleBody = null
|
|
122
|
+
const vehicleForward = new THREE.Vector3()
|
|
123
|
+
const vehicleSize = new THREE.Vector3()
|
|
124
|
+
const vehicleRoofLocal = new CANNON.Vec3( 0, 1, 0 )
|
|
125
|
+
const vehicleWorldUp = new CANNON.Vec3()
|
|
126
|
+
const vehicleRighting = new CANNON.Vec3()
|
|
127
|
+
const vehicleWallNormal = new CANNON.Vec3()
|
|
128
|
+
const vehicleRayFrom = new CANNON.Vec3()
|
|
129
|
+
const vehicleRayTo = new CANNON.Vec3()
|
|
130
|
+
const vehicleRayResult = new CANNON.RaycastResult()
|
|
131
|
+
const worldUpVec = new CANNON.Vec3( 0, 1, 0 )
|
|
132
|
+
const vehicleInvQuat = new CANNON.Quaternion()
|
|
133
|
+
let vehicleHeading = 0
|
|
134
|
+
let vehicleSpeed = 0
|
|
135
|
+
|
|
136
|
+
const playerBody = new CANNON.Body( {
|
|
137
|
+
mass: 5,
|
|
138
|
+
material: playerMaterial,
|
|
139
|
+
linearDamping: 0.15,
|
|
140
|
+
fixedRotation: true
|
|
141
|
+
} )
|
|
142
|
+
playerBody.addShape( new CANNON.Sphere( 0.35 ) )
|
|
143
|
+
playerBody.position.set( 0, 1, 0 )
|
|
144
|
+
playerBody.updateMassProperties()
|
|
145
|
+
world.addBody( playerBody )
|
|
146
|
+
|
|
147
|
+
const playerVelocity = new THREE.Vector3()
|
|
148
|
+
const playerDirection = new THREE.Vector3()
|
|
149
|
+
|
|
150
|
+
let playerOnFloor = false
|
|
151
|
+
let mouseTime = 0
|
|
152
|
+
|
|
153
|
+
const keyStates = {}
|
|
154
|
+
|
|
155
|
+
const vector1 = new THREE.Vector3()
|
|
156
|
+
|
|
157
|
+
const spheres = []
|
|
158
|
+
let sphereIdx = 0
|
|
159
|
+
|
|
160
|
+
for ( let i = 0; i < NUM_SPHERES; i ++ ) {
|
|
161
|
+
|
|
162
|
+
const mesh = new THREE.Mesh( sphereGeometry, sphereMaterial )
|
|
163
|
+
mesh.castShadow = true
|
|
164
|
+
mesh.receiveShadow = true
|
|
165
|
+
scene.add( mesh )
|
|
166
|
+
|
|
167
|
+
const body = new CANNON.Body( {
|
|
168
|
+
mass: 0.4,
|
|
169
|
+
material: ballMaterial,
|
|
170
|
+
linearDamping: 0.2,
|
|
171
|
+
angularDamping: 0.2
|
|
172
|
+
} )
|
|
173
|
+
body.addShape( new CANNON.Sphere( SPHERE_RADIUS ) )
|
|
174
|
+
body.position.set( 0, -100, 0 )
|
|
175
|
+
body.sleep()
|
|
176
|
+
world.addBody( body )
|
|
177
|
+
|
|
178
|
+
spheres.push( { mesh: mesh, body: body } )
|
|
179
|
+
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const vehicle_url = 'https://cdn.jsdelivr.net/gh/wasya-co/ishlib3js@0.3.0/public/vendor/models/vehicles/000mb car-mazda-miata/model.glb'
|
|
183
|
+
const vehicle_config = {
|
|
184
|
+
height: 0.5,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
document.addEventListener( 'keydown', ( event ) => {
|
|
188
|
+
|
|
189
|
+
keyStates[ event.code ] = true
|
|
190
|
+
|
|
191
|
+
} )
|
|
192
|
+
|
|
193
|
+
document.addEventListener( 'keyup', ( event ) => {
|
|
194
|
+
|
|
195
|
+
keyStates[ event.code ] = false
|
|
196
|
+
|
|
197
|
+
} )
|
|
198
|
+
|
|
199
|
+
container.addEventListener( 'mousedown', () => {
|
|
200
|
+
|
|
201
|
+
document.body.requestPointerLock()
|
|
202
|
+
mouseTime = performance.now()
|
|
203
|
+
|
|
204
|
+
} )
|
|
205
|
+
|
|
206
|
+
document.addEventListener( 'mouseup', () => {
|
|
207
|
+
|
|
208
|
+
if ( document.pointerLockElement !== null ) throwBall()
|
|
209
|
+
|
|
210
|
+
} )
|
|
211
|
+
|
|
212
|
+
document.body.addEventListener( 'mousemove', ( event ) => {
|
|
213
|
+
|
|
214
|
+
if ( document.pointerLockElement === document.body ) {
|
|
215
|
+
|
|
216
|
+
camera.rotation.y -= event.movementX / 500
|
|
217
|
+
camera.rotation.x -= event.movementY / 500
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
} )
|
|
222
|
+
|
|
223
|
+
window.addEventListener( 'resize', onWindowResize )
|
|
224
|
+
|
|
225
|
+
function onWindowResize() {
|
|
226
|
+
|
|
227
|
+
const aspect = window.innerWidth / window.innerHeight
|
|
228
|
+
camera.left = frustumSize * aspect / - 2
|
|
229
|
+
camera.right = frustumSize * aspect / 2
|
|
230
|
+
camera.top = frustumSize / 2
|
|
231
|
+
camera.bottom = frustumSize / - 2
|
|
232
|
+
camera.updateProjectionMatrix()
|
|
233
|
+
|
|
234
|
+
renderer.setSize( window.innerWidth, window.innerHeight )
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function throwBall() {
|
|
239
|
+
|
|
240
|
+
const sphere = spheres[ sphereIdx ]
|
|
241
|
+
|
|
242
|
+
camera.getWorldDirection( playerDirection )
|
|
243
|
+
|
|
244
|
+
sphere.body.wakeUp()
|
|
245
|
+
sphere.body.position.set(
|
|
246
|
+
playerBody.position.x + playerDirection.x * 0.8,
|
|
247
|
+
playerBody.position.y + 0.3 + playerDirection.y * 0.8,
|
|
248
|
+
playerBody.position.z + playerDirection.z * 0.8
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
const impulse = 15 + 30 * ( 1 - Math.exp( ( mouseTime - performance.now() ) * 0.001 ) )
|
|
252
|
+
|
|
253
|
+
sphere.body.velocity.set(
|
|
254
|
+
playerDirection.x * impulse + playerBody.velocity.x,
|
|
255
|
+
playerDirection.y * impulse + playerBody.velocity.y,
|
|
256
|
+
playerDirection.z * impulse + playerBody.velocity.z
|
|
257
|
+
)
|
|
258
|
+
sphere.body.angularVelocity.set( 0, 0, 0 )
|
|
259
|
+
|
|
260
|
+
sphereIdx = ( sphereIdx + 1 ) % spheres.length
|
|
261
|
+
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function bodyHasFloorContact( body ) {
|
|
265
|
+
|
|
266
|
+
for ( let i = 0; i < world.contacts.length; i ++ ) {
|
|
267
|
+
|
|
268
|
+
const contact = world.contacts[ i ]
|
|
269
|
+
if ( contact.bi !== body && contact.bj !== body ) continue
|
|
270
|
+
|
|
271
|
+
const ny = contact.bi === body ? contact.ni.y : - contact.ni.y
|
|
272
|
+
if ( ny > 0.4 ) return true
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return false
|
|
277
|
+
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function updatePlayer( deltaTime ) {
|
|
281
|
+
|
|
282
|
+
playerOnFloor = bodyHasFloorContact( playerBody )
|
|
283
|
+
|
|
284
|
+
const speedDelta = deltaTime * ( playerOnFloor ? 25 : 8 )
|
|
285
|
+
|
|
286
|
+
if ( keyStates[ 'KeyW' ] ) {
|
|
287
|
+
|
|
288
|
+
playerVelocity.add( getForwardVector().multiplyScalar( speedDelta ) )
|
|
289
|
+
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if ( keyStates[ 'KeyS' ] ) {
|
|
293
|
+
|
|
294
|
+
playerVelocity.add( getForwardVector().multiplyScalar( - speedDelta ) )
|
|
295
|
+
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
playerVelocity.x *= Math.exp( - 4 * deltaTime )
|
|
299
|
+
playerVelocity.z *= Math.exp( - 4 * deltaTime )
|
|
300
|
+
|
|
301
|
+
playerBody.velocity.x = playerVelocity.x
|
|
302
|
+
playerBody.velocity.z = playerVelocity.z
|
|
303
|
+
|
|
304
|
+
if ( playerOnFloor && keyStates[ 'Space' ] ) {
|
|
305
|
+
|
|
306
|
+
playerBody.velocity.y = 8
|
|
307
|
+
playerOnFloor = false
|
|
308
|
+
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if ( ! vehicle ) {
|
|
312
|
+
|
|
313
|
+
camera.position.set( playerBody.position.x, playerBody.position.y + 0.4, playerBody.position.z )
|
|
314
|
+
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getForwardVector() {
|
|
320
|
+
|
|
321
|
+
camera.getWorldDirection( playerDirection )
|
|
322
|
+
playerDirection.y = 0
|
|
323
|
+
playerDirection.normalize()
|
|
324
|
+
|
|
325
|
+
return playerDirection
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function rescale( model, config ) {
|
|
330
|
+
|
|
331
|
+
const box = new THREE.Box3().setFromObject( model )
|
|
332
|
+
const size = box.getSize( new THREE.Vector3() )
|
|
333
|
+
const scale = config.height / size.y
|
|
334
|
+
model.scale.setScalar( scale )
|
|
335
|
+
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function addTrimeshFromObject( object ) {
|
|
339
|
+
|
|
340
|
+
object.updateWorldMatrix( true, true )
|
|
341
|
+
|
|
342
|
+
object.traverse( child => {
|
|
343
|
+
|
|
344
|
+
if ( ! child.isMesh || ! child.geometry ) return
|
|
345
|
+
|
|
346
|
+
const geometry = child.geometry.index ? child.geometry.toNonIndexed() : child.geometry
|
|
347
|
+
const position = geometry.attributes.position
|
|
348
|
+
const vertices = []
|
|
349
|
+
const indices = []
|
|
350
|
+
const point = new THREE.Vector3()
|
|
351
|
+
|
|
352
|
+
for ( let i = 0; i < position.count; i ++ ) {
|
|
353
|
+
|
|
354
|
+
point.fromBufferAttribute( position, i ).applyMatrix4( child.matrixWorld )
|
|
355
|
+
vertices.push( point.x, point.y, point.z )
|
|
356
|
+
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for ( let i = 0; i < position.count; i += 3 ) {
|
|
360
|
+
|
|
361
|
+
indices.push( i, i + 1, i + 2 )
|
|
362
|
+
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if ( indices.length === 0 ) return
|
|
366
|
+
|
|
367
|
+
const trimesh = new CANNON.Trimesh( vertices, indices )
|
|
368
|
+
trimesh.updateTree()
|
|
369
|
+
const body = new CANNON.Body( { mass: 0, material: groundMaterial } )
|
|
370
|
+
body.addShape( trimesh )
|
|
371
|
+
body.updateBoundingRadius()
|
|
372
|
+
world.addBody( body )
|
|
373
|
+
|
|
374
|
+
} )
|
|
375
|
+
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function createVehicleBody() {
|
|
379
|
+
|
|
380
|
+
if ( ! vehicle || vehicleBody || ! worldReady ) return
|
|
381
|
+
|
|
382
|
+
vehicle.updateMatrixWorld( true )
|
|
383
|
+
const box = new THREE.Box3().setFromObject( vehicle )
|
|
384
|
+
box.getSize( vehicleSize )
|
|
385
|
+
const center = box.getCenter( vector1 )
|
|
386
|
+
const offset = center.clone().sub( vehicle.position )
|
|
387
|
+
const radius = Math.max( Math.min( vehicleSize.x, vehicleSize.y, vehicleSize.z ) * 0.42, 0.12 )
|
|
388
|
+
const hx = Math.max( vehicleSize.x * 0.5 - radius, 0 )
|
|
389
|
+
const hz = Math.max( vehicleSize.z * 0.5 - radius, 0 )
|
|
390
|
+
|
|
391
|
+
vehicleBody = new CANNON.Body( {
|
|
392
|
+
mass: VEHICLE_MASS,
|
|
393
|
+
material: vehicleMaterial,
|
|
394
|
+
linearDamping: 0.01,
|
|
395
|
+
angularDamping: 0.2,
|
|
396
|
+
allowSleep: false,
|
|
397
|
+
collisionFilterGroup: 2,
|
|
398
|
+
collisionFilterMask: 1
|
|
399
|
+
} )
|
|
400
|
+
|
|
401
|
+
const sphere = new CANNON.Sphere( radius )
|
|
402
|
+
const points = [
|
|
403
|
+
[ offset.x, offset.y, offset.z ],
|
|
404
|
+
[ offset.x + hx, offset.y, offset.z + hz ],
|
|
405
|
+
[ offset.x + hx, offset.y, offset.z - hz ],
|
|
406
|
+
[ offset.x - hx, offset.y, offset.z + hz ],
|
|
407
|
+
[ offset.x - hx, offset.y, offset.z - hz ]
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
for ( let i = 0; i < points.length; i ++ ) {
|
|
411
|
+
|
|
412
|
+
vehicleBody.addShape( sphere, new CANNON.Vec3( points[ i ][ 0 ], points[ i ][ 1 ], points[ i ][ 2 ] ) )
|
|
413
|
+
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
vehicleBody.position.set( vehicle.position.x, vehicle.position.y, vehicle.position.z )
|
|
417
|
+
vehicleBody.quaternion.set(
|
|
418
|
+
vehicle.quaternion.x,
|
|
419
|
+
vehicle.quaternion.y,
|
|
420
|
+
vehicle.quaternion.z,
|
|
421
|
+
vehicle.quaternion.w
|
|
422
|
+
)
|
|
423
|
+
vehicleInvQuat.copy( vehicleBody.quaternion ).inverse()
|
|
424
|
+
vehicleInvQuat.vmult( worldUpVec, vehicleRoofLocal )
|
|
425
|
+
world.addBody( vehicleBody )
|
|
426
|
+
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const loader = new GLTFLoader()
|
|
430
|
+
const scene_url = 'https://cdn.jsdelivr.net/gh/wasya-co/ishlib3js@0.3.0/public/vendor/models/scenes/000mb%20collision-world/collision-world.glb'
|
|
431
|
+
|
|
432
|
+
loader.load( vehicle_url, ( gltf ) => {
|
|
433
|
+
|
|
434
|
+
vehicle = gltf.scene
|
|
435
|
+
rescale( vehicle, vehicle_config )
|
|
436
|
+
|
|
437
|
+
vehicle.traverse( child => {
|
|
438
|
+
|
|
439
|
+
if ( child.isMesh ) {
|
|
440
|
+
|
|
441
|
+
child.castShadow = true
|
|
442
|
+
child.receiveShadow = true
|
|
443
|
+
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
} )
|
|
447
|
+
|
|
448
|
+
const box = new THREE.Box3().setFromObject( vehicle )
|
|
449
|
+
vehicle.position.y += 5 - box.min.y
|
|
450
|
+
scene.add( vehicle )
|
|
451
|
+
createVehicleBody()
|
|
452
|
+
|
|
453
|
+
} )
|
|
454
|
+
|
|
455
|
+
loader.load( scene_url, ( gltf ) => {
|
|
456
|
+
|
|
457
|
+
scene.add( gltf.scene )
|
|
458
|
+
addTrimeshFromObject( gltf.scene )
|
|
459
|
+
worldReady = true
|
|
460
|
+
createVehicleBody()
|
|
461
|
+
|
|
462
|
+
gltf.scene.traverse( child => {
|
|
463
|
+
|
|
464
|
+
if ( child.isMesh ) {
|
|
465
|
+
|
|
466
|
+
child.castShadow = true
|
|
467
|
+
child.receiveShadow = true
|
|
468
|
+
|
|
469
|
+
if ( child.material.map ) {
|
|
470
|
+
|
|
471
|
+
child.material.map.anisotropy = 4
|
|
472
|
+
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
} )
|
|
478
|
+
|
|
479
|
+
const gui = new GUI( { width: 200 } )
|
|
480
|
+
gui.add( { debug: false }, 'debug' )
|
|
481
|
+
.onChange( function ( value ) {
|
|
482
|
+
|
|
483
|
+
debugPhysics = value
|
|
484
|
+
|
|
485
|
+
} )
|
|
486
|
+
|
|
487
|
+
} )
|
|
488
|
+
|
|
489
|
+
function keepVehicleUpright( deltaTime ) {
|
|
490
|
+
|
|
491
|
+
if ( ! vehicleBody ) return
|
|
492
|
+
|
|
493
|
+
vehicleBody.quaternion.vmult( vehicleRoofLocal, vehicleWorldUp )
|
|
494
|
+
vehicleRighting.copy( vehicleWorldUp ).cross( worldUpVec )
|
|
495
|
+
vehicleBody.angularVelocity.x += vehicleRighting.x * VEHICLE_UPRIGHT_STRENGTH * deltaTime
|
|
496
|
+
vehicleBody.angularVelocity.z += vehicleRighting.z * VEHICLE_UPRIGHT_STRENGTH * deltaTime
|
|
497
|
+
vehicleBody.angularVelocity.x *= 0.9
|
|
498
|
+
vehicleBody.angularVelocity.z *= 0.9
|
|
499
|
+
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function wallAhead( dir_x, dir_z, distance ) {
|
|
503
|
+
|
|
504
|
+
const side_x = - dir_z
|
|
505
|
+
const side_z = dir_x
|
|
506
|
+
const y = vehicleBody.position.y + 0.22
|
|
507
|
+
const sides = [ 0, 0.18, -0.18 ]
|
|
508
|
+
|
|
509
|
+
for ( let i = 0; i < sides.length; i ++ ) {
|
|
510
|
+
|
|
511
|
+
vehicleRayFrom.set(
|
|
512
|
+
vehicleBody.position.x + side_x * sides[ i ],
|
|
513
|
+
y,
|
|
514
|
+
vehicleBody.position.z + side_z * sides[ i ]
|
|
515
|
+
)
|
|
516
|
+
vehicleRayTo.set(
|
|
517
|
+
vehicleRayFrom.x + dir_x * distance,
|
|
518
|
+
y,
|
|
519
|
+
vehicleRayFrom.z + dir_z * distance
|
|
520
|
+
)
|
|
521
|
+
vehicleRayResult.reset()
|
|
522
|
+
const hit = world.raycastClosest( vehicleRayFrom, vehicleRayTo, {
|
|
523
|
+
skipBackfaces: true,
|
|
524
|
+
collisionFilterMask: 1
|
|
525
|
+
}, vehicleRayResult )
|
|
526
|
+
|
|
527
|
+
if ( hit && vehicleRayResult.body !== vehicleBody && Math.abs( vehicleRayResult.hitNormalWorld.y ) < 0.55 ) {
|
|
528
|
+
|
|
529
|
+
return true
|
|
530
|
+
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return false
|
|
536
|
+
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function updateVehicleInput( deltaTime ) {
|
|
540
|
+
|
|
541
|
+
if ( ! vehicleBody ) return
|
|
542
|
+
|
|
543
|
+
const holding_a = !! keyStates[ 'KeyA' ]
|
|
544
|
+
const holding_d = !! keyStates[ 'KeyD' ]
|
|
545
|
+
const braking = holding_a && holding_d
|
|
546
|
+
|
|
547
|
+
if ( holding_a && ! holding_d ) {
|
|
548
|
+
|
|
549
|
+
vehicleHeading += VEHICLE_TURN_SPEED * deltaTime
|
|
550
|
+
|
|
551
|
+
} else if ( holding_d && ! holding_a ) {
|
|
552
|
+
|
|
553
|
+
vehicleHeading -= VEHICLE_TURN_SPEED * deltaTime
|
|
554
|
+
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if ( braking ) {
|
|
558
|
+
|
|
559
|
+
vehicleSpeed -= VEHICLE_BRAKE * deltaTime
|
|
560
|
+
if ( vehicleSpeed < - VEHICLE_REVERSE_SPEED ) vehicleSpeed = - VEHICLE_REVERSE_SPEED
|
|
561
|
+
|
|
562
|
+
} else {
|
|
563
|
+
|
|
564
|
+
vehicleSpeed += VEHICLE_ACCELERATION * deltaTime
|
|
565
|
+
if ( vehicleSpeed > VEHICLE_SPEED ) vehicleSpeed = VEHICLE_SPEED
|
|
566
|
+
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function applyVehicleDrive( deltaTime ) {
|
|
572
|
+
|
|
573
|
+
if ( ! vehicleBody ) return
|
|
574
|
+
|
|
575
|
+
const holding_a = !! keyStates[ 'KeyA' ]
|
|
576
|
+
const holding_d = !! keyStates[ 'KeyD' ]
|
|
577
|
+
const forward_x = - Math.sin( vehicleHeading )
|
|
578
|
+
const forward_z = - Math.cos( vehicleHeading )
|
|
579
|
+
const move_x = forward_x * vehicleSpeed * deltaTime
|
|
580
|
+
const move_z = forward_z * vehicleSpeed * deltaTime
|
|
581
|
+
const travel = Math.hypot( move_x, move_z )
|
|
582
|
+
const dir_x = travel > 1e-8 ? move_x / travel : forward_x
|
|
583
|
+
const dir_z = travel > 1e-8 ? move_z / travel : forward_z
|
|
584
|
+
const bumper = Math.max( vehicleSize.x, vehicleSize.z, 0.4 ) * 0.5
|
|
585
|
+
|
|
586
|
+
vehicleBody.wakeUp()
|
|
587
|
+
|
|
588
|
+
if ( travel > 0 && ! wallAhead( dir_x, dir_z, bumper + travel ) ) {
|
|
589
|
+
|
|
590
|
+
vehicleBody.position.x += move_x
|
|
591
|
+
vehicleBody.position.z += move_z
|
|
592
|
+
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
vehicleBody.velocity.x = forward_x * vehicleSpeed
|
|
596
|
+
vehicleBody.velocity.z = forward_z * vehicleSpeed
|
|
597
|
+
vehicleBody.angularVelocity.y = ( holding_a && ! holding_d ? VEHICLE_TURN_SPEED : 0 ) + ( holding_d && ! holding_a ? - VEHICLE_TURN_SPEED : 0 )
|
|
598
|
+
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function syncPhysicsMeshes() {
|
|
602
|
+
|
|
603
|
+
if ( vehicle && vehicleBody ) {
|
|
604
|
+
|
|
605
|
+
vehicle.position.copy( vehicleBody.position )
|
|
606
|
+
vehicle.quaternion.copy( vehicleBody.quaternion )
|
|
607
|
+
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
for ( const sphere of spheres ) {
|
|
611
|
+
|
|
612
|
+
sphere.mesh.position.copy( sphere.body.position )
|
|
613
|
+
sphere.mesh.quaternion.copy( sphere.body.quaternion )
|
|
614
|
+
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function followVehiclePosition() {
|
|
620
|
+
|
|
621
|
+
if ( ! vehicle ) return
|
|
622
|
+
|
|
623
|
+
camera.position.copy( vehicle.position )
|
|
624
|
+
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function teleportPlayerIfOob() {
|
|
628
|
+
|
|
629
|
+
if ( camera.position.y <= - 25 ) {
|
|
630
|
+
|
|
631
|
+
playerBody.position.set( 0, 1, 0 )
|
|
632
|
+
playerBody.velocity.set( 0, 0, 0 )
|
|
633
|
+
playerVelocity.set( 0, 0, 0 )
|
|
634
|
+
|
|
635
|
+
if ( ! vehicle ) {
|
|
636
|
+
|
|
637
|
+
camera.position.set( 0, 1.4, 0 )
|
|
638
|
+
camera.rotation.set( 0, 0, 0 )
|
|
639
|
+
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function animate() {
|
|
647
|
+
|
|
648
|
+
timer.update()
|
|
649
|
+
|
|
650
|
+
const deltaTime = Math.min( 0.05, timer.getDelta() )
|
|
651
|
+
|
|
652
|
+
updatePlayer( deltaTime )
|
|
653
|
+
keepVehicleUpright( deltaTime )
|
|
654
|
+
updateVehicleInput( deltaTime )
|
|
655
|
+
|
|
656
|
+
if ( worldReady ) {
|
|
657
|
+
|
|
658
|
+
world.step( 1 / 90, deltaTime, 8 )
|
|
659
|
+
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
applyVehicleDrive( deltaTime )
|
|
663
|
+
keepVehicleUpright( deltaTime )
|
|
664
|
+
syncPhysicsMeshes()
|
|
665
|
+
followVehiclePosition()
|
|
666
|
+
teleportPlayerIfOob()
|
|
667
|
+
|
|
668
|
+
if ( debugPhysics ) {
|
|
669
|
+
|
|
670
|
+
cannonDebugger.update()
|
|
671
|
+
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
renderer.render( scene, camera )
|
|
675
|
+
stats.update()
|
|
676
|
+
|
|
677
|
+
}
|