babylon.webXRSessionManager.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. module BABYLON {
  2. /**
  3. * Manages an XRSession
  4. * @see https://doc.babylonjs.com/how_to/webxr
  5. */
  6. export class WebXRSessionManager {
  7. private _xrNavigator: any;
  8. private _xrDevice: XRDevice;
  9. private _tmpMatrix = new BABYLON.Matrix();
  10. /** @hidden */
  11. public _xrSession: XRSession;
  12. /** @hidden */
  13. public _frameOfReference: XRFrameOfReference;
  14. /** @hidden */
  15. public _sessionRenderTargetTexture: RenderTargetTexture;
  16. /** @hidden */
  17. public _currentXRFrame: XRFrame;
  18. /**
  19. * Constructs a WebXRSessionManager, this must be initialized within a user action before usage
  20. * @param scene The scene which the session should be created for
  21. */
  22. constructor(private scene: BABYLON.Scene) {
  23. }
  24. /**
  25. * Initializes the manager, this must be done with a user action (eg. button click event)
  26. * After initialization enterXR can be called to start an XR session
  27. * @returns Promise which resolves after it is initialized
  28. */
  29. public initialize(): Promise<void> {
  30. // Check if the browser supports webXR
  31. this._xrNavigator = navigator;
  32. if (!this._xrNavigator.xr) {
  33. return Promise.reject("webXR not supported by this browser");
  34. }
  35. // Request the webXR device
  36. return this._xrNavigator.xr.requestDevice().then((device: XRDevice) => {
  37. this._xrDevice = device;
  38. return (<any>this.scene.getEngine()._gl).setCompatibleXRDevice(this._xrDevice);
  39. });
  40. }
  41. /**
  42. * Enters XR with the desired XR session options
  43. * @param sessionCreationOptions xr options to create the session with
  44. * @param frameOfReferenceType option to configure how the xr pose is expressed
  45. * @returns Promise which resolves after it enters XR
  46. */
  47. public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: XRFrameOfReferenceType): Promise<void> {
  48. // initialize session
  49. return this._xrDevice.requestSession(sessionCreationOptions).then((session: XRSession) => {
  50. this._xrSession = session;
  51. this._xrSession.baseLayer = new XRWebGLLayer(this._xrSession, this.scene.getEngine()._gl);
  52. return this._xrSession.requestFrameOfReference(frameOfReferenceType);
  53. }).then((frameOfRef: any) => {
  54. this._frameOfReference = frameOfRef;
  55. // Tell the engine's render loop to be driven by the xr session's refresh rate and provide xr pose information
  56. this.scene.getEngine().customAnimationFrameRequester = {
  57. requestAnimationFrame: this._xrSession.requestAnimationFrame.bind(this._xrSession),
  58. renderFunction: (timestamp: number, xrFrame: XRFrame) => {
  59. // Store the XR frame in the manager to be consumed by the XR camera to update pose
  60. this._currentXRFrame = xrFrame;
  61. this.scene.getEngine()._renderLoop();
  62. }
  63. };
  64. // Create render target texture from xr's webgl render target
  65. this._sessionRenderTargetTexture = WebXRSessionManager._CreateRenderTargetTextureFromSession(this._xrSession, this.scene);
  66. });
  67. }
  68. /**
  69. * Stops the xrSession and restores the renderloop
  70. * @returns Promise which resolves after it exits XR
  71. */
  72. public exitXR() {
  73. return new Promise((res) => {
  74. this.scene.getEngine().customAnimationFrameRequester = null;
  75. this._xrSession.end();
  76. // Restore frame buffer to avoid clear on xr framebuffer after session end
  77. this.scene.getEngine().restoreDefaultFramebuffer();
  78. // Need to restart render loop as after calling session.end the last request for new frame will never call callback
  79. this.scene.getEngine()._renderLoop();
  80. res();
  81. });
  82. }
  83. /**
  84. * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
  85. * @param ray ray to cast into the environment
  86. * @returns Promise which resolves with a collision point in the environment if it exists
  87. */
  88. public environmentPointHitTest(ray: BABYLON.Ray): Promise<Nullable<Vector3>> {
  89. return new Promise((res, rej) => {
  90. // Compute left handed inputs to request hit test
  91. var origin = new Float32Array([ray.origin.x, ray.origin.y, ray.origin.z]);
  92. var direction = new Float32Array([ray.direction.x, ray.direction.y, ray.direction.z]);
  93. if (!this.scene.useRightHandedSystem) {
  94. origin[2] *= -1;
  95. direction[2] *= -1;
  96. }
  97. // Fire hittest
  98. this._xrSession.requestHitTest(origin, direction, this._frameOfReference)
  99. .then((hits: any) => {
  100. if (hits.length > 0) {
  101. BABYLON.Matrix.FromFloat32ArrayToRefScaled(hits[0].hitMatrix, 0, 1.0, this._tmpMatrix);
  102. var hitPoint = this._tmpMatrix.getTranslation();
  103. if (!this.scene.useRightHandedSystem) {
  104. hitPoint.z *= -1;
  105. }
  106. res(hitPoint);
  107. }else {
  108. res(null);
  109. }
  110. }).catch((e: Error) => {
  111. res(null);
  112. });
  113. });
  114. }
  115. /**
  116. * @hidden
  117. * Converts the render layer of xrSession to a render target
  118. * @param session session to create render target for
  119. * @param scene scene the new render target should be created for
  120. */
  121. public static _CreateRenderTargetTextureFromSession(session: XRSession, scene: BABYLON.Scene) {
  122. // Create internal texture
  123. var internalTexture = new BABYLON.InternalTexture(scene.getEngine(), BABYLON.InternalTexture.DATASOURCE_UNKNOWN, true);
  124. internalTexture.width = session.baseLayer.framebufferWidth;
  125. internalTexture.height = session.baseLayer.framebufferHeight;
  126. internalTexture._framebuffer = session.baseLayer.framebuffer;
  127. // Create render target texture from the internal texture
  128. var renderTargetTexture = new BABYLON.RenderTargetTexture("XR renderTargetTexture", {width: internalTexture.width, height: internalTexture.height}, scene, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
  129. renderTargetTexture._texture = internalTexture;
  130. return renderTargetTexture;
  131. }
  132. }
  133. }