Pārlūkot izejas kodu

Merge remote-tracking branch 'refs/remotes/BabylonJS/master'

DESKTOP-QJU4N0L\mityh 8 gadi atpakaļ
vecāks
revīzija
77d3bcb067

+ 1 - 1
.vscode/launch.json

@@ -104,7 +104,7 @@
             "runtimeArgs": [
                 "--enable-unsafe-es3-apis" 
             ]
-        },
+        },      
         {
             "name": "Launch Build Validation (Chrome)",
             "type": "chrome",

+ 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"

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1199 - 1185
dist/preview release/babylon.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 39 - 38
dist/preview release/babylon.js


+ 244 - 179
dist/preview release/babylon.max.js

@@ -10986,6 +10986,10 @@ var BABYLON;
         Engine.prototype.createQuery = function () {
             return this._gl.createQuery();
         };
+        Engine.prototype.deleteQuery = function (query) {
+            this.deleteQuery(query);
+            return this;
+        };
         Engine.prototype.isQueryResultAvailable = function (query) {
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE);
         };
@@ -10999,6 +11003,7 @@ var BABYLON;
         Engine.prototype.endQuery = function (algorithmType) {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
+            return this;
         };
         Engine.prototype.getGlAlgorithmType = function (algorithmType) {
             return algorithmType === BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
@@ -11818,14 +11823,12 @@ var BABYLON;
             // Properties
             _this.definedFacingForward = true; // orientation for POV movement & rotation
             _this.position = BABYLON.Vector3.Zero();
-            _this._webGLVersion = _this.getEngine().webGLVersion;
-            _this._occlusionInternalRetryCounter = 0;
+            _this.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
             _this.occlusionType = AbstractMesh.OCCLUSION_TYPE_NONE;
             _this.occlusionRetryCount = -1;
+            _this._occlusionInternalRetryCounter = 0;
             _this._isOccluded = false;
-            _this.occlusionQuery = _this.getEngine().createQuery();
-            _this.isOcclusionQueryInProgress = false;
-            _this.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
+            _this._isOcclusionQueryInProgress = false;
             _this._rotation = BABYLON.Vector3.Zero();
             _this._scaling = BABYLON.Vector3.One();
             _this.billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
@@ -12016,6 +12019,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AbstractMesh.prototype, "isOcclusionQueryInProgress", {
+            get: function () {
+                return this._isOcclusionQueryInProgress;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AbstractMesh.prototype, "material", {
             get: function () {
                 return this._material;
@@ -13417,8 +13427,14 @@ var BABYLON;
                     sceneOctree.dynamicContent.splice(index, 1);
                 }
             }
+            // Query
+            var engine = this.getScene().getEngine();
+            if (this._occlusionQuery) {
+                engine.deleteQuery(this._occlusionQuery);
+                this._occlusionQuery = null;
+            }
             // Engine
-            this.getScene().getEngine().wipeCaches();
+            engine.wipeCaches();
             // Remove from scene
             this.getScene().removeMesh(this);
             if (!doNotRecurse) {
@@ -13859,24 +13875,23 @@ var BABYLON;
             this.setVerticesData(BABYLON.VertexBuffer.NormalKind, normals, updatable);
         };
         AbstractMesh.prototype.checkOcclusionQuery = function () {
-            if (this._webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
+            var engine = this.getEngine();
+            if (engine.webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
                 this._isOccluded = false;
                 return;
             }
-            var engine = this.getEngine();
             if (this.isOcclusionQueryInProgress) {
-                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this.occlusionQuery);
+                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this._occlusionQuery);
                 if (isOcclusionQueryAvailable) {
-                    var occlusionQueryResult = engine.getQueryResult(this.occlusionQuery);
-                    this.isOcclusionQueryInProgress = false;
+                    var occlusionQueryResult = engine.getQueryResult(this._occlusionQuery);
+                    this._isOcclusionQueryInProgress = false;
                     this._occlusionInternalRetryCounter = 0;
                     this._isOccluded = occlusionQueryResult === 1 ? false : true;
                 }
                 else {
                     this._occlusionInternalRetryCounter++;
                     if (this.occlusionRetryCount !== -1 && this._occlusionInternalRetryCounter > this.occlusionRetryCount) {
-                        // break;
-                        this.isOcclusionQueryInProgress = false;
+                        this._isOcclusionQueryInProgress = false;
                         this._occlusionInternalRetryCounter = 0;
                         // if optimistic set isOccluded to false regardless of the status of isOccluded. (Render in the current render loop)
                         // if strict continue the last state of the object.
@@ -13889,10 +13904,13 @@ var BABYLON;
             }
             var scene = this.getScene();
             var occlusionBoundingBoxRenderer = scene.getBoundingBoxRenderer();
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this.occlusionQuery);
+            if (!this._occlusionQuery) {
+                this._occlusionQuery = engine.createQuery();
+            }
+            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
             engine.endQuery(this.occlusionQueryAlgorithmType);
-            this.isOcclusionQueryInProgress = true;
+            this._isOcclusionQueryInProgress = true;
         };
         // Statics
         AbstractMesh._BILLBOARDMODE_NONE = 0;
@@ -14605,7 +14623,7 @@ var BABYLON;
             _this.orthoBottom = null;
             _this.orthoTop = null;
             _this.fov = 0.8;
-            _this.minZ = 1.0;
+            _this.minZ = 0.1;
             _this.maxZ = 10000.0;
             _this.inertia = 0.9;
             _this.mode = Camera.PERSPECTIVE_CAMERA;
@@ -16510,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);
@@ -18809,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();
@@ -50102,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 () {
@@ -50142,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";
         };
@@ -50181,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 () {
@@ -50216,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";
         };
@@ -50244,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.push(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(); });
                 }
@@ -50366,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;
@@ -50894,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", {
@@ -65062,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) {
@@ -65104,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.
@@ -65139,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];
@@ -65249,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;
@@ -65333,9 +65368,12 @@ var BABYLON;
             if (this._cameraRigParams["specs"] === 1.0) {
                 var eyeParams = this._cameraRigParams["eyeParameters"];
                 // deprecated!!
-                BABYLON.Matrix.PerspectiveFovWebVRToRef(eyeParams.fieldOfView, 0.1, 1000, this._projectionMatrix, this.getScene().useRightHandedSystem);
+                BABYLON.Matrix.PerspectiveFovWebVRToRef(eyeParams.fieldOfView, this.minZ, this.maxZ, this._projectionMatrix, this.getScene().useRightHandedSystem);
             }
             else {
+                var parentCamera = this.parent;
+                parentCamera._vrDevice.depthNear = this.minZ;
+                parentCamera._vrDevice.depthFar = this.maxZ;
                 var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
                 BABYLON.Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
                 //babylon compatible matrix
@@ -65350,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) {
@@ -65371,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
@@ -65387,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);
                 }
             });
         };
@@ -69908,7 +69957,7 @@ var BABYLON;
         function FramingBehavior() {
             this._mode = FramingBehavior.FitFrustumSidesMode;
             this._radiusScale = 1.0;
-            this._positionY = 0;
+            this._positionScale = 0.5;
             this._defaultElevation = 0.3;
             this._elevationReturnTime = 1500;
             this._elevationReturnWaitTime = 1000;
@@ -69961,18 +70010,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(FramingBehavior.prototype, "positionY", {
+        Object.defineProperty(FramingBehavior.prototype, "positionScale", {
             /**
-             * Gets the Y offset of the target mesh from the camera's focus.
+             * Gets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
              */
             get: function () {
-                return this._positionY;
+                return this._positionScale;
             },
             /**
-             * Sets the Y offset of the target mesh from the camera's focus.
+             * Sets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
              */
-            set: function (positionY) {
-                this._positionY = positionY;
+            set: function (scale) {
+                this._positionScale = scale;
             },
             enumerable: true,
             configurable: true
@@ -70101,19 +70150,33 @@ var BABYLON;
          * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
          * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
          */
-        FramingBehavior.prototype.zoomOnMesh = function (mesh, radius, framingPositionY, focusOnOriginXZ) {
+        FramingBehavior.prototype.zoomOnMesh = function (mesh, focusOnOriginXZ) {
             if (focusOnOriginXZ === void 0) { focusOnOriginXZ = false; }
-            if (framingPositionY == null) {
-                framingPositionY = this._positionY;
-            }
             mesh.computeWorldMatrix(true);
+            var boundingBox = mesh.getBoundingInfo().boundingBox;
+            this.zoomOnBoundingInfo(boundingBox.minimumWorld, boundingBox.maximumWorld, focusOnOriginXZ);
+        };
+        /**
+         * Targets the given mesh and updates zoom level accordingly.
+         * @param mesh  The mesh to target.
+         * @param radius Optional. If a cached radius position already exists, overrides default.
+         * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
+         * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
+         */
+        FramingBehavior.prototype.zoomOnBoundingInfo = function (minimumWorld, maximumWorld, focusOnOriginXZ) {
+            if (focusOnOriginXZ === void 0) { focusOnOriginXZ = false; }
             var zoomTarget;
-            var center = mesh.getBoundingInfo().boundingSphere.centerWorld;
+            // Find target by interpolating from bottom of bounding box in world-space to top via framingPositionY
+            var bottom = minimumWorld.y;
+            var top = maximumWorld.y;
+            var zoomTargetY = bottom + (top - bottom) * this._positionScale;
+            var radiusWorld = maximumWorld.subtract(minimumWorld).scale(0.5);
             if (focusOnOriginXZ) {
-                zoomTarget = new BABYLON.Vector3(0, center.y, 0);
+                zoomTarget = new BABYLON.Vector3(0, zoomTargetY, 0);
             }
             else {
-                zoomTarget = center.clone();
+                var centerWorld = minimumWorld.add(radiusWorld);
+                zoomTarget = new BABYLON.Vector3(centerWorld.x, zoomTargetY, centerWorld.z);
             }
             if (!this._vectorTransition) {
                 this._vectorTransition = BABYLON.Animation.CreateAnimation("target", BABYLON.Animation.ANIMATIONTYPE_VECTOR3, 60, FramingBehavior.EasingFunction);
@@ -70121,16 +70184,17 @@ var BABYLON;
             this._betaIsAnimating = true;
             this._animatables.push(BABYLON.Animation.TransitionTo("target", zoomTarget, this._attachedCamera, this._attachedCamera.getScene(), 60, this._vectorTransition, this._framingTime));
             // sets the radius and lower radius bounds
-            if (radius == null) {
-                // Small delta ensures camera is not always at lower zoom limit.
-                var delta = 0.1;
-                if (this._mode === FramingBehavior.FitFrustumSidesMode) {
-                    var position = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
-                    this._attachedCamera.lowerRadiusLimit = mesh.getBoundingInfo().boundingSphere.radiusWorld + this._attachedCamera.minZ;
-                    radius = position;
-                }
-                else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
-                    radius = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
+            // Small delta ensures camera is not always at lower zoom limit.
+            var delta = 0.1;
+            var radius = 0;
+            if (this._mode === FramingBehavior.FitFrustumSidesMode) {
+                var position = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+                this._attachedCamera.lowerRadiusLimit = radiusWorld.length() + this._attachedCamera.minZ;
+                radius = position;
+            }
+            else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
+                radius = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+                if (this._attachedCamera.lowerRadiusLimit === null) {
                     this._attachedCamera.lowerRadiusLimit = this._attachedCamera.minZ;
                 }
             }
@@ -70147,8 +70211,9 @@ var BABYLON;
          * @return The minimum distance from the primary mesh's center point at which the camera must be kept in order
          *		 to fully enclose the mesh in the viewing frustum.
          */
-        FramingBehavior.prototype._calculateLowerRadiusFromModelBoundingSphere = function (mesh) {
-            var boxVectorGlobalDiagonal = mesh.getBoundingInfo().diagonalLength;
+        FramingBehavior.prototype._calculateLowerRadiusFromModelBoundingSphere = function (minimumWorld, maximumWorld) {
+            var size = maximumWorld.subtract(minimumWorld);
+            var boxVectorGlobalDiagonal = size.length();
             var frustumSlope = this._getFrustumSlope();
             // Formula for setting distance
             // (Good explanation: http://stackoverflow.com/questions/2866350/move-camera-to-fit-3d-scene)
@@ -70208,7 +70273,7 @@ var BABYLON;
             // Slope of the frustum left/right planes in view space, relative to the forward vector.
             // Provides the amount that one side (e.g. left) of the frustum gets wider for every unit
             // along the forward vector.
-            var frustumSlopeX = frustumSlopeY / aspectRatio;
+            var frustumSlopeX = frustumSlopeY * aspectRatio;
             return new BABYLON.Vector2(frustumSlopeX, frustumSlopeY);
         };
         /**

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1199 - 1185
dist/preview release/babylon.module.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 44 - 43
dist/preview release/babylon.worker.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1532 - 1518
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 23 - 23
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 91 - 43
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -10986,6 +10986,10 @@ var BABYLON;
         Engine.prototype.createQuery = function () {
             return this._gl.createQuery();
         };
+        Engine.prototype.deleteQuery = function (query) {
+            this.deleteQuery(query);
+            return this;
+        };
         Engine.prototype.isQueryResultAvailable = function (query) {
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE);
         };
@@ -10999,6 +11003,7 @@ var BABYLON;
         Engine.prototype.endQuery = function (algorithmType) {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
+            return this;
         };
         Engine.prototype.getGlAlgorithmType = function (algorithmType) {
             return algorithmType === BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
@@ -11818,14 +11823,12 @@ var BABYLON;
             // Properties
             _this.definedFacingForward = true; // orientation for POV movement & rotation
             _this.position = BABYLON.Vector3.Zero();
-            _this._webGLVersion = _this.getEngine().webGLVersion;
-            _this._occlusionInternalRetryCounter = 0;
+            _this.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
             _this.occlusionType = AbstractMesh.OCCLUSION_TYPE_NONE;
             _this.occlusionRetryCount = -1;
+            _this._occlusionInternalRetryCounter = 0;
             _this._isOccluded = false;
-            _this.occlusionQuery = _this.getEngine().createQuery();
-            _this.isOcclusionQueryInProgress = false;
-            _this.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
+            _this._isOcclusionQueryInProgress = false;
             _this._rotation = BABYLON.Vector3.Zero();
             _this._scaling = BABYLON.Vector3.One();
             _this.billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
@@ -12016,6 +12019,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AbstractMesh.prototype, "isOcclusionQueryInProgress", {
+            get: function () {
+                return this._isOcclusionQueryInProgress;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AbstractMesh.prototype, "material", {
             get: function () {
                 return this._material;
@@ -13417,8 +13427,14 @@ var BABYLON;
                     sceneOctree.dynamicContent.splice(index, 1);
                 }
             }
+            // Query
+            var engine = this.getScene().getEngine();
+            if (this._occlusionQuery) {
+                engine.deleteQuery(this._occlusionQuery);
+                this._occlusionQuery = null;
+            }
             // Engine
-            this.getScene().getEngine().wipeCaches();
+            engine.wipeCaches();
             // Remove from scene
             this.getScene().removeMesh(this);
             if (!doNotRecurse) {
@@ -13859,24 +13875,23 @@ var BABYLON;
             this.setVerticesData(BABYLON.VertexBuffer.NormalKind, normals, updatable);
         };
         AbstractMesh.prototype.checkOcclusionQuery = function () {
-            if (this._webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
+            var engine = this.getEngine();
+            if (engine.webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
                 this._isOccluded = false;
                 return;
             }
-            var engine = this.getEngine();
             if (this.isOcclusionQueryInProgress) {
-                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this.occlusionQuery);
+                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this._occlusionQuery);
                 if (isOcclusionQueryAvailable) {
-                    var occlusionQueryResult = engine.getQueryResult(this.occlusionQuery);
-                    this.isOcclusionQueryInProgress = false;
+                    var occlusionQueryResult = engine.getQueryResult(this._occlusionQuery);
+                    this._isOcclusionQueryInProgress = false;
                     this._occlusionInternalRetryCounter = 0;
                     this._isOccluded = occlusionQueryResult === 1 ? false : true;
                 }
                 else {
                     this._occlusionInternalRetryCounter++;
                     if (this.occlusionRetryCount !== -1 && this._occlusionInternalRetryCounter > this.occlusionRetryCount) {
-                        // break;
-                        this.isOcclusionQueryInProgress = false;
+                        this._isOcclusionQueryInProgress = false;
                         this._occlusionInternalRetryCounter = 0;
                         // if optimistic set isOccluded to false regardless of the status of isOccluded. (Render in the current render loop)
                         // if strict continue the last state of the object.
@@ -13889,10 +13904,13 @@ var BABYLON;
             }
             var scene = this.getScene();
             var occlusionBoundingBoxRenderer = scene.getBoundingBoxRenderer();
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this.occlusionQuery);
+            if (!this._occlusionQuery) {
+                this._occlusionQuery = engine.createQuery();
+            }
+            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
             engine.endQuery(this.occlusionQueryAlgorithmType);
-            this.isOcclusionQueryInProgress = true;
+            this._isOcclusionQueryInProgress = true;
         };
         // Statics
         AbstractMesh._BILLBOARDMODE_NONE = 0;
@@ -14605,7 +14623,7 @@ var BABYLON;
             _this.orthoBottom = null;
             _this.orthoTop = null;
             _this.fov = 0.8;
-            _this.minZ = 1.0;
+            _this.minZ = 0.1;
             _this.maxZ = 10000.0;
             _this.inertia = 0.9;
             _this.mode = Camera.PERSPECTIVE_CAMERA;
@@ -16510,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);
@@ -18809,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();
@@ -49881,7 +49913,7 @@ var BABYLON;
         function FramingBehavior() {
             this._mode = FramingBehavior.FitFrustumSidesMode;
             this._radiusScale = 1.0;
-            this._positionY = 0;
+            this._positionScale = 0.5;
             this._defaultElevation = 0.3;
             this._elevationReturnTime = 1500;
             this._elevationReturnWaitTime = 1000;
@@ -49934,18 +49966,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(FramingBehavior.prototype, "positionY", {
+        Object.defineProperty(FramingBehavior.prototype, "positionScale", {
             /**
-             * Gets the Y offset of the target mesh from the camera's focus.
+             * Gets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
              */
             get: function () {
-                return this._positionY;
+                return this._positionScale;
             },
             /**
-             * Sets the Y offset of the target mesh from the camera's focus.
+             * Sets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
              */
-            set: function (positionY) {
-                this._positionY = positionY;
+            set: function (scale) {
+                this._positionScale = scale;
             },
             enumerable: true,
             configurable: true
@@ -50074,19 +50106,33 @@ var BABYLON;
          * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
          * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
          */
-        FramingBehavior.prototype.zoomOnMesh = function (mesh, radius, framingPositionY, focusOnOriginXZ) {
+        FramingBehavior.prototype.zoomOnMesh = function (mesh, focusOnOriginXZ) {
             if (focusOnOriginXZ === void 0) { focusOnOriginXZ = false; }
-            if (framingPositionY == null) {
-                framingPositionY = this._positionY;
-            }
             mesh.computeWorldMatrix(true);
+            var boundingBox = mesh.getBoundingInfo().boundingBox;
+            this.zoomOnBoundingInfo(boundingBox.minimumWorld, boundingBox.maximumWorld, focusOnOriginXZ);
+        };
+        /**
+         * Targets the given mesh and updates zoom level accordingly.
+         * @param mesh  The mesh to target.
+         * @param radius Optional. If a cached radius position already exists, overrides default.
+         * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
+         * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
+         */
+        FramingBehavior.prototype.zoomOnBoundingInfo = function (minimumWorld, maximumWorld, focusOnOriginXZ) {
+            if (focusOnOriginXZ === void 0) { focusOnOriginXZ = false; }
             var zoomTarget;
-            var center = mesh.getBoundingInfo().boundingSphere.centerWorld;
+            // Find target by interpolating from bottom of bounding box in world-space to top via framingPositionY
+            var bottom = minimumWorld.y;
+            var top = maximumWorld.y;
+            var zoomTargetY = bottom + (top - bottom) * this._positionScale;
+            var radiusWorld = maximumWorld.subtract(minimumWorld).scale(0.5);
             if (focusOnOriginXZ) {
-                zoomTarget = new BABYLON.Vector3(0, center.y, 0);
+                zoomTarget = new BABYLON.Vector3(0, zoomTargetY, 0);
             }
             else {
-                zoomTarget = center.clone();
+                var centerWorld = minimumWorld.add(radiusWorld);
+                zoomTarget = new BABYLON.Vector3(centerWorld.x, zoomTargetY, centerWorld.z);
             }
             if (!this._vectorTransition) {
                 this._vectorTransition = BABYLON.Animation.CreateAnimation("target", BABYLON.Animation.ANIMATIONTYPE_VECTOR3, 60, FramingBehavior.EasingFunction);
@@ -50094,16 +50140,17 @@ var BABYLON;
             this._betaIsAnimating = true;
             this._animatables.push(BABYLON.Animation.TransitionTo("target", zoomTarget, this._attachedCamera, this._attachedCamera.getScene(), 60, this._vectorTransition, this._framingTime));
             // sets the radius and lower radius bounds
-            if (radius == null) {
-                // Small delta ensures camera is not always at lower zoom limit.
-                var delta = 0.1;
-                if (this._mode === FramingBehavior.FitFrustumSidesMode) {
-                    var position = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
-                    this._attachedCamera.lowerRadiusLimit = mesh.getBoundingInfo().boundingSphere.radiusWorld + this._attachedCamera.minZ;
-                    radius = position;
-                }
-                else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
-                    radius = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
+            // Small delta ensures camera is not always at lower zoom limit.
+            var delta = 0.1;
+            var radius = 0;
+            if (this._mode === FramingBehavior.FitFrustumSidesMode) {
+                var position = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+                this._attachedCamera.lowerRadiusLimit = radiusWorld.length() + this._attachedCamera.minZ;
+                radius = position;
+            }
+            else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
+                radius = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+                if (this._attachedCamera.lowerRadiusLimit === null) {
                     this._attachedCamera.lowerRadiusLimit = this._attachedCamera.minZ;
                 }
             }
@@ -50120,8 +50167,9 @@ var BABYLON;
          * @return The minimum distance from the primary mesh's center point at which the camera must be kept in order
          *		 to fully enclose the mesh in the viewing frustum.
          */
-        FramingBehavior.prototype._calculateLowerRadiusFromModelBoundingSphere = function (mesh) {
-            var boxVectorGlobalDiagonal = mesh.getBoundingInfo().diagonalLength;
+        FramingBehavior.prototype._calculateLowerRadiusFromModelBoundingSphere = function (minimumWorld, maximumWorld) {
+            var size = maximumWorld.subtract(minimumWorld);
+            var boxVectorGlobalDiagonal = size.length();
             var frustumSlope = this._getFrustumSlope();
             // Formula for setting distance
             // (Good explanation: http://stackoverflow.com/questions/2866350/move-camera-to-fit-3d-scene)
@@ -50181,7 +50229,7 @@ var BABYLON;
             // Slope of the frustum left/right planes in view space, relative to the forward vector.
             // Provides the amount that one side (e.g. left) of the frustum gets wider for every unit
             // along the forward vector.
-            var frustumSlopeX = frustumSlopeY / aspectRatio;
+            var frustumSlopeX = frustumSlopeY * aspectRatio;
             return new BABYLON.Vector2(frustumSlopeX, frustumSlopeY);
         };
         /**

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1532 - 1518
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 22 - 22
sandbox/index.js

@@ -66,32 +66,32 @@
         // Attach camera to canvas inputs
         if (!currentScene.activeCamera || currentScene.lights.length === 0) {     
             currentScene.createDefaultCameraOrLight(true);
-        }
-        currentScene.activeCamera.attachControl(canvas);
-
-        // Enable camera's behaviors
-        currentScene.activeCamera.useBouncingBehavior = true;
-        currentScene.activeCamera.useAutoRotationBehavior  = true;
-        currentScene.activeCamera.useFramingBehavior = true;
-
-        var framingBehavior = currentScene.activeCamera.getBehaviorByName("Framing");
-        framingBehavior.framingTime = 0;
-
-        var bouncingBehavior = currentScene.activeCamera.getBehaviorByName("Bouncing");
-        bouncingBehavior.autoTransitionRange = true;        
-
-        if (currentScene.meshes.length) {
-            // Let's zoom on the first object with geometry
-            for (var index = 0; index < currentScene.meshes.length; index++) {
-                var mesh = currentScene.meshes[index];
-
-                if (mesh.getTotalVertices()) {
-                    currentScene.activeCamera.setTarget(mesh);
-                    break;
+            // Enable camera's behaviors
+            currentScene.activeCamera.useBouncingBehavior = true;
+            currentScene.activeCamera.useAutoRotationBehavior  = true;
+            currentScene.activeCamera.useFramingBehavior = true;
+
+            var framingBehavior = currentScene.activeCamera.getBehaviorByName("Framing");
+            framingBehavior.framingTime = 0;
+
+            var bouncingBehavior = currentScene.activeCamera.getBehaviorByName("Bouncing");
+            bouncingBehavior.autoTransitionRange = true;                
+
+            if (currentScene.meshes.length) {
+                // Let's zoom on the first object with geometry
+                for (var index = 0; index < currentScene.meshes.length; index++) {
+                    var mesh = currentScene.meshes[index];
+    
+                    if (mesh.getTotalVertices()) {
+                        currentScene.activeCamera.setTarget(mesh);
+                        break;
+                    }
                 }
             }
         }
 
+        currentScene.activeCamera.attachControl(canvas); 
+
         // Environment
         if (currentPluginName === "gltf") {
             var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("Assets/environment.dds", currentScene);

+ 44 - 28
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -6,7 +6,7 @@ module BABYLON {
 
         private _mode = FramingBehavior.FitFrustumSidesMode;
         private _radiusScale = 1.0;
-        private _positionY = 0;
+        private _positionScale = 0.5;
         private _defaultElevation = 0.3;
         private _elevationReturnTime = 1500;
         private _elevationReturnWaitTime = 1000;
@@ -52,17 +52,17 @@ module BABYLON {
 		}
 
 		/**
-		 * Sets the Y offset of the target mesh from the camera's focus.
+		 * Sets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
 		 */
-		public set positionY(positionY: number) {
-			this._positionY = positionY;
+		public set positionScale(scale: number) {
+			this._positionScale = scale;
 		}
 
 		/**
-		 * Gets the Y offset of the target mesh from the camera's focus.
+		 * Gets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
 		 */
-		public get positionY(): number {
-			return this._positionY;
+		public get positionScale(): number {
+			return this._positionScale;
 		}
 
 		/**
@@ -204,20 +204,34 @@ module BABYLON {
 		 * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
 		 * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
 		 */
-		public zoomOnMesh(mesh: AbstractMesh, radius?: number, framingPositionY?: number, focusOnOriginXZ: boolean = false): void {
-			if (framingPositionY == null) {
-				framingPositionY = this._positionY;
-			}
-
+		public zoomOnMesh(mesh: AbstractMesh, focusOnOriginXZ: boolean = false): void {
 			mesh.computeWorldMatrix(true);
-			
+
+			let boundingBox = mesh.getBoundingInfo().boundingBox;
+			this.zoomOnBoundingInfo(boundingBox.minimumWorld, boundingBox.maximumWorld, focusOnOriginXZ);
+		}
+
+		/**
+		 * Targets the given mesh and updates zoom level accordingly.
+		 * @param mesh  The mesh to target.
+		 * @param radius Optional. If a cached radius position already exists, overrides default.
+		 * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
+		 * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
+		 */
+		public zoomOnBoundingInfo(minimumWorld: Vector3, maximumWorld: Vector3, focusOnOriginXZ: boolean = false): void {
 			let zoomTarget: BABYLON.Vector3;
-			let center = mesh.getBoundingInfo().boundingSphere.centerWorld;
+
+			// Find target by interpolating from bottom of bounding box in world-space to top via framingPositionY
+			let bottom = minimumWorld.y;
+			let top = maximumWorld.y;
+			let zoomTargetY = bottom + (top - bottom) * this._positionScale;
+			let radiusWorld = maximumWorld.subtract(minimumWorld).scale(0.5);
 
 			if (focusOnOriginXZ) {	
-				zoomTarget = new BABYLON.Vector3(0, center.y, 0);
+				zoomTarget = new BABYLON.Vector3(0, zoomTargetY, 0);
 			} else {
-				zoomTarget = center.clone();
+				let centerWorld = minimumWorld.add(radiusWorld);
+				zoomTarget = new BABYLON.Vector3(centerWorld.x, zoomTargetY, centerWorld.z);
 			}
 
 			if (!this._vectorTransition) {
@@ -229,15 +243,16 @@ module BABYLON {
 									60, this._vectorTransition, this._framingTime));
 
 			// sets the radius and lower radius bounds
-			if (radius == null) {
-				// Small delta ensures camera is not always at lower zoom limit.
-				let delta = 0.1;
-				if (this._mode === FramingBehavior.FitFrustumSidesMode) {
-					let position = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
-					this._attachedCamera.lowerRadiusLimit = mesh.getBoundingInfo().boundingSphere.radiusWorld + this._attachedCamera.minZ;
-					radius = position;
-				} else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
-					radius = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
+			// Small delta ensures camera is not always at lower zoom limit.
+			let delta = 0.1;
+			let radius = 0;
+			if (this._mode === FramingBehavior.FitFrustumSidesMode) {
+				let position = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+				this._attachedCamera.lowerRadiusLimit = radiusWorld.length() + this._attachedCamera.minZ;
+				radius = position;
+			} else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
+				radius = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+				if (this._attachedCamera.lowerRadiusLimit === null) {
 					this._attachedCamera.lowerRadiusLimit = this._attachedCamera.minZ;
 				}
 			}
@@ -258,8 +273,9 @@ module BABYLON {
 		 * @return The minimum distance from the primary mesh's center point at which the camera must be kept in order
 		 *		 to fully enclose the mesh in the viewing frustum.
 		 */
-		protected _calculateLowerRadiusFromModelBoundingSphere(mesh: AbstractMesh): number {
-            let boxVectorGlobalDiagonal = mesh.getBoundingInfo().diagonalLength;
+		protected _calculateLowerRadiusFromModelBoundingSphere(minimumWorld: Vector3, maximumWorld: Vector3): number {
+			let size = maximumWorld.subtract(minimumWorld);
+            let boxVectorGlobalDiagonal = size.length();
 			let frustumSlope: BABYLON.Vector2 = this._getFrustumSlope();
 
 			// Formula for setting distance
@@ -333,7 +349,7 @@ module BABYLON {
 			// Slope of the frustum left/right planes in view space, relative to the forward vector.
 			// Provides the amount that one side (e.g. left) of the frustum gets wider for every unit
 			// along the forward vector.
-			var frustumSlopeX = frustumSlopeY / aspectRatio;
+			var frustumSlopeX = frustumSlopeY * aspectRatio;
 
 			return new Vector2(frustumSlopeX, frustumSlopeY);
 		}		

+ 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";
         }

+ 45 - 32
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;
@@ -367,8 +355,14 @@ module BABYLON {
             if (this._cameraRigParams["specs"] === 1.0) {
                 var eyeParams = this._cameraRigParams["eyeParameters"];
                 // deprecated!!
-                Matrix.PerspectiveFovWebVRToRef(eyeParams.fieldOfView, 0.1, 1000, this._projectionMatrix, this.getScene().useRightHandedSystem);
+                Matrix.PerspectiveFovWebVRToRef(eyeParams.fieldOfView, this.minZ, this.maxZ, this._projectionMatrix, this.getScene().useRightHandedSystem);
             } else /*WebVR 1.1*/ {
+
+                let parentCamera = <WebVRFreeCamera> this.parent;
+
+                parentCamera._vrDevice.depthNear = this.minZ;
+                parentCamera._vrDevice.depthFar = this.maxZ;
+                
                 var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
                 Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
 
@@ -382,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) {
@@ -408,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;
@@ -426,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);
                 }
             });
         }

+ 1 - 1
src/Cameras/babylon.camera.ts

@@ -86,7 +86,7 @@
         public fov = 0.8;
 
         @serialize()
-        public minZ = 1.0;
+        public minZ = 0.1;
 
         @serialize()
         public maxZ = 10000.0;

+ 33 - 19
src/Mesh/babylon.abstractMesh.ts

@@ -128,22 +128,26 @@
         public definedFacingForward = true; // orientation for POV movement & rotation
         public position = Vector3.Zero();
 
-        private _webGLVersion = this.getEngine().webGLVersion;
-        private _occlusionInternalRetryCounter = 0;
+        public occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
         public occlusionType = AbstractMesh.OCCLUSION_TYPE_NONE;
         public occlusionRetryCount = -1;
+        private _occlusionInternalRetryCounter = 0;
+
         protected _isOccluded = false;
-        get isOccluded(): boolean {
+        public get isOccluded(): boolean {
             return this._isOccluded;
         }
-        set isOccluded(value: boolean) {
+
+        public set isOccluded(value: boolean) {
             this._isOccluded = value;
         }
 
-        public occlusionQuery = this.getEngine().createQuery();
-        public isOcclusionQueryInProgress = false;
+        private _isOcclusionQueryInProgress = false;
+        public get isOcclusionQueryInProgress(): boolean {
+            return this._isOcclusionQueryInProgress;
+        }
 
-        public occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
+        private _occlusionQuery: WebGLQuery;
 
         private _rotation = Vector3.Zero();
         private _rotationQuaternion: Quaternion;
@@ -1788,8 +1792,15 @@
                 }
             }
 
+            // Query
+            let engine = this.getScene().getEngine();
+            if (this._occlusionQuery) {
+                engine.deleteQuery(this._occlusionQuery);
+                this._occlusionQuery = null;
+            }
+
             // Engine
-            this.getScene().getEngine().wipeCaches();
+            engine.wipeCaches();
 
             // Remove from scene
             this.getScene().removeMesh(this);
@@ -2271,20 +2282,20 @@
         }
 
         protected checkOcclusionQuery() {
-            if (this._webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
+            var engine = this.getEngine();
+
+            if (engine.webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
                 this._isOccluded = false;
                 return;
             }
 
-            var engine = this.getEngine();
-
             if (this.isOcclusionQueryInProgress) {
                 
-                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this.occlusionQuery);
+                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this._occlusionQuery);
                 if (isOcclusionQueryAvailable) {
-                    var occlusionQueryResult = engine.getQueryResult(this.occlusionQuery);
+                    var occlusionQueryResult = engine.getQueryResult(this._occlusionQuery);
 
-                    this.isOcclusionQueryInProgress = false;
+                    this._isOcclusionQueryInProgress = false;
                     this._occlusionInternalRetryCounter = 0;
                     this._isOccluded = occlusionQueryResult === 1 ? false : true;
                 }
@@ -2293,8 +2304,7 @@
                     this._occlusionInternalRetryCounter++;
 
                     if (this.occlusionRetryCount !== -1 && this._occlusionInternalRetryCounter > this.occlusionRetryCount) {
-                        // break;
-                        this.isOcclusionQueryInProgress = false;
+                        this._isOcclusionQueryInProgress = false;
                         this._occlusionInternalRetryCounter = 0;
 
                         // if optimistic set isOccluded to false regardless of the status of isOccluded. (Render in the current render loop)
@@ -2308,13 +2318,17 @@
                 }
             }
 
-
             var scene = this.getScene();
             var occlusionBoundingBoxRenderer = scene.getBoundingBoxRenderer();
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this.occlusionQuery);
+
+            if (!this._occlusionQuery) {
+                this._occlusionQuery = engine.createQuery();
+            }
+
+            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
             engine.endQuery(this.occlusionQueryAlgorithmType);
-            this.isOcclusionQueryInProgress = true;
+            this._isOcclusionQueryInProgress = true;
         }
 
     }

+ 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.push(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;
-}

+ 11 - 3
src/babylon.engine.ts

@@ -4395,11 +4395,17 @@
             return this._gl.createQuery();
         }
 
-        public isQueryResultAvailable(query: WebGLQuery) {
+        public deleteQuery(query: WebGLQuery): Engine {
+            this.deleteQuery(query);
+
+            return this;
+        }
+
+        public isQueryResultAvailable(query: WebGLQuery): boolean {
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE) as boolean;
         }
 
-        public getQueryResult(query: WebGLQuery) {
+        public getQueryResult(query: WebGLQuery): number {
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT) as number;
         }
 
@@ -4408,9 +4414,11 @@
             this._gl.beginQuery(glAlgorithm, query);
         }
 
-        public endQuery(algorithmType: number) {
+        public endQuery(algorithmType: number): Engine {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
+
+            return this;
         }
 
         private getGlAlgorithmType(algorithmType: number): number {

+ 8 - 0
src/babylon.mixins.ts

@@ -46,6 +46,7 @@ interface WebGLRenderingContext {
 
     // Occlusion Query
     createQuery(): WebGLQuery;
+    deleteQuery(query: WebGLQuery);
     beginQuery(target: number, query: WebGLQuery);
     endQuery(target: number): void;
     getQueryParameter(query: WebGLQuery, pname: number): any;
@@ -111,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();