原理说明
控制器是一个很麻烦的东西,需要创建更多的类来管理相机行为,并且可自定义性差,所以将部分控制器的功能绑定到相机上,可以解决这些问题,所以我以 FlyControls为例,将控制器功能绑定到相机上,然后可以通过直接给相机发送命令控制其行为,并可以根据业务自定义业务。
相机代码
控制器的核心其实没有什么业务,只有几个简单的函数,并且都是没有实际行为的 ,所以我们直接继承想用的相机到我们的相机class上,再添加控制器事件到里面就行了
typescript
import { PerspectiveCamera, Quaternion, Vector3 } from "three";
const _changeEvent: any = { type: 'change' };
const _EPS = 0.0001;
export default class FlyCamera extends PerspectiveCamera {
domElement: null | HTMLElement;
_tmpQuaternion: Quaternion;
enabled: boolean;
state: number;
keys: {};
mouseButtons: { LEFT: null; MIDDLE: null; RIGHT: null; };
touches: { ONE: null; TWO: null; };
movementSpeed: number;
rollSpeed: number;
dragToLook: boolean;
autoForward: boolean;
_moveState: { up: number; down: number; left: number; right: number; forward: number; back: number; pitchUp: number; pitchDown: number; yawLeft: number; yawRight: number; rollLeft: number; rollRight: number; };
_moveVector: Vector3;
_rotationVector: Vector3;
_lastQuaternion: Quaternion;
_lastPosition: Vector3;
_status: number;
_onKeyDown: (event: { altKey: any; code: any; }) => void;
_onKeyUp: (event: { code: any; }) => void;
_onPointerMove: (event: { pageX: number; pageY: number; }) => void;
_onPointerDown: (event: { button: any; }) => void;
_onPointerUp: (event: { button: any; }) => void;
_onPointerCancel: (this: any) => void;
_onContextMenu: (event: { preventDefault: () => void; }) => void;
constructor(fov: number | undefined, aspect: number | undefined, near: number | undefined, far: number | undefined) {
super(fov, aspect, near, far)
this.domElement = null
this._tmpQuaternion = new Quaternion();
this.enabled = true;
this.state = - 1;
this.keys = {};
this.mouseButtons = { LEFT: null, MIDDLE: null, RIGHT: null };
this.touches = { ONE: null, TWO: null };
this.movementSpeed = 1.0;
this.rollSpeed =.1;
this.dragToLook = false;
this.autoForward = false;
// internals
this._moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
this._moveVector = new Vector3(0, 0, 0);
this._rotationVector = new Vector3(0, 0, 0);
this._lastQuaternion = new Quaternion();
this._lastPosition = new Vector3();
this._status = 0;
// event listeners
this._onKeyDown = onKeyDown.bind(this);
this._onKeyUp = onKeyUp.bind(this);
this._onPointerMove = onPointerMove.bind(this);
this._onPointerDown = onPointerDown.bind(this);
this._onPointerUp = onPointerUp.bind(this);
this._onPointerCancel = onPointerCancel.bind(this);
this._onContextMenu = onContextMenu.bind(this);
}
connect(domElement: HTMLCanvasElement) {
this.domElement = domElement
window.addEventListener('keydown', this._onKeyDown);
window.addEventListener('keyup', this._onKeyUp);
this.domElement.addEventListener('pointermove', this._onPointerMove);
this.domElement.addEventListener('pointerdown', this._onPointerDown);
this.domElement.addEventListener('pointerup', this._onPointerUp);
this.domElement.addEventListener('pointercancel', this._onPointerCancel);
this.domElement.addEventListener('contextmenu', this._onContextMenu);
}
disconnect() {
window.removeEventListener('keydown', this._onKeyDown);
window.removeEventListener('keyup', this._onKeyUp);
if (this.domElement) {
this.domElement.removeEventListener('pointermove', this._onPointerMove);
this.domElement.removeEventListener('pointerdown', this._onPointerDown);
this.domElement.removeEventListener('pointerup', this._onPointerUp);
this.domElement.removeEventListener('pointercancel', this._onPointerCancel);
this.domElement.removeEventListener('contextmenu', this._onContextMenu);
this.domElement = null
}
}
dispose() {
this.disconnect();
}
update(delta: number) {
if (this.enabled === false) return;
const object = this;
const moveMult = delta * this.movementSpeed;
const rotMult = delta * this.rollSpeed;
object.translateX(this._moveVector.x * moveMult);
object.translateY(this._moveVector.y * moveMult);
object.translateZ(this._moveVector.z * moveMult);
this._tmpQuaternion.set(this._rotationVector.x * rotMult, this._rotationVector.y * rotMult, this._rotationVector.z * rotMult, 1).normalize();
object.quaternion.multiply(this._tmpQuaternion);
if (
this._lastPosition.distanceToSquared(object.position) > _EPS ||
8 * (1 - this._lastQuaternion.dot(object.quaternion)) > _EPS
) {
this.dispatchEvent(_changeEvent);
this._lastQuaternion.copy(object.quaternion);
this._lastPosition.copy(object.position);
}
console.log('update');
}
// private
_updateMovementVector() {
const forward = (this._moveState.forward || (this.autoForward && !this._moveState.back)) ? 1 : 0;
this._moveVector.x = (- this._moveState.left + this._moveState.right);
this._moveVector.y = (- this._moveState.down + this._moveState.up);
this._moveVector.z = (- forward + this._moveState.back);
//console.log( 'move:', [ this._moveVector.x, this._moveVector.y, this._moveVector.z ] );
}
_updateRotationVector() {
this._rotationVector.x = (- this._moveState.pitchDown + this._moveState.pitchUp);
this._rotationVector.y = (- this._moveState.yawRight + this._moveState.yawLeft);
this._rotationVector.z = (- this._moveState.rollRight + this._moveState.rollLeft);
//console.log( 'rotate:', [ this._rotationVector.x, this._rotationVector.y, this._rotationVector.z ] );
}
_getContainerDimensions() {
if (!this.domElement) return
// 类型保护
if (this.domElement instanceof HTMLElement && this.domElement === document.body) {
return {
size: [this.domElement.offsetWidth, this.domElement.offsetHeight],
offset: [this.domElement.offsetLeft, this.domElement.offsetTop]
};
} else {
return {
size: [window.innerWidth, window.innerHeight],
offset: [0, 0]
};
}
}
}
function onKeyDown(this: any, event: { altKey: any; code: any; }) {
if (event.altKey || this.enabled === false) {
return;
}
switch (event.code) {
case 'ShiftLeft':
case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
case 'KeyW': this._moveState.forward = 1; break;
case 'KeyS': this._moveState.back = 1; break;
case 'KeyA': this._moveState.left = 1; break;
case 'KeyD': this._moveState.right = 1; break;
case 'KeyR': this._moveState.up = 1; break;
case 'KeyF': this._moveState.down = 1; break;
case 'ArrowUp': this._moveState.pitchUp = 1; break;
case 'ArrowDown': this._moveState.pitchDown = 1; break;
case 'ArrowLeft': this._moveState.yawLeft = 1; break;
case 'ArrowRight': this._moveState.yawRight = 1; break;
case 'KeyQ': this._moveState.rollLeft = 1; break;
case 'KeyE': this._moveState.rollRight = 1; break;
}
this._updateMovementVector();
this._updateRotationVector();
}
function onKeyUp(this: any, event: { code: any; }) {
if (this.enabled === false) return;
switch (event.code) {
case 'ShiftLeft':
case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
case 'KeyW': this._moveState.forward = 0; break;
case 'KeyS': this._moveState.back = 0; break;
case 'KeyA': this._moveState.left = 0; break;
case 'KeyD': this._moveState.right = 0; break;
case 'KeyR': this._moveState.up = 0; break;
case 'KeyF': this._moveState.down = 0; break;
case 'ArrowUp': this._moveState.pitchUp = 0; break;
case 'ArrowDown': this._moveState.pitchDown = 0; break;
case 'ArrowLeft': this._moveState.yawLeft = 0; break;
case 'ArrowRight': this._moveState.yawRight = 0; break;
case 'KeyQ': this._moveState.rollLeft = 0; break;
case 'KeyE': this._moveState.rollRight = 0; break;
}
this._updateMovementVector();
this._updateRotationVector();
}
function onPointerDown(this: any, event: { button: any; }) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status++;
} else {
switch (event.button) {
case 0: this._moveState.forward = 1; break;
case 2: this._moveState.back = 1; break;
}
this._updateMovementVector();
}
}
function onPointerMove(this: any, event: { pageX: number; pageY: number; }) {
if (this.enabled === false) return;
if (!this.dragToLook || this._status > 0) {
const container = this._getContainerDimensions();
const halfWidth = container.size[0] / 2;
const halfHeight = container.size[1] / 2;
this._moveState.yawLeft = - ((event.pageX - container.offset[0]) - halfWidth) / halfWidth;
this._moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight;
this._updateRotationVector();
}
}
function onPointerUp(this: any, event: { button: any; }) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status--;
this._moveState.yawLeft = this._moveState.pitchDown = 0;
} else {
switch (event.button) {
case 0: this._moveState.forward = 0; break;
case 2: this._moveState.back = 0; break;
}
this._updateMovementVector();
}
this._updateRotationVector();
}
function onPointerCancel(this: any) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status = 0;
this._moveState.yawLeft = this._moveState.pitchDown = 0;
} else {
this._moveState.forward = 0;
this._moveState.back = 0;
this._updateMovementVector();
}
this._updateRotationVector();
}
function onContextMenu(this: any, event: { preventDefault: () => void; }) {
if (this.enabled === false) return;
event.preventDefault();
}
相机使用
typescript
import { BoxGeometry, Clock, EventDispatcher, Mesh, MeshNormalMaterial, PerspectiveCamera, Scene, WebGLRenderer } from "three";
import FlyCamera from "../Cameras/FlyCamera";
export default class Engine extends EventDispatcher {
scene: Scene
camera: FlyCamera;
renderer: WebGLRenderer
dom: HTMLElement
clock: Clock
constructor(dom: HTMLElement) {
super()
this.dom = dom
let w = dom.offsetWidth
let h = dom.offsetHeight
let scene = new Scene();
let camera = new FlyCamera(75, w / h, 0.1, 1000);
let renderer = new WebGLRenderer();
renderer.setSize(w, h);
camera.connect(renderer.domElement)//这是将鼠标互动的dom元素绑定到上面,如果不用这个dom的话,可以用其他div做成类似控制面板的东西(比如绑定触摸摇杆控件)
dom.appendChild(renderer.domElement);
const geometry = new BoxGeometry();
const material = new MeshNormalMaterial();
const cube = new Mesh(geometry, material);
scene.add(cube);
camera.position.set(0,0,5)
camera.lookAt(0, 0, 0)
dom.addEventListener('resize', this.resize)
this.scene = scene
this.camera = camera
this.renderer = renderer
this.clock = new Clock()
this.animate();
}
resize = () => {
let dom = this.dom
let w = dom.offsetWidth
let h = dom.offsetHeight
this.camera.aspect = w / h
this.renderer.setSize(w, h);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.render();
}
render() {
this.renderer.render(this.scene, this.camera);
const delta = this.clock.getDelta();
this.camera.update(delta)
}
}
关于自定义
这里我用js代码演示,绑定的任然是flyControl
javascript
import { Clock, PerspectiveCamera, Quaternion, Vector3 } from "three";
export default class FPSCamera extends PerspectiveCamera {
constructor(fov, aspect, near, far, domElement = null) {
super(fov, aspect, near, far)
this.domElement = null
this._tmpQuaternion = new Quaternion();
this.enabled = true;
this.state = - 1;
this.keys = {};
this.mouseButtons = { LEFT: null, MIDDLE: null, RIGHT: null };
this.touches = { ONE: null, TWO: null };
this.movementSpeed = 1.0;
this.rollSpeed = 0.2;
this.dragToLook = false;
this.autoForward = false;
// internals
this._moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
this._moveVector = new Vector3(0, 0, 0);
this._rotationVector = new Vector3(0, 0, 0);
this._lastQuaternion = new Quaternion();
this._lastPosition = new Vector3();
this._status = 0;
// event listeners
this._onKeyDown = onKeyDown.bind(this);
this._onKeyUp = onKeyUp.bind(this);
this._onPointerMove = onPointerMove.bind(this);
this._onPointerDown = onPointerDown.bind(this);
this._onPointerUp = onPointerUp.bind(this);
this._onPointerCancel = onPointerCancel.bind(this);
this._onContextMenu = onContextMenu.bind(this);
// animate
this._clock = new Clock()
this.active = false
if (domElement) this.connect(domElement)
}
connect(domElement) {
console.log('FPSCAMERA CONNECT', domElement);
this.domElement = domElement
window.addEventListener('keydown', this._onKeyDown);
window.addEventListener('keyup', this._onKeyUp);
this.domElement.addEventListener('pointermove', this._onPointerMove);
this.domElement.addEventListener('pointerdown', this._onPointerDown);
this.domElement.addEventListener('pointerup', this._onPointerUp);
this.domElement.addEventListener('pointercancel', this._onPointerCancel);
this.domElement.addEventListener('contextmenu', this._onContextMenu);
}
disconnect() {
window.removeEventListener('keydown', this._onKeyDown);
window.removeEventListener('keyup', this._onKeyUp);
this.domElement.removeEventListener('pointermove', this._onPointerMove);
this.domElement.removeEventListener('pointerdown', this._onPointerDown);
this.domElement.removeEventListener('pointerup', this._onPointerUp);
this.domElement.removeEventListener('pointercancel', this._onPointerCancel);
this.domElement.removeEventListener('contextmenu', this._onContextMenu);
this.domElement = null
}
dispose() {
this.disconnect();
}
update(delta) {
const _EPS = 0.000001;
if (this.enabled === false) return;
const object = this;
const moveMult = delta * this.movementSpeed;
const rotMult = delta * this.rollSpeed;
object.translateX(this._moveVector.x * moveMult);
object.translateY(this._moveVector.y * moveMult);
object.translateZ(this._moveVector.z * moveMult);
this._tmpQuaternion.set(this._rotationVector.x * rotMult, this._rotationVector.y * rotMult, this._rotationVector.z * rotMult, 1).normalize();
object.quaternion.multiply(this._tmpQuaternion);
if (
this._lastPosition.distanceToSquared(object.position) > _EPS ||
8 * (1 - this._lastQuaternion.dot(object.quaternion)) > _EPS
) {
this.dispatchEvent({ type: 'change' });
this._lastQuaternion.copy(object.quaternion);
this._lastPosition.copy(object.position);
}
// console.log('update');
}
// start
start() {
let loop = () => {
if (this.active)
requestAnimationFrame(loop)
else return
let delta = this._clock.getDelta()
this.update(delta)
}
this.active = true
loop()
}
end() {
this.active = false
}
// private
_updateMovementVector() {
const forward = (this._moveState.forward || (this.autoForward && !this._moveState.back)) ? 1 : 0;
this._moveVector.x = (- this._moveState.left + this._moveState.right);
this._moveVector.y = (- this._moveState.down + this._moveState.up);
this._moveVector.z = (- forward + this._moveState.back);
//console.log( 'move:', [ this._moveVector.x, this._moveVector.y, this._moveVector.z ] );
}
_updateRotationVector() {
this._rotationVector.x = (- this._moveState.pitchDown + this._moveState.pitchUp);
this._rotationVector.y = (- this._moveState.yawRight + this._moveState.yawLeft);
this._rotationVector.z = (- this._moveState.rollRight + this._moveState.rollLeft);
//console.log( 'rotate:', [ this._rotationVector.x, this._rotationVector.y, this._rotationVector.z ] );
}
_getContainerDimensions() {
if (this.domElement != document) {
return {
size: [this.domElement.offsetWidth, this.domElement.offsetHeight],
offset: [this.domElement.offsetLeft, this.domElement.offsetTop]
};
} else {
return {
size: [window.innerWidth, window.innerHeight],
offset: [0, 0]
};
}
}
}
function onKeyDown(event) {
if (event.altKey || this.enabled === false) {
return;
}
switch (event.code) {
case 'ShiftLeft':
case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
case 'KeyW': this._moveState.forward = 1; break;
case 'KeyS': this._moveState.back = 1; break;
case 'KeyA': this._moveState.left = 1; break;
case 'KeyD': this._moveState.right = 1; break;
case 'KeyR': this._moveState.up = 1; break;
case 'KeyF': this._moveState.down = 1; break;
case 'ArrowUp': this._moveState.pitchUp = 1; break;
case 'ArrowDown': this._moveState.pitchDown = 1; break;
case 'ArrowLeft': this._moveState.yawLeft = 1; break;
case 'ArrowRight': this._moveState.yawRight = 1; break;
case 'KeyQ': this._moveState.rollLeft = 1; break;
case 'KeyE': this._moveState.rollRight = 1; break;
}
this._updateMovementVector();
this._updateRotationVector();
}
function onKeyUp(event) {
if (this.enabled === false) return;
switch (event.code) {
case 'ShiftLeft':
case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
case 'KeyW': this._moveState.forward = 0; break;
case 'KeyS': this._moveState.back = 0; break;
case 'KeyA': this._moveState.left = 0; break;
case 'KeyD': this._moveState.right = 0; break;
case 'KeyR': this._moveState.up = 0; break;
case 'KeyF': this._moveState.down = 0; break;
case 'ArrowUp': this._moveState.pitchUp = 0; break;
case 'ArrowDown': this._moveState.pitchDown = 0; break;
case 'ArrowLeft': this._moveState.yawLeft = 0; break;
case 'ArrowRight': this._moveState.yawRight = 0; break;
case 'KeyQ': this._moveState.rollLeft = 0; break;
case 'KeyE': this._moveState.rollRight = 0; break;
}
this._updateMovementVector();
this._updateRotationVector();
}
function onPointerDown(event) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status++;
} else {
switch (event.button) {
case 0: this._moveState.forward = 1; break;
case 2: this._moveState.back = 1; break;
}
this._updateMovementVector();
}
}
function onPointerMove(event) {
if (this.enabled === false) return;
if (!this.dragToLook || this._status > 0) {
const container = this._getContainerDimensions();
const halfWidth = container.size[0] / 2;
const halfHeight = container.size[1] / 2;
this._moveState.yawLeft = - ((event.pageX - container.offset[0]) - halfWidth) / halfWidth;
this._moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight;
this._updateRotationVector();
}
}
function onPointerUp(event) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status--;
this._moveState.yawLeft = this._moveState.pitchDown = 0;
} else {
switch (event.button) {
case 0: this._moveState.forward = 0; break;
case 2: this._moveState.back = 0; break;
}
this._updateMovementVector();
}
this._updateRotationVector();
}
function onPointerCancel() {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status = 0;
this._moveState.yawLeft = this._moveState.pitchDown = 0;
} else {
this._moveState.forward = 0;
this._moveState.back = 0;
this._updateMovementVector();
}
this._updateRotationVector();
}
function onContextMenu(event) {
if (this.enabled === false) return;
event.preventDefault();
}
这里我添加了start和end方法来暂停和启动它,而在之前ts的版本中,我直接再渲染动画帧中调用了update方法
原版
这是一个最纯净的js版本
javascript
import { PerspectiveCamera, Quaternion, Vector3 } from "three";
const _changeEvent = { type: 'change' };
export default class FlyCamera extends PerspectiveCamera {
constructor(fov, aspect, near, far, domElement = null) {
super(fov, aspect, near, far)
this.domElement = null
this._tmpQuaternion = new Quaternion();
this.enabled = true;
this.state = - 1;
this.keys = {};
this.mouseButtons = { LEFT: null, MIDDLE: null, RIGHT: null };
this.touches = { ONE: null, TWO: null };
this.movementSpeed = 1.0;
this.rollSpeed = 0.005;
this.dragToLook = false;
this.autoForward = false;
// internals
this._moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
this._moveVector = new Vector3(0, 0, 0);
this._rotationVector = new Vector3(0, 0, 0);
this._lastQuaternion = new Quaternion();
this._lastPosition = new Vector3();
this._status = 0;
// event listeners
this._onKeyDown = onKeyDown.bind(this);
this._onKeyUp = onKeyUp.bind(this);
this._onPointerMove = onPointerMove.bind(this);
this._onPointerDown = onPointerDown.bind(this);
this._onPointerUp = onPointerUp.bind(this);
this._onPointerCancel = onPointerCancel.bind(this);
this._onContextMenu = onContextMenu.bind(this);
if (domElement) this.connect(domElement)
}
connect(domElement) {
this.domElement = domElement
window.addEventListener('keydown', this._onKeyDown);
window.addEventListener('keyup', this._onKeyUp);
this.domElement.addEventListener('pointermove', this._onPointerMove);
this.domElement.addEventListener('pointerdown', this._onPointerDown);
this.domElement.addEventListener('pointerup', this._onPointerUp);
this.domElement.addEventListener('pointercancel', this._onPointerCancel);
this.domElement.addEventListener('contextmenu', this._onContextMenu);
}
disconnect() {
window.removeEventListener('keydown', this._onKeyDown);
window.removeEventListener('keyup', this._onKeyUp);
this.domElement.removeEventListener('pointermove', this._onPointerMove);
this.domElement.removeEventListener('pointerdown', this._onPointerDown);
this.domElement.removeEventListener('pointerup', this._onPointerUp);
this.domElement.removeEventListener('pointercancel', this._onPointerCancel);
this.domElement.removeEventListener('contextmenu', this._onContextMenu);
this.domElement = null
}
dispose() {
this.disconnect();
}
update(delta) {
const _EPS = 0.000001;
if (this.enabled === false) return;
const object = this;
const moveMult = delta * this.movementSpeed;
const rotMult = delta * this.rollSpeed;
object.translateX(this._moveVector.x * moveMult);
object.translateY(this._moveVector.y * moveMult);
object.translateZ(this._moveVector.z * moveMult);
this._tmpQuaternion.set(this._rotationVector.x * rotMult, this._rotationVector.y * rotMult, this._rotationVector.z * rotMult, 1).normalize();
object.quaternion.multiply(this._tmpQuaternion);
if (
this._lastPosition.distanceToSquared(object.position) > _EPS ||
8 * (1 - this._lastQuaternion.dot(object.quaternion)) > _EPS
) {
this.dispatchEvent({ type: 'change' });
this._lastQuaternion.copy(object.quaternion);
this._lastPosition.copy(object.position);
}
console.log('update');
}
// private
_updateMovementVector() {
const forward = (this._moveState.forward || (this.autoForward && !this._moveState.back)) ? 1 : 0;
this._moveVector.x = (- this._moveState.left + this._moveState.right);
this._moveVector.y = (- this._moveState.down + this._moveState.up);
this._moveVector.z = (- forward + this._moveState.back);
//console.log( 'move:', [ this._moveVector.x, this._moveVector.y, this._moveVector.z ] );
}
_updateRotationVector() {
this._rotationVector.x = (- this._moveState.pitchDown + this._moveState.pitchUp);
this._rotationVector.y = (- this._moveState.yawRight + this._moveState.yawLeft);
this._rotationVector.z = (- this._moveState.rollRight + this._moveState.rollLeft);
//console.log( 'rotate:', [ this._rotationVector.x, this._rotationVector.y, this._rotationVector.z ] );
}
_getContainerDimensions() {
if (this.domElement != document) {
return {
size: [this.domElement.offsetWidth, this.domElement.offsetHeight],
offset: [this.domElement.offsetLeft, this.domElement.offsetTop]
};
} else {
return {
size: [window.innerWidth, window.innerHeight],
offset: [0, 0]
};
}
}
}
function onKeyDown(event) {
if (event.altKey || this.enabled === false) {
return;
}
switch (event.code) {
case 'ShiftLeft':
case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
case 'KeyW': this._moveState.forward = 1; break;
case 'KeyS': this._moveState.back = 1; break;
case 'KeyA': this._moveState.left = 1; break;
case 'KeyD': this._moveState.right = 1; break;
case 'KeyR': this._moveState.up = 1; break;
case 'KeyF': this._moveState.down = 1; break;
case 'ArrowUp': this._moveState.pitchUp = 1; break;
case 'ArrowDown': this._moveState.pitchDown = 1; break;
case 'ArrowLeft': this._moveState.yawLeft = 1; break;
case 'ArrowRight': this._moveState.yawRight = 1; break;
case 'KeyQ': this._moveState.rollLeft = 1; break;
case 'KeyE': this._moveState.rollRight = 1; break;
}
this._updateMovementVector();
this._updateRotationVector();
}
function onKeyUp(event) {
if (this.enabled === false) return;
switch (event.code) {
case 'ShiftLeft':
case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
case 'KeyW': this._moveState.forward = 0; break;
case 'KeyS': this._moveState.back = 0; break;
case 'KeyA': this._moveState.left = 0; break;
case 'KeyD': this._moveState.right = 0; break;
case 'KeyR': this._moveState.up = 0; break;
case 'KeyF': this._moveState.down = 0; break;
case 'ArrowUp': this._moveState.pitchUp = 0; break;
case 'ArrowDown': this._moveState.pitchDown = 0; break;
case 'ArrowLeft': this._moveState.yawLeft = 0; break;
case 'ArrowRight': this._moveState.yawRight = 0; break;
case 'KeyQ': this._moveState.rollLeft = 0; break;
case 'KeyE': this._moveState.rollRight = 0; break;
}
this._updateMovementVector();
this._updateRotationVector();
}
function onPointerDown(event) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status++;
} else {
switch (event.button) {
case 0: this._moveState.forward = 1; break;
case 2: this._moveState.back = 1; break;
}
this._updateMovementVector();
}
}
function onPointerMove(event) {
if (this.enabled === false) return;
if (!this.dragToLook || this._status > 0) {
const container = this._getContainerDimensions();
const halfWidth = container.size[0] / 2;
const halfHeight = container.size[1] / 2;
this._moveState.yawLeft = - ((event.pageX - container.offset[0]) - halfWidth) / halfWidth;
this._moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight;
this._updateRotationVector();
}
}
function onPointerUp(event) {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status--;
this._moveState.yawLeft = this._moveState.pitchDown = 0;
} else {
switch (event.button) {
case 0: this._moveState.forward = 0; break;
case 2: this._moveState.back = 0; break;
}
this._updateMovementVector();
}
this._updateRotationVector();
}
function onPointerCancel() {
if (this.enabled === false) return;
if (this.dragToLook) {
this._status = 0;
this._moveState.yawLeft = this._moveState.pitchDown = 0;
} else {
this._moveState.forward = 0;
this._moveState.back = 0;
this._updateMovementVector();
}
this._updateRotationVector();
}
function onContextMenu(event) {
if (this.enabled === false) return;
event.preventDefault();
}