Browse Source

Merge pull request #3297 from TrevorDev/lewisVrHelperIssues

VRHelper usage issues
sebavan 7 years ago
parent
commit
8a131bbee5

+ 253 - 156
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -4,10 +4,16 @@ module BABYLON {
         floorMeshes?: Mesh[];
         floorMeshes?: Mesh[];
     }
     }
 
 
+    export interface VRExperienceHelperOptions extends WebVROptions {
+        createDeviceOrientationCamera?: boolean; // Create a DeviceOrientationCamera to be used as your out of vr camera.
+        createFallbackVRDeviceOrientationFreeCamera?: boolean; // Create a VRDeviceOrientationFreeCamera to be used for VR when no external HMD is found
+    }
+
     export class VRExperienceHelper {
     export class VRExperienceHelper {
         private _scene: BABYLON.Scene;
         private _scene: BABYLON.Scene;
         private _position: Vector3;
         private _position: Vector3;
         private _btnVR: HTMLButtonElement;
         private _btnVR: HTMLButtonElement;
+        private _btnVRDisplayed: boolean;
 
 
         // Can the system support WebVR, even if a headset isn't plugged in?
         // Can the system support WebVR, even if a headset isn't plugged in?
         private _webVRsupported = false;
         private _webVRsupported = false;
@@ -23,18 +29,19 @@ module BABYLON {
 
 
         private _canvas: Nullable<HTMLCanvasElement>;
         private _canvas: Nullable<HTMLCanvasElement>;
         private _webVRCamera: WebVRFreeCamera;
         private _webVRCamera: WebVRFreeCamera;
-        private _vrDeviceOrientationCamera: VRDeviceOrientationFreeCamera;
-        private _deviceOrientationCamera: DeviceOrientationCamera;
-        
+        private _vrDeviceOrientationCamera: Nullable<VRDeviceOrientationFreeCamera>;
+        private _deviceOrientationCamera: Nullable<DeviceOrientationCamera>;
+        private _existingCamera: Camera;
+
         private _onKeyDown: (event: KeyboardEvent) => void;
         private _onKeyDown: (event: KeyboardEvent) => void;
         private _onVrDisplayPresentChange: any;
         private _onVrDisplayPresentChange: any;
-        private _onVRDisplayChanged: (eventArgs:IDisplayChangedEventArgs) => void;
+        private _onVRDisplayChanged: (eventArgs: IDisplayChangedEventArgs) => void;
         private _onVRRequestPresentStart: () => void;
         private _onVRRequestPresentStart: () => void;
         private _onVRRequestPresentComplete: (success: boolean) => void;
         private _onVRRequestPresentComplete: (success: boolean) => void;
-        
-        public onEnteringVR = new Observable(); 
-        public onExitingVR = new Observable();
-        public onControllerMeshLoaded = new Observable<WebVRController>();  
+
+        public onEnteringVR = new Observable<VRExperienceHelper>();
+        public onExitingVR = new Observable<VRExperienceHelper>();
+        public onControllerMeshLoaded = new Observable<WebVRController>();
 
 
         private _rayLength: number;
         private _rayLength: number;
         private _useCustomVRButton: boolean = false;
         private _useCustomVRButton: boolean = false;
@@ -66,7 +73,7 @@ module BABYLON {
         private _rightLaserPointer: Nullable<Mesh>;
         private _rightLaserPointer: Nullable<Mesh>;
         private _currentMeshSelected: Nullable<AbstractMesh>;
         private _currentMeshSelected: Nullable<AbstractMesh>;
         public onNewMeshSelected = new Observable<AbstractMesh>();
         public onNewMeshSelected = new Observable<AbstractMesh>();
-        private _circleEase:CircleEase;
+        private _circleEase: CircleEase;
 
 
         private _raySelectionPredicate: (mesh: AbstractMesh) => boolean;
         private _raySelectionPredicate: (mesh: AbstractMesh) => boolean;
 
 
@@ -79,11 +86,11 @@ module BABYLON {
          * To be optionaly changed by user to define custom selection logic (after ray selection)
          * To be optionaly changed by user to define custom selection logic (after ray selection)
          */
          */
         public meshSelectionPredicate: (mesh: AbstractMesh) => boolean;
         public meshSelectionPredicate: (mesh: AbstractMesh) => boolean;
-        
+
         private _currentHit: Nullable<PickingInfo>;
         private _currentHit: Nullable<PickingInfo>;
         private _pointerDownOnMeshAsked = false;
         private _pointerDownOnMeshAsked = false;
         private _isActionableMesh = false;
         private _isActionableMesh = false;
-        private _defaultHeight:number;
+        private _defaultHeight: number;
         private _teleportationEnabled = false;
         private _teleportationEnabled = false;
         private _interactionsEnabled = false;
         private _interactionsEnabled = false;
         private _interactionsRequested = false;
         private _interactionsRequested = false;
@@ -110,24 +117,24 @@ module BABYLON {
             if (!value) {
             if (!value) {
                 if (this._rightLaserPointer) {
                 if (this._rightLaserPointer) {
                     this._rightLaserPointer.isVisible = false;
                     this._rightLaserPointer.isVisible = false;
-                } 
+                }
                 if (this._leftLaserPointer) {
                 if (this._leftLaserPointer) {
                     this._leftLaserPointer.isVisible = false;
                     this._leftLaserPointer.isVisible = false;
-                } 
+                }
             }
             }
         }
         }
 
 
-        public get deviceOrientationCamera(): DeviceOrientationCamera {
+        public get deviceOrientationCamera(): Nullable<DeviceOrientationCamera> {
             return this._deviceOrientationCamera;
             return this._deviceOrientationCamera;
         }
         }
 
 
         // Based on the current WebVR support, returns the current VR camera used
         // Based on the current WebVR support, returns the current VR camera used
-        public get currentVRCamera(): FreeCamera {
+        public get currentVRCamera(): Nullable<Camera> {
             if (this._webVRready) {
             if (this._webVRready) {
                 return this._webVRCamera;
                 return this._webVRCamera;
             }
             }
             else {
             else {
-                return this._vrDeviceOrientationCamera;
+                return this._scene.activeCamera;
             }
             }
         }
         }
 
 
@@ -135,39 +142,53 @@ module BABYLON {
             return this._webVRCamera;
             return this._webVRCamera;
         }
         }
 
 
-        public get vrDeviceOrientationCamera(): VRDeviceOrientationFreeCamera {
+        public get vrDeviceOrientationCamera(): Nullable<VRDeviceOrientationFreeCamera> {
             return this._vrDeviceOrientationCamera;
             return this._vrDeviceOrientationCamera;
         }
         }
-                
-        constructor(scene: Scene, public webVROptions: WebVROptions = {}) {
+
+        constructor(scene: Scene, public webVROptions: VRExperienceHelperOptions = {}) {
             this._scene = scene;
             this._scene = scene;
+            this._canvas = scene.getEngine().getRenderingCanvas();
 
 
             this._defaultHeight = webVROptions.defaultHeight || 1.7;
             this._defaultHeight = webVROptions.defaultHeight || 1.7;
+            
+            if (webVROptions.createFallbackVRDeviceOrientationFreeCamera === undefined) {
+                webVROptions.createFallbackVRDeviceOrientationFreeCamera = true;
+            }
 
 
-            if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
-                this._position = new BABYLON.Vector3(0, this._defaultHeight, 0);
-                this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position.clone(), scene);
+            if (webVROptions.createDeviceOrientationCamera === undefined) {
+                webVROptions.createDeviceOrientationCamera = true;
             }
             }
-            else {
-                this._position = this._scene.activeCamera.position.clone();
-                this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position.clone(), scene);
-                this._deviceOrientationCamera.minZ = this._scene.activeCamera.minZ;
-                this._deviceOrientationCamera.maxZ = this._scene.activeCamera.maxZ;
-                // Set rotation from previous camera
-                if(this._scene.activeCamera instanceof TargetCamera && this._scene.activeCamera.rotation){
-                    var targetCamera = this._scene.activeCamera;
-                    if(targetCamera.rotationQuaternion){
-                        this._deviceOrientationCamera.rotationQuaternion.copyFrom(targetCamera.rotationQuaternion);
-                    }else{
-                        this._deviceOrientationCamera.rotationQuaternion.copyFrom(Quaternion.RotationYawPitchRoll(targetCamera.rotation.y, targetCamera.rotation.x, targetCamera.rotation.z));
+
+            if (!this._scene.activeCamera || webVROptions.createDeviceOrientationCamera) {
+                if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
+                    this._position = new BABYLON.Vector3(0, this._defaultHeight, 0);
+                    this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position.clone(), scene);
+                }
+                else {
+                    this._position = this._scene.activeCamera.position.clone();
+                    this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position.clone(), scene);
+                    this._deviceOrientationCamera.minZ = this._scene.activeCamera.minZ;
+                    this._deviceOrientationCamera.maxZ = this._scene.activeCamera.maxZ;
+                    // Set rotation from previous camera
+                    if (this._scene.activeCamera instanceof TargetCamera && this._scene.activeCamera.rotation) {
+                        var targetCamera = this._scene.activeCamera;
+                        if (targetCamera.rotationQuaternion) {
+                            this._deviceOrientationCamera.rotationQuaternion.copyFrom(targetCamera.rotationQuaternion);
+                        } else {
+                            this._deviceOrientationCamera.rotationQuaternion.copyFrom(Quaternion.RotationYawPitchRoll(targetCamera.rotation.y, targetCamera.rotation.x, targetCamera.rotation.z));
+                        }
+                        this._deviceOrientationCamera.rotation = targetCamera.rotation.clone();
                     }
                     }
-                    this._deviceOrientationCamera.rotation = targetCamera.rotation.clone();
+                }
+                this._scene.activeCamera = this._deviceOrientationCamera;                
+                if (this._canvas) {
+                    this._scene.activeCamera.attachControl(this._canvas);
                 }
                 }
             }
             }
-            this._scene.activeCamera = this._deviceOrientationCamera;
-            this._canvas = scene.getEngine().getRenderingCanvas();
-            if (this._canvas) {
-                this._scene.activeCamera.attachControl(this._canvas);
+            else {
+                this._existingCamera = this._scene.activeCamera;
+                this._position = this._scene.activeCamera.position.clone();
             }
             }
 
 
             if (webVROptions) {
             if (webVROptions) {
@@ -198,7 +219,7 @@ module BABYLON {
                 var style = document.createElement('style');
                 var style = document.createElement('style');
                 style.appendChild(document.createTextNode(css));
                 style.appendChild(document.createTextNode(css));
                 document.getElementsByTagName('head')[0].appendChild(style);
                 document.getElementsByTagName('head')[0].appendChild(style);
-            }  
+            }
 
 
             if (this._canvas) {
             if (this._canvas) {
                 if (!this._useCustomVRButton) {
                 if (!this._useCustomVRButton) {
@@ -228,8 +249,16 @@ module BABYLON {
             document.addEventListener("webkitfullscreenchange", () => { this._onFullscreenChange() }, false);
             document.addEventListener("webkitfullscreenchange", () => { this._onFullscreenChange() }, false);
             document.addEventListener("msfullscreenchange", () => { this._onFullscreenChange() }, false);
             document.addEventListener("msfullscreenchange", () => { this._onFullscreenChange() }, false);
 
 
-            if (!this._useCustomVRButton) {
+            this._scene.getEngine().onVRDisplayChangedObservable.add((e) => {
+                if (!this._useCustomVRButton && !this._btnVRDisplayed && e.vrDisplay) {
+                    document.body.appendChild(this._btnVR);
+                    this._btnVRDisplayed = true;
+                }
+            })
+
+            if (!this._useCustomVRButton && !this._btnVRDisplayed && webVROptions.createFallbackVRDeviceOrientationFreeCamera) {
                 document.body.appendChild(this._btnVR);
                 document.body.appendChild(this._btnVR);
+                this._btnVRDisplayed = true;
             }
             }
 
 
             // Exiting VR mode using 'ESC' key on desktop
             // Exiting VR mode using 'ESC' key on desktop
@@ -241,7 +270,7 @@ module BABYLON {
             document.addEventListener("keydown", this._onKeyDown);
             document.addEventListener("keydown", this._onKeyDown);
 
 
             // Exiting VR mode double tapping the touch screen
             // Exiting VR mode double tapping the touch screen
-            this._scene.onPrePointerObservable.add( (pointerInfo, eventState) => {
+            this._scene.onPrePointerObservable.add((pointerInfo, eventState) => {
                 if (this.isInVRMode()) {
                 if (this.isInVRMode()) {
                     this.exitVR();
                     this.exitVR();
                     if (this._fullscreenVRpresenting) {
                     if (this._fullscreenVRpresenting) {
@@ -249,9 +278,9 @@ module BABYLON {
                     }
                     }
                 }
                 }
             }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
             }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
-            
+
             // Listen for WebVR display changes
             // Listen for WebVR display changes
-            this._onVRDisplayChanged = (eventArgs:IDisplayChangedEventArgs) => this.onVRDisplayChanged(eventArgs);
+            this._onVRDisplayChanged = (eventArgs: IDisplayChangedEventArgs) => this.onVRDisplayChanged(eventArgs);
             this._onVrDisplayPresentChange = () => this.onVrDisplayPresentChange();
             this._onVrDisplayPresentChange = () => this.onVrDisplayPresentChange();
             this._onVRRequestPresentStart = () => {
             this._onVRRequestPresentStart = () => {
                 this._webVRrequesting = true;
                 this._webVRrequesting = true;
@@ -268,11 +297,14 @@ module BABYLON {
             window.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
             window.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
 
 
             // Create the cameras
             // Create the cameras
-            this._vrDeviceOrientationCamera = new BABYLON.VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene);            
+            if (webVROptions.createFallbackVRDeviceOrientationFreeCamera) {
+                this._vrDeviceOrientationCamera = new BABYLON.VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene);
+            }
             this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene, webVROptions);
             this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene, webVROptions);
             this._webVRCamera.onControllerMeshLoadedObservable.add((webVRController) => this._onDefaultMeshLoaded(webVRController));
             this._webVRCamera.onControllerMeshLoadedObservable.add((webVRController) => this._onDefaultMeshLoaded(webVRController));
             this._scene.gamepadManager.onGamepadConnectedObservable.add((pad) => this._onNewGamepadConnected(pad));
             this._scene.gamepadManager.onGamepadConnectedObservable.add((pad) => this._onNewGamepadConnected(pad));
-        
+            this._scene.gamepadManager.onGamepadDisconnectedObservable.add((pad) => this._onNewGamepadDisconnected(pad));
+
             this.updateButtonVisibility();
             this.updateButtonVisibility();
 
 
             //create easing functions
             //create easing functions
@@ -302,7 +334,7 @@ module BABYLON {
             }
             }
             try {
             try {
                 this.onControllerMeshLoaded.notifyObservers(webVRController);
                 this.onControllerMeshLoaded.notifyObservers(webVRController);
-            } 
+            }
             catch (err) {
             catch (err) {
                 Tools.Warn("Error in your custom logic onControllerMeshLoaded: " + err);
                 Tools.Warn("Error in your custom logic onControllerMeshLoaded: " + err);
             }
             }
@@ -335,10 +367,10 @@ module BABYLON {
             var vrDisplay = this._scene.getEngine().getVRDevice();
             var vrDisplay = this._scene.getEngine().getVRDevice();
             if (vrDisplay) {
             if (vrDisplay) {
                 var wasPresenting = this._webVRpresenting;
                 var wasPresenting = this._webVRpresenting;
-                
+
                 // A VR display is connected
                 // A VR display is connected
                 this._webVRpresenting = vrDisplay.isPresenting;
                 this._webVRpresenting = vrDisplay.isPresenting;
-                
+
                 if (wasPresenting && !this._webVRpresenting)
                 if (wasPresenting && !this._webVRpresenting)
                     this.exitVR();
                     this.exitVR();
             } else {
             } else {
@@ -348,7 +380,7 @@ module BABYLON {
             this.updateButtonVisibility();
             this.updateButtonVisibility();
         }
         }
 
 
-        private onVRDisplayChanged(eventArgs:IDisplayChangedEventArgs) {
+        private onVRDisplayChanged(eventArgs: IDisplayChangedEventArgs) {
             this._webVRsupported = eventArgs.vrSupported;
             this._webVRsupported = eventArgs.vrSupported;
             this._webVRready = !!eventArgs.vrDisplay;
             this._webVRready = !!eventArgs.vrDisplay;
             this._webVRpresenting = eventArgs.vrDisplay && eventArgs.vrDisplay.isPresenting;
             this._webVRpresenting = eventArgs.vrDisplay && eventArgs.vrDisplay.isPresenting;
@@ -356,13 +388,13 @@ module BABYLON {
             this.updateButtonVisibility();
             this.updateButtonVisibility();
         }
         }
 
 
-        private updateButtonVisibility() {            
+        private updateButtonVisibility() {
             if (!this._btnVR) {
             if (!this._btnVR) {
                 return;
                 return;
             }
             }
             this._btnVR.className = "babylonVRicon";
             this._btnVR.className = "babylonVRicon";
             if (this.isInVRMode()) {
             if (this.isInVRMode()) {
-                this._btnVR.className += " vrdisplaypresenting";                
+                this._btnVR.className += " vrdisplaypresenting";
             } else {
             } else {
                 if (this._webVRready) this._btnVR.className += " vrdisplayready";
                 if (this._webVRready) this._btnVR.className += " vrdisplayready";
                 if (this._webVRsupported) this._btnVR.className += " vrdisplaysupported";
                 if (this._webVRsupported) this._btnVR.className += " vrdisplaysupported";
@@ -381,7 +413,7 @@ module BABYLON {
 
 
             if (this.onEnteringVR) {
             if (this.onEnteringVR) {
                 try {
                 try {
-                    this.onEnteringVR.notifyObservers({});
+                    this.onEnteringVR.notifyObservers(this);
                 }
                 }
                 catch (err) {
                 catch (err) {
                     Tools.Warn("Error in your custom logic onEnteringVR: " + err);
                     Tools.Warn("Error in your custom logic onEnteringVR: " + err);
@@ -397,13 +429,13 @@ module BABYLON {
                     this._scene.activeCamera = this._webVRCamera;
                     this._scene.activeCamera = this._webVRCamera;
                 }
                 }
             }
             }
-            else {
+            else if (this._vrDeviceOrientationCamera) {
                 this._vrDeviceOrientationCamera.position = this._position;
                 this._vrDeviceOrientationCamera.position = this._position;
                 this._scene.activeCamera = this._vrDeviceOrientationCamera;
                 this._scene.activeCamera = this._vrDeviceOrientationCamera;
                 this._scene.getEngine().switchFullscreen(true);
                 this._scene.getEngine().switchFullscreen(true);
                 this.updateButtonVisibility();
                 this.updateButtonVisibility();
             }
             }
-            
+
             if (this._scene.activeCamera && this._canvas) {
             if (this._scene.activeCamera && this._canvas) {
                 this._scene.activeCamera.attachControl(this._canvas);
                 this._scene.activeCamera.attachControl(this._canvas);
             }
             }
@@ -415,7 +447,7 @@ module BABYLON {
         public exitVR() {
         public exitVR() {
             if (this.onExitingVR) {
             if (this.onExitingVR) {
                 try {
                 try {
-                    this.onExitingVR.notifyObservers({});
+                    this.onExitingVR.notifyObservers(this);
                 }
                 }
                 catch (err) {
                 catch (err) {
                     Tools.Warn("Error in your custom logic onExitingVR: " + err);
                     Tools.Warn("Error in your custom logic onExitingVR: " + err);
@@ -426,16 +458,21 @@ module BABYLON {
             }
             }
             if (this._scene.activeCamera) {
             if (this._scene.activeCamera) {
                 this._position = this._scene.activeCamera.position.clone();
                 this._position = this._scene.activeCamera.position.clone();
-                
+
             }
             }
-            this._deviceOrientationCamera.position = this._position;
-            this._scene.activeCamera = this._deviceOrientationCamera;
 
 
-            if (this._canvas) {
-                this._scene.activeCamera.attachControl(this._canvas);
+            if (this._deviceOrientationCamera) {
+                this._deviceOrientationCamera.position = this._position;
+                this._scene.activeCamera = this._deviceOrientationCamera;
+                if (this._canvas) {
+                    this._scene.activeCamera.attachControl(this._canvas);
+                }
+            } else if (this._existingCamera) {
+                this._existingCamera.position = this._position;
+                this._scene.activeCamera = this._existingCamera;
             }
             }
 
 
-            this.updateButtonVisibility();  
+            this.updateButtonVisibility();
         }
         }
 
 
         public get position(): Vector3 {
         public get position(): Vector3 {
@@ -472,10 +509,10 @@ module BABYLON {
                 }
                 }
 
 
                 this._raySelectionPredicate = (mesh) => {
                 this._raySelectionPredicate = (mesh) => {
-                    if (this._isTeleportationFloor(mesh) || (mesh.isVisible && mesh.name.indexOf("gazeTracker") === -1 
-                            && mesh.name.indexOf("teleportationCircle") === -1
-                            && mesh.name.indexOf("torusTeleportation") === -1
-                            && mesh.name.indexOf("laserPointer") === -1)) {
+                    if (this._isTeleportationFloor(mesh) || (mesh.isVisible && mesh.name.indexOf("gazeTracker") === -1
+                        && mesh.name.indexOf("teleportationCircle") === -1
+                        && mesh.name.indexOf("torusTeleportation") === -1
+                        && mesh.name.indexOf("laserPointer") === -1)) {
                         return this.raySelectionPredicate(mesh);
                         return this.raySelectionPredicate(mesh);
                     }
                     }
                     return false;
                     return false;
@@ -490,23 +527,46 @@ module BABYLON {
         }
         }
 
 
         private _isTeleportationFloor(mesh: AbstractMesh): boolean {
         private _isTeleportationFloor(mesh: AbstractMesh): boolean {
-            for (var i=0; i<this._floorMeshesCollection.length; i++) {
+            for (var i = 0; i < this._floorMeshesCollection.length; i++) {
                 if (this._floorMeshesCollection[i].id === mesh.id) {
                 if (this._floorMeshesCollection[i].id === mesh.id) {
                     return true;
                     return true;
                 }
                 }
             }
             }
-            if (this._floorMeshName && mesh.name.indexOf(this._floorMeshName) !== -1) {
+            if (this._floorMeshName && mesh.name === this._floorMeshName) {
                 return true;
                 return true;
             }
             }
             return false;
             return false;
         }
         }
 
 
+        public addFloorMesh(floorMesh: Mesh): void {
+            if (!this._floorMeshesCollection) {
+                return;
+            }
+
+            if (this._floorMeshesCollection.indexOf(floorMesh) > -1) {
+                return;
+            }
+
+            this._floorMeshesCollection.push(floorMesh);
+        }
+
+        public removeFloorMesh(floorMesh: Mesh): void {
+            if (!this._floorMeshesCollection) {
+                return
+            }
+
+            const meshIndex = this._floorMeshesCollection.indexOf(floorMesh);
+            if (meshIndex !== -1) {
+                this._floorMeshesCollection.splice(meshIndex, 1);
+            }
+        }
+
         public enableTeleportation(vrTeleportationOptions: VRTeleportationOptions = {}) {
         public enableTeleportation(vrTeleportationOptions: VRTeleportationOptions = {}) {
             if (!this._teleportationEnabled) {
             if (!this._teleportationEnabled) {
                 this._teleportationRequested = true;
                 this._teleportationRequested = true;
 
 
                 this.enableInteractions();
                 this.enableInteractions();
-            
+
                 if (vrTeleportationOptions) {
                 if (vrTeleportationOptions) {
                     if (vrTeleportationOptions.floorMeshName) {
                     if (vrTeleportationOptions.floorMeshName) {
                         this._floorMeshName = vrTeleportationOptions.floorMeshName;
                         this._floorMeshName = vrTeleportationOptions.floorMeshName;
@@ -529,15 +589,15 @@ module BABYLON {
                 const imageProcessingConfiguration = new ImageProcessingConfiguration();
                 const imageProcessingConfiguration = new ImageProcessingConfiguration();
                 imageProcessingConfiguration.vignetteColor = new BABYLON.Color4(0, 0, 0, 0);
                 imageProcessingConfiguration.vignetteColor = new BABYLON.Color4(0, 0, 0, 0);
                 imageProcessingConfiguration.vignetteEnabled = true;
                 imageProcessingConfiguration.vignetteEnabled = true;
-                this._postProcessMove = new BABYLON.ImageProcessingPostProcess("postProcessMove", 
-                    1.0, 
+                this._postProcessMove = new BABYLON.ImageProcessingPostProcess("postProcessMove",
+                    1.0,
                     this._webVRCamera,
                     this._webVRCamera,
                     undefined,
                     undefined,
                     undefined,
                     undefined,
                     undefined,
                     undefined,
                     undefined,
                     undefined,
                     imageProcessingConfiguration);
                     imageProcessingConfiguration);
-                
+
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
                 this._passProcessMove = new BABYLON.PassPostProcess("pass", 1.0, this._webVRCamera);
                 this._passProcessMove = new BABYLON.PassPostProcess("pass", 1.0, this._webVRCamera);
                 this._teleportationEnabled = true;
                 this._teleportationEnabled = true;
@@ -551,9 +611,9 @@ module BABYLON {
                     gamepad.onleftstickchanged((stickValues) => {
                     gamepad.onleftstickchanged((stickValues) => {
                         if (this._teleportationEnabled) {
                         if (this._teleportationEnabled) {
                             // Listening to classic/xbox gamepad only if no VR controller is active
                             // Listening to classic/xbox gamepad only if no VR controller is active
-                            if ((!this._leftLaserPointer && !this._rightLaserPointer) || 
-                                ((this._leftLaserPointer && !this._leftLaserPointer.isVisible) && 
-                                (this._rightLaserPointer && !this._rightLaserPointer.isVisible))) {
+                            if ((!this._leftLaserPointer && !this._rightLaserPointer) ||
+                                ((this._leftLaserPointer && !this._leftLaserPointer.isVisible) &&
+                                    (this._rightLaserPointer && !this._rightLaserPointer.isVisible))) {
                                 if (!this._teleportationRequestInitiated) {
                                 if (!this._teleportationRequestInitiated) {
                                     if (stickValues.y < -this._padSensibilityUp) {
                                     if (stickValues.y < -this._padSensibilityUp) {
                                         this._teleportationRequestInitiated = true;
                                         this._teleportationRequestInitiated = true;
@@ -624,6 +684,27 @@ module BABYLON {
             }
             }
         }
         }
 
 
+        private _onNewGamepadDisconnected(gamepad: Gamepad) {
+            if (gamepad instanceof WebVRController) {
+                if (gamepad.hand === "left") {
+                    this._interactionsEnabledOnLeftController = false;
+                    this._teleportationEnabledOnLeftController = false;
+                    this._leftControllerReady = false;
+                    if (this._leftLaserPointer) {
+                        this._leftLaserPointer.dispose();
+                    }
+                }
+                if (gamepad.hand === "right") {
+                    this._interactionsEnabledOnRightController = false;
+                    this._teleportationEnabledOnRightController = false;
+                    this._rightControllerReady = false;
+                    if (this._rightLaserPointer) {
+                        this._rightLaserPointer.dispose();
+                    }
+                }
+            }
+        }
+
         private _enableInteractionOnController(webVRController: WebVRController) {
         private _enableInteractionOnController(webVRController: WebVRController) {
             var controllerMesh = webVRController.mesh;
             var controllerMesh = webVRController.mesh;
             if (controllerMesh) {
             if (controllerMesh) {
@@ -704,7 +785,7 @@ module BABYLON {
                     if (!this._teleportationRequestInitiated) {
                     if (!this._teleportationRequestInitiated) {
                         if (stateObject.y < -this._padSensibilityUp) {
                         if (stateObject.y < -this._padSensibilityUp) {
                             // If laser pointer wasn't enabled yet
                             // If laser pointer wasn't enabled yet
-                            if (this._displayLaserPointer && webVRController.hand === "left" &&  this._leftLaserPointer) {
+                            if (this._displayLaserPointer && webVRController.hand === "left" && this._leftLaserPointer) {
                                 this._leftLaserPointer.isVisible = true;
                                 this._leftLaserPointer.isVisible = true;
                                 if (this._rightLaserPointer) {
                                 if (this._rightLaserPointer) {
                                     this._rightLaserPointer.isVisible = false;
                                     this._rightLaserPointer.isVisible = false;
@@ -721,7 +802,7 @@ module BABYLON {
                     }
                     }
                     else {
                     else {
                         // Listening to the proper controller values changes to confirm teleportation
                         // Listening to the proper controller values changes to confirm teleportation
-                        if ((webVRController.hand === "left" && this._leftLaserPointer && this._leftLaserPointer.isVisible) 
+                        if ((webVRController.hand === "left" && this._leftLaserPointer && this._leftLaserPointer.isVisible)
                             || (webVRController.hand === "right" && this._rightLaserPointer && this._rightLaserPointer.isVisible)) {
                             || (webVRController.hand === "right" && this._rightLaserPointer && this._rightLaserPointer.isVisible)) {
                             if (stateObject.y > -this._padSensibilityDown) {
                             if (stateObject.y > -this._padSensibilityDown) {
                                 if (this._teleportationAllowed) {
                                 if (this._teleportationAllowed) {
@@ -745,7 +826,7 @@ module BABYLON {
                             this._rotationLeftAsked = false;
                             this._rotationLeftAsked = false;
                         }
                         }
                     }
                     }
-        
+
                     if (!this._rotationRightAsked) {
                     if (!this._rotationRightAsked) {
                         if (stateObject.x > this._padSensibilityUp) {
                         if (stateObject.x > this._padSensibilityUp) {
                             this._rotationRightAsked = true;
                             this._rotationRightAsked = true;
@@ -774,22 +855,22 @@ module BABYLON {
             targetMat.specularColor = BABYLON.Color3.Black();
             targetMat.specularColor = BABYLON.Color3.Black();
             targetMat.emissiveColor = new BABYLON.Color3(0.7, 0.7, 0.7)
             targetMat.emissiveColor = new BABYLON.Color3(0.7, 0.7, 0.7)
             targetMat.backFaceCulling = false;
             targetMat.backFaceCulling = false;
-            this._gazeTracker.material = targetMat; 
+            this._gazeTracker.material = targetMat;
         }
         }
 
 
         private _createTeleportationCircles() {
         private _createTeleportationCircles() {
             this._teleportationCircle = BABYLON.Mesh.CreateGround("teleportationCircle", 2, 2, 2, this._scene);
             this._teleportationCircle = BABYLON.Mesh.CreateGround("teleportationCircle", 2, 2, 2, this._scene);
             this._teleportationCircle.isPickable = false;
             this._teleportationCircle.isPickable = false;
-            
+
             var length = 512;
             var length = 512;
             var dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", length, this._scene, true);
             var dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", length, this._scene, true);
             dynamicTexture.hasAlpha = true;
             dynamicTexture.hasAlpha = true;
             var context = dynamicTexture.getContext();
             var context = dynamicTexture.getContext();
-    
+
             var centerX = length / 2;
             var centerX = length / 2;
             var centerY = length / 2;
             var centerY = length / 2;
             var radius = 200;
             var radius = 200;
-    
+
             context.beginPath();
             context.beginPath();
             context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
             context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
             context.fillStyle = this._teleportationFillColor;
             context.fillStyle = this._teleportationFillColor;
@@ -799,17 +880,17 @@ module BABYLON {
             context.stroke();
             context.stroke();
             context.closePath();
             context.closePath();
             dynamicTexture.update();
             dynamicTexture.update();
-            
+
             var teleportationCircleMaterial = new BABYLON.StandardMaterial("TextPlaneMaterial", this._scene);
             var teleportationCircleMaterial = new BABYLON.StandardMaterial("TextPlaneMaterial", this._scene);
             teleportationCircleMaterial.diffuseTexture = dynamicTexture;
             teleportationCircleMaterial.diffuseTexture = dynamicTexture;
             this._teleportationCircle.material = teleportationCircleMaterial;
             this._teleportationCircle.material = teleportationCircleMaterial;
-            
+
             var torus = BABYLON.Mesh.CreateTorus("torusTeleportation", 0.75, 0.1, 25, this._scene, false);
             var torus = BABYLON.Mesh.CreateTorus("torusTeleportation", 0.75, 0.1, 25, this._scene, false);
             torus.isPickable = false;
             torus.isPickable = false;
             torus.parent = this._teleportationCircle;
             torus.parent = this._teleportationCircle;
-            
+
             var animationInnerCircle = new BABYLON.Animation("animationInnerCircle", "position.y", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             var animationInnerCircle = new BABYLON.Animation("animationInnerCircle", "position.y", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
-        
+
             var keys = [];
             var keys = [];
             keys.push({
             keys.push({
                 frame: 0,
                 frame: 0,
@@ -823,18 +904,18 @@ module BABYLON {
                 frame: 60,
                 frame: 60,
                 value: 0
                 value: 0
             });
             });
-        
+
             animationInnerCircle.setKeys(keys);
             animationInnerCircle.setKeys(keys);
-        
+
             var easingFunction = new BABYLON.SineEase();
             var easingFunction = new BABYLON.SineEase();
             easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
             easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
             animationInnerCircle.setEasingFunction(easingFunction);
             animationInnerCircle.setEasingFunction(easingFunction);
-        
+
             torus.animations = [];
             torus.animations = [];
             torus.animations.push(animationInnerCircle);
             torus.animations.push(animationInnerCircle);
-        
+
             this._scene.beginAnimation(torus, 0, 60, true);
             this._scene.beginAnimation(torus, 0, 60, true);
-            
+
             this._hideTeleportationCircle();
             this._hideTeleportationCircle();
         }
         }
 
 
@@ -844,7 +925,7 @@ module BABYLON {
                 (<Mesh>this._teleportationCircle.getChildren()[0]).isVisible = true;
                 (<Mesh>this._teleportationCircle.getChildren()[0]).isVisible = true;
             }
             }
         }
         }
-        
+
         private _hideTeleportationCircle() {
         private _hideTeleportationCircle() {
             if (this._teleportationEnabled) {
             if (this._teleportationEnabled) {
                 this._teleportationCircle.isVisible = false;
                 this._teleportationCircle.isVisible = false;
@@ -853,20 +934,24 @@ module BABYLON {
         }
         }
 
 
         private _rotateCamera(right: boolean) {
         private _rotateCamera(right: boolean) {
+            if (!(this.currentVRCamera instanceof FreeCamera)) {
+                return;
+            }
+
             if (right) {
             if (right) {
                 this._rotationAngle++;
                 this._rotationAngle++;
             }
             }
             else {
             else {
                 this._rotationAngle--;
                 this._rotationAngle--;
             }
             }
-        
+
             this.currentVRCamera.animations = [];
             this.currentVRCamera.animations = [];
-         
+
             var target = BABYLON.Quaternion.FromRotationMatrix(BABYLON.Matrix.RotationY(Math.PI / 4 * this._rotationAngle));
             var target = BABYLON.Quaternion.FromRotationMatrix(BABYLON.Matrix.RotationY(Math.PI / 4 * this._rotationAngle));
-        
+
             var animationRotation = new BABYLON.Animation("animationRotation", "rotationQuaternion", 90, BABYLON.Animation.ANIMATIONTYPE_QUATERNION,
             var animationRotation = new BABYLON.Animation("animationRotation", "rotationQuaternion", 90, BABYLON.Animation.ANIMATIONTYPE_QUATERNION,
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
-            
+
             var animationRotationKeys = [];
             var animationRotationKeys = [];
             animationRotationKeys.push({
             animationRotationKeys.push({
                 frame: 0,
                 frame: 0,
@@ -876,18 +961,18 @@ module BABYLON {
                 frame: 6,
                 frame: 6,
                 value: target
                 value: target
             });
             });
-        
+
             animationRotation.setKeys(animationRotationKeys);
             animationRotation.setKeys(animationRotationKeys);
 
 
             animationRotation.setEasingFunction(this._circleEase);
             animationRotation.setEasingFunction(this._circleEase);
-        
+
             this.currentVRCamera.animations.push(animationRotation);
             this.currentVRCamera.animations.push(animationRotation);
-        
+
             this._postProcessMove.animations = [];
             this._postProcessMove.animations = [];
-        
+
             var animationPP = new BABYLON.Animation("animationPP", "vignetteWeight", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
             var animationPP = new BABYLON.Animation("animationPP", "vignetteWeight", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
-        
+
             var vignetteWeightKeys = [];
             var vignetteWeightKeys = [];
             vignetteWeightKeys.push({
             vignetteWeightKeys.push({
                 frame: 0,
                 frame: 0,
@@ -901,14 +986,14 @@ module BABYLON {
                 frame: 6,
                 frame: 6,
                 value: 0
                 value: 0
             });
             });
-        
+
             animationPP.setKeys(vignetteWeightKeys);
             animationPP.setKeys(vignetteWeightKeys);
             animationPP.setEasingFunction(this._circleEase);
             animationPP.setEasingFunction(this._circleEase);
             this._postProcessMove.animations.push(animationPP);
             this._postProcessMove.animations.push(animationPP);
-        
+
             var animationPP2 = new BABYLON.Animation("animationPP2", "vignetteStretch", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
             var animationPP2 = new BABYLON.Animation("animationPP2", "vignetteStretch", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
-        
+
             var vignetteStretchKeys = [];
             var vignetteStretchKeys = [];
             vignetteStretchKeys.push({
             vignetteStretchKeys.push({
                 frame: 0,
                 frame: 0,
@@ -922,11 +1007,11 @@ module BABYLON {
                 frame: 6,
                 frame: 6,
                 value: 0
                 value: 0
             });
             });
-        
+
             animationPP2.setKeys(vignetteStretchKeys);
             animationPP2.setKeys(vignetteStretchKeys);
             animationPP2.setEasingFunction(this._circleEase);
             animationPP2.setEasingFunction(this._circleEase);
             this._postProcessMove.animations.push(animationPP2);
             this._postProcessMove.animations.push(animationPP2);
-            
+
             this._postProcessMove.imageProcessingConfiguration.vignetteWeight = 0;
             this._postProcessMove.imageProcessingConfiguration.vignetteWeight = 0;
             this._postProcessMove.imageProcessingConfiguration.vignetteStretch = 0;
             this._postProcessMove.imageProcessingConfiguration.vignetteStretch = 0;
 
 
@@ -948,7 +1033,7 @@ module BABYLON {
                 }
                 }
                 this._haloCenter.copyFrom(hit.pickedPoint);
                 this._haloCenter.copyFrom(hit.pickedPoint);
                 this._teleportationCircle.position.copyFrom(hit.pickedPoint);
                 this._teleportationCircle.position.copyFrom(hit.pickedPoint);
-                var pickNormal = hit.getNormal();
+                var pickNormal = hit.getNormal(true, false);
                 if (pickNormal) {
                 if (pickNormal) {
                     var axis1 = BABYLON.Vector3.Cross(BABYLON.Axis.Y, pickNormal);
                     var axis1 = BABYLON.Vector3.Cross(BABYLON.Axis.Y, pickNormal);
                     var axis2 = BABYLON.Vector3.Cross(pickNormal, axis1);
                     var axis2 = BABYLON.Vector3.Cross(pickNormal, axis1);
@@ -959,34 +1044,43 @@ module BABYLON {
         }
         }
         private _workingVector = Vector3.Zero();
         private _workingVector = Vector3.Zero();
         private _teleportCamera() {
         private _teleportCamera() {
+            if (!(this.currentVRCamera instanceof FreeCamera)) {
+                return;
+            }
+
             // Teleport the hmd to where the user is looking by moving the anchor to where they are looking minus the
             // Teleport the hmd to where the user is looking by moving the anchor to where they are looking minus the
             // offset of the headset from the anchor. Then add the helper's position to account for user's height offset
             // offset of the headset from the anchor. Then add the helper's position to account for user's height offset
-            this.webVRCamera.leftCamera!.globalPosition.subtractToRef(this.webVRCamera.position, this._workingVector);
-            this._haloCenter.subtractToRef(this._workingVector, this._workingVector);
+            if(this.webVRCamera.leftCamera){
+                this._workingVector.copyFrom(this.webVRCamera.leftCamera.globalPosition);
+                this._workingVector.subtractInPlace(this.webVRCamera.position);
+                this._haloCenter.subtractToRef(this._workingVector, this._workingVector);
+            }else{
+                this._workingVector.copyFrom(this._haloCenter);
+            }
             this._workingVector.y += this._defaultHeight;
             this._workingVector.y += this._defaultHeight;
-            
+
             // Create animation from the camera's position to the new location
             // Create animation from the camera's position to the new location
             this.currentVRCamera.animations = [];
             this.currentVRCamera.animations = [];
             var animationCameraTeleportation = new BABYLON.Animation("animationCameraTeleportation", "position", 90, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
             var animationCameraTeleportation = new BABYLON.Animation("animationCameraTeleportation", "position", 90, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
             var animationCameraTeleportationKeys = [{
             var animationCameraTeleportationKeys = [{
-                    frame: 0,
-                    value: this.currentVRCamera.position
-                },
-                {
-                    frame: 11,
-                    value: this._workingVector
-                }
+                frame: 0,
+                value: this.currentVRCamera.position
+            },
+            {
+                frame: 11,
+                value: this._workingVector
+            }
             ];
             ];
-            
+
             animationCameraTeleportation.setKeys(animationCameraTeleportationKeys);
             animationCameraTeleportation.setKeys(animationCameraTeleportationKeys);
             animationCameraTeleportation.setEasingFunction(this._circleEase);
             animationCameraTeleportation.setEasingFunction(this._circleEase);
             this.currentVRCamera.animations.push(animationCameraTeleportation);
             this.currentVRCamera.animations.push(animationCameraTeleportation);
-        
+
             this._postProcessMove.animations = [];
             this._postProcessMove.animations = [];
-        
+
             var animationPP = new BABYLON.Animation("animationPP", "vignetteWeight", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
             var animationPP = new BABYLON.Animation("animationPP", "vignetteWeight", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
-        
+
             var vignetteWeightKeys = [];
             var vignetteWeightKeys = [];
             vignetteWeightKeys.push({
             vignetteWeightKeys.push({
                 frame: 0,
                 frame: 0,
@@ -1000,13 +1094,13 @@ module BABYLON {
                 frame: 11,
                 frame: 11,
                 value: 0
                 value: 0
             });
             });
-        
+
             animationPP.setKeys(vignetteWeightKeys);
             animationPP.setKeys(vignetteWeightKeys);
             this._postProcessMove.animations.push(animationPP);
             this._postProcessMove.animations.push(animationPP);
-        
+
             var animationPP2 = new BABYLON.Animation("animationPP2", "vignetteStretch", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
             var animationPP2 = new BABYLON.Animation("animationPP2", "vignetteStretch", 90, BABYLON.Animation.ANIMATIONTYPE_FLOAT,
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
                 BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
-        
+
             var vignetteStretchKeys = [];
             var vignetteStretchKeys = [];
             vignetteStretchKeys.push({
             vignetteStretchKeys.push({
                 frame: 0,
                 frame: 0,
@@ -1020,13 +1114,13 @@ module BABYLON {
                 frame: 11,
                 frame: 11,
                 value: 0
                 value: 0
             });
             });
-        
+
             animationPP2.setKeys(vignetteStretchKeys);
             animationPP2.setKeys(vignetteStretchKeys);
             this._postProcessMove.animations.push(animationPP2);
             this._postProcessMove.animations.push(animationPP2);
-        
-            this._postProcessMove.imageProcessingConfiguration.vignetteWeight = 8;
-            this._postProcessMove.imageProcessingConfiguration.vignetteStretch = 10;
-            
+
+            this._postProcessMove.imageProcessingConfiguration.vignetteWeight = 0;
+            this._postProcessMove.imageProcessingConfiguration.vignetteStretch = 0;
+
             this._webVRCamera.attachPostProcess(this._postProcessMove)
             this._webVRCamera.attachPostProcess(this._postProcessMove)
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, () => {
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, () => {
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
@@ -1034,23 +1128,21 @@ module BABYLON {
             this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1);
             this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1);
         }
         }
 
 
-        private _castRayAndSelectObject () {
+        private _castRayAndSelectObject() {
+            if (!(this.currentVRCamera instanceof FreeCamera)) {
+                return;
+            }
+
             var ray;
             var ray;
-            if ((!(<WebVRFreeCamera>this.currentVRCamera).rightController && !(<WebVRFreeCamera>this.currentVRCamera).leftController) || 
-                (this._leftLaserPointer && !this._leftLaserPointer.isVisible && !this._rightLaserPointer) || 
-                (this._rightLaserPointer && !this._rightLaserPointer.isVisible && !this._leftLaserPointer) || 
-                (this._rightLaserPointer && this._leftLaserPointer && !this._rightLaserPointer.isVisible && !this._leftLaserPointer.isVisible)) {
-                    ray = this.currentVRCamera.getForwardRay(this._rayLength);
-                
+            if (this._leftLaserPointer && this._leftLaserPointer.isVisible && (<any>this.currentVRCamera).leftController) {
+                ray = (<any>this.currentVRCamera).leftController.getForwardRay(this._rayLength);
+            }
+            else if (this._rightLaserPointer && this._rightLaserPointer.isVisible && (<any>this.currentVRCamera).rightController) {
+                ray = (<any>this.currentVRCamera).rightController.getForwardRay(this._rayLength);
             } else {
             } else {
-                if (this._leftLaserPointer && this._leftLaserPointer.isVisible) {
-                    ray = (<any>this.currentVRCamera).leftController.getForwardRay(this._rayLength);
-                }
-                else {
-                    ray = (<any>this.currentVRCamera).rightController.getForwardRay(this._rayLength);
-                }
+                ray = this.currentVRCamera.getForwardRay(this._rayLength);
             }
             }
-        
+
             var hit = this._scene.pickWithRay(ray, this._raySelectionPredicate);
             var hit = this._scene.pickWithRay(ray, this._raySelectionPredicate);
 
 
             // Moving the gazeTracker on the mesh face targetted
             // Moving the gazeTracker on the mesh face targetted
@@ -1070,14 +1162,14 @@ module BABYLON {
                     var pickNormal = hit.getNormal();
                     var pickNormal = hit.getNormal();
                     // To avoid z-fighting
                     // To avoid z-fighting
                     let deltaFighting = 0.002;
                     let deltaFighting = 0.002;
-                    
+
                     if (pickNormal) {
                     if (pickNormal) {
                         var axis1 = BABYLON.Vector3.Cross(BABYLON.Axis.Y, pickNormal);
                         var axis1 = BABYLON.Vector3.Cross(BABYLON.Axis.Y, pickNormal);
                         var axis2 = BABYLON.Vector3.Cross(pickNormal, axis1);
                         var axis2 = BABYLON.Vector3.Cross(pickNormal, axis1);
                         BABYLON.Vector3.RotationFromAxisToRef(axis2, pickNormal, axis1, this._gazeTracker.rotation);
                         BABYLON.Vector3.RotationFromAxisToRef(axis2, pickNormal, axis1, this._gazeTracker.rotation);
                     }
                     }
                     this._gazeTracker.position.copyFrom(hit.pickedPoint);
                     this._gazeTracker.position.copyFrom(hit.pickedPoint);
-            
+
                     if (this._gazeTracker.position.x < 0) {
                     if (this._gazeTracker.position.x < 0) {
                         this._gazeTracker.position.x += deltaFighting;
                         this._gazeTracker.position.x += deltaFighting;
                     }
                     }
@@ -1111,7 +1203,7 @@ module BABYLON {
             else {
             else {
                 this._gazeTracker.isVisible = false;
                 this._gazeTracker.isVisible = false;
             }
             }
-        
+
             if (hit && hit.pickedMesh) {
             if (hit && hit.pickedMesh) {
                 this._currentHit = hit;
                 this._currentHit = hit;
                 if (this._pointerDownOnMeshAsked) {
                 if (this._pointerDownOnMeshAsked) {
@@ -1130,13 +1222,13 @@ module BABYLON {
                     if (this.meshSelectionPredicate(hit.pickedMesh)) {
                     if (this.meshSelectionPredicate(hit.pickedMesh)) {
                         this._currentMeshSelected = hit.pickedMesh;
                         this._currentMeshSelected = hit.pickedMesh;
                         if (hit.pickedMesh.isPickable && hit.pickedMesh.actionManager) {
                         if (hit.pickedMesh.isPickable && hit.pickedMesh.actionManager) {
-                            this.changeGazeColor(new BABYLON.Color3(0,0,1));
-                            this.changeLaserColor(new BABYLON.Color3(0.2,0.2,1));
+                            this.changeGazeColor(new BABYLON.Color3(0, 0, 1));
+                            this.changeLaserColor(new BABYLON.Color3(0.2, 0.2, 1));
                             this._isActionableMesh = true;
                             this._isActionableMesh = true;
                         }
                         }
                         else {
                         else {
-                            this.changeGazeColor(new BABYLON.Color3(0.7,0.7,0.7));
-                            this.changeLaserColor(new BABYLON.Color3(0.7,0.7,0.7));
+                            this.changeGazeColor(new BABYLON.Color3(0.7, 0.7, 0.7));
+                            this.changeLaserColor(new BABYLON.Color3(0.7, 0.7, 0.7));
                             this._isActionableMesh = false;
                             this._isActionableMesh = false;
                         }
                         }
                         try {
                         try {
@@ -1148,8 +1240,8 @@ module BABYLON {
                     }
                     }
                     else {
                     else {
                         this._currentMeshSelected = null;
                         this._currentMeshSelected = null;
-                        this.changeGazeColor(new BABYLON.Color3(0.7,0.7,0.7));
-                        this.changeLaserColor(new BABYLON.Color3(0.7,0.7,0.7));
+                        this.changeGazeColor(new BABYLON.Color3(0.7, 0.7, 0.7));
+                        this.changeLaserColor(new BABYLON.Color3(0.7, 0.7, 0.7));
                     }
                     }
                 }
                 }
             }
             }
@@ -1158,8 +1250,8 @@ module BABYLON {
                 this._currentMeshSelected = null;
                 this._currentMeshSelected = null;
                 this._teleportationAllowed = false;
                 this._teleportationAllowed = false;
                 this._hideTeleportationCircle();
                 this._hideTeleportationCircle();
-                this.changeGazeColor(new BABYLON.Color3(0.7,0.7,0.7));
-                this.changeLaserColor(new BABYLON.Color3(0.7,0.7,0.7));
+                this.changeGazeColor(new BABYLON.Color3(0.7, 0.7, 0.7));
+                this.changeLaserColor(new BABYLON.Color3(0.7, 0.7, 0.7));
             }
             }
         }
         }
 
 
@@ -1182,7 +1274,10 @@ module BABYLON {
             if (this.isInVRMode()) {
             if (this.isInVRMode()) {
                 this.exitVR();
                 this.exitVR();
             }
             }
-            this._deviceOrientationCamera.dispose();
+
+            if (this._deviceOrientationCamera) {
+                this._deviceOrientationCamera.dispose();
+            }
 
 
             if (this._passProcessMove) {
             if (this._passProcessMove) {
                 this._passProcessMove.dispose();
                 this._passProcessMove.dispose();
@@ -1202,6 +1297,8 @@ module BABYLON {
                 document.body.removeChild(this._btnVR);
                 document.body.removeChild(this._btnVR);
             }
             }
 
 
+            this._floorMeshesCollection = [];
+
             document.removeEventListener("keydown", this._onKeyDown);
             document.removeEventListener("keydown", this._onKeyDown);
             window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
             window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
         }
         }
@@ -1210,4 +1307,4 @@ module BABYLON {
             return "VRExperienceHelper";
             return "VRExperienceHelper";
         }
         }
     }
     }
-}
+}

+ 11 - 0
src/Cameras/VR/babylon.webVRCamera.ts

@@ -351,6 +351,17 @@ module BABYLON {
                     if (webVrController.defaultModel) {
                     if (webVrController.defaultModel) {
                         webVrController.defaultModel.setEnabled(false);
                         webVrController.defaultModel.setEnabled(false);
                     }
                     }
+
+                    if(webVrController.hand === "right"){
+                        this._rightController = null;
+                    }
+                    if(webVrController.hand === "left"){
+                        this._rightController = null;
+                    }
+                    const controllerIndex = this.controllers.indexOf(webVrController);
+                    if (controllerIndex !== -1) {
+                        this.controllers.splice(controllerIndex, 1);
+                    }
                 }
                 }
             });
             });
 
 

+ 1 - 1
src/babylon.scene.ts

@@ -4475,7 +4475,7 @@
             return null;
             return null;
         }
         }
 
 
-        public createDefaultVRExperience(webVROptions: WebVROptions = {}): VRExperienceHelper {
+        public createDefaultVRExperience(webVROptions: VRExperienceHelperOptions = {}): VRExperienceHelper {
             return new BABYLON.VRExperienceHelper(this, webVROptions);
             return new BABYLON.VRExperienceHelper(this, webVROptions);
         }
         }