UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

142 lines (141 loc) 5.66 kB
"use strict"; import { EventDispatcher, Quaternion } from "three"; import { setQuaternionFromEvent, EPS, CHANGE_EVENT, blendQuaternionToAbsoluteYAngle, quaternionYAngle, yAngleFromEvent, PI_DEG, PI_DEG2 } from "./DeviceOrientationControlsUtils"; import { screenOrientation } from "../../UserAgent"; export const DEFAULT_SMOOTH_AMOUNT = 0.01; export const COMPASS_READJUST_TIMESTEP_START = 1e3; export const COMPASS_READJUST_TIMESTEP_MAX = 5e3; export const COMPASS_READJUST_TIMESTEP_INCREMENT = 1e3; class DeviceOrientationControls extends EventDispatcher { constructor() { super(); this._relativeQuaternion = new Quaternion(); this._blendedQuaternion = new Quaternion(); this.enabled = true; this._relativeEvent = { alpha: 0, beta: 0, gamma: 0 }; this._screenOrientation = 0; this.alphaOffset = 0; // radians this._smoothAmount = DEFAULT_SMOOTH_AMOUNT; this._currentYAngleOffset = 0; this._targetYAngleOffset = 0; this._compassReadjustTimestep = COMPASS_READJUST_TIMESTEP_START; // in milliseconds this._absoluteYAngleProcessedAt = -1; this._bound = { onDeviceOrientationChangeEvent: this.onDeviceOrientationChangeEvent.bind(this), onDeviceOrientationAbsoluteChangeEvent: this.onDeviceOrientationAbsoluteChangeEvent.bind(this), onScreenOrientationChangeEvent: this.onScreenOrientationChangeEvent.bind(this) }; this.lastQuaternion = new Quaternion(); this.dispose = () => this.disconnect(); this.connect(); } onDeviceOrientationChangeEvent(event) { this._relativeEvent = event; const webkitCompassHeading = event.webkitCompassHeading; if (webkitCompassHeading != null) { this._setAbsoluteAngleY(webkitCompassHeading + this._screenOrientation); } } onDeviceOrientationAbsoluteChangeEvent(event) { if (event.alpha == null) { return; } const yAngleAbsolute = yAngleFromEvent(event, this._screenOrientation); this._setAbsoluteAngleY(yAngleAbsolute); } _setAbsoluteAngleY(y) { this._absoluteYAngle = y; } onScreenOrientationChangeEvent() { this._screenOrientation = screenOrientation(); this._absoluteYAngleProcessedAt = -1; } connect() { this.onScreenOrientationChangeEvent(); if (globalThis.DeviceOrientationEvent !== void 0 && // @ts-ignore typeof globalThis.DeviceOrientationEvent.requestPermission === "function") { globalThis.DeviceOrientationEvent.requestPermission().then((response) => { if (response == "granted") { globalThis.addEventListener("orientationchange", this._bound.onScreenOrientationChangeEvent); globalThis.addEventListener("deviceorientation", this._bound.onDeviceOrientationChangeEvent); globalThis.addEventListener( "deviceorientationabsolute", this._bound.onDeviceOrientationAbsoluteChangeEvent ); } }).catch((error) => { console.error("THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:", error); }); } else { globalThis.addEventListener("orientationchange", this._bound.onScreenOrientationChangeEvent); globalThis.addEventListener("deviceorientation", this._bound.onDeviceOrientationChangeEvent); globalThis.addEventListener( "deviceorientationabsolute", this._bound.onDeviceOrientationAbsoluteChangeEvent ); } this.enabled = true; } disconnect() { globalThis.removeEventListener("orientationchange", this.onScreenOrientationChangeEvent); globalThis.removeEventListener("deviceorientation", this.onDeviceOrientationChangeEvent); this.enabled = false; } update() { if (this.enabled === false) return; setQuaternionFromEvent(this._relativeEvent, this._screenOrientation, this._relativeQuaternion); const currentYAngle = quaternionYAngle(this._relativeQuaternion); const now = performance.now(); const timeSinceLastCompassReadjust = now - this._absoluteYAngleProcessedAt; const updateYAngleRequired = this._absoluteYAngleProcessedAt < 0 || timeSinceLastCompassReadjust > this._compassReadjustTimestep; if (updateYAngleRequired && this._absoluteYAngle != null) { this._targetYAngleOffset = this._absoluteYAngle - currentYAngle; if (Math.abs(this._targetYAngleOffset) > PI_DEG) { if (this._targetYAngleOffset > 0) { this._targetYAngleOffset -= PI_DEG2; } else { this._targetYAngleOffset += PI_DEG2; } } const delta = this._targetYAngleOffset - this._currentYAngleOffset; if (Math.abs(delta) > PI_DEG) { if (delta > 0) { this._targetYAngleOffset -= PI_DEG2; } else { this._targetYAngleOffset += PI_DEG2; } } this._absoluteYAngle = void 0; this._absoluteYAngleProcessedAt = now; this._compassReadjustTimestep = Math.min( this._compassReadjustTimestep + COMPASS_READJUST_TIMESTEP_INCREMENT, COMPASS_READJUST_TIMESTEP_MAX ); } this._currentYAngleOffset = (1 - this._smoothAmount) * this._currentYAngleOffset + this._smoothAmount * this._targetYAngleOffset; blendQuaternionToAbsoluteYAngle(this._relativeQuaternion, this._currentYAngleOffset, this._blendedQuaternion); if (8 * (1 - this.lastQuaternion.dot(this._blendedQuaternion)) > EPS) { this.lastQuaternion.copy(this._blendedQuaternion); this.dispatchEvent(CHANGE_EVENT); } } quaternion(target) { target.copy(this._blendedQuaternion); } setSmoothAmount(smoothAmount) { this._smoothAmount = smoothAmount; } } export { DeviceOrientationControls };