Bläddra i källkod

Reworked the gamepad engine

David Catuhe 8 år sedan
förälder
incheckning
e3f0dc06fb

+ 3 - 2
Tools/Gulp/config.json

@@ -528,8 +528,9 @@
             "files": [
                 "../../src/Cameras/Inputs/babylon.freeCameraGamepadInput.js",
                 "../../src/Cameras/Inputs/babylon.arcRotateCameraGamepadInput.js",
-                "../../src/Tools/babylon.gamepads.js",
-                "../../src/Tools/babylon.extendedGamepad.js"
+                "../../src/Tools/Gamepad/babylon.gamepadManager.js",
+                "../../src/Tools/Gamepad/babylon.gamepads.js",
+                "../../src/Tools/Gamepad/babylon.extendedGamepad.js"
             ],
             "dependUpon" : [
                 "core"

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4420 - 4416
dist/preview release/babylon.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 34 - 34
dist/preview release/babylon.js


+ 163 - 135
dist/preview release/babylon.max.js

@@ -16528,6 +16528,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Scene.prototype, "gamepadManager", {
+            get: function () {
+                if (!this._gamepadManager) {
+                    this._gamepadManager = new BABYLON.GamepadManager();
+                }
+                return this._gamepadManager;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Scene.prototype, "unTranslatedPointer", {
             get: function () {
                 return new BABYLON.Vector2(this._unTranslatedPointerX, this._unTranslatedPointerY);
@@ -18827,6 +18837,10 @@ var BABYLON;
             if (this._depthRenderer) {
                 this._depthRenderer.dispose();
             }
+            if (this._gamepadManager) {
+                this._gamepadManager.dispose();
+                this._gamepadManager = null;
+            }
             // Smart arrays
             if (this.activeCamera) {
                 this.activeCamera._activeMeshes.dispose();
@@ -50120,12 +50134,25 @@ var BABYLON;
         }
         FreeCameraGamepadInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
-            this._gamepads = new BABYLON.Gamepads(function (gamepad) { _this._onNewGameConnected(gamepad); });
+            var manager = this.camera.getScene().gamepadManager;
+            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add(function (gamepad) {
+                if (gamepad.type !== BABYLON.Gamepad.POSE_ENABLED) {
+                    // prioritize XBOX gamepads.
+                    if (!_this.gamepad || gamepad.type === BABYLON.Gamepad.XBOX) {
+                        _this.gamepad = gamepad;
+                    }
+                }
+            });
+            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add(function (gamepad) {
+                if (_this.gamepad === gamepad) {
+                    _this.gamepad = null;
+                }
+            });
+            this.gamepad = manager.getGamepadByType(BABYLON.Gamepad.XBOX);
         };
         FreeCameraGamepadInput.prototype.detachControl = function (element) {
-            if (this._gamepads) {
-                this._gamepads.dispose();
-            }
+            this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
+            this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
             this.gamepad = null;
         };
         FreeCameraGamepadInput.prototype.checkInputs = function () {
@@ -50160,15 +50187,6 @@ var BABYLON;
                 camera.cameraRotation.addInPlace(this._vector2);
             }
         };
-        FreeCameraGamepadInput.prototype._onNewGameConnected = function (gamepad) {
-            // Only the first gamepad found can control the camera
-            if (gamepad.type !== BABYLON.Gamepad.POSE_ENABLED) {
-                // prioritize XBOX gamepads.
-                if (!this.gamepad || gamepad.type === BABYLON.Gamepad.XBOX) {
-                    this.gamepad = gamepad;
-                }
-            }
-        };
         FreeCameraGamepadInput.prototype.getClassName = function () {
             return "FreeCameraGamepadInput";
         };
@@ -50199,12 +50217,25 @@ var BABYLON;
         }
         ArcRotateCameraGamepadInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
-            this._gamepads = new BABYLON.Gamepads(function (gamepad) { _this._onNewGameConnected(gamepad); });
+            var manager = this.camera.getScene().gamepadManager;
+            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add(function (gamepad) {
+                if (gamepad.type !== BABYLON.Gamepad.POSE_ENABLED) {
+                    // prioritize XBOX gamepads.
+                    if (!_this.gamepad || gamepad.type === BABYLON.Gamepad.XBOX) {
+                        _this.gamepad = gamepad;
+                    }
+                }
+            });
+            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add(function (gamepad) {
+                if (_this.gamepad === gamepad) {
+                    _this.gamepad = null;
+                }
+            });
+            this.gamepad = manager.getGamepadByType(BABYLON.Gamepad.XBOX);
         };
         ArcRotateCameraGamepadInput.prototype.detachControl = function (element) {
-            if (this._gamepads) {
-                this._gamepads.dispose();
-            }
+            this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
+            this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
             this.gamepad = null;
         };
         ArcRotateCameraGamepadInput.prototype.checkInputs = function () {
@@ -50234,14 +50265,6 @@ var BABYLON;
                 }
             }
         };
-        ArcRotateCameraGamepadInput.prototype._onNewGameConnected = function (gamepad) {
-            if (gamepad.type !== BABYLON.Gamepad.POSE_ENABLED) {
-                // prioritize XBOX gamepads.
-                if (!this.gamepad || gamepad.type === BABYLON.Gamepad.XBOX) {
-                    this.gamepad = gamepad;
-                }
-            }
-        };
         ArcRotateCameraGamepadInput.prototype.getClassName = function () {
             return "ArcRotateCameraGamepadInput";
         };
@@ -50262,115 +50285,118 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.arcRotateCameraGamepadInput.js.map
 
-
 var BABYLON;
 (function (BABYLON) {
-    var Gamepads = (function () {
-        function Gamepads(ongamedpadconnected, ongamedpaddisconnected) {
+    var GamepadManager = (function () {
+        function GamepadManager() {
             var _this = this;
-            this.babylonGamepads = [];
-            this.oneGamepadConnected = false;
-            this.isMonitoring = false;
-            this.gamepadEventSupported = 'GamepadEvent' in window;
-            this.gamepadSupport = (navigator.getGamepads ||
+            this._babylonGamepads = [];
+            this._oneGamepadConnected = false;
+            this._isMonitoring = false;
+            this._gamepadEventSupported = 'GamepadEvent' in window;
+            this._gamepadSupport = (navigator.getGamepads ||
                 navigator.webkitGetGamepads || navigator.msGetGamepads || navigator.webkitGamepads);
-            this._callbackGamepadConnected = ongamedpadconnected;
-            this._callbackGamepadDisconnected = ongamedpaddisconnected;
-            if (this.gamepadSupport) {
+            this.onGamepadConnectedObservable = new BABYLON.Observable();
+            this.onGamepadDisconnectedObservable = new BABYLON.Observable();
+            this._onGamepadConnectedEvent = function (evt) {
+                var gamepad = evt.gamepad;
+                // Protection code for Chrome which has a very buggy gamepad implementation...
+                // And raises a connected event on disconnection for instance
+                if (gamepad.index in _this._babylonGamepads) {
+                    return;
+                }
+                var newGamepad = _this._addNewGamepad(gamepad);
+                _this.onGamepadConnectedObservable.notifyObservers(newGamepad);
+                _this._startMonitoringGamepads();
+            };
+            this._onGamepadDisconnectedEvent = function (evt) {
+                var gamepad = evt.gamepad;
+                // Remove the gamepad from the list of gamepads to monitor.
+                for (var i in _this._babylonGamepads) {
+                    if (_this._babylonGamepads[i].index === gamepad.index) {
+                        var gamepadToRemove = _this._babylonGamepads[i];
+                        _this._babylonGamepads[i] = null;
+                        _this.onGamepadDisconnectedObservable.notifyObservers(gamepadToRemove);
+                        break;
+                    }
+                }
+            };
+            if (this._gamepadSupport) {
                 //first add already-connected gamepads
                 this._updateGamepadObjects();
-                if (this.babylonGamepads.length) {
+                if (this._babylonGamepads.length) {
                     this._startMonitoringGamepads();
                 }
                 // Checking if the gamepad connected event is supported (like in Firefox)
-                if (this.gamepadEventSupported) {
-                    this._onGamepadConnectedEvent = function (evt) {
-                        _this._onGamepadConnected(evt.gamepad);
-                    };
-                    this._onGamepadDisonnectedEvent = function (evt) {
-                        _this._onGamepadDisconnected(evt.gamepad);
-                    };
+                if (this._gamepadEventSupported) {
                     window.addEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
-                    window.addEventListener('gamepaddisconnected', this._onGamepadDisonnectedEvent, false);
+                    window.addEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent, false);
                 }
                 else {
                     this._startMonitoringGamepads();
                 }
             }
         }
-        Gamepads.prototype.dispose = function () {
-            if (this._onGamepadConnectedEvent) {
-                window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
-                window.removeEventListener('gamepaddisconnected', this._onGamepadDisonnectedEvent, false);
-                this._onGamepadConnectedEvent = null;
-                this._onGamepadDisonnectedEvent = null;
+        GamepadManager.prototype.getGamepadByType = function (type) {
+            if (type === void 0) { type = BABYLON.Gamepad.XBOX; }
+            for (var _i = 0, _a = this._babylonGamepads; _i < _a.length; _i++) {
+                var gamepad = _a[_i];
+                if (gamepad && gamepad.type === type) {
+                    return gamepad;
+                }
             }
-            this.oneGamepadConnected = false;
-            this._stopMonitoringGamepads();
-            this.babylonGamepads = [];
+            return null;
         };
-        Gamepads.prototype._onGamepadConnected = function (gamepad) {
-            // Protection code for Chrome which has a very buggy gamepad implementation...
-            // And raises a connected event on disconnection for instance
-            if (gamepad.index in this.babylonGamepads) {
-                return;
+        GamepadManager.prototype.dispose = function () {
+            if (this._gamepadEventSupported) {
+                window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent);
+                window.removeEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent);
+                this._onGamepadConnectedEvent = null;
+                this._onGamepadDisconnectedEvent = null;
             }
-            var newGamepad = this._addNewGamepad(gamepad);
-            if (this._callbackGamepadConnected)
-                this._callbackGamepadConnected(newGamepad);
-            this._startMonitoringGamepads();
+            this._oneGamepadConnected = false;
+            this._stopMonitoringGamepads();
+            this._babylonGamepads = [];
         };
-        Gamepads.prototype._addNewGamepad = function (gamepad) {
-            if (!this.oneGamepadConnected) {
-                this.oneGamepadConnected = true;
+        GamepadManager.prototype._addNewGamepad = function (gamepad) {
+            if (!this._oneGamepadConnected) {
+                this._oneGamepadConnected = true;
             }
             var newGamepad;
             var xboxOne = (gamepad.id.search("Xbox One") !== -1);
             if (xboxOne || gamepad.id.search("Xbox 360") !== -1 || gamepad.id.search("xinput") !== -1) {
-                newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
+                newGamepad = new BABYLON.Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
             }
             else if (gamepad.pose) {
                 newGamepad = BABYLON.PoseEnabledControllerHelper.InitiateController(gamepad);
             }
             else {
-                newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
+                newGamepad = new BABYLON.GenericPad(gamepad.id, gamepad.index, gamepad);
             }
-            this.babylonGamepads[newGamepad.index] = newGamepad;
+            this._babylonGamepads[newGamepad.index] = newGamepad;
             return newGamepad;
         };
-        Gamepads.prototype._onGamepadDisconnected = function (gamepad) {
-            // Remove the gamepad from the list of gamepads to monitor.
-            for (var i in this.babylonGamepads) {
-                if (this.babylonGamepads[i].index == gamepad.index) {
-                    this.babylonGamepads.splice(+i, 1);
-                    break;
-                }
-            }
-            // If no gamepads are left, stop the polling loop.
-            if (this.babylonGamepads.length == 0) {
-                this._stopMonitoringGamepads();
-                this.oneGamepadConnected = false;
-            }
-            if (this._callbackGamepadDisconnected)
-                this._callbackGamepadDisconnected(gamepad);
-        };
-        Gamepads.prototype._startMonitoringGamepads = function () {
-            if (!this.isMonitoring) {
-                this.isMonitoring = true;
+        GamepadManager.prototype._startMonitoringGamepads = function () {
+            if (!this._isMonitoring) {
+                this._isMonitoring = true;
                 this._checkGamepadsStatus();
             }
         };
-        Gamepads.prototype._stopMonitoringGamepads = function () {
-            this.isMonitoring = false;
+        GamepadManager.prototype._stopMonitoringGamepads = function () {
+            this._isMonitoring = false;
         };
-        Gamepads.prototype._checkGamepadsStatus = function () {
+        GamepadManager.prototype._checkGamepadsStatus = function () {
             var _this = this;
             // Hack to be compatible Chrome
             this._updateGamepadObjects();
-            for (var i in this.babylonGamepads) {
-                this.babylonGamepads[i].update();
+            for (var i in this._babylonGamepads) {
+                var gamepad = this._babylonGamepads[i];
+                if (!gamepad) {
+                    continue;
+                }
+                gamepad.update();
             }
-            if (this.isMonitoring) {
+            if (this._isMonitoring) {
                 if (window.requestAnimationFrame) {
                     window.requestAnimationFrame(function () { _this._checkGamepadsStatus(); });
                 }
@@ -50384,26 +50410,31 @@ var BABYLON;
         };
         // This function is called only on Chrome, which does not properly support
         // connection/disconnection events and forces you to recopy again the gamepad object
-        Gamepads.prototype._updateGamepadObjects = function () {
+        GamepadManager.prototype._updateGamepadObjects = function () {
             var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
             for (var i = 0; i < gamepads.length; i++) {
                 if (gamepads[i]) {
-                    if (!(gamepads[i].index in this.babylonGamepads)) {
+                    if (!this._babylonGamepads[gamepads[i].index]) {
                         var newGamepad = this._addNewGamepad(gamepads[i]);
-                        if (this._callbackGamepadConnected) {
-                            this._callbackGamepadConnected(newGamepad);
-                        }
+                        this.onGamepadConnectedObservable.notifyObservers(newGamepad);
                     }
                     else {
                         // Forced to copy again this object for Chrome for unknown reason
-                        this.babylonGamepads[i].browserGamepad = gamepads[i];
+                        this._babylonGamepads[i].browserGamepad = gamepads[i];
                     }
                 }
             }
         };
-        return Gamepads;
+        return GamepadManager;
     }());
-    BABYLON.Gamepads = Gamepads;
+    BABYLON.GamepadManager = GamepadManager;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.gamepadManager.js.map
+
+
+var BABYLON;
+(function (BABYLON) {
     var StickValues = (function () {
         function StickValues(x, y) {
             this.x = x;
@@ -50912,7 +50943,10 @@ var BABYLON;
                 this._mesh.parent = this._poseControlledCamera;
             }
         };
-        PoseEnabledController.prototype.detachMesh = function () {
+        PoseEnabledController.prototype.dispose = function () {
+            if (this._mesh) {
+                this._mesh.dispose();
+            }
             this._mesh = undefined;
         };
         Object.defineProperty(PoseEnabledController.prototype, "mesh", {
@@ -65080,6 +65114,8 @@ var BABYLON;
             _this.deviceScaleFactor = 1;
             _this.controllers = [];
             _this.nonVRControllers = [];
+            _this.onControllersAttachedObservable = new BABYLON.Observable();
+            _this.onNonVRControllersAttachedObservable = new BABYLON.Observable();
             _this.rigParenting = true; // should the rig cameras be used as parent instead of this camera.
             //legacy support - the compensation boolean was removed.
             if (arguments.length === 5) {
@@ -65122,8 +65158,6 @@ var BABYLON;
                     _this.getEngine().enableVR(_this._vrDevice);
                 }
             });
-            // try to attach the controllers, if found.
-            _this.initControllers();
             /**
              * The idea behind the following lines:
              * objects that have the camera as parent should actually have the rig cameras as a parent.
@@ -65157,27 +65191,6 @@ var BABYLON;
             });
             return _this;
         }
-        Object.defineProperty(WebVRFreeCamera.prototype, "onControllersAttached", {
-            set: function (callback) {
-                this._onControllersAttached = callback;
-                // after setting - if the controllers are already set, execute the callback.
-                if (this.controllers.length >= 2) {
-                    callback(this.controllers);
-                }
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(WebVRFreeCamera.prototype, "onNonVRControllerAttached", {
-            set: function (callback) {
-                this._onNonVRControllerAttached = callback;
-                this.nonVRControllers.forEach(function (controller) {
-                    callback(controller);
-                });
-            },
-            enumerable: true,
-            configurable: true
-        });
         WebVRFreeCamera.prototype.getControllerByName = function (name) {
             for (var _i = 0, _a = this.controllers; _i < _a.length; _i++) {
                 var gp = _a[_i];
@@ -65267,8 +65280,12 @@ var BABYLON;
             if (this._vrEnabled) {
                 this.getEngine().enableVR(this._vrDevice);
             }
+            // try to attach the controllers, if found.
+            this.initControllers();
         };
         WebVRFreeCamera.prototype.detachControl = function (element) {
+            this.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
+            this.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
             _super.prototype.detachControl.call(this, element);
             this._vrEnabled = false;
             this._attached = false;
@@ -65371,9 +65388,22 @@ var BABYLON;
         WebVRFreeCamera.prototype.initControllers = function () {
             var _this = this;
             this.controllers = [];
-            new BABYLON.Gamepads(function (gp) {
-                if (gp.type === BABYLON.Gamepad.POSE_ENABLED) {
-                    var webVrController = gp;
+            var manager = this.getScene().gamepadManager;
+            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add(function (gamepad) {
+                if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
+                    var webVrController = gamepad;
+                    var index = _this.controllers.indexOf(webVrController);
+                    if (index === -1) {
+                        // we are good
+                        return;
+                    }
+                    _this.controllers.splice(index, 1);
+                    webVrController.dispose();
+                }
+            });
+            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add(function (gamepad) {
+                if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
+                    var webVrController = gamepad;
                     if (_this.webVROptions.controllerMeshes) {
                         webVrController.initControllerMesh(_this.getScene(), function (loadedMesh) {
                             if (_this.webVROptions.defaultLightningOnControllers) {
@@ -65392,7 +65422,7 @@ var BABYLON;
                         //add to the controllers array
                         _this.controllers.push(webVrController);
                         //did we find enough controllers? Great! let the developer know.
-                        if (_this._onControllersAttached && _this.controllers.length >= 2) {
+                        if (_this.controllers.length >= 2) {
                             // Forced to add some control code for Vive as it doesn't always fill properly the "hand" property
                             // Sometimes, both controllers are set correctly (left and right), sometimes none, sometimes only one of them...
                             // So we're overriding setting left & right manually to be sure
@@ -65408,15 +65438,13 @@ var BABYLON;
                                     }
                                 }
                             }
-                            _this._onControllersAttached(_this.controllers);
+                            _this.onControllersAttachedObservable.notifyObservers(_this.controllers);
                         }
                     }
                 }
                 else {
-                    _this.nonVRControllers.push(gp);
-                    if (_this._onNonVRControllerAttached) {
-                        _this._onNonVRControllerAttached(gp);
-                    }
+                    _this.nonVRControllers.push(gamepad);
+                    _this.onNonVRControllersAttachedObservable.notifyObservers(gamepad);
                 }
             });
         };

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4420 - 4416
dist/preview release/babylon.module.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 34 - 34
dist/preview release/babylon.worker.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 5869 - 5865
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 19 - 19
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 14 - 0
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -16528,6 +16528,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Scene.prototype, "gamepadManager", {
+            get: function () {
+                if (!this._gamepadManager) {
+                    this._gamepadManager = new BABYLON.GamepadManager();
+                }
+                return this._gamepadManager;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Scene.prototype, "unTranslatedPointer", {
             get: function () {
                 return new BABYLON.Vector2(this._unTranslatedPointerX, this._unTranslatedPointerY);
@@ -18827,6 +18837,10 @@ var BABYLON;
             if (this._depthRenderer) {
                 this._depthRenderer.dispose();
             }
+            if (this._gamepadManager) {
+                this._gamepadManager.dispose();
+                this._gamepadManager = null;
+            }
             // Smart arrays
             if (this.activeCamera) {
                 this.activeCamera._activeMeshes.dispose();

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 5869 - 5865
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 21 - 14
src/Cameras/Inputs/babylon.arcRotateCameraGamepadInput.ts

@@ -3,7 +3,8 @@ module BABYLON {
         camera: ArcRotateCamera;
 
         public gamepad: Gamepad;
-        private _gamepads: Gamepads<Gamepad>;
+        private _onGamepadConnectedObserver : Observer<Gamepad>;
+        private _onGamepadDisconnectedObserver : Observer<Gamepad>;
 
         @serialize()
         public gamepadRotationSensibility = 80;
@@ -12,13 +13,28 @@ module BABYLON {
         public gamepadMoveSensibility = 40;
 
         attachControl(element: HTMLElement, noPreventDefault?: boolean) {
-            this._gamepads = new Gamepads((gamepad: Gamepad) => { this._onNewGameConnected(gamepad); });
+            let manager = this.camera.getScene().gamepadManager;
+            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
+                if (gamepad.type !== Gamepad.POSE_ENABLED) {
+                    // prioritize XBOX gamepads.
+                    if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
+                        this.gamepad = gamepad;
+                    }
+                }
+            });  
+
+            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad)=> {
+                if (this.gamepad === gamepad) {
+                    this.gamepad = null;
+                }
+            });            
+            
+            this.gamepad = manager.getGamepadByType(Gamepad.XBOX);
         }
 
         detachControl(element: HTMLElement) {
-            if (this._gamepads) {
-                this._gamepads.dispose();
-            }
+            this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);            
+            this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
             this.gamepad = null;
         }
 
@@ -54,15 +70,6 @@ module BABYLON {
             }
         }
 
-        private _onNewGameConnected(gamepad: Gamepad) {
-            if (gamepad.type !== Gamepad.POSE_ENABLED) {
-                // prioritize XBOX gamepads.
-                if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
-                    this.gamepad = gamepad;
-                }
-            }
-        }
-
         getClassName(): string {
             return "ArcRotateCameraGamepadInput";
         }

+ 22 - 16
src/Cameras/Inputs/babylon.freeCameraGamepadInput.ts

@@ -2,8 +2,9 @@ module BABYLON {
     export class FreeCameraGamepadInput implements ICameraInput<FreeCamera> {
         camera: FreeCamera;
 
-        public gamepad: Gamepad;
-        private _gamepads: Gamepads<Gamepad>;
+        public gamepad: Gamepad;        
+        private _onGamepadConnectedObserver : Observer<Gamepad>;
+        private _onGamepadDisconnectedObserver : Observer<Gamepad>;
 
         @serialize()
         public gamepadAngularSensibility = 200;
@@ -18,13 +19,28 @@ module BABYLON {
         private _vector2: Vector2 = Vector2.Zero();
 
         attachControl(element: HTMLElement, noPreventDefault?: boolean) {
-            this._gamepads = new Gamepads((gamepad: Gamepad) => { this._onNewGameConnected(gamepad); });
+            let manager = this.camera.getScene().gamepadManager;
+            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
+                if (gamepad.type !== Gamepad.POSE_ENABLED) {
+                    // prioritize XBOX gamepads.
+                    if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
+                        this.gamepad = gamepad;
+                    }
+                }
+            });  
+
+            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad)=> {
+                if (this.gamepad === gamepad) {
+                    this.gamepad = null;
+                }
+            });
+            
+            this.gamepad = manager.getGamepadByType(Gamepad.XBOX);
         }
 
         detachControl(element: HTMLElement) {
-            if (this._gamepads) {
-                this._gamepads.dispose();
-            }
+            this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
+            this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
             this.gamepad = null;
         }
 
@@ -64,16 +80,6 @@ module BABYLON {
             }
         }
 
-        private _onNewGameConnected(gamepad: Gamepad) {
-            // Only the first gamepad found can control the camera
-            if (gamepad.type !== Gamepad.POSE_ENABLED) {
-                // prioritize XBOX gamepads.
-                if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
-                    this.gamepad = gamepad;
-                }
-            }
-        }
-
         getClassName(): string {
             return "FreeCameraGamepadInput";
         }

+ 38 - 31
src/Cameras/VR/babylon.webVRCamera.ts

@@ -61,8 +61,8 @@ module BABYLON {
 
         public controllers: Array<WebVRController> = [];
         public nonVRControllers: Array<Gamepad> = [];
-        private _onControllersAttached: (controllers: Array<WebVRController>) => void;
-        private _onNonVRControllerAttached: (controller: Gamepad) => void;
+        public onControllersAttachedObservable = new Observable<Array<WebVRController>>();
+        public onNonVRControllersAttachedObservable = new Observable<Gamepad>();
 
         public rigParenting: boolean = true; // should the rig cameras be used as parent instead of this camera.
 
@@ -122,9 +122,6 @@ module BABYLON {
                 }
             });                
 
-            // try to attach the controllers, if found.
-            this.initControllers();
-
             /**
              * The idea behind the following lines:
              * objects that have the camera as parent should actually have the rig cameras as a parent.
@@ -159,21 +156,6 @@ module BABYLON {
             });
         }
 
-        public set onControllersAttached(callback: (controllers: Array<WebVRController>) => void) {
-            this._onControllersAttached = callback;
-            // after setting - if the controllers are already set, execute the callback.
-            if (this.controllers.length >= 2) {
-                callback(this.controllers);
-            }
-        }
-
-        public set onNonVRControllerAttached(callback: (controller: Gamepad) => void) {
-            this._onNonVRControllerAttached = callback;
-            this.nonVRControllers.forEach((controller) => {
-                callback(controller)
-            });
-        }
-
         public getControllerByName(name: string): WebVRController {
             for (var gp of this.controllers) {
                 if (gp.hand === name) {
@@ -264,11 +246,17 @@ module BABYLON {
             noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
 
             if (this._vrEnabled) {
-                this.getEngine().enableVR(this._vrDevice)
+                this.getEngine().enableVR(this._vrDevice);
             }
+
+            // try to attach the controllers, if found.
+            this.initControllers();
         }
 
         public detachControl(element: HTMLElement): void {
+            this.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);            
+            this.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
+            
             super.detachControl(element);
             this._vrEnabled = false;
             this._attached = false;
@@ -388,12 +376,33 @@ module BABYLON {
 
             return this._projectionMatrix;
         }
+        
+        private _onGamepadConnectedObserver : Observer<Gamepad>;
+        private _onGamepadDisconnectedObserver : Observer<Gamepad>;
 
         public initControllers() {
             this.controllers = [];
-            new BABYLON.Gamepads((gp) => {
-                if (gp.type === BABYLON.Gamepad.POSE_ENABLED) {
-                    let webVrController: WebVRController = <WebVRController>gp;
+
+            let manager = this.getScene().gamepadManager;
+            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad) => {
+                if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
+                    let webVrController: WebVRController = <WebVRController>gamepad;
+                    let index = this.controllers.indexOf(webVrController);
+
+                    if (index === -1) {
+                        // we are good
+                        return;
+                    }
+
+                    this.controllers.splice(index, 1);
+                    
+                    webVrController.dispose();
+                }
+            });
+
+            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
+                if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
+                    let webVrController: WebVRController = <WebVRController>gamepad;
                     if (this.webVROptions.controllerMeshes) {
                         webVrController.initControllerMesh(this.getScene(), (loadedMesh) => {
                             if (this.webVROptions.defaultLightningOnControllers) {
@@ -414,13 +423,13 @@ module BABYLON {
                         this.controllers.push(webVrController);
 
                         //did we find enough controllers? Great! let the developer know.
-                        if (this._onControllersAttached && this.controllers.length >= 2) {
+                        if (this.controllers.length >= 2) {
                             // Forced to add some control code for Vive as it doesn't always fill properly the "hand" property
                             // Sometimes, both controllers are set correctly (left and right), sometimes none, sometimes only one of them...
                             // So we're overriding setting left & right manually to be sure
                             let firstViveWandDetected = false;
 
-                            for (let i=0; i<this.controllers.length; i++) {
+                            for (let i = 0; i < this.controllers.length; i++) {
                                 if (this.controllers[i].controllerType === PoseEnabledControllerType.VIVE) {
                                     if (!firstViveWandDetected) {
                                         firstViveWandDetected = true;
@@ -432,15 +441,13 @@ module BABYLON {
                                 }
                             }
 
-                            this._onControllersAttached(this.controllers);
+                            this.onControllersAttachedObservable.notifyObservers(this.controllers);
                         }
                     }
                 }
                 else {
-                    this.nonVRControllers.push(gp);
-                    if (this._onNonVRControllerAttached) {
-                        this._onNonVRControllerAttached(gp);
-                    }
+                    this.nonVRControllers.push(gamepad);
+                    this.onNonVRControllersAttachedObservable.notifyObservers(gamepad);
                 }
             });
         }

+ 5 - 2
src/Tools/babylon.extendedGamepad.ts

@@ -128,7 +128,10 @@ module BABYLON {
             }
         }
 
-        public detachMesh() {
+        public dispose() {
+            if (this._mesh) {
+                this._mesh.dispose();
+            }
             this._mesh = undefined;
         }
 
@@ -205,7 +208,7 @@ module BABYLON {
 
         protected abstract handleButtonChange(buttonIdx: number, value: ExtendedGamepadButton, changes: GamepadButtonChanges);
 
-        public abstract initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void)
+        public abstract initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void);
 
         private _setButtonValue(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton, buttonIndex: number) {
             if (!newState) {

+ 160 - 0
src/Tools/Gamepad/babylon.gamepadManager.ts

@@ -0,0 +1,160 @@
+module BABYLON {
+    export class GamepadManager {
+        private _babylonGamepads: Array<Gamepad> = [];
+        private _oneGamepadConnected: boolean = false;
+
+        private _isMonitoring: boolean = false;
+        private _gamepadEventSupported: boolean = 'GamepadEvent' in window;
+        private _gamepadSupport: () => Array<any> = (navigator.getGamepads ||
+            navigator.webkitGetGamepads || navigator.msGetGamepads || navigator.webkitGamepads);
+
+        public onGamepadConnectedObservable = new Observable<Gamepad>();
+        public onGamepadDisconnectedObservable = new Observable<Gamepad>();
+
+        private _onGamepadConnectedEvent: (evt) => void;
+        private _onGamepadDisconnectedEvent: (evt) => void;
+
+        constructor() {
+            this._onGamepadConnectedEvent = (evt) => {
+                let gamepad = evt.gamepad;
+
+                // Protection code for Chrome which has a very buggy gamepad implementation...
+                // And raises a connected event on disconnection for instance
+                if (gamepad.index in this._babylonGamepads) {
+                    return;
+                }
+
+                var newGamepad = this._addNewGamepad(gamepad);
+                this.onGamepadConnectedObservable.notifyObservers(newGamepad);
+                this._startMonitoringGamepads();                
+            };
+
+            this._onGamepadDisconnectedEvent  = (evt) => {
+                let gamepad = evt.gamepad;
+
+                // Remove the gamepad from the list of gamepads to monitor.
+                for (var i in this._babylonGamepads) {
+                    if (this._babylonGamepads[i].index === gamepad.index) {
+                        let gamepadToRemove = this._babylonGamepads[i];
+                        this._babylonGamepads[i] = null;
+                        
+                        this.onGamepadDisconnectedObservable.notifyObservers(gamepadToRemove);
+                        break;
+                    }
+                }            
+            };
+
+            if (this._gamepadSupport) {
+                //first add already-connected gamepads
+                this._updateGamepadObjects();
+                if (this._babylonGamepads.length) {
+                    this._startMonitoringGamepads();
+                }
+                // Checking if the gamepad connected event is supported (like in Firefox)
+                if (this._gamepadEventSupported) {
+                    window.addEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
+                    window.addEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent, false);
+                }
+                else {
+                    this._startMonitoringGamepads();
+                }
+            }
+        }
+
+        public getGamepadByType(type: number = Gamepad.XBOX) {
+            for (var gamepad of this._babylonGamepads) {
+                if (gamepad && gamepad.type === type) {
+                    return gamepad;
+                }
+            }
+
+            return null;
+        }
+
+        public dispose() {
+            if (this._gamepadEventSupported) {
+                window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent);
+                window.removeEventListener('gamepaddisconnected', this._onGamepadDisconnectedEvent);
+                this._onGamepadConnectedEvent = null;
+                this._onGamepadDisconnectedEvent = null;
+            }
+            this._oneGamepadConnected = false;
+            this._stopMonitoringGamepads();
+            this._babylonGamepads = [];
+        }
+
+        private _addNewGamepad(gamepad): Gamepad {
+            if (!this._oneGamepadConnected) {
+                this._oneGamepadConnected = true;
+            }
+
+            var newGamepad;
+            var xboxOne: boolean = ((<string>gamepad.id).search("Xbox One") !== -1);
+            if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
+                newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
+            }
+            // (<string>gamepad.id).search("Open VR") !== -1 || (<string>gamepad.id).search("Oculus Touch") !== -1
+            // if pose is supported, use the (WebVR) pose enabled controller
+            else if (gamepad.pose) {
+                newGamepad = PoseEnabledControllerHelper.InitiateController(gamepad);
+            }
+            else {
+                newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
+            }
+            this._babylonGamepads[newGamepad.index] = newGamepad;
+            return newGamepad;
+        }
+
+        private _startMonitoringGamepads() {
+            if (!this._isMonitoring) {
+                this._isMonitoring = true;
+                this._checkGamepadsStatus();
+            }
+        }
+
+        private _stopMonitoringGamepads() {
+            this._isMonitoring = false;
+        }
+
+        private _checkGamepadsStatus() {
+            // Hack to be compatible Chrome
+            this._updateGamepadObjects();
+
+            for (var i in this._babylonGamepads) {
+                let gamepad = this._babylonGamepads[i];
+                if (!gamepad) {
+                    continue;
+                }
+                gamepad.update();
+            }
+
+            if (this._isMonitoring) {
+                if (window.requestAnimationFrame) {
+                    window.requestAnimationFrame(() => { this._checkGamepadsStatus(); });
+                } else if (window.mozRequestAnimationFrame) {
+                    window.mozRequestAnimationFrame(() => { this._checkGamepadsStatus(); });
+                } else if (window.webkitRequestAnimationFrame) {
+                    window.webkitRequestAnimationFrame(() => { this._checkGamepadsStatus(); });
+                }
+            }
+        }
+
+        // This function is called only on Chrome, which does not properly support
+        // connection/disconnection events and forces you to recopy again the gamepad object
+        private _updateGamepadObjects() {
+            var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
+            for (var i = 0; i < gamepads.length; i++) {
+                if (gamepads[i]) {
+                    if (!this._babylonGamepads[gamepads[i].index]) {
+                        var newGamepad = this._addNewGamepad(gamepads[i]);
+                        this.onGamepadConnectedObservable.notifyObservers(newGamepad);
+                    }
+                    else {
+                        // Forced to copy again this object for Chrome for unknown reason
+                        this._babylonGamepads[i].browserGamepad = gamepads[i];
+                    }
+                }
+            }
+        }
+    }
+}

+ 0 - 162
src/Tools/babylon.gamepads.ts

@@ -1,159 +1,4 @@
 module BABYLON {
-    export class Gamepads<T extends Gamepad> {
-        private babylonGamepads: Array<T> = [];
-        private oneGamepadConnected: boolean = false;
-
-        private isMonitoring: boolean = false;
-        private gamepadEventSupported: boolean = 'GamepadEvent' in window;
-        private gamepadSupport: () => Array<any> = (navigator.getGamepads ||
-            navigator.webkitGetGamepads || navigator.msGetGamepads || navigator.webkitGamepads);
-
-        private _callbackGamepadConnected: (gamepad: T) => void;
-        private _callbackGamepadDisconnected: (gamepad: Gamepad) => void;
-
-        private _onGamepadConnectedEvent: (evt) => void;
-        private _onGamepadDisonnectedEvent: (evt) => void;
-
-        constructor(ongamedpadconnected: (gamepad: T) => void, ongamedpaddisconnected?: (gamepad: T) => void) {
-            this._callbackGamepadConnected = ongamedpadconnected;
-            this._callbackGamepadDisconnected = ongamedpaddisconnected;
-            if (this.gamepadSupport) {
-                //first add already-connected gamepads
-                this._updateGamepadObjects();
-                if (this.babylonGamepads.length) {
-                    this._startMonitoringGamepads();
-                }
-                // Checking if the gamepad connected event is supported (like in Firefox)
-                if (this.gamepadEventSupported) {
-                    this._onGamepadConnectedEvent = (evt) => {
-                        this._onGamepadConnected(evt.gamepad);
-                    };
-                    this._onGamepadDisonnectedEvent = (evt) => {
-                        this._onGamepadDisconnected(evt.gamepad);
-                    };
-                    window.addEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
-                    window.addEventListener('gamepaddisconnected', this._onGamepadDisonnectedEvent, false);
-                }
-                else {
-                    this._startMonitoringGamepads();
-                }
-            }
-        }
-
-        public dispose() {
-            if (this._onGamepadConnectedEvent) {
-                window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
-                window.removeEventListener('gamepaddisconnected', this._onGamepadDisonnectedEvent, false);
-                this._onGamepadConnectedEvent = null;
-                this._onGamepadDisonnectedEvent = null;
-            }
-            this.oneGamepadConnected = false;
-            this._stopMonitoringGamepads();
-            this.babylonGamepads = [];
-
-        }
-
-        private _onGamepadConnected(gamepad: Gamepad) {
-            // Protection code for Chrome which has a very buggy gamepad implementation...
-            // And raises a connected event on disconnection for instance
-            if(gamepad.index in this.babylonGamepads) {
-                return;
-            }
-
-            var newGamepad = this._addNewGamepad(gamepad);
-            if (this._callbackGamepadConnected) this._callbackGamepadConnected(newGamepad);
-            this._startMonitoringGamepads();
-        }
-
-        private _addNewGamepad(gamepad): T {
-            if (!this.oneGamepadConnected) {
-                this.oneGamepadConnected = true;
-            }
-
-            var newGamepad;
-            var xboxOne: boolean = ((<string>gamepad.id).search("Xbox One") !== -1);
-            if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
-                newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
-            }
-            // (<string>gamepad.id).search("Open VR") !== -1 || (<string>gamepad.id).search("Oculus Touch") !== -1
-            // if pose is supported, use the (WebVR) pose enabled controller
-            else if (gamepad.pose) {
-                newGamepad = PoseEnabledControllerHelper.InitiateController(gamepad);
-            }
-            else {
-                newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
-            }
-            this.babylonGamepads[newGamepad.index] = newGamepad;
-            return newGamepad;
-        }
-
-        private _onGamepadDisconnected(gamepad: Gamepad) {
-            // Remove the gamepad from the list of gamepads to monitor.
-            for (var i in this.babylonGamepads) {
-                if (this.babylonGamepads[i].index == gamepad.index) {
-                    this.babylonGamepads.splice(+i, 1);
-                    break;
-                }
-            }
-
-            // If no gamepads are left, stop the polling loop.
-            if (this.babylonGamepads.length == 0) {
-                this._stopMonitoringGamepads();
-                this.oneGamepadConnected = false;
-            }
-            if (this._callbackGamepadDisconnected) this._callbackGamepadDisconnected(gamepad);
-        }
-
-        private _startMonitoringGamepads() {
-            if (!this.isMonitoring) {
-                this.isMonitoring = true;
-                this._checkGamepadsStatus();
-            }
-        }
-
-        private _stopMonitoringGamepads() {
-            this.isMonitoring = false;
-        }
-
-        private _checkGamepadsStatus() {
-            // Hack to be compatible Chrome
-            this._updateGamepadObjects();
-
-            for (var i in this.babylonGamepads) {
-                this.babylonGamepads[i].update();
-            }
-
-            if (this.isMonitoring) {
-                if (window.requestAnimationFrame) {
-                    window.requestAnimationFrame(() => { this._checkGamepadsStatus(); });
-                } else if (window.mozRequestAnimationFrame) {
-                    window.mozRequestAnimationFrame(() => { this._checkGamepadsStatus(); });
-                } else if (window.webkitRequestAnimationFrame) {
-                    window.webkitRequestAnimationFrame(() => { this._checkGamepadsStatus(); });
-                }
-            }
-        }
-
-        // This function is called only on Chrome, which does not properly support
-        // connection/disconnection events and forces you to recopy again the gamepad object
-        private _updateGamepadObjects() {
-            var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
-            for (var i = 0; i < gamepads.length; i++) {
-                if (gamepads[i]) {
-                    if (!(gamepads[i].index in this.babylonGamepads)) {
-                        var newGamepad = this._addNewGamepad(gamepads[i]);
-                        if (this._callbackGamepadConnected) {
-                            this._callbackGamepadConnected(newGamepad);
-                        }
-                    }
-                    else {
-                        // Forced to copy again this object for Chrome for unknown reason
-                        this.babylonGamepads[i].browserGamepad = gamepads[i];
-                    }
-                }
-            }
-        }
-    }
     export class StickValues {
         constructor(public x, public y) {
         }
@@ -512,10 +357,3 @@
         }
     }
 }
-
-interface Navigator {
-    getGamepads(func?: any): any;
-    webkitGetGamepads(func?: any): any
-    msGetGamepads(func?: any): any;
-    webkitGamepads(func?: any): any;
-}

+ 7 - 0
src/babylon.mixins.ts

@@ -112,6 +112,13 @@ interface CanvasRenderingContext2D {
     msImageSmoothingEnabled: boolean;
 }
 
+interface Navigator {
+    getGamepads(func?: any): any;
+    webkitGetGamepads(func?: any): any
+    msGetGamepads(func?: any): any;
+    webkitGamepads(func?: any): any;
+}
+
 interface WebGLTexture {
     isReady: boolean;
     isCube: boolean;

+ 16 - 0
src/babylon.scene.ts

@@ -422,6 +422,17 @@
         /** Deprecated. Use onPointerObservable instead */
         public onPointerPick: (evt: PointerEvent, pickInfo: PickingInfo) => void;
 
+        // Gamepads
+        private _gamepadManager: GamepadManager;
+
+        public get gamepadManager(): GamepadManager {
+            if (!this._gamepadManager) {
+                this._gamepadManager = new GamepadManager();
+            }
+
+            return this._gamepadManager;
+        }
+
         /**
          * This observable event is triggered when any mouse event registered during Scene.attach() is called BEFORE the 3D engine to process anything (mesh/sprite picking for instance).
          * You have the possibility to skip the 3D Engine process and the call to onPointerObservable by setting PointerInfoBase.skipOnPointerObservable to true
@@ -3349,6 +3360,11 @@
                 this._depthRenderer.dispose();
             }
 
+            if (this._gamepadManager) {
+                this._gamepadManager.dispose();
+                this._gamepadManager = null;
+            }
+
             // Smart arrays
             if (this.activeCamera) {
                 this.activeCamera._activeMeshes.dispose();