freeCameraDeviceOrientationInput.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { Nullable } from "../../types";
  2. import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
  3. import { FreeCamera } from "../../Cameras/freeCamera";
  4. import { Quaternion } from "../../Maths/math";
  5. import { Tools } from "../../Misc/tools";
  6. import { FreeCameraInputsManager } from "../../Cameras/freeCameraInputsManager";
  7. import { Observable } from '../../Misc/observable';
  8. // Module augmentation to abstract orientation inputs from camera.
  9. declare module "../../Cameras/freeCameraInputsManager" {
  10. export interface FreeCameraInputsManager {
  11. /**
  12. * @hidden
  13. */
  14. _deviceOrientationInput: Nullable<FreeCameraDeviceOrientationInput>;
  15. /**
  16. * Add orientation input support to the input manager.
  17. * @returns the current input manager
  18. */
  19. addDeviceOrientation(): FreeCameraInputsManager;
  20. }
  21. }
  22. /**
  23. * Add orientation input support to the input manager.
  24. * @returns the current input manager
  25. */
  26. FreeCameraInputsManager.prototype.addDeviceOrientation = function(): FreeCameraInputsManager {
  27. if (!this._deviceOrientationInput) {
  28. this._deviceOrientationInput = new FreeCameraDeviceOrientationInput();
  29. this.add(this._deviceOrientationInput);
  30. }
  31. return this;
  32. };
  33. /**
  34. * Takes information about the orientation of the device as reported by the deviceorientation event to orient the camera.
  35. * Screen rotation is taken into account.
  36. * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
  37. */
  38. export class FreeCameraDeviceOrientationInput implements ICameraInput<FreeCamera> {
  39. private _camera: FreeCamera;
  40. private _screenOrientationAngle: number = 0;
  41. private _constantTranform: Quaternion;
  42. private _screenQuaternion: Quaternion = new Quaternion();
  43. private _alpha: number = 0;
  44. private _beta: number = 0;
  45. private _gamma: number = 0;
  46. /**
  47. * Can be used to detect if a device orientation sensor is availible on a device
  48. * @param timeout amount of time in milliseconds to wait for a response from the sensor (default: infinite)
  49. * @returns a promise that will resolve on orientation change
  50. */
  51. public static WaitForOrientationChangeAsync(timeout?: number) {
  52. return new Promise((res, rej) => {
  53. var gotValue = false;
  54. var eventHandler = () => {
  55. window.removeEventListener("deviceorientation", eventHandler);
  56. gotValue = true;
  57. res();
  58. };
  59. // If timeout is pupulated reject the promise
  60. if (timeout) {
  61. setTimeout(() => {
  62. if (!gotValue) {
  63. window.removeEventListener("deviceorientation", eventHandler);
  64. rej("WaitForOrientationChangeAsync timed out");
  65. }
  66. }, timeout);
  67. }
  68. window.addEventListener("deviceorientation", eventHandler);
  69. });
  70. }
  71. /**
  72. * @hidden
  73. */
  74. public _onDeviceOrientationChangedObservable = new Observable<void>();
  75. /**
  76. * Instantiates a new input
  77. * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
  78. */
  79. constructor() {
  80. this._constantTranform = new Quaternion(- Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
  81. this._orientationChanged();
  82. }
  83. /**
  84. * Define the camera controlled by the input.
  85. */
  86. public get camera(): FreeCamera {
  87. return this._camera;
  88. }
  89. public set camera(camera: FreeCamera) {
  90. this._camera = camera;
  91. if (this._camera != null && !this._camera.rotationQuaternion) {
  92. this._camera.rotationQuaternion = new Quaternion();
  93. }
  94. if (this._camera) {
  95. this._camera.onDisposeObservable.add(() => {
  96. this._onDeviceOrientationChangedObservable.clear();
  97. });
  98. }
  99. }
  100. /**
  101. * Attach the input controls to a specific dom element to get the input from.
  102. * @param element Defines the element the controls should be listened from
  103. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  104. */
  105. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  106. window.addEventListener("orientationchange", this._orientationChanged);
  107. window.addEventListener("deviceorientation", this._deviceOrientation);
  108. //In certain cases, the attach control is called AFTER orientation was changed,
  109. //So this is needed.
  110. this._orientationChanged();
  111. }
  112. private _orientationChanged = () => {
  113. this._screenOrientationAngle = (<any>window.orientation !== undefined ? +<any>window.orientation : ((<any>window.screen).orientation && ((<any>window.screen).orientation)['angle'] ? ((<any>window.screen).orientation).angle : 0));
  114. this._screenOrientationAngle = -Tools.ToRadians(this._screenOrientationAngle / 2);
  115. this._screenQuaternion.copyFromFloats(0, Math.sin(this._screenOrientationAngle), 0, Math.cos(this._screenOrientationAngle));
  116. }
  117. private _deviceOrientation = (evt: DeviceOrientationEvent) => {
  118. this._alpha = evt.alpha !== null ? evt.alpha : 0;
  119. this._beta = evt.beta !== null ? evt.beta : 0;
  120. this._gamma = evt.gamma !== null ? evt.gamma : 0;
  121. if (evt.alpha !== null) {
  122. this._onDeviceOrientationChangedObservable.notifyObservers();
  123. }
  124. }
  125. /**
  126. * Detach the current controls from the specified dom element.
  127. * @param element Defines the element to stop listening the inputs from
  128. */
  129. public detachControl(element: Nullable<HTMLElement>): void {
  130. window.removeEventListener("orientationchange", this._orientationChanged);
  131. window.removeEventListener("deviceorientation", this._deviceOrientation);
  132. this._alpha = 0;
  133. }
  134. /**
  135. * Update the current camera state depending on the inputs that have been used this frame.
  136. * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
  137. */
  138. public checkInputs(): void {
  139. //if no device orientation provided, don't update the rotation.
  140. //Only testing against alpha under the assumption thatnorientation will never be so exact when set.
  141. if (!this._alpha) { return; }
  142. Quaternion.RotationYawPitchRollToRef(Tools.ToRadians(this._alpha), Tools.ToRadians(this._beta), -Tools.ToRadians(this._gamma), this.camera.rotationQuaternion);
  143. this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion);
  144. this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform);
  145. //Mirror on XY Plane
  146. this._camera.rotationQuaternion.z *= -1;
  147. this._camera.rotationQuaternion.w *= -1;
  148. }
  149. /**
  150. * Gets the class name of the current intput.
  151. * @returns the class name
  152. */
  153. public getClassName(): string {
  154. return "FreeCameraDeviceOrientationInput";
  155. }
  156. /**
  157. * Get the friendly name associated with the input class.
  158. * @returns the input friendly name
  159. */
  160. public getSimpleName(): string {
  161. return "deviceOrientation";
  162. }
  163. }
  164. (<any>CameraInputTypes)["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;