layers_tracks_UserControlledTrack.js
import {TracksLayer} from './TracksLayer'
import * as THREE from 'three'
/**
* A basic user controlled track through mouse and arrow keys.
*/
class UserControlledTrack extends TracksLayer{
/**
*
* @param {Object} properties
* @param {Object3D} properties.mesh a visual representation of the object that will be added as a child of this.tracks.
*/
constructor(properties){
super(properties);
this.isUserControlledTrack = true;
if(properties.mesh){
this.tracks.add(properties.mesh);
}else{
const geometry = new THREE.TorusGeometry( 25, 5, 16, 100 );
const material = new THREE.MeshStandardMaterial( { color: 0xeeeeee } );
material.metalness = 0.75;
material.roughness = 0.25;
const mesh = new THREE.Mesh( geometry, material );
this.tracks.add(mesh);
this.tracks.add(new THREE.BoxHelper( mesh, 0xffff00 ));
}
if(properties.position){
this.tracks.position.copy(properties.position);
}else{
this.tracks.position.set(-6382000,6382000,0);
}
this.movement = new THREE.Vector3();
this.onMouseMove = this.getOnMouseMoveFunction();
this.onMouseLeave = this.getOnMouseLeaveFunction();
this.onKeyDown = this.getOnKeyDownFunction();
this.onKeyUp = this.getOnKeyUpFunction();
this.forward = new THREE.Vector3();
this.right = new THREE.Vector3();
this.tracks.matrixAutoUpdate = false;
this.tracks.matrixWorldAutoUpdate = false;
this.acceleration = 50;
this.previousTime = 0;
// These are temporary vectors used to orrient the track's mesh
// the track has it's own orrientation but the track's mesh has it's own relative orientation
this.desiredSubTrackUp = new THREE.Vector3();
this.desiredSubTrackForward = new THREE.Vector3();
/* this.arrowHelperX = new THREE.ArrowHelper( new THREE.Vector3(1,0,0), new THREE.Vector3(0,0,0), 100, 0xff0000 );
this.arrowHelperY = new THREE.ArrowHelper( new THREE.Vector3(0,1,0), new THREE.Vector3(0,0,0), 100, 0x00ff00 );
this.arrowHelperZ = new THREE.ArrowHelper( new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,0), 100, 0x0000ff );
this.tracks.add( this.arrowHelperX );
this.tracks.add( this.arrowHelperY );
this.tracks.add( this.arrowHelperZ ); */
}
/**
* initializes controls specific to this Track.
* @param {HTMLElement} dom the dom on which to attach visual components
*/
initControls(dom){
const self = this;
if(_isMobileDevice()){
this.initControlsMobile(dom);
}
dom.addEventListener('mouseleave',this.onMouseLeave);
dom.addEventListener('mousemove',this.onMouseMove);
window.addEventListener('keydown', this.onKeyDown);
window.addEventListener('keyup', this.onKeyUp);
}
/**
* Removes controls specific to this Track.
* @param {HTMLElement} dom the dom on which to visual components are attached
*/
removeControls(dom){
if(_isMobileDevice()){
this.removeControlsMobile();
}
dom.removeEventListener('mouseleave',this.onMouseLeave);
dom.removeEventListener('mousemove',this.onMouseMove);
window.removeEventListener('keydown', this.onKeyDown);
window.removeEventListener('keyup', this.onKeyUp);
}
getOnMouseMoveFunction(){
const self = this;
return (e)=>{
self.mousePosition = new THREE.Vector2((e.offsetX/e.srcElement.offsetWidth)-0.5, -((e.offsetY/e.srcElement.offsetHeight)-0.5));
}
}
update(clock){
const delta = clock.elapsedTime - this.previousTime;
this.previousTime = clock.elapsedTime;
//console.log(delta)
if(!!this.mousePosition){
this.tracks.getWorldDirection(this.forward).normalize();
this.tracks.up.copy(this.tracks.position).normalize();
this.right.crossVectors(this.tracks.up, this.forward).normalize();
this.desiredSubTrackUp.copy(this.tracks.up);
this.desiredSubTrackUp.applyAxisAngle(this.right, -this.mousePosition.y*2*delta)
//this.forward.applyEuler(new THREE.Euler(this.mousePosition.y*0.000001*delta, 0, 0, 'XYZ'))
this.forward.applyAxisAngle(this.right, -this.mousePosition.y*2*delta);
this.forward.applyAxisAngle(this.tracks.up, -this.mousePosition.x*2*delta);
this.tracks.up.copy(this.tracks.position).normalize();
this.tracks.lookAt(this.tracks.position.x+this.forward.x, this.tracks.position.y+this.forward.y, this.tracks.position.z+this.forward.z)
}
this.tracks.children[0].rotateOnAxis(this.tracks.children[0].up, 0.01);
this.movement.multiplyScalar(Math.pow(0.5, delta));
if(this.accelerate) {
this.movement.add(this.forward.normalize().multiplyScalar(this.acceleration*delta));
}
if(this.deccelerate) {
this.movement.add(this.forward.normalize().multiplyScalar(-this.acceleration*delta));
}
if(this.accelerateRight) {
this.movement.add(this.right.normalize().multiplyScalar(-this.acceleration*delta*0.25));
}
if(this.accelerateLeft) {
this.movement.add(this.right.normalize().multiplyScalar(this.acceleration*delta*0.25));
}
this.tracks.position.add(this.movement);
this.tracks.updateMatrix();
this.tracks.updateMatrixWorld(true);
}
getOnMouseLeaveFunction(){
const self = this;
return (event)=>{
self.mousePosition = undefined;
self.accelerate = false;
self.accelerateLeft = false;
self.deccelerate = false;
self.accelerateRight = false;
}
}
getOnKeyDownFunction(){
const self = this;
return (event) => {
switch (event.code) {
case 'KeyW':
case 'ArrowUp':
self.accelerate = true;
break;
case 'KeyA':
case 'ArrowLeft':
self.accelerateLeft = true;
break;
case 'KeyS':
case 'ArrowDown':
self.deccelerate = true;
break;
case 'KeyD':
case 'ArrowRight':
self.accelerateRight = true;
break;
}
}
}
getOnKeyUpFunction(){
const self = this;
return (event) => {
switch (event.code) {
case 'KeyW':
case 'ArrowUp':
self.accelerate = false;
break;
case 'KeyA':
case 'ArrowLeft':
self.accelerateLeft = false;
break;
case 'KeyS':
case 'ArrowDown':
self.deccelerate = false;
break;
case 'KeyD':
case 'ArrowRight':
self.accelerateRight = false;
break;
}
}
}
}export{UserControlledTrack}
function _isMobileDevice() {
return (typeof window.orientation !== "undefined") || (navigator.userAgent.indexOf('IEMobile') !== -1);
};