webXRExperienceHelper.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import { Nullable } from "../../types";
  2. import { Observable } from "../../Misc/observable";
  3. import { IDisposable, Scene } from "../../scene";
  4. import { Quaternion, Vector3 } from "../../Maths/math.vector";
  5. import { AbstractMesh } from "../../Meshes/abstractMesh";
  6. import { Camera } from "../../Cameras/camera";
  7. import { WebXRSessionManager } from "./webXRSessionManager";
  8. import { WebXRCamera } from "./webXRCamera";
  9. import { WebXRState, WebXRRenderTarget } from './webXRTypes';
  10. import { WebXRFeaturesManager } from './webXRFeaturesManager';
  11. /**
  12. * Base set of functionality needed to create an XR experince (WebXRSessionManager, Camera, StateManagement, etc.)
  13. * @see https://doc.babylonjs.com/how_to/webxr
  14. */
  15. export class WebXRExperienceHelper implements IDisposable {
  16. /**
  17. * Container which stores the xr camera and controllers as children. This can be used to move the camera/user as the camera's position is updated by the xr device
  18. */
  19. public container: AbstractMesh;
  20. /**
  21. * Camera used to render xr content
  22. */
  23. public camera: WebXRCamera;
  24. /**
  25. * The current state of the XR experience (eg. transitioning, in XR or not in XR)
  26. */
  27. public state: WebXRState = WebXRState.NOT_IN_XR;
  28. private _setState(val: WebXRState) {
  29. this.state = val;
  30. this.onStateChangedObservable.notifyObservers(this.state);
  31. }
  32. private static _TmpVector = new Vector3();
  33. /**
  34. * Fires when the state of the experience helper has changed
  35. */
  36. public onStateChangedObservable = new Observable<WebXRState>();
  37. /** Session manager used to keep track of xr session */
  38. public sessionManager: WebXRSessionManager;
  39. /** A features manager for this xr session */
  40. public featuresManager: WebXRFeaturesManager;
  41. private _nonVRCamera: Nullable<Camera> = null;
  42. private _originalSceneAutoClear = true;
  43. private _supported = false;
  44. /**
  45. * Creates the experience helper
  46. * @param scene the scene to attach the experience helper to
  47. * @returns a promise for the experience helper
  48. */
  49. public static CreateAsync(scene: Scene): Promise<WebXRExperienceHelper> {
  50. var helper = new WebXRExperienceHelper(scene);
  51. return helper.sessionManager.initializeAsync().then(() => {
  52. helper._supported = true;
  53. return helper;
  54. }).catch(() => {
  55. return helper;
  56. });
  57. }
  58. /**
  59. * Creates a WebXRExperienceHelper
  60. * @param scene The scene the helper should be created in
  61. */
  62. private constructor(private scene: Scene) {
  63. this.camera = new WebXRCamera("", scene);
  64. this.sessionManager = new WebXRSessionManager(scene);
  65. this.featuresManager = new WebXRFeaturesManager(this.sessionManager);
  66. this.container = new AbstractMesh("WebXR Container", scene);
  67. this.camera.parent = this.container;
  68. scene.onDisposeObservable.add(() => {
  69. this.exitXRAsync();
  70. });
  71. }
  72. /**
  73. * Exits XR mode and returns the scene to its original state
  74. * @returns promise that resolves after xr mode has exited
  75. */
  76. public exitXRAsync() {
  77. this._setState(WebXRState.EXITING_XR);
  78. return this.sessionManager.exitXRAsync();
  79. }
  80. /**
  81. * Enters XR mode (This must be done within a user interaction in most browsers eg. button click)
  82. * @param sessionMode options for the XR session
  83. * @param referenceSpaceType frame of reference of the XR session
  84. * @param renderTarget the output canvas that will be used to enter XR mode
  85. * @returns promise that resolves after xr mode has entered
  86. */
  87. public enterXRAsync(sessionMode: XRSessionMode, referenceSpaceType: XRReferenceSpaceType, renderTarget: WebXRRenderTarget): Promise<WebXRSessionManager> {
  88. if (!this._supported) {
  89. throw "XR session not supported by this browser";
  90. }
  91. this._setState(WebXRState.ENTERING_XR);
  92. let sessionCreationOptions = {
  93. optionalFeatures: (referenceSpaceType !== "viewer" && referenceSpaceType !== "local") ? [referenceSpaceType] : []
  94. };
  95. return this.sessionManager.initializeSessionAsync(sessionMode, sessionCreationOptions).then(() => {
  96. return this.sessionManager.setReferenceSpaceAsync(referenceSpaceType);
  97. }).then(() => {
  98. return renderTarget.initializeXRLayerAsync(this.sessionManager.session);
  99. }).then(() => {
  100. return this.sessionManager.updateRenderStateAsync({ depthFar: this.camera.maxZ, depthNear: this.camera.minZ, baseLayer: renderTarget.xrLayer! });
  101. }).then(() => {
  102. return this.sessionManager.startRenderingToXRAsync();
  103. }).then(() => {
  104. // Cache pre xr scene settings
  105. this._originalSceneAutoClear = this.scene.autoClear;
  106. this._nonVRCamera = this.scene.activeCamera;
  107. // Overwrite current scene settings
  108. this.scene.autoClear = false;
  109. this.scene.activeCamera = this.camera;
  110. this.sessionManager.onXRFrameObservable.add(() => {
  111. this.camera.updateFromXRSessionManager(this.sessionManager);
  112. });
  113. this.sessionManager.onXRSessionEnded.addOnce(() => {
  114. // Reset camera rigs output render target to ensure sessions render target is not drawn after it ends
  115. this.camera.rigCameras.forEach((c) => {
  116. c.outputRenderTarget = null;
  117. });
  118. // Restore scene settings
  119. this.scene.autoClear = this._originalSceneAutoClear;
  120. this.scene.activeCamera = this._nonVRCamera;
  121. this._setState(WebXRState.NOT_IN_XR);
  122. });
  123. // Wait until the first frame arrives before setting state to in xr
  124. this.sessionManager.onXRFrameObservable.addOnce(() => {
  125. this._setState(WebXRState.IN_XR);
  126. this.setPositionOfCameraUsingContainer(new Vector3(this._nonVRCamera!.position.x, this.camera.position.y, this._nonVRCamera!.position.z));
  127. });
  128. return this.sessionManager;
  129. }).catch((e: any) => {
  130. console.log(e);
  131. console.log(e.message);
  132. throw (e);
  133. });
  134. }
  135. /**
  136. * Updates the global position of the camera by moving the camera's container
  137. * This should be used instead of modifying the camera's position as it will be overwritten by an xrSessions's update frame
  138. * @param position The desired global position of the camera
  139. */
  140. public setPositionOfCameraUsingContainer(position: Vector3) {
  141. this.camera.globalPosition.subtractToRef(position, WebXRExperienceHelper._TmpVector);
  142. this.container.position.subtractInPlace(WebXRExperienceHelper._TmpVector);
  143. }
  144. /**
  145. * Rotates the xr camera by rotating the camera's container around the camera's position
  146. * This should be used instead of modifying the camera's rotation as it will be overwritten by an xrSessions's update frame
  147. * @param rotation the desired quaternion rotation to apply to the camera
  148. */
  149. public rotateCameraByQuaternionUsingContainer(rotation: Quaternion) {
  150. if (!this.container.rotationQuaternion) {
  151. this.container.rotationQuaternion = Quaternion.FromEulerVector(this.container.rotation);
  152. }
  153. this.container.rotationQuaternion.multiplyInPlace(rotation);
  154. this.container.position.rotateByQuaternionAroundPointToRef(rotation, this.camera.globalPosition, this.container.position);
  155. }
  156. /**
  157. * Disposes of the experience helper
  158. */
  159. public dispose() {
  160. this.camera.dispose();
  161. this.container.dispose();
  162. this.onStateChangedObservable.clear();
  163. this.sessionManager.dispose();
  164. }
  165. }