Prechádzať zdrojové kódy

cleanup and api changes

Raanan Weber 5 rokov pred
rodič
commit
f23a6464bb

+ 13 - 18
src/Cameras/XR/webXRController.ts

@@ -8,6 +8,10 @@ import { WebXRMotionControllerManager } from './motionController/webXRMotionCont
 
 let idCount = 0;
 
+export interface IWebXRControllerOptions {
+    forceControllerProfile?: string;
+}
+
 /**
  * Represents an XR controller
  */
@@ -39,28 +43,28 @@ export class WebXRController {
     /**
      * Creates the controller
      * @see https://doc.babylonjs.com/how_to/webxr
-     * @param scene the scene which the controller should be associated to
+     * @param _scene the scene which the controller should be associated to
      * @param inputSource the underlying input source for the controller
      * @param controllerProfile An optional controller profile for this input. This will override the xrInput profile.
      */
     constructor(
-        private scene: Scene,
+        private _scene: Scene,
         /** The underlying input source for the controller  */
         public inputSource: XRInputSource,
-        controllerProfile?: string) {
-        this._uniqueId = `${idCount++}-${inputSource.targetRayMode}-${inputSource.handedness}`;
+        private _options: IWebXRControllerOptions = {}) {
+        this._uniqueId = `controller-${idCount++}-${inputSource.targetRayMode}-${inputSource.handedness}`;
 
-        this.pointer = new AbstractMesh("controllerPointer", scene);
+        this.pointer = new AbstractMesh(`${this._uniqueId}-pointer`, _scene);
         this.pointer.rotationQuaternion = new Quaternion();
 
         if (this.inputSource.gripSpace) {
-            this.grip = new AbstractMesh("controllerGrip", this.scene);
+            this.grip = new AbstractMesh(`${this._uniqueId}-grip`, this._scene);
             this.grip.rotationQuaternion = new Quaternion();
         }
 
         // for now only load motion controllers if gamepad available
         if (this.inputSource.gamepad) {
-            this.gamepadController = WebXRMotionControllerManager.GetMotionControllerWithXRInput(inputSource, scene, controllerProfile);
+            this.gamepadController = WebXRMotionControllerManager.GetMotionControllerWithXRInput(inputSource, _scene, this._options.forceControllerProfile);
             // if the model is loaded, do your thing
             this.gamepadController.onModelLoadedObservable.addOnce(() => {
                 this.gamepadController!.rootMesh!.parent = this.pointer;
@@ -87,7 +91,7 @@ export class WebXRController {
         if (pose) {
             this.pointer.position.copyFrom(<any>(pose.transform.position));
             this.pointer.rotationQuaternion!.copyFrom(<any>(pose.transform.orientation));
-            if (!this.scene.useRightHandedSystem) {
+            if (!this._scene.useRightHandedSystem) {
                 this.pointer.position.z *= -1;
                 this.pointer.rotationQuaternion!.z *= -1;
                 this.pointer.rotationQuaternion!.w *= -1;
@@ -100,7 +104,7 @@ export class WebXRController {
             if (pose) {
                 this.grip.position.copyFrom(<any>(pose.transform.position));
                 this.grip.rotationQuaternion!.copyFrom(<any>(pose.transform.orientation));
-                if (!this.scene.useRightHandedSystem) {
+                if (!this._scene.useRightHandedSystem) {
                     this.grip.position.z *= -1;
                     this.grip.rotationQuaternion!.z *= -1;
                     this.grip.rotationQuaternion!.w *= -1;
@@ -118,7 +122,6 @@ export class WebXRController {
      * @param result the resulting ray
      */
     public getWorldPointerRayToRef(result: Ray) {
-        // Force update to ensure picked point is synced with ray
         let worldMatrix = this.pointer.computeWorldMatrix();
         worldMatrix.decompose(undefined, this._tmpQuaternion, undefined);
         this._tmpVector.set(0, 0, 1);
@@ -129,14 +132,6 @@ export class WebXRController {
     }
 
     /**
-     * Get the scene associated with this controller
-     * @returns the scene object
-     */
-    public getScene() {
-        return this.scene;
-    }
-
-    /**
      * Disposes of the object
      */
     dispose() {

+ 1 - 1
src/Cameras/XR/webXRDefaultExperience.ts

@@ -95,7 +95,7 @@ export class WebXRDefaultExperience {
             }
 
             // Create the WebXR output target
-            result.renderTarget = result.baseExperience.sessionManager.getWebXRRenderTarget(xrHelper.onStateChangedObservable, options.outputCanvasOptions);
+            result.renderTarget = result.baseExperience.sessionManager.getWebXRRenderTarget(options.outputCanvasOptions);
 
             if (!options.disableDefaultUI) {
                 if (options.uiOptions) {

+ 1 - 1
src/Cameras/XR/webXRInput.ts

@@ -84,7 +84,7 @@ export class WebXRInput implements IDisposable {
         let sources = this.controllers.map((c) => { return c.inputSource; });
         for (let input of addInputs) {
             if (sources.indexOf(input) === -1) {
-                let controller = new WebXRController(this.xrSessionManager.scene, input, this.options.forceInputProfile);
+                let controller = new WebXRController(this.xrSessionManager.scene, input, { forceControllerProfile: this.options.forceInputProfile });
                 this.controllers.push(controller);
                 if (!this.options.doNotLoadControllerMeshes && controller.gamepadController) {
                     controller.gamepadController.loadModel();

+ 33 - 30
src/Cameras/XR/webXRManagedOutputCanvas.ts

@@ -1,7 +1,7 @@
 import { Nullable } from "../../types";
-import { Observable } from "../../Misc/observable";
 import { ThinEngine } from '../../Engines/thinEngine';
-import { WebXRState, WebXRRenderTarget } from "./webXRTypes";
+import { WebXRRenderTarget } from "./webXRTypes";
+import { WebXRSessionManager } from './webXRSessionManager';
 
 /**
  * COnfiguration object for WebXR output canvas
@@ -10,7 +10,7 @@ export class WebXRManagedOutputCanvasOptions {
     /**
      * Options for this XR Layer output
      */
-    public canvasOptions: XRWebGLLayerOptions;
+    public canvasOptions?: XRWebGLLayerOptions;
 
     /**
      * CSS styling for a newly created canvas (if not provided)
@@ -18,6 +18,12 @@ export class WebXRManagedOutputCanvasOptions {
     public newCanvasCssStyle?: string;
 
     /**
+     * An optional canvas in case you wish to create it yourself and provide it here.
+     * If not provided, a new canvas will be created
+     */
+    public canvasElement?: HTMLCanvasElement;
+
+    /**
      * Get the default values of the configuration object
      * @returns default values of this configuration object
      */
@@ -46,7 +52,7 @@ export class WebXRManagedOutputCanvas implements WebXRRenderTarget {
     private _canvas: Nullable<HTMLCanvasElement> = null;
 
     /**
-     * xrpresent context of the canvas which can be used to display/mirror xr content
+     * Rendering context of the canvas which can be used to display/mirror xr content
      */
     public canvasContext: WebGLRenderingContext;
     /**
@@ -59,16 +65,16 @@ export class WebXRManagedOutputCanvas implements WebXRRenderTarget {
      * @param xrSession xr session
      * @returns a promise that will resolve once the XR Layer has been created
      */
-    public initializeXRLayerAsync(xrSession: any) {
+    public initializeXRLayerAsync(xrSession: XRSession): Promise<XRWebGLLayer> {
 
         const createLayer = () => {
-            return this.xrLayer = new XRWebGLLayer(xrSession, this.canvasContext, this.configuration.canvasOptions);
+            return new XRWebGLLayer(xrSession, this.canvasContext, this._options.canvasOptions);
         };
 
         // support canvases without makeXRCompatible
         if (!(this.canvasContext as any).makeXRCompatible) {
             this.xrLayer = createLayer();
-            return Promise.resolve(true);
+            return Promise.resolve(this.xrLayer);
         }
 
         return (this.canvasContext as any).makeXRCompatible().then(() => {
@@ -79,29 +85,26 @@ export class WebXRManagedOutputCanvas implements WebXRRenderTarget {
 
     /**
      * Initializes the canvas to be added/removed upon entering/exiting xr
-     * @param engine the Babylon engine
-     * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
-     * @param onStateChangedObservable the mechanism by which the canvas will be added/removed based on XR state
-     * @param configuration optional configuration for this canvas output. defaults will be used if not provided
+     * @param _xrSessionManager The XR Session manager
+     * @param _options optional configuration for this canvas output. defaults will be used if not provided
      */
-    constructor(engine: ThinEngine, canvas?: HTMLCanvasElement, onStateChangedObservable?: Observable<WebXRState>, private configuration: WebXRManagedOutputCanvasOptions = WebXRManagedOutputCanvasOptions.GetDefaults()) {
-        this._engine = engine;
-        if (!canvas) {
-            canvas = document.createElement('canvas');
-            canvas.style.cssText = this.configuration.newCanvasCssStyle || "position:absolute; bottom:0px;right:0px;";
-        }
-        this._setManagedOutputCanvas(canvas);
-
-        if (onStateChangedObservable) {
-            onStateChangedObservable.add((stateInfo) => {
-                if (stateInfo == WebXRState.ENTERING_XR) {
-                    // The canvas is added to the screen before entering XR because currently the xr session must be initialized while the canvas is added render properly
-                    this._addCanvas();
-                } else if (stateInfo == WebXRState.NOT_IN_XR) {
-                    this._removeCanvas();
-                }
-            });
+    constructor(_xrSessionManager: WebXRSessionManager, private _options: WebXRManagedOutputCanvasOptions = WebXRManagedOutputCanvasOptions.GetDefaults()) {
+        this._engine = _xrSessionManager.scene.getEngine();
+        if (!_options.canvasElement) {
+            const canvas = document.createElement('canvas');
+            canvas.style.cssText = this._options.newCanvasCssStyle || "position:absolute; bottom:0px;right:0px;";
+            this._setManagedOutputCanvas(canvas);
+        } else {
+            this._setManagedOutputCanvas(_options.canvasElement);
         }
+
+        _xrSessionManager.onXRSessionInit.add(() => {
+            this._addCanvas();
+        });
+
+        _xrSessionManager.onXRSessionEnded.add(() => {
+            this._removeCanvas();
+        });
     }
     /**
      * Disposes of the object
@@ -118,9 +121,9 @@ export class WebXRManagedOutputCanvas implements WebXRRenderTarget {
             (this.canvasContext as any) = null;
         } else {
             this._canvas = canvas;
-            this.canvasContext = <any>this._canvas.getContext('webgl');
+            this.canvasContext = <any>this._canvas.getContext('webgl2');
             if (!this.canvasContext) {
-                this.canvasContext = <any>this._canvas.getContext('webgl2');
+                this.canvasContext = <any>this._canvas.getContext('webgl');
             }
         }
     }

+ 30 - 13
src/Cameras/XR/webXRSessionManager.ts

@@ -4,7 +4,7 @@ import { Nullable } from "../../types";
 import { IDisposable, Scene } from "../../scene";
 import { InternalTexture, InternalTextureSource } from "../../Materials/Textures/internalTexture";
 import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
-import { WebXRRenderTarget, WebXRState } from './webXRTypes';
+import { WebXRRenderTarget } from './webXRTypes';
 import { WebXRManagedOutputCanvas, WebXRManagedOutputCanvasOptions } from './webXRManagedOutputCanvas';
 
 interface IRenderTargetProvider {
@@ -186,6 +186,13 @@ export class WebXRSessionManager implements IDisposable {
     }
 
     /**
+     * Resets the reference space to the one started the session
+     */
+    public resetReferenceSpace() {
+        this.referenceSpace = this.baseReferenceSpace;
+    }
+
+    /**
      * Updates the render state of the session
      * @param state state to set
      * @returns a promise that resolves once the render state has been updated
@@ -202,27 +209,28 @@ export class WebXRSessionManager implements IDisposable {
      * @returns a promise that will resolve once rendering has started
      */
     public startRenderingToXRAsync() {
+        const engine = this.scene.getEngine();
         // 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 = {
+        engine.customAnimationFrameRequester = {
             requestAnimationFrame: this.session.requestAnimationFrame.bind(this.session),
             renderFunction: (timestamp: number, xrFrame: Nullable<XRFrame>) => {
                 if (this._sessionEnded) {
                     return;
                 }
-                // Store the XR frame in the manager to be consumed by the XR camera to update pose
+                // Store the XR frame and timestamp in the session manager
                 this.currentFrame = xrFrame;
                 this.currentTimestamp = timestamp;
                 if (xrFrame) {
                     this.onXRFrameObservable.notifyObservers(xrFrame);
                     // only run the render loop if a frame exists
-                    this.scene.getEngine()._renderLoop();
+                    engine._renderLoop();
                 }
             }
         };
 
         if (this._xrNavigator.xr.native) {
             this._rttProvider = this._xrNavigator.xr.getNativeRenderTargetProvider(this.session, (width: number, height: number) => {
-                return this.scene.getEngine().createRenderTargetTexture({ width: width, height: height }, false);
+                return engine.createRenderTargetTexture({ width: width, height: height }, false);
             });
         } else {
             // Create render target texture from xr's webgl render target
@@ -230,8 +238,8 @@ export class WebXRSessionManager implements IDisposable {
         }
 
         // Stop window's animation frame and trigger sessions animation frame
-        if (window.cancelAnimationFrame) { window.cancelAnimationFrame(this.scene.getEngine()._frameHandler); }
-        this.scene.getEngine()._renderLoop();
+        if (window.cancelAnimationFrame) { window.cancelAnimationFrame(engine._frameHandler); }
+        engine._renderLoop();
         return Promise.resolve();
     }
 
@@ -272,12 +280,15 @@ export class WebXRSessionManager implements IDisposable {
      * @param options optional options to provide when creating a new render target
      * @returns a WebXR render target to which the session can render
      */
-    public getWebXRRenderTarget(onStateChangedObservable?: Observable<WebXRState>, options?: WebXRManagedOutputCanvasOptions): WebXRRenderTarget {
+    public getWebXRRenderTarget(options?: WebXRManagedOutputCanvasOptions): WebXRRenderTarget {
+        const engine = this.scene.getEngine();
         if (this._xrNavigator.xr.native) {
-            return this._xrNavigator.xr.getWebXRRenderTarget(this.scene.getEngine());
+            return this._xrNavigator.xr.getWebXRRenderTarget(engine);
         }
         else {
-            return new WebXRManagedOutputCanvas(this.scene.getEngine(), this.scene.getEngine().getRenderingCanvas() as HTMLCanvasElement, onStateChangedObservable!, options);
+            options = options || {};
+            options.canvasElement = engine.getRenderingCanvas() || undefined;
+            return new WebXRManagedOutputCanvas(this, options);
         }
     }
 
@@ -288,7 +299,7 @@ export class WebXRSessionManager implements IDisposable {
      * @param scene scene the new render target should be created for
      * @param baseLayer the webgl layer to create the render target for
      */
-    public static _CreateRenderTargetTextureFromSession(session: XRSession, scene: Scene, baseLayer: XRWebGLLayer) {
+    public static _CreateRenderTargetTextureFromSession(_session: XRSession, scene: Scene, baseLayer: XRWebGLLayer) {
         if (!baseLayer) {
             throw "no layer";
         }
@@ -309,14 +320,20 @@ export class WebXRSessionManager implements IDisposable {
      * Disposes of the session manager
      */
     public dispose() {
+        // disposing without leaving XR? Exit XR first
+        if (!this._sessionEnded) {
+            this.exitXRAsync();
+        }
         this.onXRFrameObservable.clear();
         this.onXRSessionEnded.clear();
+        this.onXRReferenceSpaceChanged.clear();
+        this.onXRSessionInit.clear();
     }
 
     /**
-     * Gets a promise returning true when fullfiled if the given session mode is supported
+     * Returns a promise that resolves with a boolean indicating if the provided session mode is supported by this browser
      * @param sessionMode defines the session to test
-     * @returns a promise
+     * @returns a promise with boolean as final value
      */
     public static IsSessionSupportedAsync(sessionMode: XRSessionMode): Promise<boolean> {
         if (!(navigator as any).xr) {

+ 1 - 1
src/Cameras/XR/webXRTypes.ts

@@ -42,5 +42,5 @@ export interface WebXRRenderTarget extends IDisposable {
      * @param xrSession xr session
      * @returns a promise that will resolve once the XR Layer has been created
      */
-    initializeXRLayerAsync(xrSession: XRSession) : Promise<void>;
+    initializeXRLayerAsync(xrSession: XRSession) : Promise<XRWebGLLayer>;
 }