123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- module BABYLON {
- /**
- * Manages an XRSession
- * @see https://doc.babylonjs.com/how_to/webxr
- */
- export class WebXRSessionManager {
- private _xrNavigator: any;
- private _xrDevice: XRDevice;
- private _tmpMatrix = new BABYLON.Matrix();
- /** @hidden */
- public _xrSession: XRSession;
- /** @hidden */
- public _frameOfReference: XRFrameOfReference;
- /** @hidden */
- public _sessionRenderTargetTexture: RenderTargetTexture;
- /** @hidden */
- public _currentXRFrame: XRFrame;
- /**
- * Constructs a WebXRSessionManager, this must be initialized within a user action before usage
- * @param scene The scene which the session should be created for
- */
- constructor(private scene: BABYLON.Scene) {
- }
- /**
- * Initializes the manager, this must be done with a user action (eg. button click event)
- * After initialization enterXR can be called to start an XR session
- * @returns Promise which resolves after it is initialized
- */
- public initialize(): Promise<void> {
- // Check if the browser supports webXR
- this._xrNavigator = navigator;
- if (!this._xrNavigator.xr) {
- return Promise.reject("webXR not supported by this browser");
- }
- // Request the webXR device
- return this._xrNavigator.xr.requestDevice().then((device: XRDevice) => {
- this._xrDevice = device;
- return (<any>this.scene.getEngine()._gl).setCompatibleXRDevice(this._xrDevice);
- });
- }
- /**
- * Enters XR with the desired XR session options
- * @param sessionCreationOptions xr options to create the session with
- * @param frameOfReferenceType option to configure how the xr pose is expressed
- * @returns Promise which resolves after it enters XR
- */
- public enterXR(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: XRFrameOfReferenceType): Promise<void> {
- // initialize session
- return this._xrDevice.requestSession(sessionCreationOptions).then((session: XRSession) => {
- this._xrSession = session;
- this._xrSession.baseLayer = new XRWebGLLayer(this._xrSession, this.scene.getEngine()._gl);
- return this._xrSession.requestFrameOfReference(frameOfReferenceType);
- }).then((frameOfRef: any) => {
- this._frameOfReference = frameOfRef;
- // Tell the engine's render loop to be driven by the xr session's refresh rate and provide xr pose information
- this.scene.getEngine().customAnimationFrameRequester = {
- requestAnimationFrame: this._xrSession.requestAnimationFrame.bind(this._xrSession),
- renderFunction: (timestamp: number, xrFrame: XRFrame) => {
- // Store the XR frame in the manager to be consumed by the XR camera to update pose
- this._currentXRFrame = xrFrame;
- this.scene.getEngine()._renderLoop();
- }
- };
- // Create render target texture from xr's webgl render target
- this._sessionRenderTargetTexture = WebXRSessionManager._CreateRenderTargetTextureFromSession(this._xrSession, this.scene);
- });
- }
- /**
- * Stops the xrSession and restores the renderloop
- * @returns Promise which resolves after it exits XR
- */
- public exitXR() {
- return new Promise((res) => {
- this.scene.getEngine().customAnimationFrameRequester = null;
- this._xrSession.end();
- // Restore frame buffer to avoid clear on xr framebuffer after session end
- this.scene.getEngine().restoreDefaultFramebuffer();
- // Need to restart render loop as after calling session.end the last request for new frame will never call callback
- this.scene.getEngine()._renderLoop();
- res();
- });
- }
- /**
- * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
- * @param ray ray to cast into the environment
- * @returns Promise which resolves with a collision point in the environment if it exists
- */
- public environmentPointHitTest(ray: BABYLON.Ray): Promise<Nullable<Vector3>> {
- return new Promise((res, rej) => {
- // Compute left handed inputs to request hit test
- var origin = new Float32Array([ray.origin.x, ray.origin.y, ray.origin.z]);
- var direction = new Float32Array([ray.direction.x, ray.direction.y, ray.direction.z]);
- if (!this.scene.useRightHandedSystem) {
- origin[2] *= -1;
- direction[2] *= -1;
- }
- // Fire hittest
- this._xrSession.requestHitTest(origin, direction, this._frameOfReference)
- .then((hits: any) => {
- if (hits.length > 0) {
- BABYLON.Matrix.FromFloat32ArrayToRefScaled(hits[0].hitMatrix, 0, 1.0, this._tmpMatrix);
- var hitPoint = this._tmpMatrix.getTranslation();
- if (!this.scene.useRightHandedSystem) {
- hitPoint.z *= -1;
- }
- res(hitPoint);
- }else {
- res(null);
- }
- }).catch((e: Error) => {
- res(null);
- });
- });
- }
- /**
- * @hidden
- * Converts the render layer of xrSession to a render target
- * @param session session to create render target for
- * @param scene scene the new render target should be created for
- */
- public static _CreateRenderTargetTextureFromSession(session: XRSession, scene: BABYLON.Scene) {
- // Create internal texture
- var internalTexture = new BABYLON.InternalTexture(scene.getEngine(), BABYLON.InternalTexture.DATASOURCE_UNKNOWN, true);
- internalTexture.width = session.baseLayer.framebufferWidth;
- internalTexture.height = session.baseLayer.framebufferHeight;
- internalTexture._framebuffer = session.baseLayer.framebuffer;
- // Create render target texture from the internal texture
- var renderTargetTexture = new BABYLON.RenderTargetTexture("XR renderTargetTexture", {width: internalTexture.width, height: internalTexture.height}, scene, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
- renderTargetTexture._texture = internalTexture;
- return renderTargetTexture;
- }
- }
- }
|