瀏覽代碼

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

Julien Moreau-Mathis 8 年之前
父節點
當前提交
247892729f
共有 53 個文件被更改,包括 22005 次插入20654 次删除
  1. 2 0
      Playground/indexStable.html
  2. 2 0
      Tools/Gulp/config.json
  3. 6099 6050
      dist/preview release/babylon.d.ts
  4. 43 43
      dist/preview release/babylon.js
  5. 381 247
      dist/preview release/babylon.max.js
  6. 6099 6050
      dist/preview release/babylon.module.d.ts
  7. 44 44
      dist/preview release/babylon.worker.js
  8. 3746 3697
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  9. 33 33
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  10. 417 224
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  11. 3746 3697
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  12. 24 3
      dist/preview release/gui/babylon.gui.d.ts
  13. 210 14
      dist/preview release/gui/babylon.gui.js
  14. 3 3
      dist/preview release/gui/babylon.gui.min.js
  15. 24 3
      dist/preview release/gui/babylon.gui.module.d.ts
  16. 263 263
      dist/preview release/inspector/babylon.inspector.bundle.js
  17. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  18. 1 0
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  19. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  20. 8 1
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  21. 77 10
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  22. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  23. 9 1
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  24. 77 10
      dist/preview release/loaders/babylon.glTFFileLoader.js
  25. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  26. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  27. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  28. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  29. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  30. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  31. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  32. 4 2
      dist/preview release/what's new.md
  33. 52 7
      gui/src/advancedDynamicTexture.ts
  34. 1 0
      gui/src/controls/control.ts
  35. 185 11
      gui/src/controls/inputText.ts
  36. 1 0
      loaders/src/glTF/1.0/babylon.glTFLoaderInterfaces.ts
  37. 103 10
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  38. 1 1
      localDev/index.html
  39. 3 2
      src/Animations/babylon.animation.ts
  40. 57 78
      src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts
  41. 28 28
      src/Cameras/Inputs/babylon.freeCameraKeyboardMoveInput.ts
  42. 11 3
      src/Cameras/VR/babylon.webVRCamera.ts
  43. 4 4
      src/Cameras/babylon.freeCamera.ts
  44. 5 5
      src/Collisions/babylon.collisionCoordinator.ts
  45. 33 0
      src/Events/babylon.keyboardEvents.ts
  46. 69 0
      src/Events/babylon.pointerEvents.ts
  47. 3 3
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  48. 3 3
      src/Materials/babylon.effect.ts
  49. 1 1
      src/Materials/babylon.material.ts
  50. 1 1
      src/Materials/babylon.uniformBuffer.ts
  51. 2 2
      src/Mesh/babylon.abstractMesh.ts
  52. 48 8
      src/babylon.engine.ts
  53. 67 77
      src/babylon.scene.ts

+ 2 - 0
Playground/indexStable.html

@@ -45,6 +45,8 @@
     <script src="https://cdn.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
     <script src="https://cdn.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
     <script src="https://cdn.babylonjs.com/loaders/babylon.stlFileLoader.js"></script>
     <script src="https://cdn.babylonjs.com/loaders/babylon.stlFileLoader.js"></script>
 
 
+    <script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>    
+
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
     <link href="css/index.css" rel="stylesheet" />
     <link href="css/index.css" rel="stylesheet" />
 </head>
 </head>

+ 2 - 0
Tools/Gulp/config.json

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

File diff suppressed because it is too large
+ 6099 - 6050
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 43 - 43
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 381 - 247
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 6099 - 6050
dist/preview release/babylon.module.d.ts


File diff suppressed because it is too large
+ 44 - 44
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 3746 - 3697
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


File diff suppressed because it is too large
+ 33 - 33
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


File diff suppressed because it is too large
+ 417 - 224
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


File diff suppressed because it is too large
+ 3746 - 3697
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 24 - 3
dist/preview release/gui/babylon.gui.d.ts

@@ -1,14 +1,21 @@
 
 
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
+    interface IFocusableControl {
+        onFocus(): void;
+        onBlur(): void;
+        processKeyboard(evt: KeyboardEvent): void;
+    }
     class AdvancedDynamicTexture extends DynamicTexture {
     class AdvancedDynamicTexture extends DynamicTexture {
         private _isDirty;
         private _isDirty;
         private _renderObserver;
         private _renderObserver;
         private _resizeObserver;
         private _resizeObserver;
+        private _preKeyboardObserver;
         private _pointerMoveObserver;
         private _pointerMoveObserver;
         private _pointerObserver;
         private _pointerObserver;
-        private _canvasBlurObserver;
+        private _canvasPointerOutObserver;
         private _background;
         private _background;
         _rootContainer: Container;
         _rootContainer: Container;
+        _lastPickedControl: Control;
         _lastControlOver: Control;
         _lastControlOver: Control;
         _lastControlDown: Control;
         _lastControlDown: Control;
         _capturingControl: Control;
         _capturingControl: Control;
@@ -20,12 +27,14 @@ declare module BABYLON.GUI {
         private _idealWidth;
         private _idealWidth;
         private _idealHeight;
         private _idealHeight;
         private _renderAtIdealSize;
         private _renderAtIdealSize;
+        private _focusedControl;
         background: string;
         background: string;
         idealWidth: number;
         idealWidth: number;
         idealHeight: number;
         idealHeight: number;
         renderAtIdealSize: boolean;
         renderAtIdealSize: boolean;
         readonly layer: Layer;
         readonly layer: Layer;
         readonly rootContainer: Container;
         readonly rootContainer: Container;
+        focusedControl: IFocusableControl;
         constructor(name: string, width: number, height: number, scene: Scene, generateMipMaps?: boolean, samplingMode?: number);
         constructor(name: string, width: number, height: number, scene: Scene, generateMipMaps?: boolean, samplingMode?: number);
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         markAsDirty(): void;
         markAsDirty(): void;
@@ -39,7 +48,7 @@ declare module BABYLON.GUI {
         private _doPicking(x, y, type);
         private _doPicking(x, y, type);
         attach(): void;
         attach(): void;
         attachToMesh(mesh: AbstractMesh, supportPointerMove?: boolean): 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 CreateForMesh(mesh: AbstractMesh, width?: number, height?: number, supportPointerMove?: boolean): AdvancedDynamicTexture;
         static CreateFullscreenUI(name: string, foreground?: boolean, scene?: Scene): AdvancedDynamicTexture;
         static CreateFullscreenUI(name: string, foreground?: boolean, scene?: Scene): AdvancedDynamicTexture;
     }
     }
@@ -584,22 +593,34 @@ declare module BABYLON.GUI {
 
 
 
 
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
-    class InputText extends Control {
+    class InputText extends Control implements IFocusableControl {
         name: string;
         name: string;
         private _text;
         private _text;
         private _background;
         private _background;
+        private _focusedBackground;
         private _thickness;
         private _thickness;
         private _margin;
         private _margin;
         private _autoStretchWidth;
         private _autoStretchWidth;
         private _maxWidth;
         private _maxWidth;
+        private _isFocused;
+        private _blinkTimeout;
+        private _blinkIsEven;
+        private _cursorOffset;
+        private _scrollLeft;
         maxWidth: string | number;
         maxWidth: string | number;
         margin: string;
         margin: string;
         autoStretchWidth: boolean;
         autoStretchWidth: boolean;
         thickness: number;
         thickness: number;
+        focusedBackground: string;
         background: string;
         background: string;
         text: string;
         text: string;
         constructor(name?: string, text?: string);
         constructor(name?: string, text?: string);
+        onBlur(): void;
+        onFocus(): void;
         protected _getTypeName(): string;
         protected _getTypeName(): string;
+        processKeyboard(evt: KeyboardEvent): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+        protected _onPointerDown(coordinates: Vector2): boolean;
+        protected _onPointerUp(coordinates: Vector2): void;
     }
     }
 }
 }

+ 210 - 14
dist/preview release/gui/babylon.gui.js

@@ -30,6 +30,15 @@ var BABYLON;
                 _this._idealHeight = 0;
                 _this._idealHeight = 0;
                 _this._renderAtIdealSize = false;
                 _this._renderAtIdealSize = false;
                 _this._renderObserver = _this.getScene().onBeforeCameraRenderObservable.add(function (camera) { return _this._checkUpdate(camera); });
                 _this._renderObserver = _this.getScene().onBeforeCameraRenderObservable.add(function (camera) { return _this._checkUpdate(camera); });
+                _this._preKeyboardObserver = _this.getScene().onPreKeyboardObservable.add(function (info) {
+                    if (!_this._focusedControl) {
+                        return;
+                    }
+                    if (info.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
+                        _this._focusedControl.processKeyboard(info.event);
+                    }
+                    info.skipOnPointerObservable = true;
+                });
                 _this._rootContainer._link(null, _this);
                 _this._rootContainer._link(null, _this);
                 _this.hasAlpha = true;
                 _this.hasAlpha = true;
                 if (!width || !height) {
                 if (!width || !height) {
@@ -111,6 +120,25 @@ var BABYLON;
                 enumerable: true,
                 enumerable: true,
                 configurable: true
                 configurable: true
             });
             });
+            Object.defineProperty(AdvancedDynamicTexture.prototype, "focusedControl", {
+                get: function () {
+                    return this._focusedControl;
+                },
+                set: function (control) {
+                    if (this._focusedControl === control) {
+                        return;
+                    }
+                    if (!this._focusedControl) {
+                        control.onFocus();
+                    }
+                    else {
+                        this._focusedControl.onBlur();
+                    }
+                    this._focusedControl = control;
+                },
+                enumerable: true,
+                configurable: true
+            });
             AdvancedDynamicTexture.prototype.executeOnAllControls = function (func, container) {
             AdvancedDynamicTexture.prototype.executeOnAllControls = function (func, container) {
                 if (!container) {
                 if (!container) {
                     container = this._rootContainer;
                     container = this._rootContainer;
@@ -146,8 +174,8 @@ var BABYLON;
                 if (this._pointerObserver) {
                 if (this._pointerObserver) {
                     this.getScene().onPointerObservable.remove(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) {
                 if (this._layerToDispose) {
                     this._layerToDispose.texture = null;
                     this._layerToDispose.texture = null;
@@ -262,6 +290,12 @@ var BABYLON;
                         this._lastControlOver = null;
                         this._lastControlOver = null;
                     }
                     }
                 }
                 }
+                // Focus management
+                if (this._focusedControl) {
+                    if (this._focusedControl !== this._lastPickedControl) {
+                        this.focusedControl = null;
+                    }
+                }
             };
             };
             AdvancedDynamicTexture.prototype.attach = function () {
             AdvancedDynamicTexture.prototype.attach = function () {
                 var _this = this;
                 var _this = this;
@@ -281,7 +315,7 @@ var BABYLON;
                     _this._doPicking(x, y, pi.type);
                     _this._doPicking(x, y, pi.type);
                     pi.skipOnPointerObservable = _this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
                     pi.skipOnPointerObservable = _this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
                 });
                 });
-                this._attachToOnBlur(scene);
+                this._attachToOnPointerOut(scene);
             };
             };
             AdvancedDynamicTexture.prototype.attachToMesh = function (mesh, supportPointerMove) {
             AdvancedDynamicTexture.prototype.attachToMesh = function (mesh, supportPointerMove) {
                 var _this = this;
                 var _this = this;
@@ -312,11 +346,11 @@ var BABYLON;
                     }
                     }
                 });
                 });
                 mesh.enablePointerMoveEvents = supportPointerMove;
                 mesh.enablePointerMoveEvents = supportPointerMove;
-                this._attachToOnBlur(scene);
+                this._attachToOnPointerOut(scene);
             };
             };
-            AdvancedDynamicTexture.prototype._attachToOnBlur = function (scene) {
+            AdvancedDynamicTexture.prototype._attachToOnPointerOut = function (scene) {
                 var _this = this;
                 var _this = this;
-                this._canvasBlurObserver = scene.getEngine().onCanvasBlurObservable.add(function () {
+                this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add(function () {
                     if (_this._lastControlOver) {
                     if (_this._lastControlOver) {
                         _this._lastControlOver._onPointerOut();
                         _this._lastControlOver._onPointerOut();
                     }
                     }
@@ -1429,6 +1463,7 @@ var BABYLON;
                 if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
                 if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
                     this._onPointerDown(this._dummyVector2);
                     this._onPointerDown(this._dummyVector2);
                     this._host._lastControlDown = this;
                     this._host._lastControlDown = this;
+                    this._host._lastPickedControl = this;
                     return true;
                     return true;
                 }
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERUP) {
                 if (type === BABYLON.PointerEventTypes.POINTERUP) {
@@ -3719,10 +3754,14 @@ var BABYLON;
                 _this.name = name;
                 _this.name = name;
                 _this._text = "";
                 _this._text = "";
                 _this._background = "black";
                 _this._background = "black";
+                _this._focusedBackground = "black";
                 _this._thickness = 1;
                 _this._thickness = 1;
                 _this._margin = new GUI.ValueAndUnit(10, GUI.ValueAndUnit.UNITMODE_PIXEL);
                 _this._margin = new GUI.ValueAndUnit(10, GUI.ValueAndUnit.UNITMODE_PIXEL);
                 _this._autoStretchWidth = true;
                 _this._autoStretchWidth = true;
                 _this._maxWidth = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
                 _this._maxWidth = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
+                _this._isFocused = false;
+                _this._blinkIsEven = false;
+                _this._cursorOffset = 0;
                 _this.text = text;
                 _this.text = text;
                 return _this;
                 return _this;
             }
             }
@@ -3784,6 +3823,20 @@ var BABYLON;
                 enumerable: true,
                 enumerable: true,
                 configurable: true
                 configurable: true
             });
             });
+            Object.defineProperty(InputText.prototype, "focusedBackground", {
+                get: function () {
+                    return this._focusedBackground;
+                },
+                set: function (value) {
+                    if (this._focusedBackground === value) {
+                        return;
+                    }
+                    this._focusedBackground = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(InputText.prototype, "background", {
             Object.defineProperty(InputText.prototype, "background", {
                 get: function () {
                 get: function () {
                     return this._background;
                     return this._background;
@@ -3812,29 +3865,162 @@ var BABYLON;
                 enumerable: true,
                 enumerable: true,
                 configurable: true
                 configurable: true
             });
             });
+            InputText.prototype.onBlur = function () {
+                this._isFocused = false;
+                this._scrollLeft = null;
+                this._cursorOffset = 0;
+                clearTimeout(this._blinkTimeout);
+                this._markAsDirty();
+            };
+            InputText.prototype.onFocus = function () {
+                this._scrollLeft = null;
+                this._isFocused = true;
+                this._blinkIsEven = false;
+                this._cursorOffset = 0;
+                this._markAsDirty();
+            };
             InputText.prototype._getTypeName = function () {
             InputText.prototype._getTypeName = function () {
                 return "InputText";
                 return "InputText";
             };
             };
+            InputText.prototype.processKeyboard = function (evt) {
+                // Specific cases
+                switch (evt.keyCode) {
+                    case 8:// BACKSPACE
+                        if (this._text && this._text.length > 0) {
+                            if (this._cursorOffset === 0) {
+                                this.text = this._text.substr(0, this._text.length - 1);
+                            }
+                            else {
+                                var deletePosition = this._text.length - this._cursorOffset;
+                                if (deletePosition > 0) {
+                                    this.text = this._text.slice(0, deletePosition - 1) + this._text.slice(deletePosition);
+                                }
+                            }
+                        }
+                        return;
+                    case 46:// DELETE
+                        if (this._text && this._text.length > 0) {
+                            var deletePosition = this._text.length - this._cursorOffset;
+                            this.text = this._text.slice(0, deletePosition) + this._text.slice(deletePosition + 1);
+                            this._cursorOffset--;
+                        }
+                        return;
+                    case 13:// RETURN
+                        this._host.focusedControl = null;
+                        return;
+                    case 35:// END
+                        this._cursorOffset = 0;
+                        this._blinkIsEven = false;
+                        this._markAsDirty();
+                        return;
+                    case 36:// HOME
+                        this._cursorOffset = this._text.length;
+                        this._blinkIsEven = false;
+                        this._markAsDirty();
+                        return;
+                    case 37:// LEFT
+                        this._cursorOffset++;
+                        if (this._cursorOffset > this._text.length) {
+                            this._cursorOffset = this._text.length;
+                        }
+                        this._blinkIsEven = false;
+                        this._markAsDirty();
+                        return;
+                    case 39:// RIGHT
+                        this._cursorOffset--;
+                        if (this._cursorOffset < 0) {
+                            this._cursorOffset = 0;
+                        }
+                        this._blinkIsEven = false;
+                        this._markAsDirty();
+                        return;
+                }
+                // Printable characters
+                if ((evt.keyCode === 32) ||
+                    (evt.keyCode > 47 && evt.keyCode < 58) ||
+                    (evt.keyCode > 64 && evt.keyCode < 91) ||
+                    (evt.keyCode > 185 && evt.keyCode < 193) ||
+                    (evt.keyCode > 218 && evt.keyCode < 223) ||
+                    (evt.keyCode > 95 && evt.keyCode < 112)) {
+                    if (this._cursorOffset === 0) {
+                        this.text += evt.key;
+                    }
+                    else {
+                        var insertPosition = this._text.length - this._cursorOffset;
+                        this.text = this._text.slice(0, insertPosition) + evt.key + this._text.slice(insertPosition);
+                    }
+                }
+            };
             InputText.prototype._draw = function (parentMeasure, context) {
             InputText.prototype._draw = function (parentMeasure, context) {
+                var _this = this;
                 context.save();
                 context.save();
                 this._applyStates(context);
                 this._applyStates(context);
                 if (this._processMeasures(parentMeasure, context)) {
                 if (this._processMeasures(parentMeasure, context)) {
                     // Background
                     // Background
-                    if (this._background) {
+                    if (this._isFocused) {
+                        if (this._focusedBackground) {
+                            context.fillStyle = this._focusedBackground;
+                            context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                        }
+                    }
+                    else if (this._background) {
                         context.fillStyle = this._background;
                         context.fillStyle = this._background;
                         context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                         context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                     }
                     }
+                    if (!this._fontOffset) {
+                        this._fontOffset = GUI.Control._GetFontOffset(context.font);
+                    }
                     // Text
                     // Text
-                    if (this._text) {
-                        if (this.color) {
-                            context.fillStyle = this.color;
+                    var clipTextLeft = this._currentMeasure.left + this._margin.getValueInPixel(this._host, parentMeasure.width);
+                    if (this.color) {
+                        context.fillStyle = this.color;
+                    }
+                    var textWidth = context.measureText(this._text).width;
+                    var marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
+                    if (this._autoStretchWidth) {
+                        this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
+                    }
+                    var rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
+                    var availableWidth = this._width.getValueInPixel(this._host, parentMeasure.width) - marginWidth;
+                    context.save();
+                    context.beginPath();
+                    context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
+                    context.clip();
+                    if (this._isFocused && textWidth > availableWidth) {
+                        var textLeft = clipTextLeft - textWidth + availableWidth;
+                        if (!this._scrollLeft) {
+                            this._scrollLeft = textLeft;
                         }
                         }
-                        var rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
-                        context.fillText(this._text, this._currentMeasure.left + this._margin.getValueInPixel(this._host, parentMeasure.width), this._currentMeasure.top + rootY);
-                        if (this._autoStretchWidth) {
-                            this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), context.measureText(this._text).width + this._margin.getValueInPixel(this._host, parentMeasure.width) * 2) + "px";
+                    }
+                    else {
+                        this._scrollLeft = clipTextLeft;
+                    }
+                    context.fillText(this._text, this._scrollLeft, this._currentMeasure.top + rootY);
+                    // Cursor
+                    if (this._isFocused) {
+                        if (!this._blinkIsEven) {
+                            var cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
+                            var cursorOffsetWidth = context.measureText(cursorOffsetText).width;
+                            var cursorLeft = this._scrollLeft + textWidth - cursorOffsetWidth;
+                            if (cursorLeft < clipTextLeft) {
+                                this._scrollLeft += (clipTextLeft - cursorLeft);
+                                cursorLeft = clipTextLeft;
+                                this._markAsDirty();
+                            }
+                            else if (cursorLeft > clipTextLeft + availableWidth) {
+                                this._scrollLeft += (clipTextLeft + availableWidth - cursorLeft);
+                                cursorLeft = clipTextLeft + availableWidth;
+                                this._markAsDirty();
+                            }
+                            context.fillRect(cursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, 2, this._fontOffset.height);
                         }
                         }
+                        clearTimeout(this._blinkTimeout);
+                        this._blinkTimeout = setTimeout(function () {
+                            _this._blinkIsEven = !_this._blinkIsEven;
+                            _this._markAsDirty();
+                        }, 500);
                     }
                     }
+                    context.restore();
                     // Border
                     // Border
                     if (this._thickness) {
                     if (this._thickness) {
                         if (this.color) {
                         if (this.color) {
@@ -3846,6 +4032,16 @@ var BABYLON;
                 }
                 }
                 context.restore();
                 context.restore();
             };
             };
+            InputText.prototype._onPointerDown = function (coordinates) {
+                if (!_super.prototype._onPointerDown.call(this, coordinates)) {
+                    return false;
+                }
+                this._host.focusedControl = this;
+                return true;
+            };
+            InputText.prototype._onPointerUp = function (coordinates) {
+                _super.prototype._onPointerUp.call(this, coordinates);
+            };
             return InputText;
             return InputText;
         }(GUI.Control));
         }(GUI.Control));
         GUI.InputText = InputText;
         GUI.InputText = InputText;

File diff suppressed because it is too large
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


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

@@ -1,14 +1,21 @@
 /// <reference path="../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../dist/preview release/babylon.d.ts" />
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
+    interface IFocusableControl {
+        onFocus(): void;
+        onBlur(): void;
+        processKeyboard(evt: KeyboardEvent): void;
+    }
     class AdvancedDynamicTexture extends DynamicTexture {
     class AdvancedDynamicTexture extends DynamicTexture {
         private _isDirty;
         private _isDirty;
         private _renderObserver;
         private _renderObserver;
         private _resizeObserver;
         private _resizeObserver;
+        private _preKeyboardObserver;
         private _pointerMoveObserver;
         private _pointerMoveObserver;
         private _pointerObserver;
         private _pointerObserver;
-        private _canvasBlurObserver;
+        private _canvasPointerOutObserver;
         private _background;
         private _background;
         _rootContainer: Container;
         _rootContainer: Container;
+        _lastPickedControl: Control;
         _lastControlOver: Control;
         _lastControlOver: Control;
         _lastControlDown: Control;
         _lastControlDown: Control;
         _capturingControl: Control;
         _capturingControl: Control;
@@ -20,12 +27,14 @@ declare module BABYLON.GUI {
         private _idealWidth;
         private _idealWidth;
         private _idealHeight;
         private _idealHeight;
         private _renderAtIdealSize;
         private _renderAtIdealSize;
+        private _focusedControl;
         background: string;
         background: string;
         idealWidth: number;
         idealWidth: number;
         idealHeight: number;
         idealHeight: number;
         renderAtIdealSize: boolean;
         renderAtIdealSize: boolean;
         readonly layer: Layer;
         readonly layer: Layer;
         readonly rootContainer: Container;
         readonly rootContainer: Container;
+        focusedControl: IFocusableControl;
         constructor(name: string, width: number, height: number, scene: Scene, generateMipMaps?: boolean, samplingMode?: number);
         constructor(name: string, width: number, height: number, scene: Scene, generateMipMaps?: boolean, samplingMode?: number);
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         markAsDirty(): void;
         markAsDirty(): void;
@@ -39,7 +48,7 @@ declare module BABYLON.GUI {
         private _doPicking(x, y, type);
         private _doPicking(x, y, type);
         attach(): void;
         attach(): void;
         attachToMesh(mesh: AbstractMesh, supportPointerMove?: boolean): 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 CreateForMesh(mesh: AbstractMesh, width?: number, height?: number, supportPointerMove?: boolean): AdvancedDynamicTexture;
         static CreateFullscreenUI(name: string, foreground?: boolean, scene?: Scene): AdvancedDynamicTexture;
         static CreateFullscreenUI(name: string, foreground?: boolean, scene?: Scene): AdvancedDynamicTexture;
     }
     }
@@ -584,23 +593,35 @@ declare module BABYLON.GUI {
 
 
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
-    class InputText extends Control {
+    class InputText extends Control implements IFocusableControl {
         name: string;
         name: string;
         private _text;
         private _text;
         private _background;
         private _background;
+        private _focusedBackground;
         private _thickness;
         private _thickness;
         private _margin;
         private _margin;
         private _autoStretchWidth;
         private _autoStretchWidth;
         private _maxWidth;
         private _maxWidth;
+        private _isFocused;
+        private _blinkTimeout;
+        private _blinkIsEven;
+        private _cursorOffset;
+        private _scrollLeft;
         maxWidth: string | number;
         maxWidth: string | number;
         margin: string;
         margin: string;
         autoStretchWidth: boolean;
         autoStretchWidth: boolean;
         thickness: number;
         thickness: number;
+        focusedBackground: string;
         background: string;
         background: string;
         text: string;
         text: string;
         constructor(name?: string, text?: string);
         constructor(name?: string, text?: string);
+        onBlur(): void;
+        onFocus(): void;
         protected _getTypeName(): string;
         protected _getTypeName(): string;
+        processKeyboard(evt: KeyboardEvent): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+        protected _onPointerDown(coordinates: Vector2): boolean;
+        protected _onPointerUp(coordinates: Vector2): void;
     }
     }
 }
 }
 
 

File diff suppressed because it is too large
+ 263 - 263
dist/preview release/inspector/babylon.inspector.bundle.js


File diff suppressed because it is too large
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -155,6 +155,7 @@ declare module BABYLON.GLTF1 {
         buffer: string;
         buffer: string;
         byteOffset: number;
         byteOffset: number;
         byteLength: number;
         byteLength: number;
+        byteStride: number;
         target?: number;
         target?: number;
     }
     }
     interface IGLTFBuffer extends IGLTFChildRootProperty {
     interface IGLTFBuffer extends IGLTFChildRootProperty {

File diff suppressed because it is too large
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 8 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -344,7 +344,14 @@ declare module BABYLON.GLTF2 {
         private _loadAnimations();
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
         private _loadBufferAsync(index, onSuccess);
-        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, componentType, onSuccess);
+        private _buildInt8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildInt16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, stride, length);
+        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
         private _getByteStrideFromType(accessor);
         blockPendingTracking: boolean;
         blockPendingTracking: boolean;

+ 77 - 10
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -1012,7 +1012,73 @@ var BABYLON;
                     });
                     });
                 }
                 }
             };
             };
-            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, componentType, onSuccess) {
+            GLTFLoader.prototype._buildInt8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int8Array(buffer, byteOffset);
+                var targetBuffer = new Int8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint8Array(buffer, byteOffset);
+                var targetBuffer = new Uint8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildInt16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int16Array(buffer, byteOffset);
+                var targetBuffer = new Int16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint16Array(buffer, byteOffset);
+                var targetBuffer = new Uint16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint32Array(buffer, byteOffset);
+                var targetBuffer = new Uint32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildFloat32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Float32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Float32Array(buffer, byteOffset);
+                var targetBuffer = new Float32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._extractInterleavedData = function (sourceBuffer, targetBuffer, bytePerComponent, stride, length) {
+                var tempIndex = 0;
+                var sourceIndex = 0;
+                var storageSize = bytePerComponent;
+                while (tempIndex < length) {
+                    for (var cursor = 0; cursor < storageSize; cursor++) {
+                        targetBuffer[tempIndex] = sourceBuffer[sourceIndex + cursor];
+                        tempIndex++;
+                    }
+                    sourceIndex += stride;
+                }
+            };
+            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess) {
                 var _this = this;
                 var _this = this;
                 byteOffset += (bufferView.byteOffset || 0);
                 byteOffset += (bufferView.byteOffset || 0);
                 this._loadBufferAsync(bufferView.buffer, function (bufferData) {
                 this._loadBufferAsync(bufferView.buffer, function (bufferData) {
@@ -1025,22 +1091,22 @@ var BABYLON;
                     var bufferViewData;
                     var bufferViewData;
                     switch (componentType) {
                     switch (componentType) {
                         case GLTF2.EComponentType.BYTE:
                         case GLTF2.EComponentType.BYTE:
-                            bufferViewData = new Int8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.UNSIGNED_BYTE:
                         case GLTF2.EComponentType.UNSIGNED_BYTE:
-                            bufferViewData = new Uint8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.SHORT:
                         case GLTF2.EComponentType.SHORT:
-                            bufferViewData = new Int16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.UNSIGNED_SHORT:
                         case GLTF2.EComponentType.UNSIGNED_SHORT:
-                            bufferViewData = new Uint16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.UNSIGNED_INT:
                         case GLTF2.EComponentType.UNSIGNED_INT:
-                            bufferViewData = new Uint32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.FLOAT:
                         case GLTF2.EComponentType.FLOAT:
-                            bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         default:
                         default:
                             _this._onError("Invalid component type (" + componentType + ")");
                             _this._onError("Invalid component type (" + componentType + ")");
@@ -1052,8 +1118,9 @@ var BABYLON;
             GLTFLoader.prototype._loadAccessorAsync = function (accessor, onSuccess) {
             GLTFLoader.prototype._loadAccessorAsync = function (accessor, onSuccess) {
                 var bufferView = this._gltf.bufferViews[accessor.bufferView];
                 var bufferView = this._gltf.bufferViews[accessor.bufferView];
                 var byteOffset = accessor.byteOffset || 0;
                 var byteOffset = accessor.byteOffset || 0;
-                var byteLength = accessor.count * this._getByteStrideFromType(accessor);
-                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
+                var bytePerComponent = this._getByteStrideFromType(accessor);
+                var byteLength = accessor.count * bytePerComponent;
+                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, accessor.componentType, onSuccess);
             };
             };
             GLTFLoader.prototype._getByteStrideFromType = function (accessor) {
             GLTFLoader.prototype._getByteStrideFromType = function (accessor) {
                 switch (accessor.type) {
                 switch (accessor.type) {
@@ -1265,7 +1332,7 @@ var BABYLON;
                 };
                 };
                 if (!source.uri) {
                 if (!source.uri) {
                     var bufferView = this._gltf.bufferViews[source.bufferView];
                     var bufferView = this._gltf.bufferViews[source.bufferView];
-                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
+                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, 1, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
                 }
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(source.uri)) {
                 else if (GLTF2.GLTFUtils.IsBase64(source.uri)) {
                     setTextureData(new Uint8Array(GLTF2.GLTFUtils.DecodeBase64(source.uri)));
                     setTextureData(new Uint8Array(GLTF2.GLTFUtils.DecodeBase64(source.uri)));

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 9 - 1
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -155,6 +155,7 @@ declare module BABYLON.GLTF1 {
         buffer: string;
         buffer: string;
         byteOffset: number;
         byteOffset: number;
         byteLength: number;
         byteLength: number;
+        byteStride: number;
         target?: number;
         target?: number;
     }
     }
     interface IGLTFBuffer extends IGLTFChildRootProperty {
     interface IGLTFBuffer extends IGLTFChildRootProperty {
@@ -839,7 +840,14 @@ declare module BABYLON.GLTF2 {
         private _loadAnimations();
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
         private _loadBufferAsync(index, onSuccess);
-        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, componentType, onSuccess);
+        private _buildInt8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildInt16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, stride, length);
+        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
         private _getByteStrideFromType(accessor);
         blockPendingTracking: boolean;
         blockPendingTracking: boolean;

+ 77 - 10
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3170,7 +3170,73 @@ var BABYLON;
                     });
                     });
                 }
                 }
             };
             };
-            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, componentType, onSuccess) {
+            GLTFLoader.prototype._buildInt8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int8Array(buffer, byteOffset);
+                var targetBuffer = new Int8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint8Array(buffer, byteOffset);
+                var targetBuffer = new Uint8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildInt16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int16Array(buffer, byteOffset);
+                var targetBuffer = new Int16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint16Array(buffer, byteOffset);
+                var targetBuffer = new Uint16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint32Array(buffer, byteOffset);
+                var targetBuffer = new Uint32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildFloat32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Float32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Float32Array(buffer, byteOffset);
+                var targetBuffer = new Float32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._extractInterleavedData = function (sourceBuffer, targetBuffer, bytePerComponent, stride, length) {
+                var tempIndex = 0;
+                var sourceIndex = 0;
+                var storageSize = bytePerComponent;
+                while (tempIndex < length) {
+                    for (var cursor = 0; cursor < storageSize; cursor++) {
+                        targetBuffer[tempIndex] = sourceBuffer[sourceIndex + cursor];
+                        tempIndex++;
+                    }
+                    sourceIndex += stride;
+                }
+            };
+            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess) {
                 var _this = this;
                 var _this = this;
                 byteOffset += (bufferView.byteOffset || 0);
                 byteOffset += (bufferView.byteOffset || 0);
                 this._loadBufferAsync(bufferView.buffer, function (bufferData) {
                 this._loadBufferAsync(bufferView.buffer, function (bufferData) {
@@ -3183,22 +3249,22 @@ var BABYLON;
                     var bufferViewData;
                     var bufferViewData;
                     switch (componentType) {
                     switch (componentType) {
                         case GLTF2.EComponentType.BYTE:
                         case GLTF2.EComponentType.BYTE:
-                            bufferViewData = new Int8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.UNSIGNED_BYTE:
                         case GLTF2.EComponentType.UNSIGNED_BYTE:
-                            bufferViewData = new Uint8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.SHORT:
                         case GLTF2.EComponentType.SHORT:
-                            bufferViewData = new Int16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.UNSIGNED_SHORT:
                         case GLTF2.EComponentType.UNSIGNED_SHORT:
-                            bufferViewData = new Uint16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.UNSIGNED_INT:
                         case GLTF2.EComponentType.UNSIGNED_INT:
-                            bufferViewData = new Uint32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         case GLTF2.EComponentType.FLOAT:
                         case GLTF2.EComponentType.FLOAT:
-                            bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                             break;
                         default:
                         default:
                             _this._onError("Invalid component type (" + componentType + ")");
                             _this._onError("Invalid component type (" + componentType + ")");
@@ -3210,8 +3276,9 @@ var BABYLON;
             GLTFLoader.prototype._loadAccessorAsync = function (accessor, onSuccess) {
             GLTFLoader.prototype._loadAccessorAsync = function (accessor, onSuccess) {
                 var bufferView = this._gltf.bufferViews[accessor.bufferView];
                 var bufferView = this._gltf.bufferViews[accessor.bufferView];
                 var byteOffset = accessor.byteOffset || 0;
                 var byteOffset = accessor.byteOffset || 0;
-                var byteLength = accessor.count * this._getByteStrideFromType(accessor);
-                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
+                var bytePerComponent = this._getByteStrideFromType(accessor);
+                var byteLength = accessor.count * bytePerComponent;
+                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, accessor.componentType, onSuccess);
             };
             };
             GLTFLoader.prototype._getByteStrideFromType = function (accessor) {
             GLTFLoader.prototype._getByteStrideFromType = function (accessor) {
                 switch (accessor.type) {
                 switch (accessor.type) {
@@ -3423,7 +3490,7 @@ var BABYLON;
                 };
                 };
                 if (!source.uri) {
                 if (!source.uri) {
                     var bufferView = this._gltf.bufferViews[source.bufferView];
                     var bufferView = this._gltf.bufferViews[source.bufferView];
-                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
+                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, 1, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
                 }
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(source.uri)) {
                 else if (GLTF2.GLTFUtils.IsBase64(source.uri)) {
                     setTextureData(new Uint8Array(GLTF2.GLTFUtils.DecodeBase64(source.uri)));
                     setTextureData(new Uint8Array(GLTF2.GLTFUtils.DecodeBase64(source.uri)));

File diff suppressed because it is too large
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


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

@@ -1,14 +1,16 @@
 # 3.1.0:
 # 3.1.0:
 
 
 ## Major updates
 ## Major updates
+- Added support for webgl context lost and restored events. [Doc here](http://doc.babylonjs.com/tutorials/optimizing_your_scene#handling-webgl-context-lost) ([deltakosh](https://github.com/deltakosh))
 - Added support for non-pow2 textures when in WebGL2 mode ([deltakosh](https://github.com/deltakosh))
 - Added support for non-pow2 textures when in WebGL2 mode ([deltakosh](https://github.com/deltakosh))
 - Engine can now be initialized with an existing webgl context ([deltakosh](https://github.com/deltakosh))
 - Engine can now be initialized with an existing webgl context ([deltakosh](https://github.com/deltakosh))
-- Introduced behaviors. (Doc here)[http://doc.babylonjs.com/overviews/behaviors] ([deltakosh](https://github.com/deltakosh))
-- Added support for WebGL Occlusion queries. (Doc here)[http://doc.babylonjs.com/overviews/occlusionquery] ([Ibraheem Osama](https://github.com/IbraheemOsama))
+- Introduced behaviors. [Doc here](http://doc.babylonjs.com/overviews/behaviors) ([deltakosh](https://github.com/deltakosh))
+- Added support for WebGL Occlusion queries. [Doc here](http://doc.babylonjs.com/overviews/occlusionquery) ([Ibraheem Osama](https://github.com/IbraheemOsama))
 - New behaviors for ArcRotateCamera:
 - New behaviors for ArcRotateCamera:
  - AutoRotation ([deltakosh](https://github.com/deltakosh))
  - AutoRotation ([deltakosh](https://github.com/deltakosh))
  - Framing ([deltakosh](https://github.com/deltakosh))
  - Framing ([deltakosh](https://github.com/deltakosh))
  - Bouncing ([deltakosh](https://github.com/deltakosh))
  - Bouncing ([deltakosh](https://github.com/deltakosh))
+- New InputText for Babylon.GUI. [Doc here](http://doc.babylonjs.com/overviews/gui#inputtext) ([deltakosh](https://github.com/deltakosh))
 
 
 ## Updates
 ## Updates
 - New `camera.storeState()` and `camera.restoreState()` functions to store / restore cameras position / rotation / fov. (Doc here)[http://doc.babylonjs.com/tutorials/cameras#state] ([deltakosh](https://github.com/deltakosh))
 - New `camera.storeState()` and `camera.restoreState()` functions to store / restore cameras position / rotation / fov. (Doc here)[http://doc.babylonjs.com/tutorials/cameras#state] ([deltakosh](https://github.com/deltakosh))

+ 52 - 7
gui/src/advancedDynamicTexture.ts

@@ -1,15 +1,23 @@
 /// <reference path="../../dist/preview release/babylon.d.ts"/>
 /// <reference path="../../dist/preview release/babylon.d.ts"/>
 
 
 module BABYLON.GUI {
 module BABYLON.GUI {
+    export interface IFocusableControl {
+        onFocus(): void;
+        onBlur(): void;
+        processKeyboard(evt: KeyboardEvent): void;
+    }
+
     export class AdvancedDynamicTexture extends DynamicTexture {
     export class AdvancedDynamicTexture extends DynamicTexture {
         private _isDirty = false;
         private _isDirty = false;
         private _renderObserver: Observer<Camera>;
         private _renderObserver: Observer<Camera>;
         private _resizeObserver: Observer<Engine>;
         private _resizeObserver: Observer<Engine>;
+        private _preKeyboardObserver: Observer<KeyboardInfoPre>;
         private _pointerMoveObserver: Observer<PointerInfoPre>;
         private _pointerMoveObserver: Observer<PointerInfoPre>;
         private _pointerObserver: Observer<PointerInfo>;
         private _pointerObserver: Observer<PointerInfo>;
-        private _canvasBlurObserver: Observer<Engine>;
+        private _canvasPointerOutObserver: Observer<Engine>;
         private _background: string;
         private _background: string;
         public _rootContainer = new Container("root");
         public _rootContainer = new Container("root");
+        public _lastPickedControl: Control;
         public _lastControlOver: Control;
         public _lastControlOver: Control;
         public _lastControlDown: Control;
         public _lastControlDown: Control;
         public _capturingControl: Control;
         public _capturingControl: Control;
@@ -21,6 +29,7 @@ module BABYLON.GUI {
         private _idealWidth = 0;
         private _idealWidth = 0;
         private _idealHeight = 0;
         private _idealHeight = 0;
         private _renderAtIdealSize = false;
         private _renderAtIdealSize = false;
+        private _focusedControl: IFocusableControl;
 
 
         public get background(): string {
         public get background(): string {
             return this._background;
             return this._background;
@@ -83,11 +92,40 @@ module BABYLON.GUI {
         public get rootContainer(): Container {
         public get rootContainer(): Container {
             return this._rootContainer;
             return this._rootContainer;
         }
         }
+
+        public get focusedControl(): IFocusableControl {
+            return this._focusedControl;
+        }
+
+        public set focusedControl(control: IFocusableControl) {
+            if (this._focusedControl === control) {
+                return;
+            }
+
+            if (!this._focusedControl) {
+                control.onFocus();
+            } else {
+                this._focusedControl.onBlur();
+            }
+
+            this._focusedControl = control;
+        }
        
        
         constructor(name: string, width = 0, height = 0, scene: Scene, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
         constructor(name: string, width = 0, height = 0, scene: Scene, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
             super(name, {width: width, height: height}, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
             super(name, {width: width, height: height}, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
 
 
             this._renderObserver = this.getScene().onBeforeCameraRenderObservable.add((camera: Camera) => this._checkUpdate(camera));
             this._renderObserver = this.getScene().onBeforeCameraRenderObservable.add((camera: Camera) => this._checkUpdate(camera));
+            this._preKeyboardObserver = this.getScene().onPreKeyboardObservable.add(info => {
+                if (!this._focusedControl) {
+                    return;
+                }
+
+                if (info.type === KeyboardEventTypes.KEYDOWN) {
+                    this._focusedControl.processKeyboard(info.event);
+                }
+
+                info.skipOnPointerObservable = true;
+            });
 
 
             this._rootContainer._link(null, this);
             this._rootContainer._link(null, this);
 
 
@@ -145,8 +183,8 @@ module BABYLON.GUI {
                 this.getScene().onPointerObservable.remove(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) {
             if (this._layerToDispose) {
@@ -286,6 +324,13 @@ module BABYLON.GUI {
                     this._lastControlOver = null;
                     this._lastControlOver = null;
                 }
                 }
             }
             }
+
+            // Focus management
+            if (this._focusedControl) {
+                if (this._focusedControl !== (<any>this._lastPickedControl)) {
+                    this.focusedControl = null;
+                }
+            }
         }
         }
 
 
         public attach(): void {
         public attach(): void {
@@ -309,7 +354,7 @@ module BABYLON.GUI {
                 pi.skipOnPointerObservable = this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
                 pi.skipOnPointerObservable = this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
             });
             });
 
 
-            this._attachToOnBlur(scene);
+            this._attachToOnPointerOut(scene);
         }
         }
 
 
         public attachToMesh(mesh: AbstractMesh, supportPointerMove = true): void {
         public attachToMesh(mesh: AbstractMesh, supportPointerMove = true): void {
@@ -339,11 +384,11 @@ module BABYLON.GUI {
             });
             });
 
 
             mesh.enablePointerMoveEvents = supportPointerMove;
             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) {
                 if (this._lastControlOver) {
                     this._lastControlOver._onPointerOut();
                     this._lastControlOver._onPointerOut();
                 }            
                 }            

+ 1 - 0
gui/src/controls/control.ts

@@ -834,6 +834,7 @@ module BABYLON.GUI {
             if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
             if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
                 this._onPointerDown(this._dummyVector2);
                 this._onPointerDown(this._dummyVector2);
                 this._host._lastControlDown = this;
                 this._host._lastControlDown = this;
+                this._host._lastPickedControl = this;
                 return true;
                 return true;
             }
             }
 
 

+ 185 - 11
gui/src/controls/inputText.ts

@@ -1,13 +1,19 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
 
 
 module BABYLON.GUI {
 module BABYLON.GUI {
-    export class InputText extends Control {
+    export class InputText extends Control implements IFocusableControl {
         private _text = "";
         private _text = "";
         private _background = "black";   
         private _background = "black";   
+        private _focusedBackground = "black";   
         private _thickness = 1;
         private _thickness = 1;
         private _margin = new ValueAndUnit(10, ValueAndUnit.UNITMODE_PIXEL);
         private _margin = new ValueAndUnit(10, ValueAndUnit.UNITMODE_PIXEL);
         private _autoStretchWidth = true;        
         private _autoStretchWidth = true;        
         private _maxWidth = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
         private _maxWidth = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
+        private _isFocused = false;
+        private _blinkTimeout: number;
+        private _blinkIsEven = false;
+        private _cursorOffset = 0;        
+        private _scrollLeft: number;
 
 
         public get maxWidth(): string | number {
         public get maxWidth(): string | number {
             return this._maxWidth.toString(this._host);
             return this._maxWidth.toString(this._host);
@@ -63,6 +69,19 @@ module BABYLON.GUI {
             this._markAsDirty();
             this._markAsDirty();
         }          
         }          
 
 
+        public get focusedBackground(): string {
+            return this._focusedBackground;
+        }
+
+        public set focusedBackground(value: string) {
+            if (this._focusedBackground === value) {
+                return;
+            }
+
+            this._focusedBackground = value;
+            this._markAsDirty();
+        }  
+
         public get background(): string {
         public get background(): string {
             return this._background;
             return this._background;
         }
         }
@@ -74,7 +93,7 @@ module BABYLON.GUI {
 
 
             this._background = value;
             this._background = value;
             this._markAsDirty();
             this._markAsDirty();
-        }  
+        }          
 
 
         public get text(): string {
         public get text(): string {
             return this._text;
             return this._text;
@@ -94,37 +113,178 @@ module BABYLON.GUI {
             this.text = text;
             this.text = text;
         }
         }
 
 
+        public onBlur(): void {
+            this._isFocused = false;
+            this._scrollLeft = null;
+            this._cursorOffset = 0;
+            clearTimeout(this._blinkTimeout);
+            this._markAsDirty();
+        }
+
+        public onFocus(): void {
+            this._scrollLeft = null;
+            this._isFocused = true;
+            this._blinkIsEven = false;
+            this._cursorOffset = 0;
+            this._markAsDirty();
+        }
+
         protected _getTypeName(): string {
         protected _getTypeName(): string {
             return "InputText";
             return "InputText";
         }
         }
 
 
-       public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        public processKeyboard(evt: KeyboardEvent): void {
+            // Specific cases
+            switch (evt.keyCode) {
+                case 8: // BACKSPACE
+                    if (this._text && this._text.length > 0) {
+                        if (this._cursorOffset === 0) {
+                            this.text = this._text.substr(0, this._text.length - 1);
+                        } else {
+                            let deletePosition = this._text.length - this._cursorOffset;
+                            if (deletePosition > 0) {
+                                this.text = this._text.slice(0, deletePosition - 1) + this._text.slice(deletePosition);
+                            }
+                        }
+                    }
+                    return;
+                case 46: // DELETE
+                    if (this._text && this._text.length > 0) {
+                        let deletePosition = this._text.length - this._cursorOffset;
+                        this.text = this._text.slice(0, deletePosition) + this._text.slice(deletePosition + 1);
+                        this._cursorOffset--;
+                    }
+                    return;                    
+                case 13: // RETURN
+                    this._host.focusedControl = null;
+                    return;
+                case 35: // END
+                    this._cursorOffset = 0;
+                    this._blinkIsEven = false;
+                    this._markAsDirty();                
+                    return;
+                case 36: // HOME
+                    this._cursorOffset = this._text.length;
+                    this._blinkIsEven = false;
+                    this._markAsDirty();                
+                return;
+                case 37: // LEFT
+                    this._cursorOffset++;
+                    if (this._cursorOffset > this._text.length) {
+                        this._cursorOffset = this._text.length;
+                    }
+                    this._blinkIsEven = false;
+                    this._markAsDirty();
+                    return;
+                case 39: // RIGHT
+                    this._cursorOffset--;
+                    if (this._cursorOffset < 0) {
+                        this._cursorOffset = 0;
+                    }
+                    this._blinkIsEven = false;
+                    this._markAsDirty();
+                    return;
+            }
+
+            // Printable characters
+            if (
+                (evt.keyCode === 32) ||                         // Space
+                (evt.keyCode > 47 && evt.keyCode < 58) ||       // Numbers
+                (evt.keyCode > 64 && evt.keyCode < 91) ||       // Letters
+                (evt.keyCode > 185 && evt.keyCode < 193) ||     // Special characters
+                (evt.keyCode > 218  && evt.keyCode < 223) ||    // Special characters
+                (evt.keyCode > 95 && evt.keyCode < 112)) {      // Numpad
+                    if (this._cursorOffset === 0) {
+                        this.text += evt.key;
+                    } else {
+                        let insertPosition = this._text.length - this._cursorOffset;
+
+                        this.text = this._text.slice(0, insertPosition) + evt.key + this._text.slice(insertPosition);
+                    }
+                }
+        }
+
+        public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
             context.save();
 
 
             this._applyStates(context);
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
             if (this._processMeasures(parentMeasure, context)) {
                 
                 
                 // Background
                 // Background
-                if (this._background) {
+                if (this._isFocused) {
+                    if (this._focusedBackground) {
+                        context.fillStyle = this._focusedBackground;
+    
+                        context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                    }                        
+                } else if (this._background) {
                     context.fillStyle = this._background;
                     context.fillStyle = this._background;
 
 
                     context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                     context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                 }
                 }
 
 
+                if (!this._fontOffset) {
+                    this._fontOffset = Control._GetFontOffset(context.font);
+                }
+
                 // Text
                 // Text
-                if (this._text) {
-                    if (this.color) {
-                        context.fillStyle = this.color;
+                let clipTextLeft = this._currentMeasure.left + this._margin.getValueInPixel(this._host, parentMeasure.width);
+                if (this.color) {
+                    context.fillStyle = this.color;
+                }
+
+                let textWidth = context.measureText(this._text).width;   
+                let marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
+                if (this._autoStretchWidth) {
+                    this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
+                }
+
+                let rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
+                let availableWidth = this._width.getValueInPixel(this._host, parentMeasure.width) - marginWidth;
+                context.save();
+                context.beginPath();
+                context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
+                context.clip();
+
+                if (this._isFocused && textWidth > availableWidth) {      
+                    let textLeft = clipTextLeft - textWidth + availableWidth;
+                    if (!this._scrollLeft) {
+                        this._scrollLeft = textLeft;
                     }
                     }
+                } else {
+                    this._scrollLeft = clipTextLeft;
+                }
 
 
-                    let rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
-                    context.fillText(this._text, this._currentMeasure.left + this._margin.getValueInPixel(this._host, parentMeasure.width), this._currentMeasure.top + rootY);
+                context.fillText(this._text, this._scrollLeft, this._currentMeasure.top + rootY);
 
 
-                    if (this._autoStretchWidth) {
-                        this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), context.measureText(this._text).width + this._margin.getValueInPixel(this._host, parentMeasure.width) * 2) + "px";
+                // Cursor
+                if (this._isFocused) {         
+                    if (!this._blinkIsEven) {
+                        let cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
+                        let cursorOffsetWidth = context.measureText(cursorOffsetText).width;   
+                        let cursorLeft = this._scrollLeft  + textWidth - cursorOffsetWidth;
+    
+                        if (cursorLeft < clipTextLeft) {
+                            this._scrollLeft += (clipTextLeft - cursorLeft);
+                            cursorLeft = clipTextLeft;
+                            this._markAsDirty();
+                        } else if (cursorLeft > clipTextLeft + availableWidth) {
+                            this._scrollLeft += (clipTextLeft  + availableWidth - cursorLeft);
+                            cursorLeft = clipTextLeft + availableWidth;
+                            this._markAsDirty();
+                        }                   
+                        context.fillRect(cursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, 2, this._fontOffset.height);
                     }
                     }
+
+                    clearTimeout(this._blinkTimeout);
+                    this._blinkTimeout = setTimeout(() => {
+                        this._blinkIsEven = !this._blinkIsEven;
+                        this._markAsDirty();
+                    }, 500);
                 }
                 }
 
 
+                context.restore();
+
                 // Border
                 // Border
                 if (this._thickness) {
                 if (this._thickness) {
                     if (this.color) {
                     if (this.color) {
@@ -138,5 +298,19 @@ module BABYLON.GUI {
             }
             }
             context.restore();
             context.restore();
         }
         }
+
+        protected _onPointerDown(coordinates: Vector2): boolean {
+            if (!super._onPointerDown(coordinates)) {
+                return false;
+            }
+
+            this._host.focusedControl = this;
+
+            return true;
+        }
+
+        protected _onPointerUp(coordinates: Vector2): void {
+            super._onPointerUp(coordinates);
+        }  
     }
     }
 }
 }

+ 1 - 0
loaders/src/glTF/1.0/babylon.glTFLoaderInterfaces.ts

@@ -117,6 +117,7 @@ module BABYLON.GLTF1 {
         buffer: string;
         buffer: string;
         byteOffset: number;
         byteOffset: number;
         byteLength: number;
         byteLength: number;
+        byteStride: number;
 
 
         target?: number;
         target?: number;
     }
     }

+ 103 - 10
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -800,7 +800,99 @@ module BABYLON.GLTF2 {
             }
             }
         }
         }
 
 
-        private _loadBufferViewAsync(bufferView: IGLTFBufferView, byteOffset: number, byteLength: number, componentType: EComponentType, onSuccess: (data: ArrayBufferView) => void): void {
+        private _buildInt8ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Int8Array {
+            if (!byteStride) {
+                return new Int8Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Int8Array(buffer, byteOffset);
+            let targetBuffer = new Int8Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+
+            return targetBuffer;              
+        }
+
+        private _buildUint8ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint8Array {
+            if (!byteStride) {
+                return new Uint8Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Uint8Array(buffer, byteOffset);
+            let targetBuffer = new Uint8Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+
+            return targetBuffer;              
+        }        
+
+        private _buildInt16ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Int16Array {
+            if (!byteStride) {
+                return new Int16Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Int16Array(buffer, byteOffset);
+            let targetBuffer = new Int16Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+
+            return targetBuffer;             
+        }   
+
+        private _buildUint16ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint16Array {
+            if (!byteStride) {
+                return new Uint16Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Uint16Array(buffer, byteOffset);
+            let targetBuffer = new Uint16Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+
+            return targetBuffer;             
+        }          
+        
+        private _buildUint32ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint32Array {
+            if (!byteStride) {
+                return new Uint32Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Uint32Array(buffer, byteOffset);
+            let targetBuffer = new Uint32Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+
+            return targetBuffer;            
+        }     
+        
+        private _buildFloat32ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Float32Array {
+            if (!byteStride) {
+                return new Float32Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Float32Array(buffer, byteOffset);
+            let targetBuffer = new Float32Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+
+            return targetBuffer;
+        }    
+        
+        private _extractInterleavedData(sourceBuffer: ArrayBufferView, targetBuffer: ArrayBufferView, bytePerComponent: number, stride: number, length: number): void {
+            let tempIndex = 0;
+            let sourceIndex = 0;            
+            let storageSize = bytePerComponent;
+
+            while (tempIndex < length) {
+                for (var cursor = 0; cursor < storageSize; cursor++) {
+                    targetBuffer[tempIndex] = sourceBuffer[sourceIndex + cursor]
+                    tempIndex++;
+                }
+                sourceIndex += stride;
+            }
+        }
+
+        private _loadBufferViewAsync(bufferView: IGLTFBufferView, byteOffset: number, byteLength: number, bytePerComponent: number, componentType: EComponentType, onSuccess: (data: ArrayBufferView) => void): void {
             byteOffset += (bufferView.byteOffset || 0);
             byteOffset += (bufferView.byteOffset || 0);
 
 
             this._loadBufferAsync(bufferView.buffer, bufferData => {
             this._loadBufferAsync(bufferView.buffer, bufferData => {
@@ -815,22 +907,22 @@ module BABYLON.GLTF2 {
                 var bufferViewData;
                 var bufferViewData;
                 switch (componentType) {
                 switch (componentType) {
                     case EComponentType.BYTE:
                     case EComponentType.BYTE:
-                        bufferViewData = new Int8Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildInt8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                         break;
                     case EComponentType.UNSIGNED_BYTE:
                     case EComponentType.UNSIGNED_BYTE:
-                        bufferViewData = new Uint8Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildUint8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                         break;
                     case EComponentType.SHORT:
                     case EComponentType.SHORT:
-                        bufferViewData = new Int16Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildInt16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                         break;
                     case EComponentType.UNSIGNED_SHORT:
                     case EComponentType.UNSIGNED_SHORT:
-                        bufferViewData = new Uint16Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildUint16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                         break;
                     case EComponentType.UNSIGNED_INT:
                     case EComponentType.UNSIGNED_INT:
-                        bufferViewData = new Uint32Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildUint32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                         break;
                     case EComponentType.FLOAT:
                     case EComponentType.FLOAT:
-                        bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                         break;
                     default:
                     default:
                         this._onError("Invalid component type (" + componentType + ")");
                         this._onError("Invalid component type (" + componentType + ")");
@@ -844,8 +936,9 @@ module BABYLON.GLTF2 {
         private _loadAccessorAsync(accessor: IGLTFAccessor, onSuccess: (data: ArrayBufferView) => void): void {
         private _loadAccessorAsync(accessor: IGLTFAccessor, onSuccess: (data: ArrayBufferView) => void): void {
             var bufferView = this._gltf.bufferViews[accessor.bufferView];
             var bufferView = this._gltf.bufferViews[accessor.bufferView];
             var byteOffset = accessor.byteOffset || 0;
             var byteOffset = accessor.byteOffset || 0;
-            var byteLength = accessor.count * this._getByteStrideFromType(accessor);
-            this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
+            let bytePerComponent = this._getByteStrideFromType(accessor);
+            var byteLength = accessor.count * bytePerComponent;
+            this._loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, accessor.componentType, onSuccess);
         }
         }
 
 
         private _getByteStrideFromType(accessor: IGLTFAccessor): number {
         private _getByteStrideFromType(accessor: IGLTFAccessor): number {
@@ -1091,7 +1184,7 @@ module BABYLON.GLTF2 {
 
 
             if (!source.uri) {
             if (!source.uri) {
                 var bufferView = this._gltf.bufferViews[source.bufferView];
                 var bufferView = this._gltf.bufferViews[source.bufferView];
-                this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE, setTextureData);
+                this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, 1, EComponentType.UNSIGNED_BYTE, setTextureData);
             }
             }
             else if (GLTFUtils.IsBase64(source.uri)) {
             else if (GLTFUtils.IsBase64(source.uri)) {
                 setTextureData(new Uint8Array(GLTFUtils.DecodeBase64(source.uri)));
                 setTextureData(new Uint8Array(GLTFUtils.DecodeBase64(source.uri)));

+ 1 - 1
localDev/index.html

@@ -43,7 +43,7 @@
 	
 	
 	<script>
 	<script>
 		var canvas = document.getElementById("renderCanvas");
 		var canvas = document.getElementById("renderCanvas");
-		canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
+	//	canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
 		var divFps = document.getElementById("fps");
 		var divFps = document.getElementById("fps");
 
 
 		// Global to simulate PG.
 		// Global to simulate PG.

+ 3 - 2
src/Animations/babylon.animation.ts

@@ -89,6 +89,8 @@
     }
     }
 
 
     export class Animation {
     export class Animation {
+        public static AllowMatricesInterpolation = false;
+
         private _keys: Array<{frame:number, value: any, inTangent?: any, outTangent?: any}>;
         private _keys: Array<{frame:number, value: any, inTangent?: any, outTangent?: any}>;
         private _offsetsCache = {};
         private _offsetsCache = {};
         private _highLimitsCache = {};
         private _highLimitsCache = {};
@@ -103,7 +105,6 @@
         public targetPropertyPath: string[];
         public targetPropertyPath: string[];
         public currentFrame: number;
         public currentFrame: number;
 
 
-        public allowMatricesInterpolation = false;
 
 
         public blendingSpeed = 0.01;
         public blendingSpeed = 0.01;
         private _originalBlendValue: any;
         private _originalBlendValue: any;
@@ -520,7 +521,7 @@
                             switch (loopMode) {
                             switch (loopMode) {
                                 case Animation.ANIMATIONLOOPMODE_CYCLE:
                                 case Animation.ANIMATIONLOOPMODE_CYCLE:
                                 case Animation.ANIMATIONLOOPMODE_CONSTANT:
                                 case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    if (this.allowMatricesInterpolation) {
+                                    if (Animation.AllowMatricesInterpolation) {
                                         return this.matrixInterpolateFunction(startValue, endValue, gradient);
                                         return this.matrixInterpolateFunction(startValue, endValue, gradient);
                                     }
                                     }
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:

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

@@ -2,11 +2,6 @@ module BABYLON {
     export class ArcRotateCameraKeyboardMoveInput implements ICameraInput<ArcRotateCamera> {
     export class ArcRotateCameraKeyboardMoveInput implements ICameraInput<ArcRotateCamera> {
         camera: ArcRotateCamera;
         camera: ArcRotateCamera;
         private _keys = [];
         private _keys = [];
-        private _onKeyDown: (e: KeyboardEvent) => any;
-        private _onKeyUp: (e: KeyboardEvent) => any;
-        private _onLostFocus: (e: FocusEvent) => any;
-        private _onFocus: () => void;
-        private _onBlur: () => void;
         
         
         @serialize()
         @serialize()
         public keysUp = [38];
         public keysUp = [38];
@@ -27,97 +22,81 @@ module BABYLON {
         public panningSensibility: number = 50.0;        
         public panningSensibility: number = 50.0;        
 
 
         private _ctrlPressed: boolean;
         private _ctrlPressed: boolean;
+        private _onCanvasBlurObserver: Observer<Engine>;
+        private _onKeyboardObserver: Observer<KeyboardInfo>;
+        private _engine: Engine;
+        private _scene: Scene;
 
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
         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) {
         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._keys = [];
-            this._onKeyDown = null;
-            this._onKeyUp = null;
-            this._onLostFocus = null;
-            this._onBlur = null;
-            this._onFocus = null;
         }
         }
 
 
         public checkInputs() {
         public checkInputs() {
-            if (this._onKeyDown){
+            if (this._onKeyboardObserver){
                 var camera = this.camera;
                 var camera = this.camera;
 
 
                 for (var index = 0; index < this._keys.length; index++) {
                 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> {
     export class FreeCameraKeyboardMoveInput implements ICameraInput<FreeCamera> {
         camera: FreeCamera;
         camera: FreeCamera;
         private _keys = [];
         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()
         @serialize()
         public keysUp = [38];
         public keysUp = [38];
@@ -18,10 +20,21 @@ module BABYLON {
         public keysRight = [39];
         public keysRight = [39];
 
 
         attachControl(element : HTMLElement, noPreventDefault?: boolean) {
         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 ||
                     if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         this.keysLeft.indexOf(evt.keyCode) !== -1 ||
                         this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -35,9 +48,7 @@ module BABYLON {
                             evt.preventDefault();
                             evt.preventDefault();
                         }
                         }
                     }
                     }
-                };
-
-                this._onKeyUp = evt => {
+                } else {
                     if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         this.keysDown.indexOf(evt.keyCode) !== -1 ||
                         this.keysLeft.indexOf(evt.keyCode) !== -1 ||
                         this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -51,33 +62,22 @@ module BABYLON {
                             evt.preventDefault();
                             evt.preventDefault();
                         }
                         }
                     }
                     }
-                };
-
-                element.addEventListener("keydown", this._onKeyDown, false);
-                element.addEventListener("keyup", this._onKeyUp, false);
-
-                Tools.RegisterTopRootEvents([
-                    { name: "blur", handler: this._onLostFocus }
-                ]);
-            }
+                }
+            });     
         }
         }
 
 
         detachControl(element : HTMLElement) {
         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() {
         public checkInputs() {
-            if (this._onKeyDown){
+            if (this._onKeyboardObserver){
                 var camera = this.camera;
                 var camera = this.camera;
                 // Keyboard
                 // Keyboard
                 for (var index = 0; index < this._keys.length; index++) {
                 for (var index = 0; index < this._keys.length; index++) {

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

@@ -406,9 +406,17 @@ module BABYLON {
                                 if (!this._lightOnControllers) {
                                 if (!this._lightOnControllers) {
                                     this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), this.getScene());
                                     this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), this.getScene());
                                 }
                                 }
-                                loadedMesh.getChildren().forEach((mesh) => {
-                                    this._lightOnControllers.includedOnlyMeshes.push(<AbstractMesh>mesh);
-                                });
+                                let activateLightOnSubMeshes = function(mesh: AbstractMesh, light: HemisphericLight) {
+                                    let children = mesh.getChildren();
+                                    if (children.length !== 0) {
+                                        children.forEach((mesh) => {
+                                            light.includedOnlyMeshes.push(<AbstractMesh>mesh);
+                                            activateLightOnSubMeshes(<AbstractMesh>mesh, light);
+                                        });
+                                    }
+                                }
+                                this._lightOnControllers.includedOnlyMeshes.push(loadedMesh);
+                                activateLightOnSubMeshes(loadedMesh, this._lightOnControllers);
                             }
                             }
                         });
                         });
                     }
                     }

+ 4 - 4
src/Cameras/babylon.freeCamera.ts

@@ -114,7 +114,7 @@
             this._collisionMask = !isNaN(mask) ? mask : -1;
             this._collisionMask = !isNaN(mask) ? mask : -1;
         }
         }
 	 
 	 
-        public _collideWithWorld(direction: Vector3): void {
+        public _collideWithWorld(displacement: Vector3): void {
             var globalPosition: Vector3;
             var globalPosition: Vector3;
 
 
             if (this.parent) {
             if (this.parent) {
@@ -133,15 +133,15 @@
             this._collider.collisionMask = this._collisionMask;
             this._collider.collisionMask = this._collisionMask;
 		
 		
             //no need for clone, as long as gravity is not on.
             //no need for clone, as long as gravity is not on.
-            var actualDirection = direction;
+            var actualDisplacement = displacement;
 			
 			
             //add gravity to the direction to prevent the dual-collision checking
             //add gravity to the direction to prevent the dual-collision checking
             if (this.applyGravity) {
             if (this.applyGravity) {
                 //this prevents mending with cameraDirection, a global variable of the free camera class.
                 //this prevents mending with cameraDirection, a global variable of the free camera class.
-                actualDirection = direction.add(this.getScene().gravity);
+                actualDisplacement = displacement.add(this.getScene().gravity);
             }
             }
 
 
-            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDirection, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
 
 
         }
         }
 
 

+ 5 - 5
src/Collisions/babylon.collisionCoordinator.ts

@@ -4,7 +4,7 @@ module BABYLON {
     export var CollisionWorker = "";
     export var CollisionWorker = "";
 
 
     export interface ICollisionCoordinator {
     export interface ICollisionCoordinator {
-        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
+        getNewPosition(position: Vector3, displacement: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
         init(scene: Scene): void;
         init(scene: Scene): void;
         destroy(): void;
         destroy(): void;
 
 
@@ -186,12 +186,12 @@ module BABYLON {
             }
             }
         }
         }
 
 
-        public getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void {
+        public getNewPosition(position: Vector3, displacement: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void {
             if (!this._init) return;
             if (!this._init) return;
             if (this._collisionsCallbackArray[collisionIndex] || this._collisionsCallbackArray[collisionIndex + 100000]) return;
             if (this._collisionsCallbackArray[collisionIndex] || this._collisionsCallbackArray[collisionIndex + 100000]) return;
 
 
             position.divideToRef(collider.radius, this._scaledPosition);
             position.divideToRef(collider.radius, this._scaledPosition);
-            velocity.divideToRef(collider.radius, this._scaledVelocity);
+            displacement.divideToRef(collider.radius, this._scaledVelocity);
 
 
             this._collisionsCallbackArray[collisionIndex] = onNewPosition;
             this._collisionsCallbackArray[collisionIndex] = onNewPosition;
 
 
@@ -347,9 +347,9 @@ module BABYLON {
 
 
         private _finalPosition = Vector3.Zero();
         private _finalPosition = Vector3.Zero();
 
 
-        public getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void {
+        public getNewPosition(position: Vector3, displacement: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void {
             position.divideToRef(collider.radius, this._scaledPosition);
             position.divideToRef(collider.radius, this._scaledPosition);
-            velocity.divideToRef(collider.radius, this._scaledVelocity);
+            displacement.divideToRef(collider.radius, this._scaledVelocity);
             collider.collidedMesh = null;
             collider.collidedMesh = null;
             collider.retry = 0;
             collider.retry = 0;
             collider.initialVelocity = this._scaledVelocity;
             collider.initialVelocity = this._scaledVelocity;

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

+ 3 - 3
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -1061,7 +1061,9 @@
                 this._uniformBuffer.bindToEffect(effect, "Material");
                 this._uniformBuffer.bindToEffect(effect, "Material");
 
 
                 this.bindViewProjection(effect);
                 this.bindViewProjection(effect);
-
+                var reflectionTexture = this._getReflectionTexture();
+                var refractionTexture = this._getRefractionTexture();
+                               
                 if (!this._uniformBuffer.useUbo || !this.isFrozen || !this._uniformBuffer.isSync) {
                 if (!this._uniformBuffer.useUbo || !this.isFrozen || !this._uniformBuffer.isSync) {
 
 
                     // Texture uniforms
                     // Texture uniforms
@@ -1081,7 +1083,6 @@
                             MaterialHelper.BindTextureMatrix(this._opacityTexture, this._uniformBuffer, "opacity");
                             MaterialHelper.BindTextureMatrix(this._opacityTexture, this._uniformBuffer, "opacity");
                         }
                         }
 
 
-                        var reflectionTexture = this._getReflectionTexture();
                         if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
                         if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
                             this._uniformBuffer.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix());
                             this._uniformBuffer.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix());
                             this._uniformBuffer.updateFloat2("vReflectionInfos", reflectionTexture.level, 0);
                             this._uniformBuffer.updateFloat2("vReflectionInfos", reflectionTexture.level, 0);
@@ -1146,7 +1147,6 @@
                             }                                                         
                             }                                                         
                         }
                         }
 
 
-                        var refractionTexture = this._getRefractionTexture();
                         if (refractionTexture && StandardMaterial.RefractionTextureEnabled) {
                         if (refractionTexture && StandardMaterial.RefractionTextureEnabled) {
                             this._uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
                             this._uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
 
 

+ 3 - 3
src/Materials/babylon.effect.ts

@@ -419,7 +419,7 @@
                 // Uniform declaration
                 // Uniform declaration
                 if (includeFile.indexOf("__decl__") !== -1) {
                 if (includeFile.indexOf("__decl__") !== -1) {
                     includeFile = includeFile.replace(/__decl__/, "");
                     includeFile = includeFile.replace(/__decl__/, "");
-                    if (this._engine.webGLVersion != 1) {
+                    if (this._engine.supportsUniformBuffers) {
                         includeFile = includeFile.replace(/Vertex/, "Ubo");
                         includeFile = includeFile.replace(/Vertex/, "Ubo");
                         includeFile = includeFile.replace(/Fragment/, "Ubo");
                         includeFile = includeFile.replace(/Fragment/, "Ubo");
                     }
                     }
@@ -455,7 +455,7 @@
                             }
                             }
 
 
                             for (var i = minIndex; i < maxIndex; i++) {
                             for (var i = minIndex; i < maxIndex; i++) {
-                                if (this._engine.webGLVersion === 1) {
+                                if (!this._engine.supportsUniformBuffers) {
                                     // Ubo replacement
                                     // Ubo replacement
                                     sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
                                     sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
                                         return p1 + "{X}";
                                         return p1 + "{X}";
@@ -464,7 +464,7 @@
                                 includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
                                 includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
                             }
                             }
                         } else {
                         } else {
-                            if (this._engine.webGLVersion === 1) {
+                            if (!this._engine.supportsUniformBuffers) {
                                 // Ubo replacement
                                 // Ubo replacement
                                 includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
                                 includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
                                     return p1 + "{X}";
                                     return p1 + "{X}";

+ 1 - 1
src/Materials/babylon.material.ts

@@ -360,7 +360,7 @@
             }
             }
 
 
             this._uniformBuffer = new UniformBuffer(this._scene.getEngine());
             this._uniformBuffer = new UniformBuffer(this._scene.getEngine());
-            this._useUBO = this.getScene().getEngine().webGLVersion > 1;
+            this._useUBO = this.getScene().getEngine().supportsUniformBuffers;
 
 
             if (!doNotAdd) {
             if (!doNotAdd) {
                 this._scene.materials.push(this);
                 this._scene.materials.push(this);

+ 1 - 1
src/Materials/babylon.uniformBuffer.ts

@@ -121,7 +121,7 @@ module BABYLON {
          */
          */
         constructor(engine: Engine, data?: number[], dynamic?: boolean) {
         constructor(engine: Engine, data?: number[], dynamic?: boolean) {
             this._engine = engine;
             this._engine = engine;
-            this._noUBO = engine.webGLVersion === 1;
+            this._noUBO = !engine.supportsUniformBuffers;
             this._dynamic = dynamic;
             this._dynamic = dynamic;
 
 
             this._data = data || [];
             this._data = data || [];

+ 2 - 2
src/Mesh/babylon.abstractMesh.ts

@@ -1533,7 +1533,7 @@
             }
             }
         }
         }
 
 
-        public moveWithCollisions(direction: Vector3): AbstractMesh {
+        public moveWithCollisions(displacement: Vector3): AbstractMesh {
             var globalPosition = this.getAbsolutePosition();
             var globalPosition = this.getAbsolutePosition();
 
 
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPositionForCollisions);
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPositionForCollisions);
@@ -1545,7 +1545,7 @@
 
 
             this._collider.radius = this.ellipsoid;
             this._collider.radius = this.ellipsoid;
 
 
-            this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, direction, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, displacement, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
             return this;
             return this;
         }
         }
 
 

+ 48 - 8
src/babylon.engine.ts

@@ -539,10 +539,20 @@
         public onResizeObservable = new Observable<Engine>();
         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>();
         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
         //WebVR
 
 
         //The new WebVR uses promises.
         //The new WebVR uses promises.
@@ -556,7 +566,11 @@
         private _vrAnimationFrameHandler: number;
         private _vrAnimationFrameHandler: number;
 
 
         // Uniform buffers list
         // Uniform buffers list
+        public disableUniformBuffers = false;
         public _uniformBuffers = new Array<UniformBuffer>();
         public _uniformBuffers = new Array<UniformBuffer>();
+        public get supportsUniformBuffers(): boolean {
+            return this.webGLVersion > 1 && !this.disableUniformBuffers;
+        }
 
 
         // Private Members
         // Private Members
         private _gl: WebGLRenderingContext;
         private _gl: WebGLRenderingContext;
@@ -580,9 +594,14 @@
 
 
         public static audioEngine: AudioEngine;
         public static audioEngine: AudioEngine;
 
 
-        private _onCanvasBlur: () => void;
-        private _onBlur: () => void;
+        
+        // Focus
         private _onFocus: () => void;
         private _onFocus: () => void;
+        private _onBlur: () => void;       
+        private _onCanvasPointerOut: () => void;
+        private _onCanvasBlur: () => void;
+        private _onCanvasFocus: () => void;
+        
         private _onFullscreenChange: () => void;
         private _onFullscreenChange: () => void;
         private _onPointerLockChange: () => void;
         private _onPointerLockChange: () => void;
 
 
@@ -776,7 +795,18 @@
                 if (!this._gl) {
                 if (!this._gl) {
                     throw new Error("WebGL not supported");
                     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 = () => {
                 this._onBlur = () => {
                     if (this.disablePerformanceMonitorInBackground) {
                     if (this.disablePerformanceMonitorInBackground) {
                         this._performanceMonitor.disable();
                         this._performanceMonitor.disable();
@@ -791,14 +821,14 @@
                     this._windowIsBackground = false;
                     this._windowIsBackground = false;
                 };
                 };
 
 
-                this._onCanvasBlur = () => {
-                    this.onCanvasBlurObservable.notifyObservers(this);
+                this._onCanvasPointerOut = () => {
+                    this.onCanvasPointerOutObservable.notifyObservers(this);
                 };
                 };
 
 
                 window.addEventListener("blur", this._onBlur);
                 window.addEventListener("blur", this._onBlur);
                 window.addEventListener("focus", this._onFocus);
                 window.addEventListener("focus", this._onFocus);
 
 
-                canvas.addEventListener("pointerout", this._onCanvasBlur);
+                canvas.addEventListener("pointerout", this._onCanvasPointerOut);
             } else {
             } else {
                 this._gl = <WebGLRenderingContext>canvasOrContext;
                 this._gl = <WebGLRenderingContext>canvasOrContext;
                 this._renderingCanvas = this._gl.canvas
                 this._renderingCanvas = this._gl.canvas
@@ -2832,12 +2862,18 @@
             if (!this._rescalePostProcess) {
             if (!this._rescalePostProcess) {
                 this._rescalePostProcess = new BABYLON.PassPostProcess("rescale", 1, null, Texture.BILINEAR_SAMPLINGMODE, this, false, Engine.TEXTURETYPE_UNSIGNED_INT);
                 this._rescalePostProcess = new BABYLON.PassPostProcess("rescale", 1, null, Texture.BILINEAR_SAMPLINGMODE, this, false, Engine.TEXTURETYPE_UNSIGNED_INT);
             }
             }
+
             this._rescalePostProcess.getEffect().executeWhenCompiled(() => {
             this._rescalePostProcess.getEffect().executeWhenCompiled(() => {
                 this._rescalePostProcess.onApply = function (effect) {
                 this._rescalePostProcess.onApply = function (effect) {
                     effect._bindTexture("textureSampler", source);
                     effect._bindTexture("textureSampler", source);
                 }
                 }
 
 
-                scene.postProcessManager.directRender([this._rescalePostProcess], rtt);
+                let hostingScene = scene;
+
+                if (!hostingScene) {
+                    hostingScene = this.scenes[this.scenes.length - 1];
+                }
+                hostingScene.postProcessManager.directRender([this._rescalePostProcess], rtt);
 
 
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, destination);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, destination);
                 this._gl.copyTexImage2D(this._gl.TEXTURE_2D, 0, internalFormat, 0, 0, destination.width, destination.height, 0);
                 this._gl.copyTexImage2D(this._gl.TEXTURE_2D, 0, internalFormat, 0, 0, destination.width, destination.height, 0);
@@ -4330,6 +4366,8 @@
             window.removeEventListener("focus", this._onFocus);
             window.removeEventListener("focus", this._onFocus);
             window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
             window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
             window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
             window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
+            this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
+            this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);            
             this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
             this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
 
 
             if (!this._doNotHandleContextLost) {
             if (!this._doNotHandleContextLost) {
@@ -4360,6 +4398,8 @@
 
 
             this.onResizeObservable.clear();
             this.onResizeObservable.clear();
             this.onCanvasBlurObservable.clear();
             this.onCanvasBlurObservable.clear();
+            this.onCanvasFocusObservable.clear();
+            this.onCanvasPointerOutObservable.clear();
 
 
             BABYLON.Effect.ResetCache();
             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
      * 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>();
         public onPrePointerObservable = new Observable<PointerInfoPre>();
 
 
@@ -491,8 +422,21 @@
         public _mirroredCameraPosition: Vector3;
         public _mirroredCameraPosition: Vector3;
 
 
         // Keyboard
         // 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 _onKeyDown: (evt: Event) => void;
         private _onKeyUp: (evt: Event) => void;
         private _onKeyUp: (evt: Event) => void;
+        private _onCanvasFocusObserver: Observer<Engine>;
+        private _onCanvasBlurObserver: Observer<Engine>;
 
 
         // Coordinate system
         // Coordinate system
         /**
         /**
@@ -1537,18 +1481,56 @@
                 }).bind(this));
                 }).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) {
                 if (this.actionManager) {
                     this.actionManager.processTrigger(ActionManager.OnKeyDownTrigger, ActionEvent.CreateNewFromScene(this, evt));
                     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) {
                 if (this.actionManager) {
                     this.actionManager.processTrigger(ActionManager.OnKeyUpTrigger, ActionEvent.CreateNewFromScene(this, evt));
                     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 eventPrefix = Tools.GetPointerPrefix();
             var canvas = this._engine.getRenderingCanvas();
             var canvas = this._engine.getRenderingCanvas();
@@ -1568,25 +1550,33 @@
             }
             }
 
 
             canvas.tabIndex = 1;
             canvas.tabIndex = 1;
-
-            canvas.addEventListener("keydown", this._onKeyDown, false);
-            canvas.addEventListener("keyup", this._onKeyUp, false);
         }
         }
 
 
         public detachControl() {
         public detachControl() {
+            let engine = this.getEngine();
             var eventPrefix = Tools.GetPointerPrefix();
             var eventPrefix = Tools.GetPointerPrefix();
-            var canvas = this._engine.getRenderingCanvas();
+            var canvas = engine.getRenderingCanvas();
 
 
             canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
             canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
             canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
             canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
             canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
             canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
 
 
+            engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            engine.onCanvasFocusObservable.remove(this._onCanvasFocusObserver);
+
             // Wheel
             // Wheel
             canvas.removeEventListener('mousewheel', this._onPointerMove);
             canvas.removeEventListener('mousewheel', this._onPointerMove);
             canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
             canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
 
 
+            // Keyboard
             canvas.removeEventListener("keydown", this._onKeyDown);
             canvas.removeEventListener("keydown", this._onKeyDown);
             canvas.removeEventListener("keyup", this._onKeyUp);
             canvas.removeEventListener("keyup", this._onKeyUp);
+
+            // Observables
+            this.onKeyboardObservable.clear();
+            this.onPreKeyboardObservable.clear();
+            this.onPointerObservable.clear();
+            this.onPrePointerObservable.clear();     
         }
         }
 
 
         // Ready
         // Ready