Explorar el Código

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

Julien Moreau-Mathis hace 9 años
padre
commit
381665c2c3
Se han modificado 51 ficheros con 52437 adiciones y 65266 borrados
  1. 1 1
      bower.json
  2. 18 18
      dist/preview release/babylon.core.js
  3. 3939 3933
      dist/preview release/babylon.d.ts
  4. 37 36
      dist/preview release/babylon.js
  5. 119 49
      dist/preview release/babylon.max.js
  6. 37 36
      dist/preview release/babylon.noworker.js
  7. 6 3
      dist/preview release/what's new.md
  8. 7 0
      proceduralTexturesLibrary/config.json
  9. 207 0
      proceduralTexturesLibrary/dist/babylon.asciiArtPostProcess.js
  10. 1 0
      proceduralTexturesLibrary/dist/babylon.asciiArtPostProcess.min.js
  11. 1 1
      proceduralTexturesLibrary/dist/babylon.brickProceduralTexture.js
  12. 1 1
      proceduralTexturesLibrary/dist/babylon.brickProceduralTexture.min.js
  13. 1 1
      proceduralTexturesLibrary/dist/babylon.cloudProceduralTexture.js
  14. 1 1
      proceduralTexturesLibrary/dist/babylon.cloudProceduralTexture.min.js
  15. 1 1
      proceduralTexturesLibrary/dist/babylon.fireProceduralTexture.js
  16. 1 1
      proceduralTexturesLibrary/dist/babylon.fireProceduralTexture.min.js
  17. 1 1
      proceduralTexturesLibrary/dist/babylon.grassProceduralTexture.js
  18. 1 1
      proceduralTexturesLibrary/dist/babylon.grassProceduralTexture.min.js
  19. 1 1
      proceduralTexturesLibrary/dist/babylon.roadProceduralTexture.js
  20. 1 1
      proceduralTexturesLibrary/dist/babylon.roadProceduralTexture.min.js
  21. 1 1
      proceduralTexturesLibrary/dist/babylon.woodProceduralTexture.js
  22. 1 1
      proceduralTexturesLibrary/dist/babylon.woodProceduralTexture.min.js
  23. 3 2
      proceduralTexturesLibrary/gulpfile.js
  24. 1 1
      proceduralTexturesLibrary/package.json
  25. 50 0
      proceduralTexturesLibrary/proceduralTextures/asciiArt/asciiart.fragment.fx
  26. 265 0
      proceduralTexturesLibrary/proceduralTextures/asciiArt/babylon.asciiArtPostProcess.ts
  27. 4 0
      proceduralTexturesLibrary/test/add/addAsciiArtPP.js
  28. 0 37072
      proceduralTexturesLibrary/test/babylon.max.js
  29. 11 3
      proceduralTexturesLibrary/test/index.html
  30. 2 1
      proceduralTexturesLibrary/test/index.js
  31. 47434 23992
      proceduralTexturesLibrary/test/refs/babylon.max.js
  32. 7 0
      proceduralTexturesLibrary/tsconfig.json
  33. 1 1
      readme.md
  34. 5 4
      src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js
  35. 9 4
      src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.ts
  36. 5 4
      src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js
  37. 7 5
      src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts
  38. 5 4
      src/Cameras/Inputs/babylon.freecamera.input.keyboard.js
  39. 8 5
      src/Cameras/Inputs/babylon.freecamera.input.keyboard.ts
  40. 1 0
      src/Cameras/Inputs/babylon.freecamera.input.mouse.js
  41. 2 1
      src/Cameras/Inputs/babylon.freecamera.input.mouse.ts
  42. 12 2
      src/Materials/babylon.effect.js
  43. 16 3
      src/Materials/babylon.effect.ts
  44. 45 4
      src/Materials/babylon.shaderMaterial.js
  45. 57 5
      src/Materials/babylon.shaderMaterial.ts
  46. 5 5
      src/Mesh/babylon.groundMesh.js
  47. 5 6
      src/Mesh/babylon.groundMesh.ts
  48. 20 1
      src/babylon.engine.js
  49. 26 1
      src/babylon.engine.ts
  50. 21 25
      src/babylon.scene.js
  51. 26 27
      src/babylon.scene.ts

+ 1 - 1
bower.json

@@ -1,7 +1,7 @@
 {
   "name": "babylonjs",
   "description": "Babylon.js is a complete JavaScript framework for building 3D games with HTML 5 and WebGL",
-  "main": "./dist/babylon.2.3.js",
+  "main": "./dist/babylon.2.4.js",
   "homepage": "https://www.babylonjs.com",
   "repository": {
     "type": "git",

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 18 - 18
dist/preview release/babylon.core.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 3939 - 3933
dist/preview release/babylon.d.ts


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 37 - 36
dist/preview release/babylon.js


+ 119 - 49
dist/preview release/babylon.max.js

@@ -7768,10 +7768,14 @@ var BABYLON;
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             }
         };
-        Engine.prototype.setTexture = function (channel, texture) {
+        Engine.prototype.setTexture = function (channel, uniform, texture) {
             if (channel < 0) {
                 return;
             }
+            this._gl.uniform1i(uniform, channel);
+            this._setTexture(channel, texture);
+        };
+        Engine.prototype._setTexture = function (channel, texture) {
             // Not ready?
             if (!texture || !texture.isReady()) {
                 if (this._activeTexturesCache[channel] != null) {
@@ -7843,6 +7847,21 @@ var BABYLON;
                 this._setAnisotropicLevel(this._gl.TEXTURE_2D, texture);
             }
         };
+        Engine.prototype.setTextureArray = function (channel, uniform, textures) {
+            if (channel < 0) {
+                return;
+            }
+            if (!this._textureUnits || this._textureUnits.length !== textures.length) {
+                this._textureUnits = new Int32Array(textures.length);
+            }
+            for (var i = 0; i < textures.length; i++) {
+                this._textureUnits[i] = channel + i;
+            }
+            this._gl.uniform1iv(uniform, this._textureUnits);
+            for (var index = 0; index < textures.length; index++) {
+                this._setTexture(channel + index, textures[index]);
+            }
+        };
         Engine.prototype._setAnisotropicLevel = function (key, texture) {
             var anisotropicFilterExtension = this._caps.textureAnisotropicFilterExtension;
             var value = texture.anisotropicFilteringLevel;
@@ -12501,6 +12520,7 @@ var BABYLON;
                         };
                         if (!noPreventDefault) {
                             evt.preventDefault();
+                            element.focus();
                         }
                     }
                     else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
@@ -12578,6 +12598,7 @@ var BABYLON;
         FreeCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
             if (!this._onKeyDown) {
+                element.tabIndex = 1;
                 this._onKeyDown = function (evt) {
                     if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         _this.keysDown.indexOf(evt.keyCode) !== -1 ||
@@ -12606,18 +12627,18 @@ var BABYLON;
                         }
                     }
                 };
+                element.addEventListener("keydown", this._onKeyDown, false);
+                element.addEventListener("keyup", this._onKeyUp, false);
                 BABYLON.Tools.RegisterTopRootEvents([
-                    { name: "keydown", handler: this._onKeyDown },
-                    { name: "keyup", handler: this._onKeyUp },
                     { name: "blur", handler: this._onLostFocus }
                 ]);
             }
         };
         FreeCameraKeyboardMoveInput.prototype.detachControl = function (element) {
             if (this._onKeyDown) {
+                element.removeEventListener("keydown", this._onKeyDown);
+                element.removeEventListener("keyup", this._onKeyUp);
                 BABYLON.Tools.UnregisterTopRootEvents([
-                    { name: "keydown", handler: this._onKeyDown },
-                    { name: "keyup", handler: this._onKeyUp },
                     { name: "blur", handler: this._onLostFocus }
                 ]);
                 this._keys = [];
@@ -12987,6 +13008,7 @@ var BABYLON;
         }
         ArcRotateCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
+            element.tabIndex = 1;
             this._onKeyDown = function (evt) {
                 if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     _this.keysDown.indexOf(evt.keyCode) !== -1 ||
@@ -13022,16 +13044,16 @@ var BABYLON;
             this._onLostFocus = function () {
                 _this._keys = [];
             };
+            element.addEventListener("keydown", this._onKeyDown, false);
+            element.addEventListener("keyup", this._onKeyUp, false);
             BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         };
         ArcRotateCameraKeyboardMoveInput.prototype.detachControl = function (element) {
+            element.removeEventListener("keydown", this._onKeyDown);
+            element.removeEventListener("keyup", this._onKeyUp);
             BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
             this._keys = [];
@@ -13176,6 +13198,7 @@ var BABYLON;
                     }
                     if (!noPreventDefault) {
                         evt.preventDefault();
+                        element.focus();
                     }
                 }
                 else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
@@ -13295,9 +13318,9 @@ var BABYLON;
             element.addEventListener("mousemove", this._onMouseMove, false);
             element.addEventListener("MSPointerDown", this._onGestureStart, false);
             element.addEventListener("MSGestureChange", this._onGesture, false);
+            element.addEventListener("keydown", this._onKeyDown, false);
+            element.addEventListener("keyup", this._onKeyUp, false);
             BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         };
@@ -13309,6 +13332,8 @@ var BABYLON;
                 element.removeEventListener("mousemove", this._onMouseMove);
                 element.removeEventListener("MSPointerDown", this._onGestureStart);
                 element.removeEventListener("MSGestureChange", this._onGesture);
+                element.removeEventListener("keydown", this._onKeyDown);
+                element.removeEventListener("keyup", this._onKeyUp);
                 this._isRightClick = false;
                 this._isCtrlPushed = false;
                 this.pinchInwards = true;
@@ -13322,8 +13347,6 @@ var BABYLON;
                 this._onContextMenu = null;
             }
             BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         };
@@ -15020,6 +15043,7 @@ var BABYLON;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
+            this.hoverCursor = "pointer";
             // Events
             /**
             * An event triggered when the scene is disposed.
@@ -15206,7 +15230,6 @@ var BABYLON;
             this._particlesDuration = new BABYLON.PerfCounter();
             this._renderDuration = new BABYLON.PerfCounter();
             this._spritesDuration = new BABYLON.PerfCounter();
-            this._animationRatio = new BABYLON.PerfCounter();
             this._activeBones = new BABYLON.PerfCounter();
             this._renderId = 0;
             this._executeWhenReadyTimeoutId = -1;
@@ -15505,9 +15528,6 @@ var BABYLON;
             configurable: true
         });
         Scene.prototype.getAnimationRatio = function () {
-            return this._animationRatio.current;
-        };
-        Scene.prototype.animationRatioPerfCounter = function () {
             return this._animationRatio;
         };
         Scene.prototype.getRenderId = function () {
@@ -15566,7 +15586,7 @@ var BABYLON;
                     _this.setPointerOverSprite(null);
                     _this.setPointerOverMesh(pickResult.pickedMesh);
                     if (_this._pointerOverMesh.actionManager && _this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                        canvas.style.cursor = "pointer";
+                        canvas.style.cursor = _this.hoverCursor;
                     }
                     else {
                         canvas.style.cursor = "";
@@ -15577,7 +15597,7 @@ var BABYLON;
                     // Sprites
                     pickResult = _this.pickSprite(_this._unTranslatedPointerX, _this._unTranslatedPointerY, spritePredicate, false, _this.cameraToUseForPointers);
                     if (pickResult.hit && pickResult.pickedSprite) {
-                        canvas.style.cursor = "pointer";
+                        canvas.style.cursor = _this.hoverCursor;
                         _this.setPointerOverSprite(pickResult.pickedSprite);
                     }
                     else {
@@ -15762,35 +15782,34 @@ var BABYLON;
                 }
             };
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
+            var canvas = this._engine.getRenderingCanvas();
             if (attachMove) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "move", this._onPointerMove, false);
+                canvas.addEventListener(eventPrefix + "move", this._onPointerMove, false);
                 // Wheel
-                this._engine.getRenderingCanvas().addEventListener('mousewheel', this._onPointerMove, false);
-                this._engine.getRenderingCanvas().addEventListener('DOMMouseScroll', this._onPointerMove, false);
+                canvas.addEventListener('mousewheel', this._onPointerMove, false);
+                canvas.addEventListener('DOMMouseScroll', this._onPointerMove, false);
             }
             if (attachDown) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "down", this._onPointerDown, false);
+                canvas.addEventListener(eventPrefix + "down", this._onPointerDown, false);
             }
             if (attachUp) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "up", this._onPointerUp, false);
+                canvas.addEventListener(eventPrefix + "up", this._onPointerUp, false);
             }
-            BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp }
-            ]);
+            canvas.tabIndex = 1;
+            canvas.addEventListener("keydown", this._onKeyDown, false);
+            canvas.addEventListener("keyup", this._onKeyUp, false);
         };
         Scene.prototype.detachControl = function () {
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "move", this._onPointerMove);
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "down", this._onPointerDown);
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "up", this._onPointerUp);
+            var canvas = this._engine.getRenderingCanvas();
+            canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
+            canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
+            canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
             // Wheel
-            this._engine.getRenderingCanvas().removeEventListener('mousewheel', this._onPointerMove);
-            this._engine.getRenderingCanvas().removeEventListener('DOMMouseScroll', this._onPointerMove);
-            BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp }
-            ]);
+            canvas.removeEventListener('mousewheel', this._onPointerMove);
+            canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
+            canvas.removeEventListener("keydown", this._onKeyDown);
+            canvas.removeEventListener("keyup", this._onKeyUp);
         };
         // Ready
         Scene.prototype.isReady = function () {
@@ -16749,7 +16768,7 @@ var BABYLON;
             }
             // Animations
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
-            this._animationRatio.addCount(deltaTime * (60.0 / 1000.0), true);
+            this._animationRatio = deltaTime * (60.0 / 1000.0);
             this._animate();
             // Physics
             if (this._physicsEngine) {
@@ -23247,7 +23266,8 @@ var BABYLON;
                 this._program = engine.createShaderProgram(vertexSourceCode, fragmentSourceCode, defines);
                 this._uniforms = engine.getUniforms(this._program, this._uniformsNames);
                 this._attributes = engine.getAttributes(this._program, attributesNames);
-                for (var index = 0; index < this._samplers.length; index++) {
+                var index;
+                for (index = 0; index < this._samplers.length; index++) {
                     var sampler = this.getUniform(this._samplers[index]);
                     if (sampler == null) {
                         this._samplers.splice(index, 1);
@@ -23291,7 +23311,16 @@ var BABYLON;
             this._engine._bindTexture(this._samplers.indexOf(channel), texture);
         };
         Effect.prototype.setTexture = function (channel, texture) {
-            this._engine.setTexture(this._samplers.indexOf(channel), texture);
+            this._engine.setTexture(this._samplers.indexOf(channel), this.getUniform(channel), texture);
+        };
+        Effect.prototype.setTextureArray = function (channel, textures) {
+            if (this._samplers.indexOf(channel + "Ex") === -1) {
+                var initialPos = this._samplers.indexOf(channel);
+                for (var index = 1; index < textures.length; index++) {
+                    this._samplers.splice(initialPos + index, 0, channel + "Ex");
+                }
+            }
+            this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         };
         Effect.prototype.setTextureFromPostProcess = function (channel, postProcess) {
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
@@ -34327,10 +34356,10 @@ var BABYLON;
         // facet2 :  Vector4(a, b, c, d) = second facet 3D plane equation : ax + by + cz + d = 0
         GroundMesh.prototype._computeHeightQuads = function () {
             var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
-            var v1 = BABYLON.Tmp.Vector3[0];
-            var v2 = BABYLON.Tmp.Vector3[1];
-            var v3 = BABYLON.Tmp.Vector3[2];
-            var v4 = BABYLON.Tmp.Vector3[3];
+            var v1 = BABYLON.Tmp.Vector3[3];
+            var v2 = BABYLON.Tmp.Vector3[2];
+            var v3 = BABYLON.Tmp.Vector3[1];
+            var v4 = BABYLON.Tmp.Vector3[0];
             var v1v2 = BABYLON.Tmp.Vector3[4];
             var v1v3 = BABYLON.Tmp.Vector3[5];
             var v1v4 = BABYLON.Tmp.Vector3[6];
@@ -34371,7 +34400,7 @@ var BABYLON;
                     v2.subtractToRef(v1, v1v2);
                     v3.subtractToRef(v1, v1v3);
                     v4.subtractToRef(v1, v1v4);
-                    BABYLON.Vector3.CrossToRef(v1v4, v1v3, norm1);
+                    BABYLON.Vector3.CrossToRef(v1v4, v1v3, norm1); // caution : CrossToRef uses the Tmp class
                     BABYLON.Vector3.CrossToRef(v1v2, v1v4, norm2);
                     norm1.normalize();
                     norm2.normalize();
@@ -46373,6 +46402,7 @@ var BABYLON;
         function ShaderMaterial(name, scene, shaderPath, options) {
             _super.call(this, name, scene);
             this._textures = {};
+            this._textureArrays = {};
             this._floats = {};
             this._floatsArrays = {};
             this._colors3 = {};
@@ -46411,6 +46441,14 @@ var BABYLON;
             this._textures[name] = texture;
             return this;
         };
+        ShaderMaterial.prototype.setTextureArray = function (name, textures) {
+            if (this._options.samplers.indexOf(name) === -1) {
+                this._options.samplers.push(name);
+            }
+            this._checkUniform(name);
+            this._textureArrays[name] = textures;
+            return this;
+        };
         ShaderMaterial.prototype.setFloat = function (name, value) {
             this._checkUniform(name);
             this._floats[name] = value;
@@ -46530,10 +46568,15 @@ var BABYLON;
                 if (mesh && mesh.useBones && mesh.computeBonesUsingShaders) {
                     this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
                 }
+                var name;
                 // Texture
-                for (var name in this._textures) {
+                for (name in this._textures) {
                     this._effect.setTexture(name, this._textures[name]);
                 }
+                // Texture arrays
+                for (name in this._textureArrays) {
+                    this._effect.setTextureArray(name, this._textureArrays[name]);
+                }
                 // Float    
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
@@ -46584,9 +46627,16 @@ var BABYLON;
         };
         ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
             if (forceDisposeTextures) {
-                for (var name in this._textures) {
+                var name;
+                for (name in this._textures) {
                     this._textures[name].dispose();
                 }
+                for (name in this._textureArrays) {
+                    var array = this._textureArrays[name];
+                    for (var index = 0; index < array.length; index++) {
+                        array[index].dispose();
+                    }
+                }
             }
             this._textures = {};
             _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures);
@@ -46596,11 +46646,21 @@ var BABYLON;
             serializationObject.customType = "BABYLON.ShaderMaterial";
             serializationObject.options = this._options;
             serializationObject.shaderPath = this._shaderPath;
+            var name;
             // Texture
             serializationObject.textures = {};
-            for (var name in this._textures) {
+            for (name in this._textures) {
                 serializationObject.textures[name] = this._textures[name].serialize();
             }
+            // Texture arrays
+            serializationObject.textureArrays = {};
+            for (name in this._textureArrays) {
+                serializationObject.textureArrays[name] = [];
+                var array = this._textureArrays[name];
+                for (var index = 0; index < array.length; index++) {
+                    serializationObject.textureArrays[name].push(array[index].serialize());
+                }
+            }
             // Float    
             serializationObject.floats = {};
             for (name in this._floats) {
@@ -46655,10 +46715,20 @@ var BABYLON;
         };
         ShaderMaterial.Parse = function (source, scene, rootUrl) {
             var material = BABYLON.SerializationHelper.Parse(function () { return new ShaderMaterial(source.name, scene, source.shaderPath, source.options); }, source, scene, rootUrl);
+            var name;
             // Texture
-            for (var name in source.textures) {
+            for (name in source.textures) {
                 material.setTexture(name, BABYLON.Texture.Parse(source.textures[name], scene, rootUrl));
             }
+            // Texture arrays
+            for (name in source.textureArrays) {
+                var array = source.textureArrays[name];
+                var textureArray = new Array();
+                for (var index = 0; index < array.length; index++) {
+                    textureArray.push(BABYLON.Texture.Parse(array[index], scene, rootUrl));
+                }
+                material.setTextureArray(name, textureArray);
+            }
             // Float    
             for (name in source.floats) {
                 material.setFloat(name, source.floats[name]);

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 37 - 36
dist/preview release/babylon.noworker.js


+ 6 - 3
dist/preview release/what's new.md

@@ -3,13 +3,16 @@
 ### Major updates
     
 ### Updates
+- Added support for texture arrays ([deltakosh](https://github.com/deltakosh)) 
 - Added `camera.isInFrustum` and `camera.isCompletelyInFrustum`. Can be used with meshes, submeshes and boundingInfo ([deltakosh](https://github.com/deltakosh)) 
 - Several memory allocation reduction ([benaadams](https://github.com/benaadams)) 
 - Several GPU state change reduction ([benaadams](https://github.com/benaadams)) 
 - MapTexture: add `supersample` mode to double font quality. ([nockawa](https://github.com/nockawa))
-- new `invertUV` parameter an all ribbon based shapes : ribbon, tube, lathe, basic and custom extrusion
-- Text2D: new `fontSuperSample` setting to use high quality font.
-- PerfCounter class added to monitor time/counter and expose min/max/average/lastSecondAverage/current metrics. Updated engine/scene current counter to use this class, exposing new properties as well to access the PerfCounter object. ([nockawa](https://github.com/nockawa))
+- New `invertUV` parameter an all ribbon based shapes : ribbon, tube, lathe, basic and custom extrusion ([jerome](https://github.com/jbousquie))
+- Text2D: new `fontSuperSample` setting to use high quality font ([nockawa](https://github.com/nockawa))
+- PerfCounter class added to monitor time/counter and expose min/max/average/lastSecondAverage/current metrics. Updated engine/scene current counter to use this class, exposing new properties as well to access the PerfCounter object ([nockawa](https://github.com/nockawa))
+- Better keyboard event handling which is now done at canvas level and not at window level ([deltakosh](https://github.com/deltakosh)) 
+- New `scene.hoverCursor` property to define a custom cursor when moving mouse over meshes ([deltakosh](https://github.com/deltakosh)) 
 
 ### Exporters
     

+ 7 - 0
proceduralTexturesLibrary/config.json

@@ -55,6 +55,13 @@
         "proceduralTextures/starfield/starfieldProceduralTexture.fragment.fx"
       ],
       "output": "babylon.starfieldProceduralTexture.js"
+    },
+    {
+      "file": "proceduralTextures/asciiArt/babylon.asciiArtPostProcess.ts",
+      "shaderFiles": [
+        "proceduralTextures/asciiArt/asciiart.fragment.fx"
+      ],
+      "output": "babylon.asciiArtPostProcess.js"
     }
   ],
   "build": {

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 207 - 0
proceduralTexturesLibrary/dist/babylon.asciiArtPostProcess.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
proceduralTexturesLibrary/dist/babylon.asciiArtPostProcess.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.brickProceduralTexture.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.brickProceduralTexture.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.cloudProceduralTexture.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.cloudProceduralTexture.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.fireProceduralTexture.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.fireProceduralTexture.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.grassProceduralTexture.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.grassProceduralTexture.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.roadProceduralTexture.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.roadProceduralTexture.min.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.woodProceduralTexture.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
proceduralTexturesLibrary/dist/babylon.woodProceduralTexture.min.js


+ 3 - 2
proceduralTexturesLibrary/gulpfile.js

@@ -20,7 +20,7 @@ function shadersName(filename) {
 }
 
 gulp.task('copyReference', function () {
-    return gulp.src("../dist/preview release/babylon.max.js").pipe(gulp.dest("test"));
+    return gulp.src("../dist/preview release/babylon.max.js").pipe(gulp.dest("test/refs"));
 });
 
 /*
@@ -33,7 +33,8 @@ gulp.task('default', ["copyReference"], function () {
                 noExternalResolve: false,
                 target: 'ES5',
                 declarationFiles: true,
-                typescript: require('typescript')
+                typescript: require('typescript'),
+                experimentalDecorators: true
             })).js;
 
         var shader = gulp.src(proceduralTexture.shaderFiles).pipe(srcToVariable("BABYLON.Effect.ShadersStore", true, shadersName));

+ 1 - 1
proceduralTexturesLibrary/package.json

@@ -10,7 +10,7 @@
     "gulp": "^3.8.11",
     "gulp-uglify": "~1.4.2",
     "typescript": "~1.6.2",
-    "gulp-typescript": "~2.9.0",
+    "gulp-typescript": "~2.13.0",
     "through2": "~0.6.5",
     "gulp-util": "~3.0.4",
     "gulp-concat": "~2.6.0",

+ 50 - 0
proceduralTexturesLibrary/proceduralTextures/asciiArt/asciiart.fragment.fx

@@ -0,0 +1,50 @@
+// Samplers.
+varying vec2 vUV;
+uniform sampler2D textureSampler;
+uniform sampler2D asciiArtFont;
+
+// Infos.
+uniform vec4 asciiArtFontInfos;
+uniform vec4 asciiArtOptions;
+
+// Transform color to luminance.
+float getLuminance(vec3 color)
+{
+    return clamp(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0., 1.);
+}
+
+// Main functions.
+void main(void) 
+{
+    float caracterSize = asciiArtFontInfos.x;
+    float numChar = asciiArtFontInfos.y - 1.0;
+    float fontx = asciiArtFontInfos.z;
+    float fonty = asciiArtFontInfos.w;
+
+    float screenx = asciiArtOptions.x;
+    float screeny = asciiArtOptions.y;
+
+    float tileX = float(floor((gl_FragCoord.x) / caracterSize)) * caracterSize / screenx;
+    float tileY = float(floor((gl_FragCoord.y) / caracterSize)) * caracterSize / screeny;
+
+    vec2 tileUV = vec2(tileX, tileY);
+    vec4 tileColor = texture2D(textureSampler, tileUV);
+    vec4 baseColor = texture2D(textureSampler, vUV);
+
+    float tileLuminance = getLuminance(tileColor.rgb);
+
+    float offsetx = (float(floor(tileLuminance * numChar))) * caracterSize / fontx;
+    float offsety = 0.0;
+
+    float x = float(mod(gl_FragCoord.x, caracterSize)) / fontx;
+    float y = float(mod(gl_FragCoord.y, caracterSize)) / fonty;
+
+    vec4 finalColor =  texture2D(asciiArtFont, vec2(offsetx + x, offsety + (caracterSize / fonty - y)));
+    finalColor.rgb *= tileColor.rgb;
+    finalColor.a = 1.0;
+
+    finalColor =  mix(finalColor, tileColor, asciiArtOptions.w);
+    finalColor =  mix(finalColor, baseColor, asciiArtOptions.z);
+
+    gl_FragColor = finalColor;
+}

+ 265 - 0
proceduralTexturesLibrary/proceduralTextures/asciiArt/babylon.asciiArtPostProcess.ts

@@ -0,0 +1,265 @@
+ /// <reference path="../../../dist/preview release/babylon.d.ts"/>
+ 
+module BABYLON {
+
+    /**
+     * AsciiArtFontTexture is the helper class used to easily create your ascii art font texture.
+     * 
+     * It basically takes care rendering the font front the given font size to a texture.
+     * This is used later on in the postprocess.
+     */
+    export class AsciiArtFontTexture extends BaseTexture {
+        
+        @serialize("font")
+        private _font: string;
+
+        @serialize("text")
+        private _text: string;
+
+        private _charSize: number;
+
+        /**
+         * Gets the size of one char in the texture (each char fits in size * size space in the texture). 
+         */
+        public get charSize(): number {
+            return this._charSize;
+        }
+
+        /**
+         * Create a new instance of the Ascii Art FontTexture class
+         * @param name the name of the texture
+         * @param font the font to use, use the W3C CSS notation
+         * @param text the caracter set to use in the rendering.
+         * @param scene the scene that owns the texture
+         */
+        constructor(name: string, font: string, text: string, scene: Scene) {
+            super(scene);
+
+            this.name = name;
+            this._text == text;
+            this._font == font;
+
+            this.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this.wrapV = Texture.CLAMP_ADDRESSMODE;
+            //this.anisotropicFilteringLevel = 1;
+
+            // Get the font specific info.
+            var maxCharHeight = this.getFontHeight(font);
+            var maxCharWidth = this.getFontWidth(font); 
+
+            this._charSize = Math.max(maxCharHeight.height, maxCharWidth);
+
+            // This is an approximate size, but should always be able to fit at least the maxCharCount.
+            var textureWidth = Math.ceil(this._charSize * text.length);
+            var textureHeight = this._charSize;
+
+            // Create the texture that will store the font characters.
+            this._texture = scene.getEngine().createDynamicTexture(textureWidth, textureHeight, false, Texture.NEAREST_SAMPLINGMODE);
+            //scene.getEngine().setclamp
+            var textureSize = this.getSize();
+
+            // Create a canvas with the final size: the one matching the texture.
+            var canvas = document.createElement("canvas");
+            canvas.width = textureSize.width;
+            canvas.height = textureSize.height;
+            var context = canvas.getContext("2d");
+            context.textBaseline = "top";
+            context.font = font;
+            context.fillStyle = "white";
+            context.imageSmoothingEnabled = false;
+
+            // Sets the text in the texture.
+            for (var i = 0; i < text.length; i++) {
+                context.fillText(text[i], i * this._charSize, -maxCharHeight.offset);
+            }        
+
+            // Flush the text in the dynamic texture.
+            this.getScene().getEngine().updateDynamicTexture(this._texture, canvas, false, true);
+        }
+
+        /**
+         * Gets the max char width of a font.
+         * @param font the font to use, use the W3C CSS notation
+         * @return the max char width
+         */
+        private getFontWidth(font: string): number {
+            var fontDraw = document.createElement("canvas");
+            var ctx = fontDraw.getContext('2d');
+            ctx.fillStyle = 'white';
+            ctx.font = font;
+
+            return ctx.measureText("W").width;
+        }
+
+        // More info here: https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
+        /**
+         * Gets the max char height of a font.
+         * @param font the font to use, use the W3C CSS notation
+         * @return the max char height
+         */
+        private getFontHeight(font: string): {height: number, offset: number} {
+            var fontDraw = document.createElement("canvas");
+            var ctx = fontDraw.getContext('2d');
+            ctx.fillRect(0, 0, fontDraw.width, fontDraw.height);
+            ctx.textBaseline = 'top';
+            ctx.fillStyle = 'white';
+            ctx.font = font;
+            ctx.fillText('jH|', 0, 0);
+            var pixels = ctx.getImageData(0, 0, fontDraw.width, fontDraw.height).data;
+            var start = -1;
+            var end = -1;
+            for (var row = 0; row < fontDraw.height; row++) {
+                for (var column = 0; column < fontDraw.width; column++) {
+                    var index = (row * fontDraw.width + column) * 4;
+                    if (pixels[index] === 0) {
+                        if (column === fontDraw.width - 1 && start !== -1) {
+                            end = row;
+                            row = fontDraw.height;
+                            break;
+                        }
+                        continue;
+                    }
+                    else {
+                        if (start === -1) {
+                            start = row;
+                        }
+                        break;
+                    }
+                }
+            }
+            return { height: (end - start)+1, offset: start-1}
+        }
+
+        /**
+         * Clones the current AsciiArtTexture.
+         * @return the clone of the texture.
+         */
+        public clone(): AsciiArtFontTexture {
+            return new AsciiArtFontTexture(this.name, this._font, this._text, this.getScene());
+        }
+
+        /**
+         * Parses a json object representing the texture and returns an instance of it.
+         * @param source the source JSON representation
+         * @param scene the scene to create the texture for
+         * @return the parsed texture
+         */
+        public static Parse(source: any, scene: Scene): AsciiArtFontTexture {
+            var texture = SerializationHelper.Parse(() => new AsciiArtFontTexture(source.name, source.font, source.text, scene), 
+                source, scene, null);
+
+            return texture;
+        }
+    }
+
+    /**
+     * Option available in the Ascii Art Post Process.
+     */
+    export interface IAsciiArtPostProcessOptions {
+
+        /**
+         * The font to use following the w3c font definition.
+         */
+        font?: string;
+
+        /**
+         * The character set to use in the postprocess.
+         */
+        characterSet?: string;
+
+        /**
+         * This defines the amount you want to mix the "tile" or caracter space colored in the ascii art.
+         * This number is defined between 0 and 1;
+         */
+        mixToTile?:number;
+
+        /**
+         * This defines the amount you want to mix the normal rendering pass in the ascii art.
+         * This number is defined between 0 and 1;
+         */
+        mixToNormal?:number;
+    }
+
+    /**
+     * AsciiArtPostProcess helps rendering everithing in Ascii Art.
+     * 
+     * Simmply add it to your scene and let the nerd that lives in you have fun.
+     * Example usage: var pp = new AsciiArtPostProcess("myAscii", "20px Monospace", camera);
+     */
+    export class AsciiArtPostProcess extends PostProcess {
+
+        /**
+         * The font texture used to render the char in the post process.
+         */
+        private _asciiArtFontTexture: AsciiArtFontTexture;
+
+        /**
+         * This defines the amount you want to mix the "tile" or caracter space colored in the ascii art.
+         * This number is defined between 0 and 1;
+         */
+        public mixToTile:number = 0;
+
+        /**
+         * This defines the amount you want to mix the normal rendering pass in the ascii art.
+         * This number is defined between 0 and 1;
+         */
+        public mixToNormal:number = 0;
+
+        /**
+         * Instantiates a new Ascii Art Post Process.
+         * @param name the name to give to the postprocess
+         * @camera the camera to apply the post process to.
+         * @param options can either be the font name or an option object following the IAsciiArtPostProcessOptions format
+         */
+        constructor(name: string, camera: Camera, options?: string | IAsciiArtPostProcessOptions) {
+            super(name, 
+                'asciiart', 
+                ['asciiArtFontInfos', 'asciiArtOptions'], 
+                ['asciiArtFont'],
+                { 
+                    width: camera.getEngine().getRenderWidth(), 
+                    height: camera.getEngine().getRenderHeight()
+                }, 
+                camera, 
+                Texture.TRILINEAR_SAMPLINGMODE, 
+                camera.getEngine(), 
+                true);
+
+            // Default values.
+            var font = "40px Monospace";
+            var characterSet =  " `-.'_:,\"=^;<+!*?/cL\\zrs7TivJtC{3F)Il(xZfY5S2eajo14[nuyE]P6V9kXpKwGhqAUbOd8#HRDB0$mgMW&Q%N@";
+
+            // Use options.
+            if (options) {
+                if (typeof(options) === "string") {
+                    font = <string>options;
+                }   
+                else {
+                    font = (<IAsciiArtPostProcessOptions>options).font || font;
+                    characterSet = (<IAsciiArtPostProcessOptions>options).characterSet || characterSet;
+                    this.mixToTile = (<IAsciiArtPostProcessOptions>options).mixToTile || this.mixToTile;
+                    this.mixToNormal = (<IAsciiArtPostProcessOptions>options).mixToNormal || this.mixToNormal;
+                } 
+            }
+
+            this._asciiArtFontTexture = new AsciiArtFontTexture(name, font, characterSet, camera.getScene());
+            var textureSize = this._asciiArtFontTexture.getSize();
+
+            this.onApply = (effect: Effect) => {
+                effect.setTexture("asciiArtFont", this._asciiArtFontTexture);
+				
+                effect.setFloat4("asciiArtFontInfos", 
+                    this._asciiArtFontTexture.charSize, 
+                    characterSet.length, 
+                    textureSize.width, 
+                    textureSize.height);
+
+                effect.setFloat4("asciiArtOptions",
+                    this.width, 
+                    this.height,
+                    this.mixToNormal, 
+                    this.mixToTile);
+            };
+        }
+    }
+} 

+ 4 - 0
proceduralTexturesLibrary/test/add/addAsciiArtPP.js

@@ -0,0 +1,4 @@
+window.addAsciiArtPP = function(camera) {
+    var postProcess = new BABYLON.AsciiArtPostProcess("asciiArt", camera);
+    return postProcess;
+};

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 37072
proceduralTexturesLibrary/test/babylon.max.js


+ 11 - 3
proceduralTexturesLibrary/test/index.html

@@ -12,6 +12,7 @@
 	<script src="../dist/babylon.brickProceduralTexture.js"></script>
 	<script src="../dist/babylon.marbleProceduralTexture.js"></script>
 	<script src="../dist/babylon.starfieldProceduralTexture.js"></script>
+	<script src="../dist/babylon.asciiArtPostProcess.js"></script>
 
 	<style>
 		html, body {
@@ -54,10 +55,8 @@
     <script src="add/addBrickPT.js"></script>
     <script src="add/addMarblePT.js"></script>
     <script src="add/addStarfieldPT.js"></script>
+    <script src="add/addAsciiArtPP.js"></script>
 	<script>
-		
-		
-		
 		if (BABYLON.Engine.isSupported()) {
 			var canvas = document.getElementById("renderCanvas");
 			var engine = new BABYLON.Engine(canvas, true);
@@ -67,6 +66,7 @@
 
 			var camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 6, 50, BABYLON.Vector3.Zero(), scene);
 			camera.attachControl(canvas, true);
+			scene.postProcessesEnabled = false;
 
 			// Lights
 			var hemisphericLight = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
@@ -192,6 +192,9 @@
 				
 				// Starfield Procedural Texture
                 var starfieldPT = addStarfieldPT();
+
+				// Ascii Art
+				var asciiPostProcess = addAsciiArtPP(camera);
 								
 				// Default to std
 				var currentTexture = diffuseTexture;
@@ -215,6 +218,7 @@
 				}
 				
 				var resetPTOptions = function(){
+					scene.postProcessesEnabled = false;
 					//empty options
 					while(PTOptions.length > 0){
 						var option = PTOptions.pop()
@@ -409,6 +413,10 @@
 				gui.add(options, 'skybox').onChange(function() {
 					skybox.setEnabled(options.skybox);
 				});
+
+				gui.add(options, 'ascii').onChange(function() {
+					scene.postProcessesEnabled = !scene.postProcessesEnabled;
+				});
 			});
 		}
 

+ 2 - 1
proceduralTexturesLibrary/test/index.js

@@ -9,7 +9,8 @@ var options = {
 	castShadows: false,
 	spotLight: false,
 	fog: false,
-	skybox: false
+	skybox: false,
+	ascii: false
 }
 
 var registeredUIs = {};

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 47434 - 23992
proceduralTexturesLibrary/test/refs/babylon.max.js


+ 7 - 0
proceduralTexturesLibrary/tsconfig.json

@@ -0,0 +1,7 @@
+{
+    "compilerOptions": {
+        "experimentalDecorators": true,
+        "module": "commonjs", 
+        "target": "es5"
+    }
+}

+ 1 - 1
readme.md

@@ -5,7 +5,7 @@ Getting started? Play directly with the Babylon.js API via our [playground](http
 
 [![Build Status](https://travis-ci.org/BabylonJS/Babylon.js.svg)](https://travis-ci.org/BabylonJS/Babylon.js)
 
-Any questions? Here is our official [forum](http://www.html5gamedevs.com/forum/16-babylonjs/) on www.html5gamedevs.com.
+**Any questions?** Here is our official [forum](http://www.html5gamedevs.com/forum/16-babylonjs/) on www.html5gamedevs.com.
 
 ## CDN
 - http://cdn.babylonjs.com/2-4/babylon.js 

+ 5 - 4
src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js

@@ -16,6 +16,7 @@ var BABYLON;
         }
         ArcRotateCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
+            element.tabIndex = 1;
             this._onKeyDown = function (evt) {
                 if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     _this.keysDown.indexOf(evt.keyCode) !== -1 ||
@@ -51,16 +52,16 @@ var BABYLON;
             this._onLostFocus = function () {
                 _this._keys = [];
             };
+            element.addEventListener("keydown", this._onKeyDown, false);
+            element.addEventListener("keyup", this._onKeyUp, false);
             BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         };
         ArcRotateCameraKeyboardMoveInput.prototype.detachControl = function (element) {
+            element.removeEventListener("keydown", this._onKeyDown);
+            element.removeEventListener("keyup", this._onKeyUp);
             BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
             this._keys = [];

+ 9 - 4
src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.ts

@@ -19,8 +19,10 @@ module BABYLON {
         public keysRight = [39];
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
+            element.tabIndex = 1;
 
             this._onKeyDown = evt => {
+
                 if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     this.keysDown.indexOf(evt.keyCode) !== -1 ||
                     this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -40,6 +42,7 @@ module BABYLON {
             };
 
             this._onKeyUp = evt => {
+
                 if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     this.keysDown.indexOf(evt.keyCode) !== -1 ||
                     this.keysLeft.indexOf(evt.keyCode) !== -1 ||
@@ -62,17 +65,19 @@ module BABYLON {
                 this._keys = [];
             };
 
+            element.addEventListener("keydown", this._onKeyDown, false);
+            element.addEventListener("keyup", this._onKeyUp, false);
+
             Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         }
 
         public detachControl(element: HTMLElement) {
+            element.removeEventListener("keydown", this._onKeyDown);
+            element.removeEventListener("keyup", this._onKeyUp);
+
             Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
             

+ 5 - 4
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js

@@ -43,6 +43,7 @@ var BABYLON;
                     }
                     if (!noPreventDefault) {
                         evt.preventDefault();
+                        element.focus();
                     }
                 }
                 else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
@@ -162,9 +163,9 @@ var BABYLON;
             element.addEventListener("mousemove", this._onMouseMove, false);
             element.addEventListener("MSPointerDown", this._onGestureStart, false);
             element.addEventListener("MSGestureChange", this._onGesture, false);
+            element.addEventListener("keydown", this._onKeyDown, false);
+            element.addEventListener("keyup", this._onKeyUp, false);
             BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         };
@@ -176,6 +177,8 @@ var BABYLON;
                 element.removeEventListener("mousemove", this._onMouseMove);
                 element.removeEventListener("MSPointerDown", this._onGestureStart);
                 element.removeEventListener("MSGestureChange", this._onGesture);
+                element.removeEventListener("keydown", this._onKeyDown);
+                element.removeEventListener("keyup", this._onKeyUp);
                 this._isRightClick = false;
                 this._isCtrlPushed = false;
                 this.pinchInwards = true;
@@ -189,8 +192,6 @@ var BABYLON;
                 this._onContextMenu = null;
             }
             BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         };

+ 7 - 5
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts

@@ -60,6 +60,7 @@ module BABYLON {
                     }
                     if (!noPreventDefault) {
                         evt.preventDefault();
+                        element.focus();
                     }
                 } else if (p.type === PointerEventTypes.POINTERUP) {
                     try {
@@ -201,9 +202,10 @@ module BABYLON {
             element.addEventListener("MSPointerDown", this._onGestureStart, false);
             element.addEventListener("MSGestureChange", this._onGesture, false);
 
+            element.addEventListener("keydown", this._onKeyDown, false);
+            element.addEventListener("keyup", this._onKeyUp, false);
+
             Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         }
@@ -218,6 +220,9 @@ module BABYLON {
                 element.removeEventListener("MSPointerDown", this._onGestureStart);
                 element.removeEventListener("MSGestureChange", this._onGesture);
 
+                element.removeEventListener("keydown", this._onKeyDown);
+                element.removeEventListener("keyup", this._onKeyUp);
+
                 this._isRightClick = false;
                 this._isCtrlPushed = false;
                 this.pinchInwards = true;
@@ -230,12 +235,9 @@ module BABYLON {
                 this._MSGestureHandler = null;
                 this._onLostFocus = null;
                 this._onContextMenu = null;
-
             }
 
             Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
                 { name: "blur", handler: this._onLostFocus }
             ]);
         }

+ 5 - 4
src/Cameras/Inputs/babylon.freecamera.input.keyboard.js

@@ -17,6 +17,7 @@ var BABYLON;
         FreeCameraKeyboardMoveInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
             if (!this._onKeyDown) {
+                element.tabIndex = 1;
                 this._onKeyDown = function (evt) {
                     if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
                         _this.keysDown.indexOf(evt.keyCode) !== -1 ||
@@ -45,18 +46,18 @@ var BABYLON;
                         }
                     }
                 };
+                element.addEventListener("keydown", this._onKeyDown, false);
+                element.addEventListener("keyup", this._onKeyUp, false);
                 BABYLON.Tools.RegisterTopRootEvents([
-                    { name: "keydown", handler: this._onKeyDown },
-                    { name: "keyup", handler: this._onKeyUp },
                     { name: "blur", handler: this._onLostFocus }
                 ]);
             }
         };
         FreeCameraKeyboardMoveInput.prototype.detachControl = function (element) {
             if (this._onKeyDown) {
+                element.removeEventListener("keydown", this._onKeyDown);
+                element.removeEventListener("keyup", this._onKeyUp);
                 BABYLON.Tools.UnregisterTopRootEvents([
-                    { name: "keydown", handler: this._onKeyDown },
-                    { name: "keyup", handler: this._onKeyUp },
                     { name: "blur", handler: this._onLostFocus }
                 ]);
                 this._keys = [];

+ 8 - 5
src/Cameras/Inputs/babylon.freecamera.input.keyboard.ts

@@ -19,6 +19,7 @@ module BABYLON {
 
         attachControl(element : HTMLElement, noPreventDefault?: boolean) {
             if (!this._onKeyDown) {
+                element.tabIndex = 1;
 
                 this._onKeyDown = evt => {
                     if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
@@ -52,19 +53,21 @@ module BABYLON {
                     }
                 };
 
+                element.addEventListener("keydown", this._onKeyDown, false);
+                element.addEventListener("keyup", this._onKeyUp, false);
+
                 Tools.RegisterTopRootEvents([
-                    { name: "keydown", handler: this._onKeyDown },
-                    { name: "keyup", handler: this._onKeyUp },
                     { name: "blur", handler: this._onLostFocus }
                 ]);
             }
         }
 
         detachControl(element : HTMLElement) {
-            if (this._onKeyDown){
+            if (this._onKeyDown) {
+                element.removeEventListener("keydown", this._onKeyDown);
+                element.removeEventListener("keyup", this._onKeyUp);
+
                 Tools.UnregisterTopRootEvents([
-                    { name: "keydown", handler: this._onKeyDown },
-                    { name: "keyup", handler: this._onKeyUp },
                     { name: "blur", handler: this._onLostFocus }
                 ]);
                 this._keys = [];

+ 1 - 0
src/Cameras/Inputs/babylon.freecamera.input.mouse.js

@@ -34,6 +34,7 @@ var BABYLON;
                         };
                         if (!noPreventDefault) {
                             evt.preventDefault();
+                            element.focus();
                         }
                     }
                     else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {

+ 2 - 1
src/Cameras/Inputs/babylon.freecamera.input.mouse.ts

@@ -14,7 +14,6 @@ module BABYLON {
         }
 
         attachControl(element: HTMLElement, noPreventDefault?: boolean) {
-
             if (!this._pointerInput) {
                 var camera = this.camera;
                 var engine = this.camera.getEngine();
@@ -39,6 +38,7 @@ module BABYLON {
 
                         if (!noPreventDefault) {
                             evt.preventDefault();
+                            element.focus();
                         }
                     }
                     else if (p.type === PointerEventTypes.POINTERUP) {
@@ -47,6 +47,7 @@ module BABYLON {
                         } catch (e) {
                             //Nothing to do with the error.
                         }
+
                         this.previousPosition = null;
                         if (!noPreventDefault) {
                             evt.preventDefault();

+ 12 - 2
src/Materials/babylon.effect.js

@@ -265,7 +265,8 @@ var BABYLON;
                 this._program = engine.createShaderProgram(vertexSourceCode, fragmentSourceCode, defines);
                 this._uniforms = engine.getUniforms(this._program, this._uniformsNames);
                 this._attributes = engine.getAttributes(this._program, attributesNames);
-                for (var index = 0; index < this._samplers.length; index++) {
+                var index;
+                for (index = 0; index < this._samplers.length; index++) {
                     var sampler = this.getUniform(this._samplers[index]);
                     if (sampler == null) {
                         this._samplers.splice(index, 1);
@@ -309,7 +310,16 @@ var BABYLON;
             this._engine._bindTexture(this._samplers.indexOf(channel), texture);
         };
         Effect.prototype.setTexture = function (channel, texture) {
-            this._engine.setTexture(this._samplers.indexOf(channel), texture);
+            this._engine.setTexture(this._samplers.indexOf(channel), this.getUniform(channel), texture);
+        };
+        Effect.prototype.setTextureArray = function (channel, textures) {
+            if (this._samplers.indexOf(channel + "Ex") === -1) {
+                var initialPos = this._samplers.indexOf(channel);
+                for (var index = 1; index < textures.length; index++) {
+                    this._samplers.splice(initialPos + index, 0, channel + "Ex");
+                }
+            }
+            this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         };
         Effect.prototype.setTextureFromPostProcess = function (channel, postProcess) {
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);

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

@@ -291,7 +291,7 @@
 
                     Tools.LoadFile(includeShaderUrl, (fileContent) => {
                         Effect.IncludesShadersStore[includeFile] = fileContent;
-                        this._processIncludes(<string>returnValue, callback)
+                        this._processIncludes(<string>returnValue, callback);
                     });
                     return;
                 }
@@ -331,7 +331,8 @@
                 this._uniforms = engine.getUniforms(this._program, this._uniformsNames);
                 this._attributes = engine.getAttributes(this._program, attributesNames);
 
-                for (var index = 0; index < this._samplers.length; index++) {
+                var index: number;
+                for (index = 0; index < this._samplers.length; index++) {
                     var sampler = this.getUniform(this._samplers[index]);
 
                     if (sampler == null) {
@@ -339,6 +340,7 @@
                         index--;
                     }
                 }
+
                 engine.bindSamplers(this);
 
                 this._isReady = true;
@@ -375,7 +377,18 @@
         }
 
         public setTexture(channel: string, texture: BaseTexture): void {
-            this._engine.setTexture(this._samplers.indexOf(channel), texture);
+            this._engine.setTexture(this._samplers.indexOf(channel), this.getUniform(channel), texture);
+        }
+
+        public setTextureArray(channel: string, textures: BaseTexture[]): void {
+            if (this._samplers.indexOf(channel + "Ex") === -1) {
+                var initialPos = this._samplers.indexOf(channel);
+                for (var index = 1; index < textures.length; index++) {
+                    this._samplers.splice(initialPos + index, 0, channel + "Ex");
+                }
+            }
+
+            this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         }
 
         public setTextureFromPostProcess(channel: string, postProcess: PostProcess): void {

+ 45 - 4
src/Materials/babylon.shaderMaterial.js

@@ -10,6 +10,7 @@ var BABYLON;
         function ShaderMaterial(name, scene, shaderPath, options) {
             _super.call(this, name, scene);
             this._textures = {};
+            this._textureArrays = {};
             this._floats = {};
             this._floatsArrays = {};
             this._colors3 = {};
@@ -48,6 +49,14 @@ var BABYLON;
             this._textures[name] = texture;
             return this;
         };
+        ShaderMaterial.prototype.setTextureArray = function (name, textures) {
+            if (this._options.samplers.indexOf(name) === -1) {
+                this._options.samplers.push(name);
+            }
+            this._checkUniform(name);
+            this._textureArrays[name] = textures;
+            return this;
+        };
         ShaderMaterial.prototype.setFloat = function (name, value) {
             this._checkUniform(name);
             this._floats[name] = value;
@@ -167,10 +176,15 @@ var BABYLON;
                 if (mesh && mesh.useBones && mesh.computeBonesUsingShaders) {
                     this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
                 }
+                var name;
                 // Texture
-                for (var name in this._textures) {
+                for (name in this._textures) {
                     this._effect.setTexture(name, this._textures[name]);
                 }
+                // Texture arrays
+                for (name in this._textureArrays) {
+                    this._effect.setTextureArray(name, this._textureArrays[name]);
+                }
                 // Float    
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
@@ -221,9 +235,16 @@ var BABYLON;
         };
         ShaderMaterial.prototype.dispose = function (forceDisposeEffect, forceDisposeTextures) {
             if (forceDisposeTextures) {
-                for (var name in this._textures) {
+                var name;
+                for (name in this._textures) {
                     this._textures[name].dispose();
                 }
+                for (name in this._textureArrays) {
+                    var array = this._textureArrays[name];
+                    for (var index = 0; index < array.length; index++) {
+                        array[index].dispose();
+                    }
+                }
             }
             this._textures = {};
             _super.prototype.dispose.call(this, forceDisposeEffect, forceDisposeTextures);
@@ -233,11 +254,21 @@ var BABYLON;
             serializationObject.customType = "BABYLON.ShaderMaterial";
             serializationObject.options = this._options;
             serializationObject.shaderPath = this._shaderPath;
+            var name;
             // Texture
             serializationObject.textures = {};
-            for (var name in this._textures) {
+            for (name in this._textures) {
                 serializationObject.textures[name] = this._textures[name].serialize();
             }
+            // Texture arrays
+            serializationObject.textureArrays = {};
+            for (name in this._textureArrays) {
+                serializationObject.textureArrays[name] = [];
+                var array = this._textureArrays[name];
+                for (var index = 0; index < array.length; index++) {
+                    serializationObject.textureArrays[name].push(array[index].serialize());
+                }
+            }
             // Float    
             serializationObject.floats = {};
             for (name in this._floats) {
@@ -292,10 +323,20 @@ var BABYLON;
         };
         ShaderMaterial.Parse = function (source, scene, rootUrl) {
             var material = BABYLON.SerializationHelper.Parse(function () { return new ShaderMaterial(source.name, scene, source.shaderPath, source.options); }, source, scene, rootUrl);
+            var name;
             // Texture
-            for (var name in source.textures) {
+            for (name in source.textures) {
                 material.setTexture(name, BABYLON.Texture.Parse(source.textures[name], scene, rootUrl));
             }
+            // Texture arrays
+            for (name in source.textureArrays) {
+                var array = source.textureArrays[name];
+                var textureArray = new Array();
+                for (var index = 0; index < array.length; index++) {
+                    textureArray.push(BABYLON.Texture.Parse(array[index], scene, rootUrl));
+                }
+                material.setTextureArray(name, textureArray);
+            }
             // Float    
             for (name in source.floats) {
                 material.setFloat(name, source.floats[name]);

+ 57 - 5
src/Materials/babylon.shaderMaterial.ts

@@ -3,6 +3,7 @@
         private _shaderPath: any;
         private _options: any;
         private _textures: { [name: string]: Texture } = {};
+        private _textureArrays: { [name: string]: Texture[] } = {};
         private _floats: { [name: string]: number } = {};
         private _floatsArrays: { [name: string]: number[] } = {};
         private _colors3: { [name: string]: Color3 } = {};
@@ -53,6 +54,18 @@
             return this;
         }
 
+        public setTextureArray(name: string, textures: Texture[]): ShaderMaterial {
+            if (this._options.samplers.indexOf(name) === -1) {
+                this._options.samplers.push(name);
+            }
+
+            this._checkUniform(name);
+
+            this._textureArrays[name] = textures;
+
+            return this;
+        }
+
         public setFloat(name: string, value: number): ShaderMaterial {
             this._checkUniform(name);
             this._floats[name] = value;
@@ -217,11 +230,17 @@
                     this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
                 }
 
+                var name: string;
                 // Texture
-                for (var name in this._textures) {
+                for (name in this._textures) {
                     this._effect.setTexture(name, this._textures[name]);
                 }
 
+                // Texture arrays
+                for (name in this._textureArrays) {
+                    this._effect.setTextureArray(name, this._textureArrays[name]);
+                }
+
                 // Float    
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
@@ -286,9 +305,17 @@
         public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
 
             if (forceDisposeTextures) {
-                for (var name in this._textures) {
+                var name: string;
+                for (name in this._textures) {
                     this._textures[name].dispose();
                 }
+
+                for (name in this._textureArrays) {
+                    var array = this._textureArrays[name];
+                    for (var index = 0; index < array.length; index++) {
+                        array[index].dispose();
+                    }
+                }
             }
 
             this._textures = {};
@@ -303,12 +330,24 @@
             serializationObject.options = this._options;
             serializationObject.shaderPath = this._shaderPath;
 
+            var name: string;
+
             // Texture
             serializationObject.textures = {};
-            for (var name in this._textures) {
+            for (name in this._textures) {
                 serializationObject.textures[name] = this._textures[name].serialize();
             }
 
+            // Texture arrays
+            serializationObject.textureArrays = {};
+            for (name in this._textureArrays) {
+                serializationObject.textureArrays[name] = [];
+                var array = this._textureArrays[name];
+                for (var index = 0; index < array.length; index++) {
+                    serializationObject.textureArrays[name].push(array[index].serialize());
+                }
+            }
+
             // Float    
             serializationObject.floats = {};
             for (name in this._floats) {
@@ -374,12 +413,25 @@
 
         public static Parse(source: any, scene: Scene, rootUrl: string): ShaderMaterial {
             var material = SerializationHelper.Parse(() => new ShaderMaterial(source.name, scene, source.shaderPath, source.options), source, scene, rootUrl);
-			
+
+            var name: string;
+
             // Texture
-            for (var name in source.textures) {
+            for (name in source.textures) {
                 material.setTexture(name, <Texture>Texture.Parse(source.textures[name], scene, rootUrl));
             }
 
+            // Texture arrays
+            for (name in source.textureArrays) {
+                var array = source.textureArrays[name];
+                var textureArray = new Array<Texture>();
+
+                for (var index = 0; index < array.length; index++) {
+                    textureArray.push(<Texture>Texture.Parse(array[index], scene, rootUrl));
+                }
+                material.setTextureArray(name, textureArray);
+            }
+
             // Float    
             for (name in source.floats) {
                 material.setFloat(name, source.floats[name]);

+ 5 - 5
src/Mesh/babylon.groundMesh.js

@@ -130,10 +130,10 @@ var BABYLON;
         // facet2 :  Vector4(a, b, c, d) = second facet 3D plane equation : ax + by + cz + d = 0
         GroundMesh.prototype._computeHeightQuads = function () {
             var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
-            var v1 = BABYLON.Tmp.Vector3[0];
-            var v2 = BABYLON.Tmp.Vector3[1];
-            var v3 = BABYLON.Tmp.Vector3[2];
-            var v4 = BABYLON.Tmp.Vector3[3];
+            var v1 = BABYLON.Tmp.Vector3[3];
+            var v2 = BABYLON.Tmp.Vector3[2];
+            var v3 = BABYLON.Tmp.Vector3[1];
+            var v4 = BABYLON.Tmp.Vector3[0];
             var v1v2 = BABYLON.Tmp.Vector3[4];
             var v1v3 = BABYLON.Tmp.Vector3[5];
             var v1v4 = BABYLON.Tmp.Vector3[6];
@@ -174,7 +174,7 @@ var BABYLON;
                     v2.subtractToRef(v1, v1v2);
                     v3.subtractToRef(v1, v1v3);
                     v4.subtractToRef(v1, v1v4);
-                    BABYLON.Vector3.CrossToRef(v1v4, v1v3, norm1);
+                    BABYLON.Vector3.CrossToRef(v1v4, v1v3, norm1); // caution : CrossToRef uses the Tmp class
                     BABYLON.Vector3.CrossToRef(v1v2, v1v4, norm2);
                     norm1.normalize();
                     norm2.normalize();

+ 5 - 6
src/Mesh/babylon.groundMesh.ts

@@ -136,10 +136,10 @@
         // facet2 :  Vector4(a, b, c, d) = second facet 3D plane equation : ax + by + cz + d = 0
         private _computeHeightQuads(): void {
             var positions = this.getVerticesData(VertexBuffer.PositionKind);
-            var v1 = Tmp.Vector3[0];
-            var v2 = Tmp.Vector3[1];
-            var v3 = Tmp.Vector3[2];
-            var v4 = Tmp.Vector3[3];
+            var v1 = Tmp.Vector3[3];
+            var v2 = Tmp.Vector3[2];
+            var v3 = Tmp.Vector3[1];
+            var v4 = Tmp.Vector3[0];
             var v1v2 = Tmp.Vector3[4];
             var v1v3 = Tmp.Vector3[5];
             var v1v4 = Tmp.Vector3[6];
@@ -183,7 +183,7 @@
                     v2.subtractToRef(v1, v1v2);
                     v3.subtractToRef(v1, v1v3);
                     v4.subtractToRef(v1, v1v4);
-                    Vector3.CrossToRef(v1v4, v1v3, norm1);
+                    Vector3.CrossToRef(v1v4, v1v3, norm1);  // caution : CrossToRef uses the Tmp class
                     Vector3.CrossToRef(v1v2, v1v4, norm2);
                     norm1.normalize();
                     norm2.normalize();
@@ -199,4 +199,3 @@
         }
     }
 }
-

+ 20 - 1
src/babylon.engine.js

@@ -1923,10 +1923,14 @@ var BABYLON;
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             }
         };
-        Engine.prototype.setTexture = function (channel, texture) {
+        Engine.prototype.setTexture = function (channel, uniform, texture) {
             if (channel < 0) {
                 return;
             }
+            this._gl.uniform1i(uniform, channel);
+            this._setTexture(channel, texture);
+        };
+        Engine.prototype._setTexture = function (channel, texture) {
             // Not ready?
             if (!texture || !texture.isReady()) {
                 if (this._activeTexturesCache[channel] != null) {
@@ -1998,6 +2002,21 @@ var BABYLON;
                 this._setAnisotropicLevel(this._gl.TEXTURE_2D, texture);
             }
         };
+        Engine.prototype.setTextureArray = function (channel, uniform, textures) {
+            if (channel < 0) {
+                return;
+            }
+            if (!this._textureUnits || this._textureUnits.length !== textures.length) {
+                this._textureUnits = new Int32Array(textures.length);
+            }
+            for (var i = 0; i < textures.length; i++) {
+                this._textureUnits[i] = channel + i;
+            }
+            this._gl.uniform1iv(uniform, this._textureUnits);
+            for (var index = 0; index < textures.length; index++) {
+                this._setTexture(channel + index, textures[index]);
+            }
+        };
         Engine.prototype._setAnisotropicLevel = function (key, texture) {
             var anisotropicFilterExtension = this._caps.textureAnisotropicFilterExtension;
             var value = texture.anisotropicFilteringLevel;

+ 26 - 1
src/babylon.engine.ts

@@ -355,6 +355,7 @@
         private _currentBufferPointers: Array<{ indx: number, size: number, type: number, normalized: boolean, stride: number, offset: number, buffer: WebGLBuffer }> = [];
         private _currentInstanceLocations = new Array<number>();
         private _currentInstanceBuffers = new Array<WebGLBuffer>();
+        private _textureUnits: Int32Array;
 
         private _workingCanvas: HTMLCanvasElement;
         private _workingContext: CanvasRenderingContext2D;
@@ -2329,10 +2330,16 @@
             }
         }
 
-        public setTexture(channel: number, texture: BaseTexture): void {
+        public setTexture(channel: number, uniform: WebGLUniformLocation, texture: BaseTexture): void {
             if (channel < 0) {
                 return;
             }
+
+            this._gl.uniform1i(uniform, channel);
+            this._setTexture(channel, texture);
+        }
+
+        private _setTexture(channel: number, texture: BaseTexture): void {
             // Not ready?
             if (!texture || !texture.isReady()) {
                 if (this._activeTexturesCache[channel] != null) {
@@ -2414,6 +2421,24 @@
             }
         }
 
+        public setTextureArray(channel: number, uniform: WebGLUniformLocation, textures: BaseTexture[]): void {
+            if (channel < 0) {
+                return;
+            }
+
+            if (!this._textureUnits || this._textureUnits.length !== textures.length) {
+                this._textureUnits = new Int32Array(textures.length);
+            }
+            for (let i = 0; i < textures.length; i++) {
+                this._textureUnits[i] = channel + i;
+            }
+            this._gl.uniform1iv(uniform, this._textureUnits);
+
+            for (var index = 0; index < textures.length; index++) {
+                this._setTexture(channel + index, textures[index]);
+            }
+        }
+
         public _setAnisotropicLevel(key: number, texture: BaseTexture) {
             var anisotropicFilterExtension = this._caps.textureAnisotropicFilterExtension;
             var value = texture.anisotropicFilteringLevel;

+ 21 - 25
src/babylon.scene.js

@@ -105,6 +105,7 @@ var BABYLON;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
+            this.hoverCursor = "pointer";
             // Events
             /**
             * An event triggered when the scene is disposed.
@@ -291,7 +292,6 @@ var BABYLON;
             this._particlesDuration = new BABYLON.PerfCounter();
             this._renderDuration = new BABYLON.PerfCounter();
             this._spritesDuration = new BABYLON.PerfCounter();
-            this._animationRatio = new BABYLON.PerfCounter();
             this._activeBones = new BABYLON.PerfCounter();
             this._renderId = 0;
             this._executeWhenReadyTimeoutId = -1;
@@ -590,9 +590,6 @@ var BABYLON;
             configurable: true
         });
         Scene.prototype.getAnimationRatio = function () {
-            return this._animationRatio.current;
-        };
-        Scene.prototype.animationRatioPerfCounter = function () {
             return this._animationRatio;
         };
         Scene.prototype.getRenderId = function () {
@@ -651,7 +648,7 @@ var BABYLON;
                     _this.setPointerOverSprite(null);
                     _this.setPointerOverMesh(pickResult.pickedMesh);
                     if (_this._pointerOverMesh.actionManager && _this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                        canvas.style.cursor = "pointer";
+                        canvas.style.cursor = _this.hoverCursor;
                     }
                     else {
                         canvas.style.cursor = "";
@@ -662,7 +659,7 @@ var BABYLON;
                     // Sprites
                     pickResult = _this.pickSprite(_this._unTranslatedPointerX, _this._unTranslatedPointerY, spritePredicate, false, _this.cameraToUseForPointers);
                     if (pickResult.hit && pickResult.pickedSprite) {
-                        canvas.style.cursor = "pointer";
+                        canvas.style.cursor = _this.hoverCursor;
                         _this.setPointerOverSprite(pickResult.pickedSprite);
                     }
                     else {
@@ -847,35 +844,34 @@ var BABYLON;
                 }
             };
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
+            var canvas = this._engine.getRenderingCanvas();
             if (attachMove) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "move", this._onPointerMove, false);
+                canvas.addEventListener(eventPrefix + "move", this._onPointerMove, false);
                 // Wheel
-                this._engine.getRenderingCanvas().addEventListener('mousewheel', this._onPointerMove, false);
-                this._engine.getRenderingCanvas().addEventListener('DOMMouseScroll', this._onPointerMove, false);
+                canvas.addEventListener('mousewheel', this._onPointerMove, false);
+                canvas.addEventListener('DOMMouseScroll', this._onPointerMove, false);
             }
             if (attachDown) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "down", this._onPointerDown, false);
+                canvas.addEventListener(eventPrefix + "down", this._onPointerDown, false);
             }
             if (attachUp) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "up", this._onPointerUp, false);
+                canvas.addEventListener(eventPrefix + "up", this._onPointerUp, false);
             }
-            BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp }
-            ]);
+            canvas.tabIndex = 1;
+            canvas.addEventListener("keydown", this._onKeyDown, false);
+            canvas.addEventListener("keyup", this._onKeyUp, false);
         };
         Scene.prototype.detachControl = function () {
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "move", this._onPointerMove);
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "down", this._onPointerDown);
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "up", this._onPointerUp);
+            var canvas = this._engine.getRenderingCanvas();
+            canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
+            canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
+            canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
             // Wheel
-            this._engine.getRenderingCanvas().removeEventListener('mousewheel', this._onPointerMove);
-            this._engine.getRenderingCanvas().removeEventListener('DOMMouseScroll', this._onPointerMove);
-            BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp }
-            ]);
+            canvas.removeEventListener('mousewheel', this._onPointerMove);
+            canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
+            canvas.removeEventListener("keydown", this._onKeyDown);
+            canvas.removeEventListener("keyup", this._onKeyUp);
         };
         // Ready
         Scene.prototype.isReady = function () {
@@ -1834,7 +1830,7 @@ var BABYLON;
             }
             // Animations
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
-            this._animationRatio.addCount(deltaTime * (60.0 / 1000.0), true);
+            this._animationRatio = deltaTime * (60.0 / 1000.0);
             this._animate();
             // Physics
             if (this._physicsEngine) {

+ 26 - 27
src/babylon.scene.ts

@@ -104,6 +104,8 @@
         public animationsEnabled = true;
         public constantlyUpdateMeshUnderPointer = false;
 
+        public hoverCursor = "pointer";
+
         // Events
 
         /**
@@ -434,9 +436,10 @@
         public  _particlesDuration            = new PerfCounter();
         private _renderDuration               = new PerfCounter();
         public  _spritesDuration              = new PerfCounter();
-        private _animationRatio               = new PerfCounter();
         public  _activeBones                  = new PerfCounter();
 
+        private _animationRatio: number;
+
         private _animationStartDate: number;
         public _cachedMaterial: Material;
 
@@ -672,10 +675,6 @@
         }
 
         public getAnimationRatio(): number {
-            return this._animationRatio.current;
-        }
-
-        public animationRatioPerfCounter(): PerfCounter {
             return this._animationRatio;
         }
 
@@ -748,7 +747,7 @@
                     this.setPointerOverMesh(pickResult.pickedMesh);
 
                     if (this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                        canvas.style.cursor = "pointer";
+                        canvas.style.cursor = this.hoverCursor;
                     } else {
                         canvas.style.cursor = "";
                     }
@@ -758,7 +757,7 @@
                     pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, spritePredicate, false, this.cameraToUseForPointers);
 
                     if (pickResult.hit && pickResult.pickedSprite) {
-                        canvas.style.cursor = "pointer";
+                        canvas.style.cursor = this.hoverCursor;
                         this.setPointerOverSprite(pickResult.pickedSprite);
                     } else {
                         this.setPointerOverSprite(null);
@@ -977,42 +976,42 @@
 
 
             var eventPrefix = Tools.GetPointerPrefix();
-
+            var canvas = this._engine.getRenderingCanvas();
             if (attachMove) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "move", this._onPointerMove, false);
+                canvas.addEventListener(eventPrefix + "move", this._onPointerMove, false);
                 // Wheel
-                this._engine.getRenderingCanvas().addEventListener('mousewheel', this._onPointerMove, false);
-                this._engine.getRenderingCanvas().addEventListener('DOMMouseScroll', this._onPointerMove, false);
+                canvas.addEventListener('mousewheel', this._onPointerMove, false);
+                canvas.addEventListener('DOMMouseScroll', this._onPointerMove, false);
             }
 
             if (attachDown) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "down", this._onPointerDown, false);
+                canvas.addEventListener(eventPrefix + "down", this._onPointerDown, false);
             }
 
             if (attachUp) {
-                this._engine.getRenderingCanvas().addEventListener(eventPrefix + "up", this._onPointerUp, false);
+                canvas.addEventListener(eventPrefix + "up", this._onPointerUp, false);
             }
 
-            Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp }
-            ]);
+            canvas.tabIndex = 1;
+
+            canvas.addEventListener("keydown", this._onKeyDown, false);
+            canvas.addEventListener("keyup", this._onKeyUp, false);
         }
 
         public detachControl() {
             var eventPrefix = Tools.GetPointerPrefix();
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "move", this._onPointerMove);
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "down", this._onPointerDown);
-            this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "up", this._onPointerUp);
+            var canvas = this._engine.getRenderingCanvas();
+
+            canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
+            canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
+            canvas.removeEventListener(eventPrefix + "up", this._onPointerUp);
 
             // Wheel
-            this._engine.getRenderingCanvas().removeEventListener('mousewheel', this._onPointerMove);
-            this._engine.getRenderingCanvas().removeEventListener('DOMMouseScroll', this._onPointerMove);
+            canvas.removeEventListener('mousewheel', this._onPointerMove);
+            canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
 
-            Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp }
-            ]);
+            canvas.removeEventListener("keydown", this._onKeyDown);
+            canvas.removeEventListener("keyup", this._onKeyUp);
         }
 
         // Ready
@@ -2176,7 +2175,7 @@
 
             // Animations
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
-            this._animationRatio.addCount(deltaTime * (60.0 / 1000.0), true);
+            this._animationRatio = deltaTime * (60.0 / 1000.0);
             this._animate();
 
             // Physics