소스 검색

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 5 년 전
부모
커밋
790a38bc49

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

@@ -191,7 +191,7 @@
 - Camera's API is Babylon-conform (position, rotationQuaternion, world matrix, direction etc') ([#7239](https://github.com/BabylonJS/Babylon.js/issues/7239)) ([RaananW](https://github.com/RaananW/))
 - XR Input now using standard profiles and completely separated from the gamepad class ([#7348](https://github.com/BabylonJS/Babylon.js/issues/7348)) ([RaananW](https://github.com/RaananW/))
 - Teleportation and controller selection are now WebXR features. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
-- Teleportation allows selecting direction before teleporting when using thumbstick / touchpad. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
+- Teleportation allows selecting direction before teleporting when using thumbstick or touchpad. ([#7290](https://github.com/BabylonJS/Babylon.js/issues/7290)) ([RaananW](https://github.com/RaananW/))
 - It is now possible to force a certain profile type for the controllers ([#7348](https://github.com/BabylonJS/Babylon.js/issues/7375)) ([RaananW](https://github.com/RaananW/))
 - WebXR camera is initialized on the first frame ([#7389](https://github.com/BabylonJS/Babylon.js/issues/7389)) ([RaananW](https://github.com/RaananW/))
 - Selection has gaze mode (which can be forced) and touch-screen support ([#7395](https://github.com/BabylonJS/Babylon.js/issues/7395)) ([RaananW](https://github.com/RaananW/))

+ 22 - 36
src/Cameras/VR/vrExperienceHelper.ts

@@ -714,6 +714,14 @@ export class VRExperienceHelper {
         this._scene = scene;
         this._inputElement = scene.getEngine().getInputElement();
 
+        // check for VR support:
+
+        const vrSupported = 'getVRDisplays' in navigator;
+        // no VR support? force XR
+        if (!vrSupported) {
+            webVROptions.useXR = true;
+        }
+
         // Parse options
         if (webVROptions.createFallbackVRDeviceOrientationFreeCamera === undefined) {
             webVROptions.createFallbackVRDeviceOrientationFreeCamera = true;
@@ -800,38 +808,16 @@ export class VRExperienceHelper {
                             switch (state) {
                                 case WebXRState.ENTERING_XR:
                                     this.onEnteringVRObservable.notifyObservers(this);
-                                    if (this._interactionsEnabled) {
-                                        this._scene.registerBeforeRender(this.beforeRender);
-                                    }
-                                    if (this._displayLaserPointer) {
-                                        [this._leftController, this._rightController].forEach((controller) => {
-                                            if (controller) {
-                                                controller._activatePointer();
-                                            }
-                                        });
+                                    if (!this._interactionsEnabled) {
+                                        this.xr.pointerSelection.detach();
                                     }
+                                    this.xr.pointerSelection.displayLaserPointer = this._displayLaserPointer;
                                     break;
                                 case WebXRState.EXITING_XR:
                                     this.onExitingVRObservable.notifyObservers(this);
-                                    if (this._interactionsEnabled) {
-                                        this._scene.unregisterBeforeRender(this.beforeRender);
-                                        this._cameraGazer._gazeTracker.isVisible = false;
-                                        if (this._leftController) {
-                                            this._leftController._gazeTracker.isVisible = false;
-                                        }
-                                        if (this._rightController) {
-                                            this._rightController._gazeTracker.isVisible = false;
-                                        }
-                                    }
 
                                     // resize to update width and height when exiting vr exits fullscreen
                                     this._scene.getEngine().resize();
-
-                                    [this._leftController, this._rightController].forEach((controller) => {
-                                        if (controller) {
-                                            controller._deactivatePointer();
-                                        }
-                                    });
                                     break;
                                 case WebXRState.IN_XR:
                                     this._hasEnteredVR = true;
@@ -1332,6 +1318,14 @@ export class VRExperienceHelper {
         if (!this._interactionsEnabled) {
             this._interactionsRequested = true;
 
+            // in XR it is enabled by default, but just to make sure, re-attach
+            if (this.xr) {
+                if (this.xr.baseExperience.state === WebXRState.IN_XR) {
+                    this.xr.pointerSelection.attach();
+                }
+                return;
+            }
+
             if (this._leftController) {
                 this._enableInteractionOnController(this._leftController);
             }
@@ -1444,6 +1438,9 @@ export class VRExperienceHelper {
                     }
                 }
                 if (this.xr) {
+                    floorMeshes.forEach((mesh) => {
+                        this.xr.teleportation.addFloorMesh(mesh);
+                    });
                     if (!this.xr.teleportation.attached) {
                         this.xr.teleportation.attach();
                     }
@@ -1466,17 +1463,6 @@ export class VRExperienceHelper {
                 }
             }
 
-            if (this.xr && vrTeleportationOptions.floorMeshes) {
-                if (!this.xr.teleportation.attached) {
-                    this.xr.teleportation.attach();
-                }
-                return;
-            } else {
-                if (this.webVROptions.useXR && !this.xrTestDone) {
-
-                }
-            }
-
             if (vrTeleportationOptions.floorMeshName) {
                 this._floorMeshName = vrTeleportationOptions.floorMeshName;
             }

+ 8 - 2
src/Cameras/XR/features/WebXRAbstractFeature.ts

@@ -34,9 +34,12 @@ export abstract class WebXRAbstractFeature implements IWebXRFeature {
     /**
      * attach this feature
      *
-     * @returns true if successful.
+     * @returns true if successful, false is failed or already attached
      */
     public attach(): boolean {
+        if (this.attached) {
+            return false;
+        }
         this._attached = true;
         this._addNewAttachObserver(this._xrSessionManager.onXRFrameObservable, (frame) => this._onXRFrame(frame));
         return true;
@@ -45,9 +48,12 @@ export abstract class WebXRAbstractFeature implements IWebXRFeature {
     /**
      * detach this feature.
      *
-     * @returns true if successful.
+     * @returns true if successful, false if failed or already detached
      */
     public detach(): boolean {
+        if (!this._attached) {
+            return false;
+        }
         this._attached = false;
         this._removeOnDetach.forEach((toRemove) => {
             toRemove.observable.remove(toRemove.observer);

+ 6 - 2
src/Cameras/XR/features/WebXRAnchorSystem.ts

@@ -122,7 +122,9 @@ export class WebXRAnchorSystem extends WebXRAbstractFeature implements IWebXRFea
      * @returns true if successful.
      */
     attach(): boolean {
-        super.attach();
+        if (!super.attach()) {
+            return false;
+        }
         if (this._options.addAnchorOnSelect) {
             this._xrSessionManager.session.addEventListener('select', this._onSelect, false);
         }
@@ -136,7 +138,9 @@ export class WebXRAnchorSystem extends WebXRAbstractFeature implements IWebXRFea
      * @returns true if successful.
      */
     detach(): boolean {
-        super.detach();
+        if (!super.detach()) {
+            return false;
+        }
 
         this._xrSessionManager.session.removeEventListener('select', this._onSelect);
 

+ 21 - 6
src/Cameras/XR/features/WebXRControllerPointerSelection.ts

@@ -92,6 +92,15 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
      */
     public lasterPointerDefaultColor: Color3 = new Color3(0.5, 0.5, 0.5);
 
+    /**
+     * Should the laser pointer be displayed
+     */
+    public displayLaserPointer: boolean = true;
+    /**
+     * Should the selection mesh be displayed (The ring at the end of the laser pointer)
+     */
+    public displaySelectionMesh: boolean = true;
+
     private static _idCounter = 0;
 
     private _tmpRay = new Ray(new Vector3(), new Vector3());
@@ -128,7 +137,9 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
      * @returns true if successful.
      */
     attach(): boolean {
-        super.attach();
+        if (!super.attach()) {
+            return false;
+        }
 
         this._options.xrInput.controllers.forEach(this._attachController);
         this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable, this._attachController);
@@ -147,7 +158,9 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
      * @returns true if successful.
      */
     detach(): boolean {
-        super.detach();
+        if (!super.detach()) {
+            return false;
+        }
 
         Object.keys(this._controllers).forEach((controllerId) => {
             this._detachController(controllerId);
@@ -203,7 +216,7 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
                     Vector3.RotationFromAxisToRef(axis2, pickNormal, axis1, controllerData.selectionMesh.rotation);
                     controllerData.selectionMesh.position.addInPlace(pickNormal.scale(deltaFighting));
                 }
-                controllerData.selectionMesh.isVisible = true;
+                controllerData.selectionMesh.isVisible = true && this.displaySelectionMesh;
             } else {
                 controllerData.selectionMesh.isVisible = false;
             }
@@ -336,7 +349,7 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
     }
 
     private _attachTrackedPointerRayMode(xrController: WebXRController) {
-        if (!xrController.gamepadController) {
+        if (!xrController.motionController) {
             return;
         }
 
@@ -347,10 +360,10 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
         const controllerData = this._controllers[xrController.uniqueId];
 
         if (this._options.overrideButtonId) {
-            controllerData.selectionComponent = xrController.gamepadController.getComponent(this._options.overrideButtonId);
+            controllerData.selectionComponent = xrController.motionController.getComponent(this._options.overrideButtonId);
         }
         if (!controllerData.selectionComponent) {
-            controllerData.selectionComponent = xrController.gamepadController.getMainComponent();
+            controllerData.selectionComponent = xrController.motionController.getMainComponent();
         }
 
         controllerData.onFrameObserver = this._xrSessionManager.onXRFrameObservable.add(() => {
@@ -362,6 +375,8 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature implem
                 (<StandardMaterial>controllerData.laserPointer.material).emissiveColor = this.lasterPointerDefaultColor;
             }
 
+            controllerData.laserPointer.isVisible = this.displayLaserPointer;
+
             if (controllerData.pick) {
                 this._scene.simulatePointerMove(controllerData.pick, { pointerId: controllerData.id });
             }

+ 9 - 5
src/Cameras/XR/features/WebXRControllerTeleportation.ts

@@ -205,7 +205,9 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature imp
     }
 
     public attach(): boolean {
-        super.attach();
+        if (!super.attach()) {
+            return false;
+        }
 
         this._options.xrInput.controllers.forEach(this._attachController);
         this._addNewAttachObserver(this._options.xrInput.onControllerAddedObservable, this._attachController);
@@ -218,7 +220,9 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature imp
     }
 
     public detach(): boolean {
-        super.detach();
+        if (!super.detach()) {
+            return false;
+        }
 
         Object.keys(this._controllers).forEach((controllerId) => {
             this._detachController(controllerId);
@@ -314,11 +318,11 @@ export class WebXRMotionControllerTeleportation extends WebXRAbstractFeature imp
         };
         const controllerData = this._controllers[xrController.uniqueId];
         // motion controller support
-        if (xrController.gamepadController) {
-            const movementController = xrController.gamepadController.getComponent(WebXRControllerComponent.THUMBSTICK) || xrController.gamepadController.getComponent(WebXRControllerComponent.TOUCHPAD);
+        if (xrController.motionController) {
+            const movementController = xrController.motionController.getComponent(WebXRControllerComponent.THUMBSTICK) || xrController.motionController.getComponent(WebXRControllerComponent.TOUCHPAD);
             if (!movementController || this._options.useMainComponentOnly) {
                 // use trigger to move on long press
-                const mainComponent = xrController.gamepadController.getMainComponent();
+                const mainComponent = xrController.motionController.getMainComponent();
                 if (!mainComponent) {
                     return;
                 }

+ 6 - 2
src/Cameras/XR/features/WebXRHitTestLegacy.ts

@@ -121,7 +121,9 @@ export class WebXRHitTestLegacy extends WebXRAbstractFeature implements IWebXRFe
      * @returns true if successful.
      */
     attach(): boolean {
-        super.attach();
+        if (!super.attach()) {
+            return false;
+        }
         if (this.options.testOnPointerDownOnly) {
             this._xrSessionManager.session.addEventListener('select', this._onSelect, false);
         }
@@ -136,7 +138,9 @@ export class WebXRHitTestLegacy extends WebXRAbstractFeature implements IWebXRFe
      * @returns true if successful.
      */
     detach(): boolean {
-        super.detach();
+        if (!super.detach()) {
+            return false;
+        }
         // disable select
         this._onSelectEnabled = false;
         this._xrSessionManager.session.removeEventListener('select', this._onSelect);

+ 8 - 8
src/Cameras/XR/webXRController.ts

@@ -36,7 +36,7 @@ export class WebXRController {
      * Using this object it is possible to get click events and trackpad changes of the
      * webxr controller that is currently being used.
      */
-    public gamepadController?: WebXRAbstractMotionController;
+    public motionController?: WebXRAbstractMotionController;
 
     /**
      * Event that fires when the controller is removed/disposed
@@ -71,10 +71,10 @@ export class WebXRController {
 
         // for now only load motion controllers if gamepad available
         if (this.inputSource.gamepad) {
-            this.gamepadController = WebXRMotionControllerManager.GetMotionControllerWithXRInput(inputSource, _scene, this._options.forceControllerProfile);
+            this.motionController = 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;
+            this.motionController.onModelLoadedObservable.addOnce(() => {
+                this.motionController!.rootMesh!.parent = this.pointer;
             });
         }
     }
@@ -118,9 +118,9 @@ export class WebXRController {
                 }
             }
         }
-        if (this.gamepadController) {
+        if (this.motionController) {
             // either update buttons only or also position, if in gamepad mode
-            this.gamepadController.updateFromXRFrame(xrFrame);
+            this.motionController.updateFromXRFrame(xrFrame);
         }
     }
 
@@ -145,8 +145,8 @@ export class WebXRController {
         if (this.grip) {
             this.grip.dispose();
         }
-        if (this.gamepadController) {
-            this.gamepadController.dispose();
+        if (this.motionController) {
+            this.motionController.dispose();
         }
         this.pointer.dispose();
         this.onDisposeObservable.notifyObservers({});

+ 5 - 0
src/Cameras/XR/webXRDefaultExperience.ts

@@ -7,6 +7,7 @@ import { WebXREnterExitUI, WebXREnterExitUIOptions } from './webXREnterExitUI';
 import { AbstractMesh } from '../../Meshes/abstractMesh';
 import { WebXRManagedOutputCanvasOptions } from './webXRManagedOutputCanvas';
 import { WebXRMotionControllerTeleportation } from './features/WebXRControllerTeleportation';
+import { Logger } from '../../Misc/logger';
 
 /**
  * Options for the default xr helper
@@ -116,6 +117,10 @@ export class WebXRDefaultExperience {
             }
         }).then(() => {
             return result;
+        }).catch((error) => {
+            Logger.Error("Error initializing XR");
+            Logger.Error(error);
+            return result;
         });
     }
 

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

@@ -80,7 +80,7 @@ export class WebXREnterExitUI implements IDisposable {
     public static CreateAsync(scene: Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions): Promise<WebXREnterExitUI> {
         var ui = new WebXREnterExitUI(scene, options);
         var supportedPromises = ui._buttons.map((btn) => {
-            return helper.sessionManager.supportsSessionAsync(btn.sessionMode);
+            return helper.sessionManager.isSessionSupportedAsync(btn.sessionMode);
         });
         helper.onStateChangedObservable.add((state) => {
             if (state == WebXRState.NOT_IN_XR) {

+ 15 - 9
src/Cameras/XR/webXRExperienceHelper.ts

@@ -62,8 +62,9 @@ export class WebXRExperienceHelper implements IDisposable {
         return helper.sessionManager.initializeAsync().then(() => {
             helper._supported = true;
             return helper;
-        }).catch(() => {
-            return helper;
+        }).catch((e) => {
+            helper.dispose();
+            throw e;
         });
     }
 
@@ -97,23 +98,26 @@ export class WebXRExperienceHelper implements IDisposable {
      * @param renderTarget the output canvas that will be used to enter XR mode
      * @returns promise that resolves after xr mode has entered
      */
-    public enterXRAsync(sessionMode: XRSessionMode, referenceSpaceType: XRReferenceSpaceType, renderTarget: WebXRRenderTarget): Promise<WebXRSessionManager> {
+    public enterXRAsync(sessionMode: XRSessionMode, referenceSpaceType: XRReferenceSpaceType, renderTarget: WebXRRenderTarget = this.sessionManager.getWebXRRenderTarget()): Promise<WebXRSessionManager> {
         if (!this._supported) {
-            throw "XR session not supported by this browser";
+            throw "XR not available";
         }
         this._setState(WebXRState.ENTERING_XR);
-        let sessionCreationOptions = {
+        let sessionCreationOptions: XRSessionInit = {
             optionalFeatures: (referenceSpaceType !== "viewer" && referenceSpaceType !== "local") ? [referenceSpaceType] : []
         };
-        return this.sessionManager.initializeSessionAsync(sessionMode, sessionCreationOptions).then(() => {
+        // make sure that the session mode is supported
+        return this.sessionManager.isSessionSupportedAsync(sessionMode).then(() => {
+            return this.sessionManager.initializeSessionAsync(sessionMode, sessionCreationOptions);
+        }).then(() => {
             return this.sessionManager.setReferenceSpaceAsync(referenceSpaceType);
         }).then(() => {
             return renderTarget.initializeXRLayerAsync(this.sessionManager.session);
         }).then(() => {
             return this.sessionManager.updateRenderStateAsync({ depthFar: this.camera.maxZ, depthNear: this.camera.minZ, baseLayer: renderTarget.xrLayer! });
         }).then(() => {
-            return this.sessionManager.startRenderingToXRAsync();
-        }).then(() => {
+            // run the render loop
+            this.sessionManager.runXRRenderLoop();
             // Cache pre xr scene settings
             this._originalSceneAutoClear = this.scene.autoClear;
             this._nonVRCamera = this.scene.activeCamera;
@@ -162,7 +166,9 @@ export class WebXRExperienceHelper implements IDisposable {
         this.onStateChangedObservable.clear();
         this.onInitialXRPoseSetObservable.clear();
         this.sessionManager.dispose();
-        this.scene.activeCamera = this._nonVRCamera;
+        if (this._nonVRCamera) {
+            this.scene.activeCamera = this._nonVRCamera;
+        }
     }
 
     private _nonXRToXRCamera() {

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

@@ -86,8 +86,8 @@ export class WebXRInput implements IDisposable {
             if (sources.indexOf(input) === -1) {
                 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();
+                if (!this.options.doNotLoadControllerMeshes && controller.motionController) {
+                    controller.motionController.loadModel();
                 }
                 this.onControllerAddedObservable.notifyObservers(controller);
             }

+ 8 - 9
src/Cameras/XR/webXRSessionManager.ts

@@ -120,7 +120,7 @@ export class WebXRSessionManager implements IDisposable {
         // Check if the browser supports webXR
         this._xrNavigator = navigator;
         if (!this._xrNavigator.xr) {
-            return Promise.reject("webXR not supported by this browser");
+            return Promise.reject("WebXR not available");
         }
         return Promise.resolve();
     }
@@ -128,11 +128,11 @@ export class WebXRSessionManager implements IDisposable {
     /**
      * Initializes an xr session
      * @param xrSessionMode mode to initialize
-     * @param optionalFeatures defines optional values to pass to the session builder
+     * @param xrSessionInit defines optional and required values to pass to the session builder
      * @returns a promise which will resolve once the session has been initialized
      */
-    public initializeSessionAsync(xrSessionMode: XRSessionMode, optionalFeatures: any = {}): Promise<XRSession> {
-        return this._xrNavigator.xr.requestSession(xrSessionMode, optionalFeatures).then((session: XRSession) => {
+    public initializeSessionAsync(xrSessionMode: XRSessionMode = 'immersive-vr', xrSessionInit: XRSessionInit = {}): Promise<XRSession> {
+        return this._xrNavigator.xr.requestSession(xrSessionMode, xrSessionInit).then((session: XRSession) => {
             this.session = session;
             this.onXRSessionInit.notifyObservers(session);
             this._sessionEnded = false;
@@ -160,7 +160,7 @@ export class WebXRSessionManager implements IDisposable {
      * @param referenceSpace space to set
      * @returns a promise that will resolve once the reference space has been set
      */
-    public setReferenceSpaceAsync(referenceSpace: XRReferenceSpaceType) {
+    public setReferenceSpaceAsync(referenceSpace: XRReferenceSpaceType = "local-floor"): Promise<XRReferenceSpace> {
         return this.session.requestReferenceSpace(referenceSpace).then((referenceSpace: XRReferenceSpace) => {
             return referenceSpace;
         }, (rejectionReason) => {
@@ -182,6 +182,7 @@ export class WebXRSessionManager implements IDisposable {
             this.session.requestReferenceSpace("viewer").then((referenceSpace: XRReferenceSpace) => {
                 this.viewerReferenceSpace = referenceSpace;
             });
+            return this.referenceSpace;
         });
     }
 
@@ -206,9 +207,8 @@ export class WebXRSessionManager implements IDisposable {
 
     /**
      * Starts rendering to the xr layer
-     * @returns a promise that will resolve once rendering has started
      */
-    public startRenderingToXRAsync() {
+    public runXRRenderLoop() {
         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
         engine.customAnimationFrameRequester = {
@@ -240,7 +240,6 @@ export class WebXRSessionManager implements IDisposable {
         // Stop window's animation frame and trigger sessions animation frame
         if (window.cancelAnimationFrame) { window.cancelAnimationFrame(engine._frameHandler); }
         engine._renderLoop();
-        return Promise.resolve();
     }
 
     /**
@@ -270,7 +269,7 @@ export class WebXRSessionManager implements IDisposable {
      * @param sessionMode session mode to check if supported eg. immersive-vr
      * @returns true if supported
      */
-    public supportsSessionAsync(sessionMode: XRSessionMode) {
+    public isSessionSupportedAsync(sessionMode: XRSessionMode) {
         return WebXRSessionManager.IsSessionSupportedAsync(sessionMode);
     }
 

+ 5 - 0
src/LibDeclarations/webxr.d.ts

@@ -55,6 +55,11 @@ interface XRInputSource {
     profiles: Array<string>;
 }
 
+interface XRSessionInit {
+    optionalFeatures?: XRReferenceSpaceType[];
+    requiredFeatures?: XRReferenceSpaceType[];
+}
+
 interface XRSession extends XRAnchorCreator {
     addEventListener: Function;
     removeEventListener: Function;