File size: 4,205 Bytes
8194362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import {
  Object3D,
  PerspectiveCamera,
  Vector3,
  Quaternion,
  Matrix4,
  Ray,
  Sphere
} from 'three'

import Simulation from './Simulation'

// For improved performance, we initialize certain variables only once instead of in the step function
const __prevPosition = new Vector3()
const __newVelocity = new Vector3()
const __acceleration = new Vector3()
const __gravityVector = new Vector3()
const __direction = new Vector3()
const __intersection = new Vector3()
const __axis = new Vector3()
const __rotation = new Quaternion()
const __temp = new Vector3()
const __ray = new Ray()

export default class Player {

  constructor () {
    this.object = new Object3D()
    this.eyes = new PerspectiveCamera()
    this.object.add(this.eyes)

    this.velocity = new Vector3()
    this.eyeAngularVelocity = new Vector3()

    this.galaxy = 0

    this.controllers = []
  }

  lookAt (position) {
    // this.object.lookAt makes it look in the exact opposite __direction, for some reason
    const lookAtMatrix = new Matrix4()
    lookAtMatrix.lookAt(this.object.position, position, this.object.up)
    this.object.quaternion.setFromRotationMatrix(lookAtMatrix)
    this.object.quaternion.multiply(this.eyes.quaternion.clone().inverse())
  }

  addController (controller) {
    this.controllers.push(controller)
  }

  handleInput () {
    for (let i = 0; i < this.controllers.length; i++) {
      this.controllers[i].update()
    }
  }

  step (delta) {
    const wormhole = Simulation.config.wormhole
    const wormholeSphere = new Sphere(wormhole.position, wormhole.radius)

    if (this.velocity.lengthSq() > 0.00001) {
      __prevPosition.copy(this.object.position)

      // 1. Compute wormhole curvature/gravity.
      __gravityVector.subVectors(wormhole.position, __prevPosition)
      const rayDistance = __gravityVector.length() - wormhole.radius * (1 - wormhole.gravityRatio)
      const amount = wormhole.gravityRatio / rayDistance
      __acceleration.copy(__gravityVector.normalize()).multiplyScalar(wormhole.radius * this.velocity.lengthSq() * amount * amount)

      // Apply curvature to velocity
      __newVelocity.copy(this.velocity).add(__acceleration.multiplyScalar(delta))

      // Adjust new velocity (keep magnitude of old velocity)
      __newVelocity.normalize().multiplyScalar(this.velocity.length())

      // Update the player's position and orientation accordingly
      this.object.position.addVectors(__prevPosition, __newVelocity.multiplyScalar(delta))
      this.object.quaternion.multiplyQuaternions(
        __rotation.setFromUnitVectors(this.velocity.normalize(), __newVelocity.normalize()),
        this.object.quaternion
      )

      this.velocity.copy(__newVelocity)

      // 2. Check if we're going through the wormhole
      __direction.copy(this.velocity).normalize()

      __ray.set(__prevPosition, __direction)

      const distanceTravelledSq = __direction.subVectors(this.object.position, __prevPosition).lengthSq()

      const at = __ray.intersectSphere(wormholeSphere, __intersection)
      if (at && at.distanceToSquared(__prevPosition) <= distanceTravelledSq) {
        // Rotate 180 degrees around __axis pointing at exit point
        __axis.subVectors(__intersection, wormhole.position).normalize()
        __rotation.setFromAxisAngle(__axis, Math.PI)
        this.object.quaternion.multiplyQuaternions(__rotation, this.object.quaternion)
        this.velocity.reflect(__axis).multiplyScalar(-1)

        // Set new position a tiny bit outside mirrored __intersection point
        this.object.position
          .copy(wormhole.position)
          .add(
            __temp.subVectors(wormhole.position, __intersection)
              .multiplyScalar(1.0001)
          )

        this.galaxy = 1 - this.galaxy
      }
    }

    __rotation.set(
      this.eyeAngularVelocity.x * delta,
      this.eyeAngularVelocity.y * delta,
      this.eyeAngularVelocity.z * delta,
      1
    ).normalize()
    this.eyes.quaternion.multiply(__rotation)
  }

  update (delta) {
    this.handleInput()
    this.step(delta)

    // Object isn't actually part of a rendered scene, so we need to call this manually
    this.object.updateMatrixWorld(true)
  }

}