UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

333 lines 12 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { EventDispatcher, MathUtils, Spherical, Vector3 } from 'three'; import { now, serialize } from 'ts-browser-helpers'; import { uiInput, uiPanelContainer, uiToggle } from 'uiconfig.js'; // eslint-disable-next-line @typescript-eslint/naming-convention const _lookDirection = new Vector3(); // eslint-disable-next-line @typescript-eslint/naming-convention const _spherical = new Spherical(); // eslint-disable-next-line @typescript-eslint/naming-convention const _target = new Vector3(); // eslint-disable-next-line @typescript-eslint/naming-convention const _changeEvent = { type: 'change' }; // todo bug - this is not showing in the UI. To test, switch to threeFirstPerson controlsMode for Default Camera in the tweakpane editor let FirstPersonControls2 = class FirstPersonControls2 extends EventDispatcher { constructor(object, domElement) { super(); // API this.enabled = true; this.enableKeys = true; this.movementSpeed = 1.0; this.lookSpeed = 0.005; this.lookVertical = true; this.autoForward = false; this.activeLook = true; this.heightSpeed = false; this.heightCoef = 1.0; this.heightMin = 0.0; this.heightMax = 1.0; this.constrainVertical = false; this.verticalMin = 0; this.verticalMax = Math.PI; this.mouseDragOn = false; // internals this.autoSpeedFactor = 0.0; this.pointerX = 0; this.pointerY = 0; this.moveForward = false; this.moveBackward = false; this.moveLeft = false; this.moveRight = false; this.moveUp = false; this.moveDown = false; this.viewHalfX = 0; this.viewHalfY = 0; // private variables // eslint-disable-next-line @typescript-eslint/naming-convention this.lat = 0; // eslint-disable-next-line @typescript-eslint/naming-convention this.lon = 0; // eslint-disable-next-line @typescript-eslint/naming-convention this.targetPosition = new Vector3(); this._lastTime = -1; // in ms this.object = object; this.domElement = domElement; this.onPointerMove = this.onPointerMove.bind(this); this.onPointerDown = this.onPointerDown.bind(this); this.onPointerUp = this.onPointerUp.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.onKeyUp = this.onKeyUp.bind(this); this.onContextMenu = this.onContextMenu.bind(this); this.domElement.addEventListener('contextmenu', this.onContextMenu); this.domElement.addEventListener('pointermove', this.onPointerMove); this.domElement.addEventListener('pointerdown', this.onPointerDown); this.domElement.addEventListener('pointerup', this.onPointerUp); window.addEventListener('keydown', this.onKeyDown); window.addEventListener('keyup', this.onKeyUp); this.handleResize(); this.setOrientation(); } setOrientation() { const quaternion = this.object.quaternion; _lookDirection.set(0, 0, -1).applyQuaternion(quaternion); _spherical.setFromVector3(_lookDirection); this.lat = 90 - MathUtils.radToDeg(_spherical.phi); this.lon = MathUtils.radToDeg(_spherical.theta); } handleResize() { if (this.domElement === document) { this.viewHalfX = window.innerWidth / 2; this.viewHalfY = window.innerHeight / 2; } else { this.viewHalfX = this.domElement.offsetWidth / 2; this.viewHalfY = this.domElement.offsetHeight / 2; } } onPointerDown(event) { if (this.domElement !== document) { this.domElement.focus(); } if (this.activeLook) { switch (event.button) { case 0: this.moveForward = true; break; case 2: this.moveBackward = true; break; default: break; } } this.mouseDragOn = true; } onPointerUp(event) { if (this.activeLook) { switch (event.button) { case 0: this.moveForward = false; break; case 2: this.moveBackward = false; break; default: break; } } this.mouseDragOn = false; } onPointerMove(event) { if (this.domElement === document) { this.pointerX = event.pageX - this.viewHalfX; this.pointerY = event.pageY - this.viewHalfY; } else { this.pointerX = event.pageX - this.domElement.offsetLeft - this.viewHalfX; this.pointerY = event.pageY - this.domElement.offsetTop - this.viewHalfY; } } onKeyDown(event) { if (!this.enableKeys) return; switch (event.code) { case 'ArrowUp': case 'KeyW': this.moveForward = true; break; case 'ArrowLeft': case 'KeyA': this.moveLeft = true; break; case 'ArrowDown': case 'KeyS': this.moveBackward = true; break; case 'ArrowRight': case 'KeyD': this.moveRight = true; break; case 'KeyR': this.moveUp = true; break; case 'KeyF': this.moveDown = true; break; default: break; } } onKeyUp(event) { if (!this.enableKeys) return; switch (event.code) { case 'ArrowUp': case 'KeyW': this.moveForward = false; break; case 'ArrowLeft': case 'KeyA': this.moveLeft = false; break; case 'ArrowDown': case 'KeyS': this.moveBackward = false; break; case 'ArrowRight': case 'KeyD': this.moveRight = false; break; case 'KeyR': this.moveUp = false; break; case 'KeyF': this.moveDown = false; break; default: break; } } lookAt(x, y, z) { if (x.isVector3) { _target.copy(x); } else { if (y === undefined || z === undefined) console.error('FirstPersonControls2.lookAt: y and z parameters are required'); else _target.set(x, y, z); } this.object.lookAt(_target); this.setOrientation(); return this; } update() { const time = now(); // in ms const delta = (this._lastTime < 0 ? 16 : Math.min(time - this._lastTime, 1000)) / 1000; // in secs this._lastTime = time; // console.log(delta) if (!this.enabled) return; if (this.heightSpeed) { const y = MathUtils.clamp(this.object.position.y, this.heightMin, this.heightMax); const heightDelta = y - this.heightMin; this.autoSpeedFactor = delta * (heightDelta * this.heightCoef); } else { this.autoSpeedFactor = 0.0; } const actualMoveSpeed = delta * this.movementSpeed; if (this.moveForward || this.autoForward && !this.moveBackward) this.object.translateZ(-(actualMoveSpeed + this.autoSpeedFactor)); if (this.moveBackward) this.object.translateZ(actualMoveSpeed); if (this.moveLeft) this.object.translateX(-actualMoveSpeed); if (this.moveRight) this.object.translateX(actualMoveSpeed); if (this.moveUp) this.object.translateY(actualMoveSpeed); if (this.moveDown) this.object.translateY(-actualMoveSpeed); let actualLookSpeed = delta * this.lookSpeed; if (!this.activeLook) { actualLookSpeed = 0; } let verticalLookRatio = 1; if (this.constrainVertical) { verticalLookRatio = Math.PI / (this.verticalMax - this.verticalMin); } this.lon -= this.pointerX * actualLookSpeed; if (this.lookVertical) this.lat -= this.pointerY * actualLookSpeed * verticalLookRatio; this.lat = Math.max(-85, Math.min(85, this.lat)); let phi = MathUtils.degToRad(90 - this.lat); const theta = MathUtils.degToRad(this.lon); if (this.constrainVertical) { phi = MathUtils.mapLinear(phi, 0, Math.PI, this.verticalMin, this.verticalMax); } const position = this.object.position; this.targetPosition.setFromSphericalCoords(1, phi, theta).add(position); this.object.lookAt(this.targetPosition); this.dispatchEvent(_changeEvent); } dispose() { this.domElement.removeEventListener('contextmenu', this.onContextMenu); this.domElement.removeEventListener('pointerdown', this.onPointerDown); this.domElement.removeEventListener('pointermove', this.onPointerMove); this.domElement.removeEventListener('pointerup', this.onPointerUp); window.removeEventListener('keydown', this.onKeyDown); window.removeEventListener('keyup', this.onKeyUp); } onContextMenu(event) { if (!this.enableKeys) return; event.preventDefault(); } }; __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "enabled", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "enableKeys", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "movementSpeed", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "lookSpeed", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "lookVertical", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "autoForward", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "activeLook", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "heightSpeed", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "heightCoef", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "heightMin", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "heightMax", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "constrainVertical", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "verticalMin", void 0); __decorate([ serialize(), uiInput() ], FirstPersonControls2.prototype, "verticalMax", void 0); __decorate([ serialize(), uiToggle() ], FirstPersonControls2.prototype, "mouseDragOn", void 0); FirstPersonControls2 = __decorate([ uiPanelContainer('First Person Controls') ], FirstPersonControls2); export { FirstPersonControls2 }; //# sourceMappingURL=FirstPersonControls2.js.map