@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
142 lines (141 loc) • 5.66 kB
JavaScript
"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 };