123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- /*
- * AutoPilotController.js
- *
- * @author realor
- */
- import { Controller } from './Controller.js'
- import { Solid } from '../core/Solid.js'
- import * as THREE from '../lib/three.module.js'
- class AutoPilotController extends Controller {
- constructor(object, name) {
- super(object, name)
- this.distance = 0
- this.maxVelocity = 1
- this.maxAcceleration = 1
- this.vehiclesGroup = 'vehicles'
- this.trafficLightsGroup = 'trafficLights'
- this._animate = this.animate.bind(this)
- this._position0 = this.object.position.clone()
- this._direction = new THREE.Vector3()
- this.object.updateMatrix()
- this.object.updateMatrixWorld()
- var te = this.object.matrix.elements
- this._direction.set(te[4], te[5], te[6]).normalize() // relative to parent
- this._vector1 = new THREE.Vector3()
- this._vector2 = new THREE.Vector3(0, 1, 0) // y axis
- this.object.localToWorld(this._vector1)
- this.object.localToWorld(this._vector2)
- this._directionWorld = new THREE.Vector3()
- this._directionWorld.subVectors(this._vector2, this._vector1).normalize()
- this._velocity = 0
- this._acceleration = 0
- this._sleep = 0
- }
- onStart() {
- const application = this.application
- this._vehicles = application.scene.getObjectByName(this.vehiclesGroup)
- this._trafficLights = application.scene.getObjectByName(this.trafficLightsGroup)
- application.updateVisibility(this.object, true)
- application.addEventListener('animation', this._animate)
- }
- onStop() {
- const application = this.application
- application.removeEventListener('animation', this._animate)
- }
- animate(event) {
- if (this._sleep > 0) {
- this._sleep -= event.delta
- if (this._sleep <= 0) {
- // reset position, velocity and acceleration
- this.object.position.copy(this._position0)
- this._velocity = 0
- this._acceleration = 0
- this.object.visible = true
- }
- } else {
- var safetyDistance = 8
- var reductionDistance = 15
- var vehicle = this.getClosestVehicle()
- var trafficLight = this.getClosestTrafficLight()
- var obstacle = vehicle.distance < trafficLight.distance ? vehicle : trafficLight
- console.info(obstacle)
- if (obstacle.distance >= reductionDistance || this._velocity < obstacle.velocity) {
- if (obstacle.distance > safetyDistance) {
- this._acceleration = parseFloat(this.maxAcceleration)
- }
- } // match velocities
- else {
- var vdelta = this._velocity - obstacle.velocity
- this._acceleration = -(vdelta * vdelta) / Math.max(obstacle.distance - safetyDistance, 0.01)
- }
- this._velocity += this._acceleration * event.delta
- if (this._velocity > this.maxVelocity) {
- this._velocity = parseFloat(this.maxVelocity)
- } else if (this._velocity < 0) {
- this._velocity = 0
- }
- var delta = this._velocity * event.delta
- // TODO: add v-axis vector
- this.object.position.x += delta * this._direction.x
- this.object.position.y += delta * this._direction.y
- this.object.position.z += delta * this._direction.z
- if (this.object.position.distanceTo(this._position0) > this.distance) {
- this._sleep = Math.random() * 10
- this.object.visible = false
- }
- var changeEvent = { type: 'nodeChanged', objects: [this.object], source: this }
- this.application.notifyEventListeners('scene', changeEvent)
- }
- }
- getClosestVehicle() {
- var minDistance = 1000
- var velocity = 0
- var closestVehicle = null
- if (this._vehicles) {
- var position = new THREE.Vector3()
- this.object.localToWorld(position)
- var vehiclePosition = new THREE.Vector3()
- var vector = new THREE.Vector3()
- var children = this._vehicles.children
- for (var i = 0; i < children.length; i++) {
- var vehicle = children[i]
- if (vehicle !== this.object && vehicle.visible && vehicle instanceof Solid) {
- vehiclePosition.set(0, 0, 0)
- vehicle.localToWorld(vehiclePosition)
- vector.subVectors(vehiclePosition, position)
- var vehicleDistance = vector.length()
- if (vehicleDistance < minDistance) {
- var linearDistance = vector.dot(this._directionWorld)
- if (linearDistance > 0) {
- // is not behind
- var margin = Math.sqrt(vehicleDistance * vehicleDistance - linearDistance * linearDistance)
- if (margin < 2) {
- // car width
- closestVehicle = vehicle
- minDistance = vehicleDistance
- if (vehicle.controllers && vehicle.controllers[0] && vehicle.controllers[0]._velocity) {
- velocity = vehicle.controllers[0]._velocity
- } else {
- velocity = 0
- }
- }
- }
- }
- }
- }
- }
- return { distance: minDistance, velocity: velocity, object: closestVehicle }
- }
- getClosestTrafficLight() {
- var minDistance = 1000
- var closestLight = null
- if (this._trafficLights) {
- var position = new THREE.Vector3()
- this.object.localToWorld(position)
- var lightPosition = new THREE.Vector3()
- var lightDirection = new THREE.Vector3()
- var vector = new THREE.Vector3()
- var children = this._trafficLights.children
- for (var i = 0; i < children.length; i++) {
- var light = children[i]
- if (light.userData['Dynamic'] && light.userData['Dynamic']['state'] > 1) {
- // not green
- lightPosition.set(0, 0, 0)
- light.localToWorld(lightPosition)
- vector.subVectors(lightPosition, position)
- var lightDistance = vector.length()
- if (lightDistance < minDistance) {
- vector.normalize()
- if (vector.dot(this._directionWorld) > 0.8) {
- // light is in front of us
- this._vector1.set(0, 0, 0)
- this._vector2.set(0, 1, 0) // y axis
- light.localToWorld(this._vector1)
- light.localToWorld(this._vector2)
- lightDirection.subVectors(this._vector2, this._vector1).normalize()
- if (lightDirection.dot(this._directionWorld) < -0.8) {
- // light looks at car
- minDistance = lightDistance
- closestLight = light
- }
- }
- }
- }
- }
- }
- return { distance: minDistance, velocity: 0, object: closestLight }
- }
- }
- Controller.addClass(AutoPilotController)
- export { AutoPilotController }
|