Przeglądaj źródła

Keyboard is now handled with observables.

David Catuhe 8 lat temu
rodzic
commit
59d5e27302

+ 2 - 0
Tools/Gulp/config.json

@@ -45,6 +45,8 @@
         "core" :
         {
             "files":[
+                "../../src/Events/babylon.keyboardEvents.js",
+                "../../src/Events/babylon.pointerEvents.js",
                 "../../src/Math/babylon.math.js",
                 "../../src/Math/babylon.math.scalar.js",
                 "../../src/babylon.mixins.js",

Plik diff jest za duży
+ 1885 - 1838
dist/preview release/babylon.d.ts


Plik diff jest za duży
+ 32 - 32
dist/preview release/babylon.js


+ 351 - 227
dist/preview release/babylon.max.js

@@ -14,7 +14,164 @@ var __extends = (this && this.__extends) || (function () {
                 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
             };
         })();
-        var BABYLON;
+        
+var BABYLON;
+(function (BABYLON) {
+    var KeyboardEventTypes = (function () {
+        function KeyboardEventTypes() {
+        }
+        Object.defineProperty(KeyboardEventTypes, "KEYDOWN", {
+            get: function () {
+                return KeyboardEventTypes._KEYDOWN;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(KeyboardEventTypes, "KEYUP", {
+            get: function () {
+                return KeyboardEventTypes._KEYUP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        KeyboardEventTypes._KEYDOWN = 0x01;
+        KeyboardEventTypes._KEYUP = 0x02;
+        return KeyboardEventTypes;
+    }());
+    BABYLON.KeyboardEventTypes = KeyboardEventTypes;
+    var KeyboardInfo = (function () {
+        function KeyboardInfo(type, event) {
+            this.type = type;
+            this.event = event;
+        }
+        return KeyboardInfo;
+    }());
+    BABYLON.KeyboardInfo = KeyboardInfo;
+    /**
+     * This class is used to store keyboard related info for the onPreKeyboardObservable event.
+     * Set the skipOnKeyboardObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onKeyboardObservable
+     */
+    var KeyboardInfoPre = (function (_super) {
+        __extends(KeyboardInfoPre, _super);
+        function KeyboardInfoPre(type, event) {
+            var _this = _super.call(this, type, event) || this;
+            _this.skipOnPointerObservable = false;
+            return _this;
+        }
+        return KeyboardInfoPre;
+    }(KeyboardInfo));
+    BABYLON.KeyboardInfoPre = KeyboardInfoPre;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.keyboardEvents.js.map
+
+
+var BABYLON;
+(function (BABYLON) {
+    var PointerEventTypes = (function () {
+        function PointerEventTypes() {
+        }
+        Object.defineProperty(PointerEventTypes, "POINTERDOWN", {
+            get: function () {
+                return PointerEventTypes._POINTERDOWN;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERUP", {
+            get: function () {
+                return PointerEventTypes._POINTERUP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERMOVE", {
+            get: function () {
+                return PointerEventTypes._POINTERMOVE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERWHEEL", {
+            get: function () {
+                return PointerEventTypes._POINTERWHEEL;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERPICK", {
+            get: function () {
+                return PointerEventTypes._POINTERPICK;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERTAP", {
+            get: function () {
+                return PointerEventTypes._POINTERTAP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERDOUBLETAP", {
+            get: function () {
+                return PointerEventTypes._POINTERDOUBLETAP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        PointerEventTypes._POINTERDOWN = 0x01;
+        PointerEventTypes._POINTERUP = 0x02;
+        PointerEventTypes._POINTERMOVE = 0x04;
+        PointerEventTypes._POINTERWHEEL = 0x08;
+        PointerEventTypes._POINTERPICK = 0x10;
+        PointerEventTypes._POINTERTAP = 0x20;
+        PointerEventTypes._POINTERDOUBLETAP = 0x40;
+        return PointerEventTypes;
+    }());
+    BABYLON.PointerEventTypes = PointerEventTypes;
+    var PointerInfoBase = (function () {
+        function PointerInfoBase(type, event) {
+            this.type = type;
+            this.event = event;
+        }
+        return PointerInfoBase;
+    }());
+    BABYLON.PointerInfoBase = PointerInfoBase;
+    /**
+     * This class is used to store pointer related info for the onPrePointerObservable event.
+     * Set the skipOnPointerObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onPointerObservable
+     */
+    var PointerInfoPre = (function (_super) {
+        __extends(PointerInfoPre, _super);
+        function PointerInfoPre(type, event, localX, localY) {
+            var _this = _super.call(this, type, event) || this;
+            _this.skipOnPointerObservable = false;
+            _this.localPosition = new BABYLON.Vector2(localX, localY);
+            return _this;
+        }
+        return PointerInfoPre;
+    }(PointerInfoBase));
+    BABYLON.PointerInfoPre = PointerInfoPre;
+    /**
+     * This type contains all the data related to a pointer event in Babylon.js.
+     * The event member is an instance of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel. The different event types can be found in the PointerEventTypes class.
+     */
+    var PointerInfo = (function (_super) {
+        __extends(PointerInfo, _super);
+        function PointerInfo(type, event, pickInfo) {
+            var _this = _super.call(this, type, event) || this;
+            _this.pickInfo = pickInfo;
+            return _this;
+        }
+        return PointerInfo;
+    }(PointerInfoBase));
+    BABYLON.PointerInfo = PointerInfo;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.pointerEvents.js.map
+
+var BABYLON;
 (function (BABYLON) {
     BABYLON.ToGammaSpace = 1 / 2.2;
     BABYLON.ToLinearSpace = 2.2;
@@ -7459,9 +7616,17 @@ var BABYLON;
              */
             this.onResizeObservable = new BABYLON.Observable();
             /**
-             * Observable event triggered each time the canvas lost focus
+             * Observable event triggered each time the canvas loses focus
              */
             this.onCanvasBlurObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time the canvas gains focus
+             */
+            this.onCanvasFocusObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time the canvas receives pointerout event
+             */
+            this.onCanvasPointerOutObservable = new BABYLON.Observable();
             // Uniform buffers list
             this.disableUniformBuffers = false;
             this._uniformBuffers = new Array();
@@ -7580,24 +7745,34 @@ var BABYLON;
                 if (!this._gl) {
                     throw new Error("WebGL not supported");
                 }
+                this._onCanvasFocus = function () {
+                    _this.onCanvasFocusObservable.notifyObservers(_this);
+                };
+                this._onCanvasBlur = function () {
+                    _this.onCanvasBlurObservable.notifyObservers(_this);
+                };
+                canvas.addEventListener("focus", this._onCanvasFocus);
+                canvas.addEventListener("blur", this._onCanvasBlur);
                 this._onBlur = function () {
                     if (_this.disablePerformanceMonitorInBackground) {
                         _this._performanceMonitor.disable();
                     }
                     _this._windowIsBackground = true;
+                    _this.onCanvasBlurObservable.notifyObservers(_this);
                 };
                 this._onFocus = function () {
                     if (_this.disablePerformanceMonitorInBackground) {
                         _this._performanceMonitor.enable();
                     }
                     _this._windowIsBackground = false;
+                    _this.onCanvasFocusObservable.notifyObservers(_this);
                 };
-                this._onCanvasBlur = function () {
-                    _this.onCanvasBlurObservable.notifyObservers(_this);
+                this._onCanvasPointerOut = function () {
+                    _this.onCanvasPointerOutObservable.notifyObservers(_this);
                 };
                 window.addEventListener("blur", this._onBlur);
                 window.addEventListener("focus", this._onFocus);
-                canvas.addEventListener("pointerout", this._onCanvasBlur);
+                canvas.addEventListener("pointerout", this._onCanvasPointerOut);
             }
             else {
                 this._gl = canvasOrContext;
@@ -10911,6 +11086,8 @@ var BABYLON;
             window.removeEventListener("focus", this._onFocus);
             window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
             window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
+            this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
+            this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
             this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
             if (!this._doNotHandleContextLost) {
                 this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
@@ -10936,6 +11113,8 @@ var BABYLON;
             this._currentProgram = null;
             this.onResizeObservable.clear();
             this.onCanvasBlurObservable.clear();
+            this.onCanvasFocusObservable.clear();
+            this.onCanvasPointerOutObservable.clear();
             BABYLON.Effect.ResetCache();
         };
         // Loading screen
@@ -16125,7 +16304,6 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.renderingGroup.js.map
 
-
 var BABYLON;
 (function (BABYLON) {
     var ClickInfo = (function () {
@@ -16177,105 +16355,6 @@ var BABYLON;
         });
         return ClickInfo;
     }());
-    var PointerEventTypes = (function () {
-        function PointerEventTypes() {
-        }
-        Object.defineProperty(PointerEventTypes, "POINTERDOWN", {
-            get: function () {
-                return PointerEventTypes._POINTERDOWN;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERUP", {
-            get: function () {
-                return PointerEventTypes._POINTERUP;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERMOVE", {
-            get: function () {
-                return PointerEventTypes._POINTERMOVE;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERWHEEL", {
-            get: function () {
-                return PointerEventTypes._POINTERWHEEL;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERPICK", {
-            get: function () {
-                return PointerEventTypes._POINTERPICK;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERTAP", {
-            get: function () {
-                return PointerEventTypes._POINTERTAP;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERDOUBLETAP", {
-            get: function () {
-                return PointerEventTypes._POINTERDOUBLETAP;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        PointerEventTypes._POINTERDOWN = 0x01;
-        PointerEventTypes._POINTERUP = 0x02;
-        PointerEventTypes._POINTERMOVE = 0x04;
-        PointerEventTypes._POINTERWHEEL = 0x08;
-        PointerEventTypes._POINTERPICK = 0x10;
-        PointerEventTypes._POINTERTAP = 0x20;
-        PointerEventTypes._POINTERDOUBLETAP = 0x40;
-        return PointerEventTypes;
-    }());
-    BABYLON.PointerEventTypes = PointerEventTypes;
-    var PointerInfoBase = (function () {
-        function PointerInfoBase(type, event) {
-            this.type = type;
-            this.event = event;
-        }
-        return PointerInfoBase;
-    }());
-    BABYLON.PointerInfoBase = PointerInfoBase;
-    /**
-     * This class is used to store pointer related info for the onPrePointerObservable event.
-     * Set the skipOnPointerObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onPointerObservable
-     */
-    var PointerInfoPre = (function (_super) {
-        __extends(PointerInfoPre, _super);
-        function PointerInfoPre(type, event, localX, localY) {
-            var _this = _super.call(this, type, event) || this;
-            _this.skipOnPointerObservable = false;
-            _this.localPosition = new BABYLON.Vector2(localX, localY);
-            return _this;
-        }
-        return PointerInfoPre;
-    }(PointerInfoBase));
-    BABYLON.PointerInfoPre = PointerInfoPre;
-    /**
-     * This type contains all the data related to a pointer event in Babylon.js.
-     * The event member is an instance of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel. The different event types can be found in the PointerEventTypes class.
-     */
-    var PointerInfo = (function (_super) {
-        __extends(PointerInfo, _super);
-        function PointerInfo(type, event, pickInfo) {
-            var _this = _super.call(this, type, event) || this;
-            _this.pickInfo = pickInfo;
-            return _this;
-        }
-        return PointerInfo;
-    }(PointerInfoBase));
-    BABYLON.PointerInfo = PointerInfo;
     /**
      * This class is used by the onRenderingGroupObservable
      */
@@ -16419,8 +16498,8 @@ var BABYLON;
             // Animations
             this.animations = [];
             /**
-             * 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
+             * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
+             * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
              */
             this.onPrePointerObservable = new BABYLON.Observable();
             /**
@@ -16443,6 +16522,16 @@ var BABYLON;
             this._timeAccumulator = 0;
             this._currentStepId = 0;
             this._currentInternalStep = 0;
+            // Keyboard
+            /**
+             * This observable event is triggered when any keyboard event si raised and registered during Scene.attachControl()
+             * You have the possibility to skip the process and the call to onKeyboardObservable by setting KeyboardInfoPre.skipOnPointerObservable to true
+             */
+            this.onPreKeyboardObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time an keyboard event is received from the hosting window
+             */
+            this.onKeyboardObservable = new BABYLON.Observable();
             // Coordinate system
             /**
             * use right-handed coordinate system on this scene.
@@ -17137,9 +17226,9 @@ var BABYLON;
                 var clickInfo = new ClickInfo();
                 _this._currentPickResult = null;
                 var act;
-                var checkPicking = obs1.hasSpecificMask(PointerEventTypes.POINTERPICK) || obs2.hasSpecificMask(PointerEventTypes.POINTERPICK)
-                    || obs1.hasSpecificMask(PointerEventTypes.POINTERTAP) || obs2.hasSpecificMask(PointerEventTypes.POINTERTAP)
-                    || obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) || obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                var checkPicking = obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERPICK) || obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERPICK)
+                    || obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP) || obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)
+                    || obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP) || obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP);
                 if (!checkPicking && BABYLON.ActionManager && BABYLON.ActionManager.HasPickTriggers) {
                     act = _this._initActionManager(act, clickInfo);
                     if (act)
@@ -17152,8 +17241,8 @@ var BABYLON;
                     if (!clickInfo.hasSwiped) {
                         var checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
                         if (!checkSingleClickImmediately) {
-                            checkSingleClickImmediately = !obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) &&
-                                !obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                            checkSingleClickImmediately = !obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP) &&
+                                !obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP);
                             if (checkSingleClickImmediately && !BABYLON.ActionManager.HasSpecificTrigger(BABYLON.ActionManager.OnDoublePickTrigger)) {
                                 act = _this._initActionManager(act, clickInfo);
                                 if (act)
@@ -17173,8 +17262,8 @@ var BABYLON;
                             _this._previousDelayedSimpleClickTimeout = _this._delayedSimpleClickTimeout;
                             _this._delayedSimpleClickTimeout = window.setTimeout(_this._delayedSimpleClick.bind(_this, btn, clickInfo, cb), Scene.DoubleClickDelay);
                         }
-                        var checkDoubleClick = obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) ||
-                            obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                        var checkDoubleClick = obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP) ||
+                            obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP);
                         if (!checkDoubleClick && BABYLON.ActionManager.HasSpecificTrigger(BABYLON.ActionManager.OnDoublePickTrigger)) {
                             act = _this._initActionManager(act, clickInfo);
                             if (act)
@@ -17238,8 +17327,8 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 // PreObservable support
                 if (_this.onPrePointerObservable.hasObservers()) {
-                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE;
-                    var pi = new PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
+                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
+                    var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
                     if (pi.skipOnPointerObservable) {
                         return;
@@ -17292,8 +17381,8 @@ var BABYLON;
                     _this.onPointerMove(evt, pickResult);
                 }
                 if (_this.onPointerObservable.hasObservers()) {
-                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE;
-                    var pi = new PointerInfo(type, evt, pickResult);
+                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
+                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     _this.onPointerObservable.notifyObservers(pi, type);
                 }
             };
@@ -17304,8 +17393,8 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 // PreObservable support
                 if (_this.onPrePointerObservable.hasObservers()) {
-                    var type = PointerEventTypes.POINTERDOWN;
-                    var pi = new PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
+                    var type = BABYLON.PointerEventTypes.POINTERDOWN;
+                    var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
                     if (pi.skipOnPointerObservable) {
                         return;
@@ -17364,8 +17453,8 @@ var BABYLON;
                     _this.onPointerDown(evt, pickResult);
                 }
                 if (_this.onPointerObservable.hasObservers()) {
-                    var type = PointerEventTypes.POINTERDOWN;
-                    var pi = new PointerInfo(type, evt, pickResult);
+                    var type = BABYLON.PointerEventTypes.POINTERDOWN;
+                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     _this.onPointerObservable.notifyObservers(pi, type);
                 }
                 // Sprites
@@ -17403,17 +17492,17 @@ var BABYLON;
                     if (this.onPrePointerObservable.hasObservers()) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
-                                if (clickInfo.singleClick && this.onPrePointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
-                                    var type = PointerEventTypes.POINTERTAP;
-                                    var pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                                if (clickInfo.singleClick && this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERTAP;
+                                    var pi = new BABYLON.PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
                                     this.onPrePointerObservable.notifyObservers(pi, type);
                                     if (pi.skipOnPointerObservable) {
                                         return;
                                     }
                                 }
-                                if (clickInfo.doubleClick && this.onPrePointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
-                                    var type = PointerEventTypes.POINTERDOUBLETAP;
-                                    var pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                                if (clickInfo.doubleClick && this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
+                                    var pi = new BABYLON.PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
                                     this.onPrePointerObservable.notifyObservers(pi, type);
                                     if (pi.skipOnPointerObservable) {
                                         return;
@@ -17422,8 +17511,8 @@ var BABYLON;
                             }
                         }
                         else {
-                            var type = PointerEventTypes.POINTERUP;
-                            var pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                            var type = BABYLON.PointerEventTypes.POINTERUP;
+                            var pi = new BABYLON.PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
                             this.onPrePointerObservable.notifyObservers(pi, type);
                             if (pi.skipOnPointerObservable) {
                                 return;
@@ -17452,8 +17541,8 @@ var BABYLON;
                                 this.onPointerPick(evt, pickResult);
                             }
                             if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
-                                var type = PointerEventTypes.POINTERPICK;
-                                var pi = new PointerInfo(type, evt, pickResult);
+                                var type = BABYLON.PointerEventTypes.POINTERPICK;
+                                var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                                 this.onPointerObservable.notifyObservers(pi, type);
                             }
                         }
@@ -17481,21 +17570,21 @@ var BABYLON;
                     if (this.onPointerObservable.hasObservers()) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
-                                if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
-                                    var type = PointerEventTypes.POINTERTAP;
-                                    var pi = new PointerInfo(type, evt, pickResult);
+                                if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERTAP;
+                                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                                     this.onPointerObservable.notifyObservers(pi, type);
                                 }
-                                if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
-                                    var type = PointerEventTypes.POINTERDOUBLETAP;
-                                    var pi = new PointerInfo(type, evt, pickResult);
+                                if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
+                                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                                     this.onPointerObservable.notifyObservers(pi, type);
                                 }
                             }
                         }
                         else {
-                            var type = PointerEventTypes.POINTERUP;
-                            var pi = new PointerInfo(type, evt, pickResult);
+                            var type = BABYLON.PointerEventTypes.POINTERUP;
+                            var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                             this.onPointerObservable.notifyObservers(pi, type);
                         }
                     }
@@ -17520,15 +17609,48 @@ var BABYLON;
                 }).bind(_this));
             };
             this._onKeyDown = function (evt) {
+                var type = BABYLON.KeyboardEventTypes.KEYDOWN;
+                if (_this.onPreKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfoPre(type, evt);
+                    _this.onPreKeyboardObservable.notifyObservers(pi, type);
+                    if (pi.skipOnPointerObservable) {
+                        return;
+                    }
+                }
+                if (_this.onKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfo(type, evt);
+                    _this.onKeyboardObservable.notifyObservers(pi, type);
+                }
                 if (_this.actionManager) {
                     _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyDownTrigger, BABYLON.ActionEvent.CreateNewFromScene(_this, evt));
                 }
             };
             this._onKeyUp = function (evt) {
+                var type = BABYLON.KeyboardEventTypes.KEYUP;
+                if (_this.onPreKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfoPre(type, evt);
+                    _this.onPreKeyboardObservable.notifyObservers(pi, type);
+                    if (pi.skipOnPointerObservable) {
+                        return;
+                    }
+                }
+                if (_this.onKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfo(type, evt);
+                    _this.onKeyboardObservable.notifyObservers(pi, type);
+                }
                 if (_this.actionManager) {
                     _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyUpTrigger, BABYLON.ActionEvent.CreateNewFromScene(_this, evt));
                 }
             };
+            var engine = this.getEngine();
+            this._onCanvasFocusObserver = engine.onCanvasFocusObservable.add(function () {
+                canvas.addEventListener("keydown", _this._onKeyDown, false);
+                canvas.addEventListener("keyup", _this._onKeyUp, false);
+            });
+            this._onCanvasBlurObserver = engine.onCanvasBlurObservable.add(function () {
+                canvas.removeEventListener("keydown", _this._onKeyDown);
+                canvas.removeEventListener("keyup", _this._onKeyUp);
+            });
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
             var canvas = this._engine.getRenderingCanvas();
             if (attachMove) {
@@ -17544,20 +17666,27 @@ var BABYLON;
                 canvas.addEventListener(eventPrefix + "up", this._onPointerUp, false);
             }
             canvas.tabIndex = 1;
-            canvas.addEventListener("keydown", this._onKeyDown, false);
-            canvas.addEventListener("keyup", this._onKeyUp, false);
         };
         Scene.prototype.detachControl = function () {
+            var engine = this.getEngine();
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
-            var canvas = this._engine.getRenderingCanvas();
+            var canvas = engine.getRenderingCanvas();
             canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
             canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
             canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
+            engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            engine.onCanvasFocusObservable.remove(this._onCanvasFocusObserver);
             // Wheel
             canvas.removeEventListener('mousewheel', this._onPointerMove);
             canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
+            // Keyboard
             canvas.removeEventListener("keydown", this._onKeyDown);
             canvas.removeEventListener("keyup", this._onKeyUp);
+            // Observables
+            this.onKeyboardObservable.clear();
+            this.onPreKeyboardObservable.clear();
+            this.onPointerObservable.clear();
+            this.onPrePointerObservable.clear();
         };
         // Ready
         Scene.prototype.isReady = function () {
@@ -34122,9 +34251,17 @@ var BABYLON;
         }
         FreeCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
-            if (!this._onKeyDown) {
-                element.tabIndex = 1;
-                this._onKeyDown = function (evt) {
+            if (this._onCanvasBlurObserver) {
+                return;
+            }
+            this._scene = this.camera.getScene();
+            this._engine = this._scene.getEngine();
+            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(function () {
+                _this._keys = [];
+            });
+            this._onKeyboardObserver = this._scene.onKeyboardObservable.add(function (info) {
+                var evt = info.event;
+                if (info.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
                     if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         _this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -34137,8 +34274,8 @@ var BABYLON;
                             evt.preventDefault();
                         }
                     }
-                };
-                this._onKeyUp = function (evt) {
+                }
+                else {
                     if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         _this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -34151,28 +34288,20 @@ var BABYLON;
                             evt.preventDefault();
                         }
                     }
-                };
-                element.addEventListener("keydown", this._onKeyDown, false);
-                element.addEventListener("keyup", this._onKeyUp, false);
-                BABYLON.Tools.RegisterTopRootEvents([
-                    { name: "blur", handler: this._onLostFocus }
-                ]);
-            }
+                }
+            });
         };
         FreeCameraKeyboardMoveInput.prototype.detachControl = function (element) {
-            if (this._onKeyDown) {
-                element.removeEventListener("keydown", this._onKeyDown);
-                element.removeEventListener("keyup", this._onKeyUp);
-                BABYLON.Tools.UnregisterTopRootEvents([
-                    { name: "blur", handler: this._onLostFocus }
-                ]);
-                this._keys = [];
-                this._onKeyDown = null;
-                this._onKeyUp = null;
+            if (this._scene) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                this._onKeyboardObserver = null;
+                this._onCanvasBlurObserver = null;
             }
+            this._keys = [];
         };
         FreeCameraKeyboardMoveInput.prototype.checkInputs = function () {
-            if (this._onKeyDown) {
+            if (this._onKeyboardObserver) {
                 var camera = this.camera;
                 // Keyboard
                 for (var index = 0; index < this._keys.length; index++) {
@@ -34797,77 +34926,64 @@ var BABYLON;
         }
         ArcRotateCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
-            element.tabIndex = 1;
-            this._onKeyDown = function (evt) {
-                _this._ctrlPressed = evt.ctrlKey;
-                if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysReset.indexOf(evt.keyCode) !== -1) {
-                    var index = _this._keys.indexOf(evt.keyCode);
-                    if (index === -1) {
-                        _this._keys.push(evt.keyCode);
-                    }
-                    if (evt.preventDefault) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
+            if (this._onCanvasBlurObserver) {
+                return;
+            }
+            this._scene = this.camera.getScene();
+            this._engine = this._scene.getEngine();
+            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(function () {
+                _this._keys = [];
+            });
+            this._onKeyboardObserver = this._scene.onKeyboardObservable.add(function (info) {
+                var evt = info.event;
+                if (info.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
+                    _this._ctrlPressed = evt.ctrlKey;
+                    if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = _this._keys.indexOf(evt.keyCode);
+                        if (index === -1) {
+                            _this._keys.push(evt.keyCode);
+                        }
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
                         }
                     }
                 }
-            };
-            this._onKeyUp = function (evt) {
-                if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysReset.indexOf(evt.keyCode) !== -1) {
-                    var index = _this._keys.indexOf(evt.keyCode);
-                    if (index >= 0) {
-                        _this._keys.splice(index, 1);
-                    }
-                    if (evt.preventDefault) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
+                else {
+                    if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = _this._keys.indexOf(evt.keyCode);
+                        if (index >= 0) {
+                            _this._keys.splice(index, 1);
+                        }
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
                         }
                     }
                 }
-            };
-            this._onLostFocus = function () {
-                _this._keys = [];
-            };
-            this._onFocus = function () {
-                element.addEventListener("keydown", _this._onKeyDown, false);
-                element.addEventListener("keyup", _this._onKeyUp, false);
-            };
-            this._onBlur = function () {
-                element.removeEventListener("keydown", _this._onKeyDown);
-                element.removeEventListener("keyup", _this._onKeyUp);
-            };
-            element.addEventListener("focus", this._onFocus);
-            element.addEventListener("blur", this._onBlur);
-            BABYLON.Tools.RegisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
+            });
         };
         ArcRotateCameraKeyboardMoveInput.prototype.detachControl = function (element) {
-            if (element && this._onBlur) {
-                this._onBlur();
-                element.removeEventListener("focus", this._onFocus);
-                element.removeEventListener("blur", this._onBlur);
+            if (this._scene) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                this._onKeyboardObserver = null;
+                this._onCanvasBlurObserver = null;
             }
-            BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
             this._keys = [];
-            this._onKeyDown = null;
-            this._onKeyUp = null;
-            this._onLostFocus = null;
-            this._onBlur = null;
-            this._onFocus = null;
         };
         ArcRotateCameraKeyboardMoveInput.prototype.checkInputs = function () {
-            if (this._onKeyDown) {
+            if (this._onKeyboardObserver) {
                 var camera = this.camera;
                 for (var index = 0; index < this._keys.length; index++) {
                     var keyCode = this._keys[index];
@@ -66115,9 +66231,17 @@ var BABYLON;
                                 if (!_this._lightOnControllers) {
                                     _this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), _this.getScene());
                                 }
-                                loadedMesh.getChildren().forEach(function (mesh) {
-                                    _this._lightOnControllers.includedOnlyMeshes.push(mesh);
-                                });
+                                var activateLightOnSubMeshes_1 = function (mesh, light) {
+                                    var children = mesh.getChildren();
+                                    if (children.length !== 0) {
+                                        children.forEach(function (mesh) {
+                                            light.includedOnlyMeshes.push(mesh);
+                                            activateLightOnSubMeshes_1(mesh, light);
+                                        });
+                                    }
+                                };
+                                _this._lightOnControllers.includedOnlyMeshes.push(loadedMesh);
+                                activateLightOnSubMeshes_1(loadedMesh, _this._lightOnControllers);
                             }
                         });
                     }

Plik diff jest za duży
+ 1885 - 1838
dist/preview release/babylon.module.d.ts


Plik diff jest za duży
+ 36 - 36
dist/preview release/babylon.worker.js


Plik diff jest za duży
+ 8420 - 8373
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Plik diff jest za duży
+ 33 - 33
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 318 - 202
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -14,7 +14,164 @@ var __extends = (this && this.__extends) || (function () {
                 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
             };
         })();
-        var BABYLON;
+        
+var BABYLON;
+(function (BABYLON) {
+    var KeyboardEventTypes = (function () {
+        function KeyboardEventTypes() {
+        }
+        Object.defineProperty(KeyboardEventTypes, "KEYDOWN", {
+            get: function () {
+                return KeyboardEventTypes._KEYDOWN;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(KeyboardEventTypes, "KEYUP", {
+            get: function () {
+                return KeyboardEventTypes._KEYUP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        KeyboardEventTypes._KEYDOWN = 0x01;
+        KeyboardEventTypes._KEYUP = 0x02;
+        return KeyboardEventTypes;
+    }());
+    BABYLON.KeyboardEventTypes = KeyboardEventTypes;
+    var KeyboardInfo = (function () {
+        function KeyboardInfo(type, event) {
+            this.type = type;
+            this.event = event;
+        }
+        return KeyboardInfo;
+    }());
+    BABYLON.KeyboardInfo = KeyboardInfo;
+    /**
+     * This class is used to store keyboard related info for the onPreKeyboardObservable event.
+     * Set the skipOnKeyboardObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onKeyboardObservable
+     */
+    var KeyboardInfoPre = (function (_super) {
+        __extends(KeyboardInfoPre, _super);
+        function KeyboardInfoPre(type, event) {
+            var _this = _super.call(this, type, event) || this;
+            _this.skipOnPointerObservable = false;
+            return _this;
+        }
+        return KeyboardInfoPre;
+    }(KeyboardInfo));
+    BABYLON.KeyboardInfoPre = KeyboardInfoPre;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.keyboardEvents.js.map
+
+
+var BABYLON;
+(function (BABYLON) {
+    var PointerEventTypes = (function () {
+        function PointerEventTypes() {
+        }
+        Object.defineProperty(PointerEventTypes, "POINTERDOWN", {
+            get: function () {
+                return PointerEventTypes._POINTERDOWN;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERUP", {
+            get: function () {
+                return PointerEventTypes._POINTERUP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERMOVE", {
+            get: function () {
+                return PointerEventTypes._POINTERMOVE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERWHEEL", {
+            get: function () {
+                return PointerEventTypes._POINTERWHEEL;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERPICK", {
+            get: function () {
+                return PointerEventTypes._POINTERPICK;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERTAP", {
+            get: function () {
+                return PointerEventTypes._POINTERTAP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PointerEventTypes, "POINTERDOUBLETAP", {
+            get: function () {
+                return PointerEventTypes._POINTERDOUBLETAP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        PointerEventTypes._POINTERDOWN = 0x01;
+        PointerEventTypes._POINTERUP = 0x02;
+        PointerEventTypes._POINTERMOVE = 0x04;
+        PointerEventTypes._POINTERWHEEL = 0x08;
+        PointerEventTypes._POINTERPICK = 0x10;
+        PointerEventTypes._POINTERTAP = 0x20;
+        PointerEventTypes._POINTERDOUBLETAP = 0x40;
+        return PointerEventTypes;
+    }());
+    BABYLON.PointerEventTypes = PointerEventTypes;
+    var PointerInfoBase = (function () {
+        function PointerInfoBase(type, event) {
+            this.type = type;
+            this.event = event;
+        }
+        return PointerInfoBase;
+    }());
+    BABYLON.PointerInfoBase = PointerInfoBase;
+    /**
+     * This class is used to store pointer related info for the onPrePointerObservable event.
+     * Set the skipOnPointerObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onPointerObservable
+     */
+    var PointerInfoPre = (function (_super) {
+        __extends(PointerInfoPre, _super);
+        function PointerInfoPre(type, event, localX, localY) {
+            var _this = _super.call(this, type, event) || this;
+            _this.skipOnPointerObservable = false;
+            _this.localPosition = new BABYLON.Vector2(localX, localY);
+            return _this;
+        }
+        return PointerInfoPre;
+    }(PointerInfoBase));
+    BABYLON.PointerInfoPre = PointerInfoPre;
+    /**
+     * This type contains all the data related to a pointer event in Babylon.js.
+     * The event member is an instance of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel. The different event types can be found in the PointerEventTypes class.
+     */
+    var PointerInfo = (function (_super) {
+        __extends(PointerInfo, _super);
+        function PointerInfo(type, event, pickInfo) {
+            var _this = _super.call(this, type, event) || this;
+            _this.pickInfo = pickInfo;
+            return _this;
+        }
+        return PointerInfo;
+    }(PointerInfoBase));
+    BABYLON.PointerInfo = PointerInfo;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.pointerEvents.js.map
+
+var BABYLON;
 (function (BABYLON) {
     BABYLON.ToGammaSpace = 1 / 2.2;
     BABYLON.ToLinearSpace = 2.2;
@@ -7459,9 +7616,17 @@ var BABYLON;
              */
             this.onResizeObservable = new BABYLON.Observable();
             /**
-             * Observable event triggered each time the canvas lost focus
+             * Observable event triggered each time the canvas loses focus
              */
             this.onCanvasBlurObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time the canvas gains focus
+             */
+            this.onCanvasFocusObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time the canvas receives pointerout event
+             */
+            this.onCanvasPointerOutObservable = new BABYLON.Observable();
             // Uniform buffers list
             this.disableUniformBuffers = false;
             this._uniformBuffers = new Array();
@@ -7580,24 +7745,34 @@ var BABYLON;
                 if (!this._gl) {
                     throw new Error("WebGL not supported");
                 }
+                this._onCanvasFocus = function () {
+                    _this.onCanvasFocusObservable.notifyObservers(_this);
+                };
+                this._onCanvasBlur = function () {
+                    _this.onCanvasBlurObservable.notifyObservers(_this);
+                };
+                canvas.addEventListener("focus", this._onCanvasFocus);
+                canvas.addEventListener("blur", this._onCanvasBlur);
                 this._onBlur = function () {
                     if (_this.disablePerformanceMonitorInBackground) {
                         _this._performanceMonitor.disable();
                     }
                     _this._windowIsBackground = true;
+                    _this.onCanvasBlurObservable.notifyObservers(_this);
                 };
                 this._onFocus = function () {
                     if (_this.disablePerformanceMonitorInBackground) {
                         _this._performanceMonitor.enable();
                     }
                     _this._windowIsBackground = false;
+                    _this.onCanvasFocusObservable.notifyObservers(_this);
                 };
-                this._onCanvasBlur = function () {
-                    _this.onCanvasBlurObservable.notifyObservers(_this);
+                this._onCanvasPointerOut = function () {
+                    _this.onCanvasPointerOutObservable.notifyObservers(_this);
                 };
                 window.addEventListener("blur", this._onBlur);
                 window.addEventListener("focus", this._onFocus);
-                canvas.addEventListener("pointerout", this._onCanvasBlur);
+                canvas.addEventListener("pointerout", this._onCanvasPointerOut);
             }
             else {
                 this._gl = canvasOrContext;
@@ -10911,6 +11086,8 @@ var BABYLON;
             window.removeEventListener("focus", this._onFocus);
             window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
             window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
+            this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
+            this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
             this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
             if (!this._doNotHandleContextLost) {
                 this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
@@ -10936,6 +11113,8 @@ var BABYLON;
             this._currentProgram = null;
             this.onResizeObservable.clear();
             this.onCanvasBlurObservable.clear();
+            this.onCanvasFocusObservable.clear();
+            this.onCanvasPointerOutObservable.clear();
             BABYLON.Effect.ResetCache();
         };
         // Loading screen
@@ -16125,7 +16304,6 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.renderingGroup.js.map
 
-
 var BABYLON;
 (function (BABYLON) {
     var ClickInfo = (function () {
@@ -16177,105 +16355,6 @@ var BABYLON;
         });
         return ClickInfo;
     }());
-    var PointerEventTypes = (function () {
-        function PointerEventTypes() {
-        }
-        Object.defineProperty(PointerEventTypes, "POINTERDOWN", {
-            get: function () {
-                return PointerEventTypes._POINTERDOWN;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERUP", {
-            get: function () {
-                return PointerEventTypes._POINTERUP;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERMOVE", {
-            get: function () {
-                return PointerEventTypes._POINTERMOVE;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERWHEEL", {
-            get: function () {
-                return PointerEventTypes._POINTERWHEEL;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERPICK", {
-            get: function () {
-                return PointerEventTypes._POINTERPICK;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERTAP", {
-            get: function () {
-                return PointerEventTypes._POINTERTAP;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(PointerEventTypes, "POINTERDOUBLETAP", {
-            get: function () {
-                return PointerEventTypes._POINTERDOUBLETAP;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        PointerEventTypes._POINTERDOWN = 0x01;
-        PointerEventTypes._POINTERUP = 0x02;
-        PointerEventTypes._POINTERMOVE = 0x04;
-        PointerEventTypes._POINTERWHEEL = 0x08;
-        PointerEventTypes._POINTERPICK = 0x10;
-        PointerEventTypes._POINTERTAP = 0x20;
-        PointerEventTypes._POINTERDOUBLETAP = 0x40;
-        return PointerEventTypes;
-    }());
-    BABYLON.PointerEventTypes = PointerEventTypes;
-    var PointerInfoBase = (function () {
-        function PointerInfoBase(type, event) {
-            this.type = type;
-            this.event = event;
-        }
-        return PointerInfoBase;
-    }());
-    BABYLON.PointerInfoBase = PointerInfoBase;
-    /**
-     * This class is used to store pointer related info for the onPrePointerObservable event.
-     * Set the skipOnPointerObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onPointerObservable
-     */
-    var PointerInfoPre = (function (_super) {
-        __extends(PointerInfoPre, _super);
-        function PointerInfoPre(type, event, localX, localY) {
-            var _this = _super.call(this, type, event) || this;
-            _this.skipOnPointerObservable = false;
-            _this.localPosition = new BABYLON.Vector2(localX, localY);
-            return _this;
-        }
-        return PointerInfoPre;
-    }(PointerInfoBase));
-    BABYLON.PointerInfoPre = PointerInfoPre;
-    /**
-     * This type contains all the data related to a pointer event in Babylon.js.
-     * The event member is an instance of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel. The different event types can be found in the PointerEventTypes class.
-     */
-    var PointerInfo = (function (_super) {
-        __extends(PointerInfo, _super);
-        function PointerInfo(type, event, pickInfo) {
-            var _this = _super.call(this, type, event) || this;
-            _this.pickInfo = pickInfo;
-            return _this;
-        }
-        return PointerInfo;
-    }(PointerInfoBase));
-    BABYLON.PointerInfo = PointerInfo;
     /**
      * This class is used by the onRenderingGroupObservable
      */
@@ -16419,8 +16498,8 @@ var BABYLON;
             // Animations
             this.animations = [];
             /**
-             * 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
+             * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
+             * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
              */
             this.onPrePointerObservable = new BABYLON.Observable();
             /**
@@ -16443,6 +16522,16 @@ var BABYLON;
             this._timeAccumulator = 0;
             this._currentStepId = 0;
             this._currentInternalStep = 0;
+            // Keyboard
+            /**
+             * This observable event is triggered when any keyboard event si raised and registered during Scene.attachControl()
+             * You have the possibility to skip the process and the call to onKeyboardObservable by setting KeyboardInfoPre.skipOnPointerObservable to true
+             */
+            this.onPreKeyboardObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time an keyboard event is received from the hosting window
+             */
+            this.onKeyboardObservable = new BABYLON.Observable();
             // Coordinate system
             /**
             * use right-handed coordinate system on this scene.
@@ -17137,9 +17226,9 @@ var BABYLON;
                 var clickInfo = new ClickInfo();
                 _this._currentPickResult = null;
                 var act;
-                var checkPicking = obs1.hasSpecificMask(PointerEventTypes.POINTERPICK) || obs2.hasSpecificMask(PointerEventTypes.POINTERPICK)
-                    || obs1.hasSpecificMask(PointerEventTypes.POINTERTAP) || obs2.hasSpecificMask(PointerEventTypes.POINTERTAP)
-                    || obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) || obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                var checkPicking = obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERPICK) || obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERPICK)
+                    || obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP) || obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)
+                    || obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP) || obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP);
                 if (!checkPicking && BABYLON.ActionManager && BABYLON.ActionManager.HasPickTriggers) {
                     act = _this._initActionManager(act, clickInfo);
                     if (act)
@@ -17152,8 +17241,8 @@ var BABYLON;
                     if (!clickInfo.hasSwiped) {
                         var checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
                         if (!checkSingleClickImmediately) {
-                            checkSingleClickImmediately = !obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) &&
-                                !obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                            checkSingleClickImmediately = !obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP) &&
+                                !obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP);
                             if (checkSingleClickImmediately && !BABYLON.ActionManager.HasSpecificTrigger(BABYLON.ActionManager.OnDoublePickTrigger)) {
                                 act = _this._initActionManager(act, clickInfo);
                                 if (act)
@@ -17173,8 +17262,8 @@ var BABYLON;
                             _this._previousDelayedSimpleClickTimeout = _this._delayedSimpleClickTimeout;
                             _this._delayedSimpleClickTimeout = window.setTimeout(_this._delayedSimpleClick.bind(_this, btn, clickInfo, cb), Scene.DoubleClickDelay);
                         }
-                        var checkDoubleClick = obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) ||
-                            obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                        var checkDoubleClick = obs1.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP) ||
+                            obs2.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP);
                         if (!checkDoubleClick && BABYLON.ActionManager.HasSpecificTrigger(BABYLON.ActionManager.OnDoublePickTrigger)) {
                             act = _this._initActionManager(act, clickInfo);
                             if (act)
@@ -17238,8 +17327,8 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 // PreObservable support
                 if (_this.onPrePointerObservable.hasObservers()) {
-                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE;
-                    var pi = new PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
+                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
+                    var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
                     if (pi.skipOnPointerObservable) {
                         return;
@@ -17292,8 +17381,8 @@ var BABYLON;
                     _this.onPointerMove(evt, pickResult);
                 }
                 if (_this.onPointerObservable.hasObservers()) {
-                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? PointerEventTypes.POINTERWHEEL : PointerEventTypes.POINTERMOVE;
-                    var pi = new PointerInfo(type, evt, pickResult);
+                    var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
+                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     _this.onPointerObservable.notifyObservers(pi, type);
                 }
             };
@@ -17304,8 +17393,8 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 // PreObservable support
                 if (_this.onPrePointerObservable.hasObservers()) {
-                    var type = PointerEventTypes.POINTERDOWN;
-                    var pi = new PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
+                    var type = BABYLON.PointerEventTypes.POINTERDOWN;
+                    var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
                     if (pi.skipOnPointerObservable) {
                         return;
@@ -17364,8 +17453,8 @@ var BABYLON;
                     _this.onPointerDown(evt, pickResult);
                 }
                 if (_this.onPointerObservable.hasObservers()) {
-                    var type = PointerEventTypes.POINTERDOWN;
-                    var pi = new PointerInfo(type, evt, pickResult);
+                    var type = BABYLON.PointerEventTypes.POINTERDOWN;
+                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     _this.onPointerObservable.notifyObservers(pi, type);
                 }
                 // Sprites
@@ -17403,17 +17492,17 @@ var BABYLON;
                     if (this.onPrePointerObservable.hasObservers()) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
-                                if (clickInfo.singleClick && this.onPrePointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
-                                    var type = PointerEventTypes.POINTERTAP;
-                                    var pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                                if (clickInfo.singleClick && this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERTAP;
+                                    var pi = new BABYLON.PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
                                     this.onPrePointerObservable.notifyObservers(pi, type);
                                     if (pi.skipOnPointerObservable) {
                                         return;
                                     }
                                 }
-                                if (clickInfo.doubleClick && this.onPrePointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
-                                    var type = PointerEventTypes.POINTERDOUBLETAP;
-                                    var pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                                if (clickInfo.doubleClick && this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
+                                    var pi = new BABYLON.PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
                                     this.onPrePointerObservable.notifyObservers(pi, type);
                                     if (pi.skipOnPointerObservable) {
                                         return;
@@ -17422,8 +17511,8 @@ var BABYLON;
                             }
                         }
                         else {
-                            var type = PointerEventTypes.POINTERUP;
-                            var pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                            var type = BABYLON.PointerEventTypes.POINTERUP;
+                            var pi = new BABYLON.PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
                             this.onPrePointerObservable.notifyObservers(pi, type);
                             if (pi.skipOnPointerObservable) {
                                 return;
@@ -17452,8 +17541,8 @@ var BABYLON;
                                 this.onPointerPick(evt, pickResult);
                             }
                             if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
-                                var type = PointerEventTypes.POINTERPICK;
-                                var pi = new PointerInfo(type, evt, pickResult);
+                                var type = BABYLON.PointerEventTypes.POINTERPICK;
+                                var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                                 this.onPointerObservable.notifyObservers(pi, type);
                             }
                         }
@@ -17481,21 +17570,21 @@ var BABYLON;
                     if (this.onPointerObservable.hasObservers()) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
-                                if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
-                                    var type = PointerEventTypes.POINTERTAP;
-                                    var pi = new PointerInfo(type, evt, pickResult);
+                                if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERTAP;
+                                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                                     this.onPointerObservable.notifyObservers(pi, type);
                                 }
-                                if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
-                                    var type = PointerEventTypes.POINTERDOUBLETAP;
-                                    var pi = new PointerInfo(type, evt, pickResult);
+                                if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
+                                    var type = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
+                                    var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                                     this.onPointerObservable.notifyObservers(pi, type);
                                 }
                             }
                         }
                         else {
-                            var type = PointerEventTypes.POINTERUP;
-                            var pi = new PointerInfo(type, evt, pickResult);
+                            var type = BABYLON.PointerEventTypes.POINTERUP;
+                            var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                             this.onPointerObservable.notifyObservers(pi, type);
                         }
                     }
@@ -17520,15 +17609,48 @@ var BABYLON;
                 }).bind(_this));
             };
             this._onKeyDown = function (evt) {
+                var type = BABYLON.KeyboardEventTypes.KEYDOWN;
+                if (_this.onPreKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfoPre(type, evt);
+                    _this.onPreKeyboardObservable.notifyObservers(pi, type);
+                    if (pi.skipOnPointerObservable) {
+                        return;
+                    }
+                }
+                if (_this.onKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfo(type, evt);
+                    _this.onKeyboardObservable.notifyObservers(pi, type);
+                }
                 if (_this.actionManager) {
                     _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyDownTrigger, BABYLON.ActionEvent.CreateNewFromScene(_this, evt));
                 }
             };
             this._onKeyUp = function (evt) {
+                var type = BABYLON.KeyboardEventTypes.KEYUP;
+                if (_this.onPreKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfoPre(type, evt);
+                    _this.onPreKeyboardObservable.notifyObservers(pi, type);
+                    if (pi.skipOnPointerObservable) {
+                        return;
+                    }
+                }
+                if (_this.onKeyboardObservable.hasObservers()) {
+                    var pi = new BABYLON.KeyboardInfo(type, evt);
+                    _this.onKeyboardObservable.notifyObservers(pi, type);
+                }
                 if (_this.actionManager) {
                     _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyUpTrigger, BABYLON.ActionEvent.CreateNewFromScene(_this, evt));
                 }
             };
+            var engine = this.getEngine();
+            this._onCanvasFocusObserver = engine.onCanvasFocusObservable.add(function () {
+                canvas.addEventListener("keydown", _this._onKeyDown, false);
+                canvas.addEventListener("keyup", _this._onKeyUp, false);
+            });
+            this._onCanvasBlurObserver = engine.onCanvasBlurObservable.add(function () {
+                canvas.removeEventListener("keydown", _this._onKeyDown);
+                canvas.removeEventListener("keyup", _this._onKeyUp);
+            });
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
             var canvas = this._engine.getRenderingCanvas();
             if (attachMove) {
@@ -17544,20 +17666,27 @@ var BABYLON;
                 canvas.addEventListener(eventPrefix + "up", this._onPointerUp, false);
             }
             canvas.tabIndex = 1;
-            canvas.addEventListener("keydown", this._onKeyDown, false);
-            canvas.addEventListener("keyup", this._onKeyUp, false);
         };
         Scene.prototype.detachControl = function () {
+            var engine = this.getEngine();
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
-            var canvas = this._engine.getRenderingCanvas();
+            var canvas = engine.getRenderingCanvas();
             canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
             canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
             canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
+            engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            engine.onCanvasFocusObservable.remove(this._onCanvasFocusObserver);
             // Wheel
             canvas.removeEventListener('mousewheel', this._onPointerMove);
             canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
+            // Keyboard
             canvas.removeEventListener("keydown", this._onKeyDown);
             canvas.removeEventListener("keyup", this._onKeyUp);
+            // Observables
+            this.onKeyboardObservable.clear();
+            this.onPreKeyboardObservable.clear();
+            this.onPointerObservable.clear();
+            this.onPrePointerObservable.clear();
         };
         // Ready
         Scene.prototype.isReady = function () {
@@ -31889,77 +32018,64 @@ var BABYLON;
         }
         ArcRotateCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
-            element.tabIndex = 1;
-            this._onKeyDown = function (evt) {
-                _this._ctrlPressed = evt.ctrlKey;
-                if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysReset.indexOf(evt.keyCode) !== -1) {
-                    var index = _this._keys.indexOf(evt.keyCode);
-                    if (index === -1) {
-                        _this._keys.push(evt.keyCode);
-                    }
-                    if (evt.preventDefault) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
+            if (this._onCanvasBlurObserver) {
+                return;
+            }
+            this._scene = this.camera.getScene();
+            this._engine = this._scene.getEngine();
+            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(function () {
+                _this._keys = [];
+            });
+            this._onKeyboardObserver = this._scene.onKeyboardObservable.add(function (info) {
+                var evt = info.event;
+                if (info.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
+                    _this._ctrlPressed = evt.ctrlKey;
+                    if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = _this._keys.indexOf(evt.keyCode);
+                        if (index === -1) {
+                            _this._keys.push(evt.keyCode);
+                        }
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
                         }
                     }
                 }
-            };
-            this._onKeyUp = function (evt) {
-                if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                    _this.keysReset.indexOf(evt.keyCode) !== -1) {
-                    var index = _this._keys.indexOf(evt.keyCode);
-                    if (index >= 0) {
-                        _this._keys.splice(index, 1);
-                    }
-                    if (evt.preventDefault) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
+                else {
+                    if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = _this._keys.indexOf(evt.keyCode);
+                        if (index >= 0) {
+                            _this._keys.splice(index, 1);
+                        }
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
                         }
                     }
                 }
-            };
-            this._onLostFocus = function () {
-                _this._keys = [];
-            };
-            this._onFocus = function () {
-                element.addEventListener("keydown", _this._onKeyDown, false);
-                element.addEventListener("keyup", _this._onKeyUp, false);
-            };
-            this._onBlur = function () {
-                element.removeEventListener("keydown", _this._onKeyDown);
-                element.removeEventListener("keyup", _this._onKeyUp);
-            };
-            element.addEventListener("focus", this._onFocus);
-            element.addEventListener("blur", this._onBlur);
-            BABYLON.Tools.RegisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
+            });
         };
         ArcRotateCameraKeyboardMoveInput.prototype.detachControl = function (element) {
-            if (element && this._onBlur) {
-                this._onBlur();
-                element.removeEventListener("focus", this._onFocus);
-                element.removeEventListener("blur", this._onBlur);
+            if (this._scene) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                this._onKeyboardObserver = null;
+                this._onCanvasBlurObserver = null;
             }
-            BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
             this._keys = [];
-            this._onKeyDown = null;
-            this._onKeyUp = null;
-            this._onLostFocus = null;
-            this._onBlur = null;
-            this._onFocus = null;
         };
         ArcRotateCameraKeyboardMoveInput.prototype.checkInputs = function () {
-            if (this._onKeyDown) {
+            if (this._onKeyboardObserver) {
                 var camera = this.camera;
                 for (var index = 0; index < this._keys.length; index++) {
                     var keyCode = this._keys[index];

Plik diff jest za duży
+ 8420 - 8373
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 2 - 2
dist/preview release/gui/babylon.gui.d.ts

@@ -6,7 +6,7 @@ declare module BABYLON.GUI {
         private _resizeObserver;
         private _pointerMoveObserver;
         private _pointerObserver;
-        private _canvasBlurObserver;
+        private _canvasPointerOutObserver;
         private _background;
         _rootContainer: Container;
         _lastControlOver: Control;
@@ -39,7 +39,7 @@ declare module BABYLON.GUI {
         private _doPicking(x, y, type);
         attach(): void;
         attachToMesh(mesh: AbstractMesh, supportPointerMove?: boolean): void;
-        private _attachToOnBlur(scene);
+        private _attachToOnPointerOut(scene);
         static CreateForMesh(mesh: AbstractMesh, width?: number, height?: number, supportPointerMove?: boolean): AdvancedDynamicTexture;
         static CreateFullscreenUI(name: string, foreground?: boolean, scene?: Scene): AdvancedDynamicTexture;
     }

+ 6 - 6
dist/preview release/gui/babylon.gui.js

@@ -146,8 +146,8 @@ var BABYLON;
                 if (this._pointerObserver) {
                     this.getScene().onPointerObservable.remove(this._pointerObserver);
                 }
-                if (this._canvasBlurObserver) {
-                    this.getScene().getEngine().onCanvasBlurObservable.remove(this._canvasBlurObserver);
+                if (this._canvasPointerOutObserver) {
+                    this.getScene().getEngine().onCanvasPointerOutObservable.remove(this._canvasPointerOutObserver);
                 }
                 if (this._layerToDispose) {
                     this._layerToDispose.texture = null;
@@ -281,7 +281,7 @@ var BABYLON;
                     _this._doPicking(x, y, pi.type);
                     pi.skipOnPointerObservable = _this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
                 });
-                this._attachToOnBlur(scene);
+                this._attachToOnPointerOut(scene);
             };
             AdvancedDynamicTexture.prototype.attachToMesh = function (mesh, supportPointerMove) {
                 var _this = this;
@@ -312,11 +312,11 @@ var BABYLON;
                     }
                 });
                 mesh.enablePointerMoveEvents = supportPointerMove;
-                this._attachToOnBlur(scene);
+                this._attachToOnPointerOut(scene);
             };
-            AdvancedDynamicTexture.prototype._attachToOnBlur = function (scene) {
+            AdvancedDynamicTexture.prototype._attachToOnPointerOut = function (scene) {
                 var _this = this;
-                this._canvasBlurObserver = scene.getEngine().onCanvasBlurObservable.add(function () {
+                this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add(function () {
                     if (_this._lastControlOver) {
                         _this._lastControlOver._onPointerOut();
                     }

Plik diff jest za duży
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


+ 2 - 2
dist/preview release/gui/babylon.gui.module.d.ts

@@ -6,7 +6,7 @@ declare module BABYLON.GUI {
         private _resizeObserver;
         private _pointerMoveObserver;
         private _pointerObserver;
-        private _canvasBlurObserver;
+        private _canvasPointerOutObserver;
         private _background;
         _rootContainer: Container;
         _lastControlOver: Control;
@@ -39,7 +39,7 @@ declare module BABYLON.GUI {
         private _doPicking(x, y, type);
         attach(): void;
         attachToMesh(mesh: AbstractMesh, supportPointerMove?: boolean): void;
-        private _attachToOnBlur(scene);
+        private _attachToOnPointerOut(scene);
         static CreateForMesh(mesh: AbstractMesh, width?: number, height?: number, supportPointerMove?: boolean): AdvancedDynamicTexture;
         static CreateFullscreenUI(name: string, foreground?: boolean, scene?: Scene): AdvancedDynamicTexture;
     }

+ 7 - 7
gui/src/advancedDynamicTexture.ts

@@ -7,7 +7,7 @@ module BABYLON.GUI {
         private _resizeObserver: Observer<Engine>;
         private _pointerMoveObserver: Observer<PointerInfoPre>;
         private _pointerObserver: Observer<PointerInfo>;
-        private _canvasBlurObserver: Observer<Engine>;
+        private _canvasPointerOutObserver: Observer<Engine>;
         private _background: string;
         public _rootContainer = new Container("root");
         public _lastControlOver: Control;
@@ -145,8 +145,8 @@ module BABYLON.GUI {
                 this.getScene().onPointerObservable.remove(this._pointerObserver);
             }
 
-            if (this._canvasBlurObserver) {
-                this.getScene().getEngine().onCanvasBlurObservable.remove(this._canvasBlurObserver);
+            if (this._canvasPointerOutObserver) {
+                this.getScene().getEngine().onCanvasPointerOutObservable.remove(this._canvasPointerOutObserver);
             }
 
             if (this._layerToDispose) {
@@ -309,7 +309,7 @@ module BABYLON.GUI {
                 pi.skipOnPointerObservable = this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
             });
 
-            this._attachToOnBlur(scene);
+            this._attachToOnPointerOut(scene);
         }
 
         public attachToMesh(mesh: AbstractMesh, supportPointerMove = true): void {
@@ -339,11 +339,11 @@ module BABYLON.GUI {
             });
 
             mesh.enablePointerMoveEvents = supportPointerMove;
-            this._attachToOnBlur(scene);
+            this._attachToOnPointerOut(scene);
         }
 
-        private _attachToOnBlur(scene: Scene): void {
-            this._canvasBlurObserver = scene.getEngine().onCanvasBlurObservable.add(() => {
+        private _attachToOnPointerOut(scene: Scene): void {
+            this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add(() => {
                 if (this._lastControlOver) {
                     this._lastControlOver._onPointerOut();
                 }            

+ 57 - 78
src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts

@@ -2,11 +2,6 @@ module BABYLON {
     export class ArcRotateCameraKeyboardMoveInput implements ICameraInput<ArcRotateCamera> {
         camera: ArcRotateCamera;
         private _keys = [];
-        private _onKeyDown: (e: KeyboardEvent) => any;
-        private _onKeyUp: (e: KeyboardEvent) => any;
-        private _onLostFocus: (e: FocusEvent) => any;
-        private _onFocus: () => void;
-        private _onBlur: () => void;
         
         @serialize()
         public keysUp = [38];
@@ -27,97 +22,81 @@ module BABYLON {
         public panningSensibility: number = 50.0;        
 
         private _ctrlPressed: boolean;
+        private _onCanvasBlurObserver: Observer<Engine>;
+        private _onKeyboardObserver: Observer<KeyboardInfo>;
+        private _engine: Engine;
+        private _scene: Scene;
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
-            element.tabIndex = 1;
-
-            this._onKeyDown = evt => {
-
-                this._ctrlPressed = evt.ctrlKey;
-
-                if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                    this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                    this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                    this.keysReset.indexOf(evt.keyCode) !== -1) {
-                    var index = this._keys.indexOf(evt.keyCode);
+            if (this._onCanvasBlurObserver) {
+                return;
+            }
+            
+            this._scene = this.camera.getScene();
+            this._engine = this._scene.getEngine();
 
-                    if (index === -1) {
-                        this._keys.push(evt.keyCode);
-                    }
+            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(()=>{
+                this._keys = [];
+            });
+
+            this._onKeyboardObserver = this._scene.onKeyboardObservable.add(info => {
+                let evt = info.event;
+
+                if (info.type === KeyboardEventTypes.KEYDOWN) {
+                    this._ctrlPressed = evt.ctrlKey;
+                    
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index === -1) {
+                            this._keys.push(evt.keyCode);
+                        }
 
-                    if (evt.preventDefault) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
                         }
                     }
-                }
-            };
-
-            this._onKeyUp = evt => {
-                
-                if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                    this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                    this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                    this.keysReset.indexOf(evt.keyCode) !== -1) {
-                    var index = this._keys.indexOf(evt.keyCode);
-
-                    if (index >= 0) {
-                        this._keys.splice(index, 1);
-                    }
-
-                    if (evt.preventDefault) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
+                } else {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+    
+                        if (index >= 0) {
+                            this._keys.splice(index, 1);
+                        }
+    
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
                         }
                     }
                 }
-            };
-
-            this._onLostFocus = () => {
-                this._keys = [];
-            };
-
-            this._onFocus = () => {
-                element.addEventListener("keydown", this._onKeyDown, false);
-                element.addEventListener("keyup", this._onKeyUp, false);   
-            }
-
-            this._onBlur = () => {
-                element.removeEventListener("keydown", this._onKeyDown);
-                element.removeEventListener("keyup", this._onKeyUp);
-            }
-
-            element.addEventListener("focus", this._onFocus);
-            element.addEventListener("blur", this._onBlur);
-
-            Tools.RegisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
+            });    
         }
 
         public detachControl(element: HTMLElement) {
-            if (element && this._onBlur) {
-                this._onBlur();
-                element.removeEventListener("focus", this._onFocus);
-                element.removeEventListener("blur", this._onBlur);
+            if (this._scene) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                this._onKeyboardObserver = null;
+                this._onCanvasBlurObserver = null;
             }
 
-            Tools.UnregisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
-            
             this._keys = [];
-            this._onKeyDown = null;
-            this._onKeyUp = null;
-            this._onLostFocus = null;
-            this._onBlur = null;
-            this._onFocus = null;
         }
 
         public checkInputs() {
-            if (this._onKeyDown){
+            if (this._onKeyboardObserver){
                 var camera = this.camera;
 
                 for (var index = 0; index < this._keys.length; index++) {

+ 28 - 28
src/Cameras/Inputs/babylon.freeCameraKeyboardMoveInput.ts

@@ -2,8 +2,10 @@ module BABYLON {
     export class FreeCameraKeyboardMoveInput implements ICameraInput<FreeCamera> {
         camera: FreeCamera;
         private _keys = [];
-        private _onKeyDown: (e: KeyboardEvent) => any;
-        private _onKeyUp: (e: KeyboardEvent) => any;
+        private _onCanvasBlurObserver: Observer<Engine>;
+        private _onKeyboardObserver: Observer<KeyboardInfo>;
+        private _engine: Engine;
+        private _scene: Scene;        
 
         @serialize()
         public keysUp = [38];
@@ -18,10 +20,21 @@ module BABYLON {
         public keysRight = [39];
 
         attachControl(element : HTMLElement, noPreventDefault?: boolean) {
-            if (!this._onKeyDown) {
-                element.tabIndex = 1;
+            if (this._onCanvasBlurObserver) {
+                return;
+            }
+
+            this._scene = this.camera.getScene();
+            this._engine = this._scene.getEngine();
+
+            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(()=>{
+                this._keys = [];
+            });
 
-                this._onKeyDown = evt => {
+            this._onKeyboardObserver = this._scene.onKeyboardObservable.add(info => {
+                let evt = info.event;
+
+                if (info.type === KeyboardEventTypes.KEYDOWN) {
                     if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -35,9 +48,7 @@ module BABYLON {
                             evt.preventDefault();
                         }
                     }
-                };
-
-                this._onKeyUp = evt => {
+                } else {
                     if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -51,33 +62,22 @@ module BABYLON {
                             evt.preventDefault();
                         }
                     }
-                };
-
-                element.addEventListener("keydown", this._onKeyDown, false);
-                element.addEventListener("keyup", this._onKeyUp, false);
-
-                Tools.RegisterTopRootEvents([
-                    { name: "blur", handler: this._onLostFocus }
-                ]);
-            }
+                }
+            });     
         }
 
         detachControl(element : HTMLElement) {
-            if (this._onKeyDown) {
-                element.removeEventListener("keydown", this._onKeyDown);
-                element.removeEventListener("keyup", this._onKeyUp);
-
-                Tools.UnregisterTopRootEvents([
-                    { name: "blur", handler: this._onLostFocus }
-                ]);
-                this._keys = [];
-                this._onKeyDown = null;
-                this._onKeyUp = null;
+            if (this._scene) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                this._onKeyboardObserver = null;
+                this._onCanvasBlurObserver = null;
             }
+            this._keys = [];
         }
         
         public checkInputs() {
-            if (this._onKeyDown){
+            if (this._onKeyboardObserver){
                 var camera = this.camera;
                 // Keyboard
                 for (var index = 0; index < this._keys.length; index++) {

+ 33 - 0
src/Events/babylon.keyboardEvents.ts

@@ -0,0 +1,33 @@
+module BABYLON {
+    
+    export class KeyboardEventTypes {
+        static _KEYDOWN = 0x01;
+        static _KEYUP = 0x02;
+
+        public static get KEYDOWN(): number {
+            return KeyboardEventTypes._KEYDOWN;
+        }
+
+        public static get KEYUP(): number {
+            return KeyboardEventTypes._KEYUP;
+        }
+    }
+
+    export class KeyboardInfo {
+        constructor(public type: number, public event: KeyboardEvent) {
+        }
+    }
+
+    /**
+     * This class is used to store keyboard related info for the onPreKeyboardObservable event.
+     * Set the skipOnKeyboardObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onKeyboardObservable
+     */
+    export class KeyboardInfoPre extends KeyboardInfo {
+        constructor(type: number, event: KeyboardEvent) {
+            super(type, event);
+            this.skipOnPointerObservable = false;
+        }
+
+        public skipOnPointerObservable: boolean;
+    }   
+}

+ 69 - 0
src/Events/babylon.pointerEvents.ts

@@ -0,0 +1,69 @@
+module BABYLON {
+    export class PointerEventTypes {
+        static _POINTERDOWN = 0x01;
+        static _POINTERUP = 0x02;
+        static _POINTERMOVE = 0x04;
+        static _POINTERWHEEL = 0x08;
+        static _POINTERPICK = 0x10;
+        static _POINTERTAP = 0x20;
+        static _POINTERDOUBLETAP = 0x40;
+
+        public static get POINTERDOWN(): number {
+            return PointerEventTypes._POINTERDOWN;
+        }
+
+        public static get POINTERUP(): number {
+            return PointerEventTypes._POINTERUP;
+        }
+
+        public static get POINTERMOVE(): number {
+            return PointerEventTypes._POINTERMOVE;
+        }
+
+        public static get POINTERWHEEL(): number {
+            return PointerEventTypes._POINTERWHEEL;
+        }
+
+        public static get POINTERPICK(): number {
+            return PointerEventTypes._POINTERPICK;
+        }
+
+        public static get POINTERTAP(): number {
+            return PointerEventTypes._POINTERTAP;
+        }
+
+        public static get POINTERDOUBLETAP(): number {
+            return PointerEventTypes._POINTERDOUBLETAP;
+        }
+    }
+
+    export class PointerInfoBase {
+        constructor(public type: number, public event: PointerEvent | MouseWheelEvent) {
+        }
+    }
+
+    /**
+     * This class is used to store pointer related info for the onPrePointerObservable event.
+     * Set the skipOnPointerObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onPointerObservable
+     */
+    export class PointerInfoPre extends PointerInfoBase {
+        constructor(type: number, event: PointerEvent | MouseWheelEvent, localX, localY) {
+            super(type, event);
+            this.skipOnPointerObservable = false;
+            this.localPosition = new Vector2(localX, localY);
+        }
+
+        public localPosition: Vector2;
+        public skipOnPointerObservable: boolean;
+    }
+
+    /**
+     * This type contains all the data related to a pointer event in Babylon.js.
+     * The event member is an instance of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel. The different event types can be found in the PointerEventTypes class.
+     */
+    export class PointerInfo extends PointerInfoBase {
+        constructor(type: number, event: PointerEvent | MouseWheelEvent, public pickInfo: PickingInfo) {
+            super(type, event);
+        }
+    }    
+}

+ 39 - 7
src/babylon.engine.ts

@@ -539,10 +539,20 @@
         public onResizeObservable = new Observable<Engine>();
 
         /**
-         * Observable event triggered each time the canvas lost focus
+         * Observable event triggered each time the canvas loses focus
          */
         public onCanvasBlurObservable = new Observable<Engine>();
 
+        /**
+         * Observable event triggered each time the canvas gains focus
+         */
+        public onCanvasFocusObservable = new Observable<Engine>();        
+
+        /**
+         * Observable event triggered each time the canvas receives pointerout event
+         */
+        public onCanvasPointerOutObservable = new Observable<Engine>();
+
         //WebVR
 
         //The new WebVR uses promises.
@@ -584,9 +594,14 @@
 
         public static audioEngine: AudioEngine;
 
-        private _onCanvasBlur: () => void;
-        private _onBlur: () => void;
+        
+        // Focus
         private _onFocus: () => void;
+        private _onBlur: () => void;       
+        private _onCanvasPointerOut: () => void;
+        private _onCanvasBlur: () => void;
+        private _onCanvasFocus: () => void;
+        
         private _onFullscreenChange: () => void;
         private _onPointerLockChange: () => void;
 
@@ -780,12 +795,24 @@
                 if (!this._gl) {
                     throw new Error("WebGL not supported");
                 }
-
+    
+                this._onCanvasFocus = () => {
+                    this.onCanvasFocusObservable.notifyObservers(this);
+                }
+    
+                this._onCanvasBlur = () => {
+                    this.onCanvasBlurObservable.notifyObservers(this);
+                }
+    
+                canvas.addEventListener("focus", this._onCanvasFocus);
+                canvas.addEventListener("blur", this._onCanvasBlur);
+    
                 this._onBlur = () => {
                     if (this.disablePerformanceMonitorInBackground) {
                         this._performanceMonitor.disable();
                     }
                     this._windowIsBackground = true;
+                    this.onCanvasBlurObservable.notifyObservers(this);
                 };
 
                 this._onFocus = () => {
@@ -793,16 +820,17 @@
                         this._performanceMonitor.enable();
                     }
                     this._windowIsBackground = false;
+                    this.onCanvasFocusObservable.notifyObservers(this);
                 };
 
-                this._onCanvasBlur = () => {
-                    this.onCanvasBlurObservable.notifyObservers(this);
+                this._onCanvasPointerOut = () => {
+                    this.onCanvasPointerOutObservable.notifyObservers(this);
                 };
 
                 window.addEventListener("blur", this._onBlur);
                 window.addEventListener("focus", this._onFocus);
 
-                canvas.addEventListener("pointerout", this._onCanvasBlur);
+                canvas.addEventListener("pointerout", this._onCanvasPointerOut);
             } else {
                 this._gl = <WebGLRenderingContext>canvasOrContext;
                 this._renderingCanvas = this._gl.canvas
@@ -4315,6 +4343,8 @@
             window.removeEventListener("focus", this._onFocus);
             window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
             window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
+            this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
+            this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);            
             this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
 
             if (!this._doNotHandleContextLost) {
@@ -4345,6 +4375,8 @@
 
             this.onResizeObservable.clear();
             this.onCanvasBlurObservable.clear();
+            this.onCanvasFocusObservable.clear();
+            this.onCanvasPointerOutObservable.clear();
 
             BABYLON.Effect.ResetCache();
         }

+ 67 - 77
src/babylon.scene.ts

@@ -36,75 +36,6 @@
         }
     }
 
-    export class PointerEventTypes {
-        static _POINTERDOWN = 0x01;
-        static _POINTERUP = 0x02;
-        static _POINTERMOVE = 0x04;
-        static _POINTERWHEEL = 0x08;
-        static _POINTERPICK = 0x10;
-        static _POINTERTAP = 0x20;
-        static _POINTERDOUBLETAP = 0x40;
-
-        public static get POINTERDOWN(): number {
-            return PointerEventTypes._POINTERDOWN;
-        }
-
-        public static get POINTERUP(): number {
-            return PointerEventTypes._POINTERUP;
-        }
-
-        public static get POINTERMOVE(): number {
-            return PointerEventTypes._POINTERMOVE;
-        }
-
-        public static get POINTERWHEEL(): number {
-            return PointerEventTypes._POINTERWHEEL;
-        }
-
-        public static get POINTERPICK(): number {
-            return PointerEventTypes._POINTERPICK;
-        }
-
-        public static get POINTERTAP(): number {
-            return PointerEventTypes._POINTERTAP;
-        }
-
-        public static get POINTERDOUBLETAP(): number {
-            return PointerEventTypes._POINTERDOUBLETAP;
-        }
-    }
-
-    export class PointerInfoBase {
-        constructor(public type: number, public event: PointerEvent | MouseWheelEvent) {
-        }
-
-    }
-
-    /**
-     * This class is used to store pointer related info for the onPrePointerObservable event.
-     * Set the skipOnPointerObservable property to true if you want the engine to stop any process after this event is triggered, even not calling onPointerObservable
-     */
-    export class PointerInfoPre extends PointerInfoBase {
-        constructor(type: number, event: PointerEvent | MouseWheelEvent, localX, localY) {
-            super(type, event);
-            this.skipOnPointerObservable = false;
-            this.localPosition = new Vector2(localX, localY);
-        }
-
-        public localPosition: Vector2;
-        public skipOnPointerObservable: boolean;
-    }
-
-    /**
-     * This type contains all the data related to a pointer event in Babylon.js.
-     * The event member is an instance of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel. The different event types can be found in the PointerEventTypes class.
-     */
-    export class PointerInfo extends PointerInfoBase {
-        constructor(type: number, event: PointerEvent | MouseWheelEvent, public pickInfo: PickingInfo) {
-            super(type, event);
-        }
-    }
-
     /**
      * This class is used by the onRenderingGroupObservable
      */
@@ -434,8 +365,8 @@
         }
 
         /**
-         * 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
+         * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
+         * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
          */
         public onPrePointerObservable = new Observable<PointerInfoPre>();
 
@@ -491,8 +422,21 @@
         public _mirroredCameraPosition: Vector3;
 
         // Keyboard
+
+        /**
+         * This observable event is triggered when any keyboard event si raised and registered during Scene.attachControl()
+         * You have the possibility to skip the process and the call to onKeyboardObservable by setting KeyboardInfoPre.skipOnPointerObservable to true
+         */
+        public onPreKeyboardObservable = new Observable<KeyboardInfoPre>();
+        
+        /**
+         * Observable event triggered each time an keyboard event is received from the hosting window
+         */
+        public onKeyboardObservable = new Observable<KeyboardInfo>();
         private _onKeyDown: (evt: Event) => void;
         private _onKeyUp: (evt: Event) => void;
+        private _onCanvasFocusObserver: Observer<Engine>;
+        private _onCanvasBlurObserver: Observer<Engine>;
 
         // Coordinate system
         /**
@@ -1537,18 +1481,56 @@
                 }).bind(this));
             };
 
-            this._onKeyDown = (evt: Event) => {
+            this._onKeyDown = (evt: KeyboardEvent) => {
+                let type = KeyboardEventTypes.KEYDOWN;
+                if (this.onPreKeyboardObservable.hasObservers()) {
+                    let pi = new KeyboardInfoPre(type, evt);
+                    this.onPreKeyboardObservable.notifyObservers(pi, type);
+                    if (pi.skipOnPointerObservable) {
+                        return;
+                    }
+                }
+
+                if (this.onKeyboardObservable.hasObservers()) {
+                    let pi = new KeyboardInfo(type, evt);
+                    this.onKeyboardObservable.notifyObservers(pi, type);
+                }
+
                 if (this.actionManager) {
                     this.actionManager.processTrigger(ActionManager.OnKeyDownTrigger, ActionEvent.CreateNewFromScene(this, evt));
                 }
             };
 
-            this._onKeyUp = (evt: Event) => {
+            this._onKeyUp = (evt: KeyboardEvent) => {
+                let type = KeyboardEventTypes.KEYUP;
+                if (this.onPreKeyboardObservable.hasObservers()) {
+                    let pi = new KeyboardInfoPre(type, evt);
+                    this.onPreKeyboardObservable.notifyObservers(pi, type);
+                    if (pi.skipOnPointerObservable) {
+                        return;
+                    }
+                }
+
+                if (this.onKeyboardObservable.hasObservers()) {
+                    let pi = new KeyboardInfo(type, evt);
+                    this.onKeyboardObservable.notifyObservers(pi, type);
+                }
+
                 if (this.actionManager) {
                     this.actionManager.processTrigger(ActionManager.OnKeyUpTrigger, ActionEvent.CreateNewFromScene(this, evt));
                 }
             };
 
+            let engine = this.getEngine();
+            this._onCanvasFocusObserver = engine.onCanvasFocusObservable.add(()=>{
+                canvas.addEventListener("keydown", this._onKeyDown, false);
+                canvas.addEventListener("keyup", this._onKeyUp, false);   
+            });
+
+            this._onCanvasBlurObserver = engine.onCanvasBlurObservable.add(()=>{                
+                canvas.removeEventListener("keydown", this._onKeyDown);
+                canvas.removeEventListener("keyup", this._onKeyUp);
+            });
 
             var eventPrefix = Tools.GetPointerPrefix();
             var canvas = this._engine.getRenderingCanvas();
@@ -1568,25 +1550,33 @@
             }
 
             canvas.tabIndex = 1;
-
-            canvas.addEventListener("keydown", this._onKeyDown, false);
-            canvas.addEventListener("keyup", this._onKeyUp, false);
         }
 
         public detachControl() {
+            let engine = this.getEngine();
             var eventPrefix = Tools.GetPointerPrefix();
-            var canvas = this._engine.getRenderingCanvas();
+            var canvas = engine.getRenderingCanvas();
 
             canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
             canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
             canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
 
+            engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            engine.onCanvasFocusObservable.remove(this._onCanvasFocusObserver);
+
             // Wheel
             canvas.removeEventListener('mousewheel', this._onPointerMove);
             canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
 
+            // Keyboard
             canvas.removeEventListener("keydown", this._onKeyDown);
             canvas.removeEventListener("keyup", this._onKeyUp);
+
+            // Observables
+            this.onKeyboardObservable.clear();
+            this.onPreKeyboardObservable.clear();
+            this.onPointerObservable.clear();
+            this.onPrePointerObservable.clear();     
         }
 
         // Ready