Trevor Baron 6 лет назад
Родитель
Сommit
3f15e94adc

+ 2 - 1
Tools/Gulp/config.json

@@ -1301,7 +1301,8 @@
                 "../../src/Cameras/XR/babylon.webXRCamera.js",
                 "../../src/Cameras/XR/babylon.webXRSessionManager.js",
                 "../../src/Cameras/XR/babylon.webXRExperienceHelper.js",
-                "../../src/Cameras/XR/babylon.webXREnterExitUI.js"
+                "../../src/Cameras/XR/babylon.webXREnterExitUI.js",
+                "../../src/Cameras/XR/babylon.webXRManagedOutputCanvas.js"
             ],
             "dependUpon": [
                 "core",

+ 1 - 0
dist/preview release/what's new.md

@@ -11,6 +11,7 @@
   - webXR camera which can be updated by a webXRSession ([TrevorDev](https://github.com/TrevorDev))
   - webXRSessionManager to bridge xrSession to babylon's engine/camera ([TrevorDev](https://github.com/TrevorDev))
   - webXRExperienceHelper to setup a default XR experience ([TrevorDev](https://github.com/TrevorDev))
+  - WebXREnterExitUI and WebXRManagedOutputCanvas classes to configure the XR experience ([TrevorDev](https://github.com/TrevorDev))
 
 ## Updates
 

+ 56 - 35
src/Cameras/XR/babylon.webXREnterExitUI.ts

@@ -1,55 +1,76 @@
 module BABYLON {
+    /**
+     * Options to create the webXR UI
+     */
+    export class WebXREnterExitUIOptions {
+        /**
+         * Context to enter xr with
+         */
+        outputCanvasContext?: Nullable<WebGLRenderingContext>;
+    }
+    /**
+     * UI to allow the user to enter/exit XR mode
+     */
     export class WebXREnterExitUI implements IDisposable {
-        public overlay:HTMLDivElement;
-        public buttons:Array<{element:HTMLElement, initializationOptions:XRSessionCreationOptions}> = []
-        public static CreateAsync(scene:BABYLON.Scene, helper:WebXRExperienceHelper){
-            var ui = new WebXREnterExitUI(scene, helper);
-            var supportedPromises = ui.buttons.map((btn)=>{
+        private _overlay: HTMLDivElement;
+        private _buttons: Array<{element: HTMLElement, initializationOptions: XRSessionCreationOptions}> = [];
+        /**
+         * Creates UI to allow the user to enter/exit XR mode
+         * @param scene the scene to add the ui to
+         * @param helper the xr experience helper to enter/exit xr with
+         * @param options options to configure the UI
+         * @returns the created ui
+         */
+        public static CreateAsync(scene: BABYLON.Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions) {
+            var ui = new WebXREnterExitUI(scene, options);
+            var supportedPromises = ui._buttons.map((btn) => {
                 return helper.supportsSession(btn.initializationOptions);
             });
-            return Promise.all(supportedPromises).then((results)=>{
-                results.forEach((supported, i)=>{
-                    if(supported){
-                        ui.overlay.appendChild(ui.buttons[i].element)
-                        ui.buttons[i].element.onclick = async()=>{
-                            if(helper.state == BABYLON.WebXRState.IN_XR){
-                                await helper.exitXR()
-                                return
-                            }else if(helper.state == BABYLON.WebXRState.NOT_IN_XR){
-                                await helper.enterXR(ui.buttons[i].initializationOptions, "eye-level");
+            return Promise.all(supportedPromises).then((results) => {
+                results.forEach((supported, i) => {
+                    if (supported) {
+                        ui._overlay.appendChild(ui._buttons[i].element);
+                        ui._buttons[i].element.onclick = async() => {
+                            if (helper.state == BABYLON.WebXRState.IN_XR) {
+                                await helper.exitXR();
+                                return;
+                            }else if (helper.state == BABYLON.WebXRState.NOT_IN_XR) {
+                                await helper.enterXR(ui._buttons[i].initializationOptions, "eye-level");
                             }
-                        }
+                        };
                     }
-                })
-            })
+                });
+            });
         }
-        private constructor(private scene:BABYLON.Scene, private helper:WebXRExperienceHelper){
-            this.overlay = document.createElement("div");
-            this.overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;"
+        private constructor(private scene: BABYLON.Scene, options: WebXREnterExitUIOptions) {
+            this._overlay = document.createElement("div");
+            this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
 
             var hmdBtn = document.createElement("button");
             hmdBtn.style.cssText = "color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-repeat:no-repeat; background-position: center; outline: none;";
-            hmdBtn.innerText = "HMD"
-            this.buttons.push({element:hmdBtn, initializationOptions: {immersive: true}})
-            
+            hmdBtn.innerText = "HMD";
+            this._buttons.push({element: hmdBtn, initializationOptions: {immersive: true}});
+
             var windowBtn = document.createElement("button");
             windowBtn.style.cssText = hmdBtn.style.cssText;
-            windowBtn.innerText = "Window"
-            this.buttons.push({element:windowBtn, initializationOptions: {immersive: false, environmentIntegration: true, outputContext: helper.managedOutputCanvasContext}})
-        
+            windowBtn.innerText = "Window";
+            this._buttons.push({element: windowBtn, initializationOptions: {immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext}});
+
             var renderCanvas = scene.getEngine().getRenderingCanvas();
-            if(renderCanvas && renderCanvas.parentNode){
-                renderCanvas.parentNode.appendChild(this.overlay)
-                scene.onDisposeObservable.addOnce(()=>{
+            if (renderCanvas && renderCanvas.parentNode) {
+                renderCanvas.parentNode.appendChild(this._overlay);
+                scene.onDisposeObservable.addOnce(() => {
                     this.dispose();
-                })
+                });
             }
         }
-
-        dispose(){
+        /**
+         * Disposes of the object
+         */
+        dispose() {
             var renderCanvas = this.scene.getEngine().getRenderingCanvas();
-            if(renderCanvas && renderCanvas.parentNode){
-                renderCanvas.parentNode.removeChild(this.overlay)
+            if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
+                renderCanvas.parentNode.removeChild(this._overlay);
             }
         }
     }

+ 16 - 39
src/Cameras/XR/babylon.webXRExperienceHelper.ts

@@ -45,19 +45,19 @@ module BABYLON {
         private _nonVRCamera: Nullable<Camera> = null;
         private _originalSceneAutoClear = true;
 
-        
-        private _managedOutputCanvas: Nullable<HTMLCanvasElement> = null;
-        public managedOutputCanvasContext: Nullable<WebGLRenderingContext> = null;
-
         private _supported = false;
 
-        public static CreateAsync(scene: BABYLON.Scene):Promise<WebXRExperienceHelper>{
-            var helper = new WebXRExperienceHelper(scene)
-
-            return helper._sessionManager.initialize().then(()=>{
+        /**
+         * Creates the experience helper
+         * @param scene the scene to attach the exprience helper to
+         * @returns a promise for the experience helper
+         */
+        public static CreateAsync(scene: BABYLON.Scene): Promise<WebXRExperienceHelper> {
+            var helper = new WebXRExperienceHelper(scene);
+            return helper._sessionManager.initialize().then(() => {
                 helper._supported = true;
                 return helper;
-            }).catch(()=>{
+            }).catch(() => {
                 return helper;
             });
         }
@@ -92,8 +92,6 @@ module BABYLON {
             this.state = WebXRState.TRANSITION;
             this.onStateChangedObservable.notifyObservers(this.state);
 
-            this._addCanvas();
-
             return this._sessionManager.enterXR(sessionCreationOptions, frameOfReference).then(() => {
                 // Cache pre xr scene settings
                 this._originalSceneAutoClear = this.scene.autoClear;
@@ -117,7 +115,6 @@ module BABYLON {
                     this.scene.autoClear = this._originalSceneAutoClear;
                     this.scene.activeCamera = this._nonVRCamera;
                     this._sessionManager.onXRFrameObservable.clear();
-                    this._removeCanvas();
 
                     this.state = WebXRState.NOT_IN_XR;
                     this.onStateChangedObservable.notifyObservers(this.state);
@@ -127,8 +124,13 @@ module BABYLON {
             });
         }
 
-        public supportsSession(options:XRSessionCreationOptions){
-            if(!this._supported){
+        /**
+         * Checks if the creation options are supported by the xr session
+         * @param options creation options
+         * @returns true if supported
+         */
+        public supportsSession(options: XRSessionCreationOptions) {
+            if (!this._supported) {
                 return Promise.resolve(false);
             }
             return this._sessionManager.supportsSession(options);
@@ -140,33 +142,8 @@ module BABYLON {
         public dispose() {
             this.camera.dispose();
             this.container.dispose();
-            this._removeCanvas();
-            this.setManagedOutputCanvas(null);
             this.onStateChangedObservable.clear();
             this._sessionManager.dispose();
         }
-
-        // create canvas used to mirror/vr xr content in fullscreen
-        public setManagedOutputCanvas(canvas:Nullable<HTMLCanvasElement>){
-            this._removeCanvas();
-            if(!canvas){
-                this._managedOutputCanvas = null;
-                this.managedOutputCanvasContext = null;
-            }else{
-                this._managedOutputCanvas = canvas;
-                this.managedOutputCanvasContext = <any>this._managedOutputCanvas.getContext('xrpresent');
-            }
-        }
-        
-        private _addCanvas() {
-            if(this._managedOutputCanvas){
-                document.body.appendChild(this._managedOutputCanvas);
-            }
-        }
-        private _removeCanvas() {
-            if(this._managedOutputCanvas && document.body.contains(this._managedOutputCanvas)){
-                document.body.removeChild(this._managedOutputCanvas);
-            }
-        }
     }
 }

+ 54 - 3
src/Cameras/XR/babylon.webXRManagedOutputCanvas.ts

@@ -1,10 +1,61 @@
 module BABYLON {
+    /**
+     * Creates a canvas that is added/removed from the webpage when entering/exiting XR
+     */
     export class WebXRManagedOutputCanvas implements IDisposable {
-        public static CreateAsync(scene:BABYLON.Scene, helper:WebXRExperienceHelper){
+        private _canvas: Nullable<HTMLCanvasElement> = null;
+        /**
+         * xrpresent context of the canvas which can be used to display/mirror xr content
+         */
+        public canvasContext: Nullable<WebGLRenderingContext> = null;
+        /**
+         * Initializes the canvas to be added/removed upon entering/exiting xr
+         * @param helper the xr expreince helper used to trigger adding/removing of the canvas
+         * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
+         */
+        public constructor(helper: WebXRExperienceHelper, canvas?: HTMLCanvasElement) {
+            if (!canvas) {
+                canvas = document.createElement('canvas');
+                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #48989e;";
+            }
+            this._setManagedOutputCanvas(canvas);
+            helper.onStateChangedObservable.add(() => {
+                if (helper.state == WebXRState.IN_XR) {
+                    this._addCanvas();
+                }else if (helper.state == WebXRState.NOT_IN_XR) {
+                    this._removeCanvas();
+                }
+            });
         }
-        private constructor(private scene:BABYLON.Scene, private helper:WebXRExperienceHelper){
+        /**
+         * Disposes of the object
+         */
+        public dispose() {
+            this._removeCanvas();
+            this._setManagedOutputCanvas(null);
         }
-        dispose(){
+
+        private _setManagedOutputCanvas(canvas: Nullable<HTMLCanvasElement>) {
+            this._removeCanvas();
+            if (!canvas) {
+                this._canvas = null;
+                this.canvasContext = null;
+            }else {
+                this._canvas = canvas;
+                this.canvasContext = <any>this._canvas.getContext('xrpresent');
+            }
+        }
+
+        private _addCanvas() {
+            if (this._canvas) {
+                document.body.appendChild(this._canvas);
+            }
+        }
+
+        private _removeCanvas() {
+            if (this._canvas && document.body.contains(this._canvas)) {
+                document.body.removeChild(this._canvas);
+            }
         }
     }
 }

+ 4 - 4
src/Cameras/XR/babylon.webXRSessionManager.ts

@@ -146,12 +146,12 @@ module BABYLON {
          * @param options creation options to check if they are supported
          * @returns true if supported
          */
-        public supportsSession(options:XRSessionCreationOptions){
-            return this._xrDevice.supportsSession(options).then(()=>{
+        public supportsSession(options: XRSessionCreationOptions) {
+            return this._xrDevice.supportsSession(options).then(() => {
                 return true;
-            }).catch((e)=>{
+            }).catch((e) => {
                 return false;
-            })
+            });
         }
 
         /**

+ 14 - 2
src/Helpers/babylon.sceneHelpers.ts

@@ -52,6 +52,13 @@ module BABYLON {
          * @returns a new VREXperienceHelper
          */
         createDefaultVRExperience(webVROptions?: VRExperienceHelperOptions): VRExperienceHelper;
+
+        /**
+         * Creates a new XREXperienceHelper
+         * @see http://doc.babylonjs.com/how_to/webxr
+         * @returns a promise for a new XREXperienceHelper
+         */
+        createDefaultXRExperienceAsync(): Promise<WebXRExperienceHelper>;
     }
 
     Scene.prototype.createDefaultLight = function(replace = false): void {
@@ -170,7 +177,12 @@ module BABYLON {
         return null;
     };
 
-    Scene.prototype.createDefaultVRExperience = function(webVROptions: VRExperienceHelperOptions = {}): VRExperienceHelper {
-        return new VRExperienceHelper(this, webVROptions);
+    Scene.prototype.createDefaultXRExperienceAsync = function(): Promise<BABYLON.WebXRExperienceHelper> {
+        return BABYLON.WebXRExperienceHelper.CreateAsync(this).then((helper) => {
+            var outputCanvas = new BABYLON.WebXRManagedOutputCanvas(helper);
+            return BABYLON.WebXREnterExitUI.CreateAsync(this, helper, {outputCanvasContext: outputCanvas.canvasContext}).then((ui) => {
+                return helper;
+            });
+        });
     };
 }

+ 2 - 2
src/babylon.mixins.ts

@@ -187,7 +187,7 @@ interface WebGLUniformLocation {
 // WebXR
 interface XRDevice {
     requestSession(options: XRSessionCreationOptions): Promise<XRSession>;
-    supportsSession(options: XRSessionCreationOptions):Promise<void>;
+    supportsSession(options: XRSessionCreationOptions): Promise<void>;
 }
 interface XRSession {
     baseLayer: XRWebGLLayer;
@@ -200,7 +200,7 @@ interface XRSession {
 interface XRSessionCreationOptions {
     outputContext?: WebGLRenderingContext | null;
     immersive?: boolean;
-    environmentIntegration?:boolean;
+    environmentIntegration?: boolean;
 }
 interface XRLayer {
     getViewport: Function;