UNPKG

@polygonjs/polygonjs

Version:

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

110 lines (99 loc) 3.5 kB
import {BaseEvent, Euler, Quaternion, Vector3, Object3D} from 'three'; import {degToRad, radToDeg} from 'three/src/math/MathUtils'; export type ScreenOrientation = number; export interface DeviceOrientationEventExtended extends DeviceOrientationEvent { requestPermission?: () => Promise<string>; webkitCompassHeading?: number; } export const CHANGE_EVENT: BaseEvent<'change'> = {type: 'change'}; export const EPS = 0.000001; export const PI_DEG = radToDeg(Math.PI); export const PI_DEG2 = 2 * radToDeg(Math.PI); const AXIS_Z = new Vector3(0, 0, 1); const euler = new Euler(); const q0 = new Quaternion(); const q1 = new Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)); // - PI/2 around the x-axis const q2 = new Quaternion(); const ROTATION_ORDER_YXZ = 'YXZ'; function _setQuaternionFromAlphaBetaGamma( alpha: number, beta: number, gamma: number, orient: number, target: Quaternion ): void { euler.set(beta, alpha, -gamma, ROTATION_ORDER_YXZ); // 'ZXY' for the device, but 'YXZ' for us target.setFromEuler(euler); // orient the device target.multiply(q1); // camera looks out the back of the device, not the top target.multiply(q0.setFromAxisAngle(AXIS_Z, -orient)); // adjust for screen orientation } const alphaOffset = 0; export function setQuaternionFromEvent( event: Partial<DeviceOrientationEvent>, screenOrientation: ScreenOrientation, target: Quaternion ): void { const alpha = event.alpha ? degToRad(event.alpha) + alphaOffset : 0; // Z const beta = event.beta ? degToRad(event.beta) : 0; // X' const gamma = event.gamma ? degToRad(event.gamma) : 0; // Y'' const orient = screenOrientation ? degToRad(screenOrientation as number) : 0; // O _setQuaternionFromAlphaBetaGamma(alpha, beta, gamma, orient, target); } export function quaternionYAngle(relativeQuaternion: Quaternion) { euler.setFromQuaternion(relativeQuaternion); return -radToDeg(euler.y); } export function yAngleFromEvent(event: Partial<DeviceOrientationEvent>, screenOrientation: ScreenOrientation): number { setQuaternionFromEvent(event, screenOrientation, q2); return quaternionYAngle(q2); } interface RotationHierachy { ryOffset: Object3D; ry: Object3D; rx: Object3D; rz: Object3D; } function _createObjects(): RotationHierachy { const ryOffset = new Object3D(); const ry = new Object3D(); const rx = new Object3D(); const rz = new Object3D(); ryOffset.add(ry); ry.add(rx); rx.add(rz); return { ryOffset, ry, rx, rz, }; } let _rotationHierarchy: RotationHierachy | undefined; function _getRotationHierarchy() { return _rotationHierarchy || (_rotationHierarchy = _createObjects()); } export function blendQuaternionToAbsoluteYAngle( relativeQuaternion: Quaternion, yAngleOffset: number, target: Quaternion ) { euler.setFromQuaternion(relativeQuaternion); const rotationHierarchy = _getRotationHierarchy(); rotationHierarchy.ryOffset.rotation.set(0, -degToRad(yAngleOffset), 0); rotationHierarchy.ry.rotation.set(0, euler.y, 0); rotationHierarchy.rx.rotation.set(euler.x, 0, 0); rotationHierarchy.rz.rotation.set(0, 0, euler.z); rotationHierarchy.rz.updateWorldMatrix(true, true); target.setFromRotationMatrix(rotationHierarchy.rz.matrixWorld); } // export function ensureDeltaLessThan2PI(initValue: number, comparisonValue:number): number { // const delta = initValue - comparisonValue; // if (Math.abs(delta) > PI_DEG) { // if (delta > 0) { // return delta - PI_DEG2; // } else { // return delta + PI_DEG2; // } // } // return delta; // }