js/Mobilizing/renderer/3D/three/types/Quaternion.js
import * as THREE from 'three';
import Vector3 from "./Vector3";
import Euler from "./Euler";
import * as _Math from "../../../../core/util/Math";
/**
* Represents a Quaternion, a way to describe a spatial rotation that is less susceptible to gimbal lock.
*
* This class extends the one from Three.js, API available here : https://threejs.org/docs/index.html#api/en/math/Quaternion
*/
export default class Quaternion extends THREE.Quaternion {
/**
* @returns {Object} {upVector, forwardVector, leftVector}
*/
getDirections() {
const upVector = new Vector3();
upVector.x = 2 * (this.x * this.y - this.w * this.z);
upVector.y = 1 - 2 * (this.x * this.x + this.z * this.z);
upVector.z = 2 * (this.y * this.z + this.w * this.x);
const forwardVector = new Vector3();
forwardVector.x = 2 * (this.x * this.z + this.w * this.y);
forwardVector.y = 2 * (this.y * this.z - this.w * this.x);
forwardVector.z = 1 - 2 * (this.x * this.x + this.y * this.y);
const leftVector = new Vector3();
leftVector.x = 1 - 2 * (this.y * this.y + this.z * this.z);
leftVector.y = 2 * (this.x * this.y + this.w * this.z);
leftVector.z = 2 * (this.x * this.z - this.w * this.y);
const result = {
upVector,
forwardVector,
leftVector
};
return result;
}
/**
* Creates a Quaternion that matches the device's current orientation. Calculated in radians.
* @private
* @param {Number} alpha the angle to use for the quaternion creation
* @param {Number} beta the angle to use for the quaternion creation
* @param {Number} gamma the angle to use for the quaternion creation
* @param {Object} screenOrientation orientation of the screen in degree! Can be very different depending on the plateform...
* @author Rich Tibbett, https://github.com/richtr/threeVR
*/
createGyroQuaternion(alpha, beta, gamma, screenOrientation) {
if (alpha !== 0 && beta !== 0 && gamma !== 0) {
//const finalQuaternion = new Quaternion();
const deviceEuler = new Euler();
const screenTransform = new Quaternion();
const worldTransform = new Quaternion(- Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)); // - PI/2 around the x-axis
let minusHalfAngle = 0;
deviceEuler.set(beta, alpha, -gamma, 'YXZ');
this.setFromEuler(deviceEuler);
minusHalfAngle = -screenOrientation / 2;
screenTransform.set(0, Math.sin(minusHalfAngle), 0, Math.cos(minusHalfAngle));
this.multiply(screenTransform);
this.multiply(worldTransform);
}
}
/**
* Can be used in the transform of a perspective camera to produce a "blind camera" effect : move the screen around to frame a part of the current scene like if you were using a camera.
*/
setFromGyro(compass) {
if (compass.alpha) {
this.createGyroQuaternion(_Math.degToRad(compass.alpha), _Math.degToRad(compass.beta), _Math.degToRad(compass.gamma), _Math.degToRad(window.orientation || 0));
}
}
}