Ver código fonte

Merge remote-tracking branch 'upstream/master'

nockawa 8 anos atrás
pai
commit
0b1d77dd55
94 arquivos alterados com 8724 adições e 6737 exclusões
  1. BIN
      Exporters/Blender/Blender2Babylon-5.0.zip
  2. 1 1
      Exporters/Blender/src/__init__.py
  3. 1 1
      Exporters/Blender/src/armature.py
  4. 2 0
      canvas2D/config.json
  5. 11 6
      canvas2D/gulp-addModuleExports.js
  6. 31 36
      canvas2D/src/Engine/babylon.canvas2d.ts
  7. 2 2
      canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts
  8. 62 33
      canvas2D/src/Engine/babylon.group2d.ts
  9. 48 12
      canvas2D/src/Engine/babylon.prim2dBase.ts
  10. 15 3
      canvas2D/src/Engine/babylon.smartPropertyPrim.ts
  11. 15 11
      canvas2D/src/Engine/babylon.sprite2d.ts
  12. 85 45
      canvas2D/src/Engine/babylon.text2d.ts
  13. 106 0
      canvas2D/src/GUI/Layouts/babylon.gui.stackPanel.ts
  14. 241 87
      canvas2D/src/GUI/babylon.gui.UIElement.ts
  15. 107 77
      canvas2D/src/GUI/babylon.gui.button.ts
  16. 128 0
      canvas2D/src/GUI/babylon.gui.contentControl.ts
  17. 0 137
      canvas2D/src/GUI/babylon.gui.control.ts
  18. 110 41
      canvas2D/src/GUI/babylon.gui.window.ts
  19. 3 1
      canvas2D/src/Tools/babylon.observableStringDictionary.ts
  20. 19 1
      canvas2D/src/shaders/text2d.fragment.fx
  21. 38 0
      canvas2D/tools/canvas2dinspector/README.md
  22. 153 70
      dist/preview release/babylon.canvas2d.d.ts
  23. 10 9
      dist/preview release/babylon.canvas2d.js
  24. 722 282
      dist/preview release/babylon.canvas2d.max.js
  25. 26 26
      dist/preview release/babylon.core.js
  26. 4270 4211
      dist/preview release/babylon.d.ts
  27. 36 36
      dist/preview release/babylon.js
  28. 729 204
      dist/preview release/babylon.max.js
  29. 36 36
      dist/preview release/babylon.noworker.js
  30. 4 0
      dist/preview release/what's new.md
  31. 1 1
      materialsLibrary/dist/babylon.gridMaterial.js
  32. 1 1
      materialsLibrary/dist/babylon.gridMaterial.min.js
  33. 4 2
      materialsLibrary/dist/babylon.skyMaterial.js
  34. 1 1
      materialsLibrary/dist/babylon.skyMaterial.min.js
  35. 1 1
      materialsLibrary/materials/grid/grid.fragment.fx
  36. 4 1
      materialsLibrary/materials/sky/babylon.skyMaterial.ts
  37. 8 9
      materialsLibrary/materials/sky/sky.fragment.fx
  38. 1 0
      src/Actions/babylon.actionManager.js
  39. 2 0
      src/Actions/babylon.actionManager.ts
  40. 7 0
      src/Animations/babylon.animatable.js
  41. 8 0
      src/Animations/babylon.animatable.ts
  42. 256 0
      src/Bones/babylon.bone.js
  43. 365 0
      src/Bones/babylon.bone.ts
  44. 9 0
      src/Bones/babylon.skeleton.js
  45. 14 0
      src/Bones/babylon.skeleton.ts
  46. 8 0
      src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts
  47. 7 0
      src/Cameras/Inputs/babylon.freecamera.input.mouse.ts
  48. 13 1
      src/Cameras/babylon.camera.js
  49. 14 2
      src/Cameras/babylon.camera.ts
  50. 10 4
      src/Layer/babylon.highlightlayer.js
  51. 15 4
      src/Layer/babylon.highlightlayer.ts
  52. 11 0
      src/Materials/Textures/babylon.baseTexture.js
  53. 1 1
      src/Materials/Textures/babylon.cubeTexture.js
  54. 1 1
      src/Materials/Textures/babylon.cubeTexture.ts
  55. 187 18
      src/Materials/Textures/babylon.fontTexture.js
  56. 219 19
      src/Materials/Textures/babylon.fontTexture.ts
  57. 1 1
      src/Materials/Textures/babylon.hdrCubeTexture.js
  58. 1 1
      src/Materials/Textures/babylon.hdrCubeTexture.ts
  59. 5 5
      src/Materials/Textures/babylon.texture.js
  60. 6 4
      src/Materials/Textures/babylon.texture.ts
  61. 12 0
      src/Materials/babylon.effect.js
  62. 14 0
      src/Materials/babylon.effect.ts
  63. 1 1
      src/Materials/babylon.fresnelParameters.js
  64. 1 1
      src/Materials/babylon.fresnelParameters.ts
  65. 103 103
      src/Materials/babylon.pbrMaterial.js
  66. 99 100
      src/Materials/babylon.pbrMaterial.ts
  67. 6 3
      src/Math/babylon.math.js
  68. 35 3
      src/Math/babylon.math.ts
  69. 1 1
      src/Mesh/babylon.mesh.js
  70. 1 1
      src/Mesh/babylon.mesh.ts
  71. 5 1
      src/Particles/babylon.solidParticle.js
  72. 6 1
      src/Particles/babylon.solidParticle.ts
  73. 27 34
      src/Particles/babylon.solidParticleSystem.js
  74. 29 37
      src/Particles/babylon.solidParticleSystem.ts
  75. 5 10
      src/Shaders/ShadersInclude/bumpFragment.fx
  76. 6 10
      src/Shaders/default.fragment.fx
  77. 0 226
      src/Shaders/legacydefault.fragment.fx
  78. 0 208
      src/Shaders/legacydefault.vertex.fx
  79. 0 331
      src/Shaders/legacypbr.fragment.fx
  80. 0 142
      src/Shaders/legacypbr.vertex.fx
  81. 18 16
      src/Shaders/pbr.fragment.fx
  82. 1 1
      src/Shaders/pbr.vertex.fx
  83. 6 3
      src/Sprites/babylon.spriteManager.js
  84. 6 6
      src/Sprites/babylon.spriteManager.ts
  85. 4 0
      src/States/babylon.alphaCullingState.js
  86. 7 0
      src/States/babylon.alphaCullingState.ts
  87. 4 0
      src/States/babylon.depthCullingState.js
  88. 7 0
      src/States/babylon.depthCullingState.ts
  89. 13 2
      src/Tools/babylon.rectPackingMap.js
  90. 17 6
      src/Tools/babylon.rectPackingMap.ts
  91. 3 0
      src/babylon.engine.js
  92. 3 0
      src/babylon.engine.ts
  93. 42 22
      src/babylon.scene.js
  94. 50 23
      src/babylon.scene.ts

BIN
Exporters/Blender/Blender2Babylon-5.0.zip


+ 1 - 1
Exporters/Blender/src/__init__.py

@@ -1,7 +1,7 @@
 bl_info = {
     'name': 'Babylon.js',
     'author': 'David Catuhe, Jeff Palmer',
-    'version': (5, 0, 6),
+    'version': (5, 0, 7),
     'blender': (2, 76, 0),
     'location': 'File > Export > Babylon.js (.babylon)',
     'description': 'Export Babylon.js scenes (.babylon)',

+ 1 - 1
Exporters/Blender/src/armature.py

@@ -119,7 +119,7 @@ class Skeleton:
 
         self.dimensions = self.getDimensions()
 
-        bpy.ops.object.mode_set(mode='POSE')
+        bpy.ops.object.mode_set(mode='OBJECT')
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     # do not use .dimensions from blender, it might be including IK bones
     def getDimensions(self):

+ 2 - 0
canvas2D/config.json

@@ -33,7 +33,9 @@
       "src/Engine/babylon.canvas2d.js",
       "src/Engine/babylon.worldSpaceCanvas2dNode.js",
       "src/GUI/babylon.gui.UIElement.js",
+      "src/GUI/Layouts/babylon.gui.stackPanel.js",
       "src/GUI/babylon.gui.control.js",
+      "src/GUI/babylon.gui.contentControl.js",
       "src/GUI/babylon.gui.window.js",
       "src/GUI/babylon.gui.label.js",
       "src/GUI/babylon.gui.button.js"

+ 11 - 6
canvas2D/gulp-addModuleExports.js

@@ -18,11 +18,16 @@ module.exports = function (varName) {
         '};\n';
 
         var decorateAddition =
-        'var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n' +
-            'var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n' +
-            'if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);\n' +
-            'else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n' +
-            'return c > 3 && r && Object.defineProperty(target, key, r), r;\n' +
+        'var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n' +
+            'var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n' +
+            'if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);\n' +
+            'else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n' +
+            'return c > 3 && r && Object.defineProperty(target, key, r), r;\n' +
+        '};\n';
+
+        var dependencyAddition =
+        'if (typeof BABYLON === "undefined") {\n' +
+            'throw "BabylonJS is a required dependency, please include it first!"\n' +
         '};\n';
 
         if (file.isNull()) {
@@ -36,7 +41,7 @@ module.exports = function (varName) {
         }
 
         try {
-            file.contents = new Buffer(decorateAddition.concat(new Buffer(extendsAddition.concat(String(file.contents)).concat(moduleExportsAddition))));
+            file.contents = new Buffer(decorateAddition.concat(new Buffer(extendsAddition.concat(dependencyAddition).concat(String(file.contents)).concat(moduleExportsAddition))));
             this.push(file);
 
         } catch (err) {

+ 31 - 36
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -63,7 +63,6 @@
          */
         public static RENDEROBSERVABLE_POST = 2;
 
-
         private static _INSTANCES : Array<Canvas2D> = [];
 
         constructor(scene: Scene, settings?: {
@@ -76,7 +75,6 @@
             isScreenSpace?: boolean,
             cachingStrategy?: number,
             enableInteraction?: boolean,
-            allow3DEventBelowCanvas?: boolean,
             origin?: Vector2,
             isVisible?: boolean,
             backgroundRoundRadius?: number,
@@ -102,6 +100,10 @@
             this._uid = null;
             this._cachedCanvasGroup = null;
 
+            this._renderingGroupObserver = null;
+            this._beforeRenderObserver = null;
+            this._afterRenderObserver = null;
+
             this._profileInfoText = null;
 
             Prim2DBase._isCanvasInit = false;
@@ -170,8 +172,6 @@
             this._maxAdaptiveWorldSpaceCanvasSize = null;
             this._groupCacheMaps = new StringDictionary<MapTexture[]>();
 
-            this._changeFlags(SmartPropertyPrim.flagAllow3DEventsBelowCanvas, (settings.allow3DEventBelowCanvas != null) && settings.allow3DEventBelowCanvas);
-
             this._patchHierarchy(this);
 
             let enableInteraction = (settings.enableInteraction == null) ? true : settings.enableInteraction;
@@ -191,8 +191,8 @@
                     if (!settings.renderingPhase.camera || settings.renderingPhase.renderingGroupID==null) {
                         throw Error("You have to specify a valid camera and renderingGroup");
                     }
-                    this._scene.onRenderingGroupObservable.add((e, s) => {
-                        if (this._scene.activeCamera === settings.renderingPhase.camera) {
+                    this._renderingGroupObserver = this._scene.onRenderingGroupObservable.add((e, s) => {
+                        if ((this._scene.activeCamera === settings.renderingPhase.camera) && (e.renderStage===RenderingGroupInfo.STAGE_POSTTRANSPARENT)) {
                             this._engine.clear(null, false, true, true);
                             this._render();
                         }
@@ -490,17 +490,12 @@
             }
 
             eventState.skipNextObservers = skip;
-            if (!skip && (this._isFlagSet(SmartPropertyPrim.flagAllow3DEventsBelowCanvas)===false)) {
-                eventState.skipNextObservers = true;
-                if (eventData instanceof PointerInfoPre) {
-                    eventData.skipOnPointerObservable = true;
-                }
-            }
         }
 
         private _updatePointerInfo(eventData: PointerInfoBase, localPosition: Vector2): boolean {
             let s = this.scale;
             let pii = this._primPointerInfo;
+            pii.cancelBubble = false;
             if (!pii.canvasPointerPos) {
                 pii.canvasPointerPos = Vector2.Zero();
             }
@@ -612,7 +607,7 @@
                 let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
 
                 // Notify the previous "over" prim that the pointer is no longer over it
-                if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim)) {
+                if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim && !prevPrim.isDisposed)) {
                     this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
                     this._bubbleNotifyPrimPointerObserver(prevPrim, PrimitivePointerInfo.PointerOut, null);
                 }
@@ -852,6 +847,11 @@
                 this._setupInteraction(false);
             }
 
+            if (this._renderingGroupObserver) {
+                this._scene.onRenderingGroupObservable.remove(this._renderingGroupObserver);
+                this._renderingGroupObserver = null;
+            }
+
             if (this._beforeRenderObserver) {
                 this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
                 this._beforeRenderObserver = null;
@@ -1048,6 +1048,10 @@
             this._setupInteraction(enable);
         }
 
+        public get fitRenderingDevice(): boolean {
+            return this._fitRenderingDevice;
+        }
+
         public get designSize(): Size {
             return this._designSize;
         }
@@ -1071,21 +1075,6 @@
             return this.__engineData;
         }
 
-        /**
-         * If true is returned, pointerEvent occurring above the Canvas area also sent in 3D scene, if false they are not sent in the 3D Scene
-         */
-        public get allow3DEventBelowCanvas(): boolean {
-            return this._isFlagSet(SmartPropertyPrim.flagAllow3DEventsBelowCanvas);
-        }
-
-        /**
-         * Set true if you want pointerEvent occurring above the Canvas area to also be sent in the 3D scene.
-         * Set false if you don't want the Scene to get the events
-         */
-        public set allow3DEventBelowCanvas(value: boolean) {
-            this._changeFlags(SmartPropertyPrim.flagAllow3DEventsBelowCanvas, value);
-        }
-
         public createCanvasProfileInfoCanvas(): Canvas2D {
             if (this._profilingCanvas) {
                 return this._profilingCanvas;
@@ -1097,7 +1086,7 @@
                     new Rectangle2D({
                         id: "ProfileBorder", border: "#FFFFFFFF", borderThickness: 2, roundRadius: 5, fill: "#C04040C0", marginAlignment: "h: left, v: top", margin: "10", padding: "10", children:
                         [
-                            new Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "10pt Lucida Console" })
+                            new Text2D("Stats", { id: "ProfileInfoText", marginAlignment: "h: left, v: top", fontName: "12pt Lucida Console", fontSignedDistanceField: true })
                         ]
                     })
 
@@ -1109,6 +1098,11 @@
             return canvas;
         }
 
+        /**
+         * Instanced Array will be create if there's at least this number of parts/prim that can fit into it
+         */
+        public minPartCountToUseInstancedArray = 5;
+
         private checkBackgroundAvailability() {
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
@@ -1243,6 +1237,7 @@
         private _cachingStrategy: number;
         private _hierarchyLevelMaxSiblingCount: number;
         private _groupCacheMaps: StringDictionary<MapTexture[]>;
+        private _renderingGroupObserver: Observer<RenderingGroupInfo>;
         private _beforeRenderObserver: Observer<Scene>;
         private _afterRenderObserver: Observer<Scene>;
         private _supprtInstancedArray: boolean;
@@ -1459,8 +1454,6 @@
             }
         }
 
-        private static _unS = new Vector2(1, 1);
-
         /**
          * Internal method that allocate a cache for the given group.
          * Caching is made using a collection of MapTexture where many groups have their bitmap cache stored inside.
@@ -1484,6 +1477,7 @@
             // Determine size
             let size = group.actualSize;
             size = new Size(Math.ceil(size.width * scale.x), Math.ceil(size.height * scale.y));
+            let originalSize = size.clone();
             if (minSize) {
                 size.width = Math.max(minSize.width, size.width);
                 size.height = Math.max(minSize.height, size.height);
@@ -1532,15 +1526,18 @@
 
                 let sprite: Sprite2D;
                 if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
+                    if (this._cachedCanvasGroup) {
+                        this._cachedCanvasGroup.dispose();
+                    }
                     this._cachedCanvasGroup = Group2D._createCachedCanvasGroup(this);
-                    sprite = new Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: node.contentSize, spriteLocation: node.pos });
+                    sprite = new Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: originalSize, spriteLocation: node.pos });
                     sprite.zOrder = 1;
                     sprite.origin = Vector2.Zero();
                 }
 
                 // Create a Sprite that will be used to render this cache, the "__cachedSpriteOfGroup__" starting id is a hack to bypass exception throwing in case of the Canvas doesn't normally allows direct primitives
                 else {
-                    sprite = new Sprite2D(map, { parent: parent, id: `__cachedSpriteOfGroup__${group.id}`, x: group.actualPosition.x, y: group.actualPosition.y, spriteSize: node.contentSize, spriteLocation: node.pos, dontInheritParentScale: true });
+                    sprite = new Sprite2D(map, { parent: parent, id: `__cachedSpriteOfGroup__${group.id}`, x: group.actualPosition.x, y: group.actualPosition.y, spriteSize: originalSize, spriteLocation: node.pos, dontInheritParentScale: true });
                     sprite.origin = group.origin.clone();
                     sprite.addExternalData("__cachedGroup__", group);
                     sprite.pointerEventObservable.add((e, s) => {
@@ -1810,7 +1807,7 @@
             this.propertyChanged.add((e, st) => {
                 let mesh = this._worldSpaceNode as AbstractMesh;
                 if (mesh) {
-                    mesh.isVisible = this.isVisible;
+                    mesh.isVisible = e.newValue;
                 }
             }, Prim2DBase.isVisibleProperty.flagId);
         }
@@ -1841,7 +1838,6 @@
          *  - designUseHorizAxis: you can set this member if you use designSize to specify which axis is priority to compute the scale when the ratio of the canvas' size is different from the designSize's one.
          *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_DONTCACHE
          *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is true.
-         *  - allow3DEventBelowCanvas: by default pointerEvent occurring above the Canvas will prevent to be also sent in the 3D Scene. If you set this setting to true, events will be sent both for Canvas and 3D Scene
          *  - isVisible: true if the canvas must be visible, false for hidden. Default is true.
          * - backgroundRoundRadius: the round radius of the background, either backgroundFill or backgroundBorder must be specified.
          * - backgroundFill: the brush to use to create a background fill for the canvas. can be a string value (see BABYLON.Canvas2D.GetBrushFromString) or a IBrush2D instance.
@@ -1871,7 +1867,6 @@
             cachingStrategy?: number,
             cacheBehavior?: number,
             enableInteraction?: boolean,
-            allow3DEventBelowCanvas?: boolean,
             isVisible?: boolean,
             backgroundRoundRadius?: number,
             backgroundFill?: IBrush2D | string,

+ 2 - 2
canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts

@@ -62,12 +62,12 @@
         private _doUpdate(prim: Prim2DBase) {
             // Canvas ?
             if (prim instanceof Canvas2D) {
-                prim.layoutArea = prim.actualSize;
+                prim.layoutArea = prim.actualSize; //.multiplyByFloats(prim.scaleX, prim.scaleY);
             }
 
             // Direct child of Canvas ?
             else if (prim.parent instanceof Canvas2D) {
-                prim.layoutArea = prim.owner.actualSize;
+                prim.layoutArea = prim.owner.actualSize; //.multiplyByFloats(prim.owner.scaleX, prim.owner.scaleY);
             }
 
             // Indirect child of Canvas

+ 62 - 33
canvas2D/src/Engine/babylon.group2d.ts

@@ -373,12 +373,9 @@
 
             // The dimension must be overridden when using the designSize feature, the ratio is maintain to compute a uniform scale, which is mandatory but if the designSize's ratio is different from the rendering surface's ratio, content will be clipped in some cases.
             // So we set the width/height to the rendering's one because that's what we want for the viewport!
-            if (this instanceof Canvas2D) {
-                let c = <Canvas2D><any>this;
-                if (c.designSize != null) {
-                    sw = this.owner.engine.getRenderWidth();
-                    sh = this.owner.engine.getRenderHeight();
-                }
+            if ((this instanceof Canvas2D || this.id === "__cachedCanvasGroup__") && this.owner.designSize != null) {
+                sw = this.owner.engine.getRenderWidth();
+                sh = this.owner.engine.getRenderHeight();
             }
 
             // Setup the size of the rendering viewport
@@ -655,9 +652,9 @@
                 let engine = this.owner.engine;
                 let count = ts.endDataIndex - ts.startDataIndex;
 
-                // Use Instanced Array if it's supported and if there's at least 5 prims to draw.
-                // We don't want to create an Instanced Buffer for less that 5 prims
-                if (useInstanced && count >= 5) {
+                // Use Instanced Array if it's supported and if there's at least minPartCountToUseInstancedArray prims to draw.
+                // We don't want to create an Instanced Buffer for less that minPartCountToUseInstancedArray prims
+                if (useInstanced && count >= this.owner.minPartCountToUseInstancedArray) {
 
                     if (!ts.partBuffers) {
                         let buffers = new Array<WebGLBuffer>();
@@ -829,8 +826,18 @@
                 scale = this.actualScale;
             }
 
-            Group2D._s.width  = Math.ceil(this.actualSize.width * scale.x * rs);
-            Group2D._s.height = Math.ceil(this.actualSize.height * scale.y * rs);
+            if (isCanvas && this.owner.cachingStrategy===Canvas2D.CACHESTRATEGY_CANVAS && this.owner.isScreenSpace) {
+                if(this.owner.designSize || this.owner.fitRenderingDevice){
+                    Group2D._s.width = this.owner.engine.getRenderWidth();
+                    Group2D._s.height = this.owner.engine.getRenderHeight();
+                }
+                else{
+                    Group2D._s.copyFrom(this.owner.size);
+                }
+            } else {
+                Group2D._s.width = Math.ceil(this.actualSize.width * scale.x * rs);
+                Group2D._s.height = Math.ceil(this.actualSize.height * scale.y * rs);
+            }
 
             let sizeChanged = !Group2D._s.equals(rd._cacheSize);
 
@@ -855,6 +862,9 @@
                 var res = this.owner._allocateGroupCache(this, this.parent && this.parent.renderGroup, curWidth ? new Size(curWidth, curHeight) : null, rd._useMipMap, rd._anisotropicLevel);
                 rd._cacheNode = res.node;
                 rd._cacheTexture = res.texture;
+                if (rd._cacheRenderSprite) {
+                    rd._cacheRenderSprite.dispose();
+                }
                 rd._cacheRenderSprite = res.sprite;
                 sizeChanged = true;
             }
@@ -879,6 +889,16 @@
             }
         }
 
+        protected _spreadActualScaleDirty() {
+            if (this._renderableData && this._renderableData._cacheRenderSprite) {
+                this.handleGroupChanged(Prim2DBase.actualScaleProperty);
+            }
+
+            super._spreadActualScaleDirty();
+        }
+
+        protected static _unS = new Vector2(1, 1);
+
         protected handleGroupChanged(prop: Prim2DPropInfo) {
             // This method is only for cachedGroup
             let rd = this._renderableData;
@@ -893,20 +913,25 @@
 
             // For now we only support these property changes
             // TODO: add more! :)
-            if (prop.id === Prim2DBase.actualPositionProperty.id) {
-                cachedSprite.actualPosition = this.actualPosition.clone();
-                if (cachedSprite.position != null) {
-                    cachedSprite.position = cachedSprite.actualPosition.clone();
-                }
-            } else if (prop.id === Prim2DBase.rotationProperty.id) {
-                cachedSprite.rotation = this.rotation;
-            } else if (prop.id === Prim2DBase.scaleProperty.id) {
-                cachedSprite.scale = this.scale;
-            } else if (prop.id === Prim2DBase.originProperty.id) {
-                cachedSprite.origin = this.origin.clone();
-            } else if (prop.id === Group2D.actualSizeProperty.id) {
-                cachedSprite.size = this.actualSize.clone();
-                //console.log(`[${this._globalTransformProcessStep}] Sync Sprite ${this.id}, width: ${this.actualSize.width}, height: ${this.actualSize.height}`);
+            switch (prop.id) {
+                case Prim2DBase.actualPositionProperty.id:
+                    cachedSprite.actualPosition = this.actualPosition.clone();
+                    if (cachedSprite.position != null) {
+                        cachedSprite.position = cachedSprite.actualPosition.clone();
+                    }
+                    break;
+                case Prim2DBase.rotationProperty.id:
+                    cachedSprite.rotation = this.rotation;
+                    break;
+                case Prim2DBase.scaleProperty.id:
+                    cachedSprite.scale = this.scale;
+                    break;
+                case Prim2DBase.originProperty.id:
+                    cachedSprite.origin = this.origin.clone();
+                    break;
+                case Group2D.actualSizeProperty.id:
+                    cachedSprite.size = this.actualSize.clone();
+                    break;
             }
         }
 
@@ -949,19 +974,23 @@
 
             // All Group cached mode, all groups are renderable/cached, including the Canvas, groups with the behavior DONTCACHE are renderable/not cached, groups with CACHEINPARENT are logical ones
             else if (canvasStrat === Canvas2D.CACHESTRATEGY_ALLGROUPS) {
-                var gcb = this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_OPTIONMASK;
-                if ((gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE) || (gcb === Group2D.GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP)) {
-                    this._isRenderableGroup = gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE;
+                if (isCanvas) {
+                    this._isRenderableGroup = true;
                     this._isCachedGroup = false;
-                }
+                } else {
+                    var gcb = this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_OPTIONMASK;
+                    if ((gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE) || (gcb === Group2D.GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP)) {
+                        this._isRenderableGroup = gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE;
+                        this._isCachedGroup = false;
+                    }
 
-                if (gcb === Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
-                    this._isRenderableGroup = true;
-                    this._isCachedGroup = true;
+                    if (gcb === Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
+                        this._isRenderableGroup = true;
+                        this._isCachedGroup = true;
+                    }
                 }
             }
 
-
             if (this._isRenderableGroup) {
                 // Yes, we do need that check, trust me, unfortunately we can call _detectGroupStates many time on the same object...
                 if (!this._renderableData) {

+ 48 - 12
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1353,7 +1353,7 @@
      * Base class for a Primitive of the Canvas2D feature
      */
     export class Prim2DBase extends SmartPropertyPrim {
-        static PRIM2DBASE_PROPCOUNT: number = 24;
+        static PRIM2DBASE_PROPCOUNT: number = 25;
 
         public  static _bigInt = Math.pow(2, 30);
 
@@ -1662,6 +1662,14 @@
             return this._id;
         }
 
+        public set id(value: string) {
+            if (this._id === value) {
+                return;
+            }
+            let oldValue = this._id;
+            this.onPropertyChanged("id", oldValue, this._id);
+        }
+
         /**
          * Metadata of the position property
          */
@@ -1763,7 +1771,7 @@
         public static paddingProperty: Prim2DPropInfo;
 
         /**
-         * Metadata of the hAlignment property
+         * Metadata of the marginAlignment property
          */
         public static marginAlignmentProperty: Prim2DPropInfo;
 
@@ -1783,6 +1791,11 @@
          */
         public static scaleYProperty: Prim2DPropInfo;
 
+        /**
+         * Metadata of the actualScale property
+         */
+        public static actualScaleProperty: Prim2DPropInfo;
+
         @instanceLevelProperty(1, pi => Prim2DBase.actualPositionProperty = pi, false, false, true)
         /**
          * Return the position where the primitive is rendered in the Canvas, this position may be different than the one returned by the position property due to layout/alignment/margin/padding computing.
@@ -2070,7 +2083,7 @@
          */
         @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 13, pi => Prim2DBase.actualHeightProperty = pi, false, true)
         public get actualHeight(): number {
-            return this.actualSize.width;
+            return this.actualSize.height;
         }
 
         public set actualHeight(val: number) {
@@ -2320,7 +2333,7 @@
             return this._scale.y;
         }
 
-        private _spreadActualScaleDirty() {
+        protected _spreadActualScaleDirty() {
             for (let child of this._children) {
                 child._setFlags(SmartPropertyPrim.flagActualScaleDirty);
                 child._spreadActualScaleDirty();
@@ -2330,6 +2343,7 @@
         /**
          * Returns the actual scale of this Primitive, the value is computed from the scale property of this primitive, multiplied by the actualScale of its parent one (if any). The Vector2 object returned contains the scale for both X and Y axis
          */
+        @instanceLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 24, pi => Prim2DBase.actualScaleProperty = pi, false, true)
         public get actualScale(): Vector2 {
             if (this._isFlagSet(SmartPropertyPrim.flagActualScaleDirty)) {
                 let cur = this._isFlagSet(SmartPropertyPrim.flagDontInheritParentScale) ? null : this.parent;
@@ -2656,7 +2670,7 @@
 
                 this._debugAreaGroup = new Group2D
                     (
-                    {
+                    {   dontInheritParentScale: true,
                         parent: (this.parent!=null) ? this.parent : this, id: "###DEBUG AREA GROUP###", children:
                         [
                             new Group2D({
@@ -2758,11 +2772,13 @@
                 areaInfo[curAreaIndex++] = { off: pos, size: size, min: min, max: max };
             }
 
+            let isCanvas = this instanceof Canvas2D;
             let marginH = this._marginOffset.x + this._marginOffset.z;
             let marginV = this._marginOffset.y + this._marginOffset.w;
+            let actualSize = this.actualSize.multiplyByFloats(isCanvas ? 1 : this.scaleX, isCanvas ? 1 : this.scaleY);
 
-            let w = hasLayout ? (this.layoutAreaPos.x + this.layoutArea.width)  : (marginH + this.actualSize.width);
-            let h = hasLayout ? (this.layoutAreaPos.y + this.layoutArea.height) : (marginV + this.actualSize.height);
+            let w = hasLayout ? (this.layoutAreaPos.x + this.layoutArea.width)  : (marginH + actualSize.width);
+            let h = hasLayout ? (this.layoutAreaPos.y + this.layoutArea.height) : (marginV + actualSize.height);
             let pos = (!hasLayout && !hasMargin && !hasPadding && hasPos) ? this.actualPosition : Vector2.Zero();
 
             storeAreaInfo(pos, new Size(w, h));
@@ -2771,7 +2787,7 @@
             if (hasLayout) {
                 let layoutOffset = this.layoutAreaPos.clone();
 
-                storeAreaInfo(layoutOffset, (hasMargin || hasPadding) ? this.layoutArea.clone() : this.actualSize.clone());
+                storeAreaInfo(layoutOffset, (hasMargin || hasPadding) ? this.layoutArea.clone() : actualSize.clone());
                 curOffset = layoutOffset.clone();
             }
 
@@ -2780,7 +2796,7 @@
                 let marginOffset = curOffset.clone();
                 marginOffset.x += this._marginOffset.x;
                 marginOffset.y += this._marginOffset.y;
-                let marginArea = this.actualSize;
+                let marginArea = actualSize;
 
                 storeAreaInfo(marginOffset, marginArea);
                 curOffset = marginOffset.clone();
@@ -2920,6 +2936,8 @@
          * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
          * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
          */
+        private static _bypassGroup2DExclusion = false;
+
         public intersect(intersectInfo: IntersectInfo2D): boolean {
             if (!intersectInfo) {
                 return false;
@@ -2936,14 +2954,27 @@
                 intersectInfo.topMostIntersectedPrimitive = null;
             }
 
+            if (!Prim2DBase._bypassGroup2DExclusion && this instanceof Group2D && (<Group2D><any>this).isCachedGroup && !(<Group2D><any>this).isRenderableGroup) {
+                // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
+                intersectInfo._exit(firstLevel);
+                return false;
+            }
+
             if (!intersectInfo.intersectHidden && !this.isVisible) {
+                // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
+                intersectInfo._exit(firstLevel);
                 return false;
             }
 
             let id = this.id;
-            if (id!=null && id.indexOf("__cachedSpriteOfGroup__") === 0) {
-                let ownerGroup = this.getExternalData<Group2D>("__cachedGroup__");
-                return ownerGroup.intersect(intersectInfo);
+            if (id != null && id.indexOf("__cachedSpriteOfGroup__") === 0) {
+                try {
+                    Prim2DBase._bypassGroup2DExclusion = true;
+                    let ownerGroup = this.getExternalData<Group2D>("__cachedGroup__");
+                    return ownerGroup.intersect(intersectInfo);
+                } finally  {
+                    Prim2DBase._bypassGroup2DExclusion = false;
+                } 
             }
 
             // If we're testing a cachedGroup, we must reject pointer outside its levelBoundingInfo because children primitives could be partially clipped outside so we must not accept them as intersected when it's the case (because they're not visually visible).
@@ -3088,6 +3119,11 @@
                 return false;
             }
 
+            if (this._pointerEventObservable) {
+                this._pointerEventObservable.clear();
+                this._pointerEventObservable = null;
+            }
+
             if (this._actionManager) {
                 this._actionManager.dispose();
                 this._actionManager = null;

+ 15 - 3
canvas2D/src/Engine/babylon.smartPropertyPrim.ts

@@ -1057,7 +1057,20 @@
                         }
                     }
 
-                    modelKey += v.name + ":" + ((propVal != null) ? ((v.typeLevelCompare) ? Tools.getClassName(propVal) : propVal.toString()) : "[null]") + ";";
+                    let value = "[null]";
+                    if (propVal != null) {
+                        if (v.typeLevelCompare) {
+                            value = Tools.getClassName(propVal);
+                        } else {
+                            if (propVal instanceof BaseTexture) {
+                                value = propVal.uid;
+                            } else {
+                                value = propVal.toString();
+                            }
+                        }
+                    }
+
+                    modelKey += v.name + ":" + value + ";";
                 }
             });
 
@@ -1114,7 +1127,7 @@
             }
 
             // If the property belong to a group, check if it's a cached one, and dirty its render sprite accordingly
-            if (this instanceof Group2D) {
+            if (this instanceof Group2D && (<Group2D><any>this)._renderableData) {
                 (<SmartPropertyPrim>this).handleGroupChanged(propInfo);
             }
 
@@ -1266,7 +1279,6 @@
         public static flagDontInheritParentScale  = 0x0080000;    // set if the actualScale must not use its parent's scale to be computed
         public static flagGlobalTransformDirty    = 0x0100000;    // set if the global transform must be recomputed due to a local transform change
         public static flagLayoutBoundingInfoDirty = 0x0200000;    // set if the layout bounding info is dirty
-        public static flagAllow3DEventsBelowCanvas= 0x0400000;    // set if pointer events should be sent to 3D Engine when the pointer is over the Canvas
 
         private   _flags              : number;
         private   _modelKey           : string;

+ 15 - 11
canvas2D/src/Engine/babylon.sprite2d.ts

@@ -381,35 +381,39 @@
             this.texture = texture;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this.size = settings.spriteSize;
-            this.spriteLocation = settings.spriteLocation || new Vector2(0, 0);
-            this.spriteScaleFactor = settings.spriteScaleFactor || new Vector2(1, 1);
+            this.size = (settings.spriteSize!=null) ? settings.spriteSize.clone() : null;
+            this.spriteLocation = (settings.spriteLocation!=null) ? settings.spriteLocation.clone() : new Vector2(0, 0);
+            this.spriteScaleFactor = (settings.spriteScaleFactor!=null) ? settings.spriteScaleFactor : new Vector2(1, 1);
             this.spriteFrame = 0;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
             this.useAlphaFromTexture = true;
 
+            // If the user doesn't set a size, we'll use the texture's one, but if the texture is not loading, we HAVE to set a temporary dummy size otherwise the positioning engine will switch the marginAlignement to stretch/stretch, and WE DON'T WANT THAT.
+            // The fucking delayed texture sprite bug is fixed!
+            if (settings.spriteSize == null) {
+                this.size = new Size(10, 10);
+            }
+
             if (settings.spriteSize == null || !texture.isReady()) {
                 if (texture.isReady()) {
-                    this.size = <Size>texture.getBaseSize();
+                    let s = texture.getBaseSize();
+                    this.size = new Size(s.width, s.height);
                 } else {
+
                     texture.onLoadObservable.add(() => {
                         if (settings.spriteSize == null) {
-                            this.size = <Size>texture.getBaseSize();
+                            let s = texture.getBaseSize();
+                        this.size = new Size(s.width, s.height);
                         }
                         this._positioningDirty();
+                        this._setLayoutDirty();
                         this._instanceDirtyFlags |= Prim2DBase.originProperty.flagId | Sprite2D.textureProperty.flagId;  // To make sure the sprite is issued again for render
                     });
                 }
             }
         }
 
-        static _createCachedCanvasSprite(owner: Canvas2D, texture: MapTexture, size: Size, pos: Vector2): Sprite2D {
-
-            let sprite = new Sprite2D(texture, { parent: owner, id: "__cachedCanvasSprite__", position: Vector2.Zero(), origin: Vector2.Zero(), spriteSize: size, spriteLocation: pos, alignToPixel: true });
-            return sprite;
-        }
-
         protected createModelRenderCache(modelKey: string): ModelRenderCache {
             let renderCache = new Sprite2DRenderCache(this.owner.engine, modelKey);
             return renderCache;

Diferenças do arquivo suprimidas por serem muito extensas
+ 85 - 45
canvas2D/src/Engine/babylon.text2d.ts


+ 106 - 0
canvas2D/src/GUI/Layouts/babylon.gui.stackPanel.ts

@@ -0,0 +1,106 @@
+module BABYLON {
+
+    @className("StackPanel", "BABYLON")
+    export class StackPanel extends UIElement {
+
+        static STACKPANEL_PROPCOUNT = UIElement.UIELEMENT_PROPCOUNT + 3;
+
+        static orientationHorizontalProperty: Prim2DPropInfo;
+
+        constructor(settings?: {
+
+            id                      ?: string,
+            parent                  ?: UIElement,
+            children                ?: Array<UIElement>,
+            templateName            ?: string,
+            styleName               ?: string,
+            isOrientationHorizontal ?: any,
+            marginTop               ?: number | string,
+            marginLeft              ?: number | string,
+            marginRight             ?: number | string,
+            marginBottom            ?: number | string,
+            margin                  ?: number | string,
+            marginHAlignment        ?: number,
+            marginVAlignment        ?: number,
+            marginAlignment         ?: string,
+            paddingTop              ?: number | string,
+            paddingLeft             ?: number | string,
+            paddingRight            ?: number | string,
+            paddingBottom           ?: number | string,
+            padding                 ?: string,
+            paddingHAlignment       ?: number,
+            paddingVAlignment       ?: number,
+            paddingAlignment        ?: string,
+        }) {
+            if (!settings) {
+                settings = {};
+            }
+
+            super(settings);
+
+            this.isOrientationHorizontal = (settings.isOrientationHorizontal == null) ? true : settings.isOrientationHorizontal;
+            this._children = new Array<UIElement>();
+
+            if (settings.children != null) {
+                for (let child of settings.children) {
+                    this._children.push(child);
+                }
+            }
+
+        }
+
+        @dependencyProperty(StackPanel.STACKPANEL_PROPCOUNT + 0, pi => StackPanel.orientationHorizontalProperty = pi)
+        public get isOrientationHorizontal(): boolean {
+            return this._isOrientationHorizontal;
+        }
+
+        public set isOrientationHorizontal(value: boolean) {
+            this._isOrientationHorizontal = value;
+        }
+
+        protected createVisualTree() {
+            super.createVisualTree();
+
+            // A StackPanel Control has a Group2D, child of the visualPlaceHolder, which is the Children placeholder.
+            // The Children UIElement Tree will be create inside this placeholder.
+            this._childrenPlaceholder = new Group2D({ parent: this._visualPlaceholder, id: `StackPanel Children Placeholder of ${this.id}` });
+            let p = this._childrenPlaceholder;
+
+            p.layoutEngine = this.isOrientationHorizontal ? StackPanelLayoutEngine.Horizontal : StackPanelLayoutEngine.Vertical;
+
+            // The UIElement padding properties (padding and paddingAlignment) are bound to the Group2D Children placeholder, we bound to the Margin properties as the Group2D acts as an inner element already, so margin of inner is padding.
+            p.dataSource = this;
+            p.createSimpleDataBinding(Prim2DBase.marginProperty, "padding", DataBinding.MODE_ONEWAY);
+            p.createSimpleDataBinding(Prim2DBase.marginAlignmentProperty, "paddingAlignment", DataBinding.MODE_ONEWAY);
+
+            // The UIElement set the childrenPlaceholder with the visual returned by the renderingTemplate.
+            // But it's not the case for a StackPanel, the placeholder of UIElement Children (the content)
+            this._visualChildrenPlaceholder = this._childrenPlaceholder;
+        }
+
+        public get children(): Array<UIElement> {
+            return this._children;
+        }
+
+        protected _getChildren(): Array<UIElement> {
+            return this.children;
+        }
+
+        private _childrenPlaceholder: Group2D;
+        private _children;
+        private _isOrientationHorizontal: boolean;
+    }
+
+
+    @registerWindowRenderingTemplate("BABYLON.StackPanel", "Default", () => new DefaultStackPanelRenderingTemplate())
+    export class DefaultStackPanelRenderingTemplate extends UIElementRenderingTemplateBase {
+
+        createVisualTree(owner: UIElement, visualPlaceholder: Group2D): { root: Prim2DBase; contentPlaceholder: Prim2DBase } {
+            return { root: visualPlaceholder, contentPlaceholder: visualPlaceholder };
+        }
+
+        attach(owner: UIElement): void {
+            super.attach(owner);
+        }
+    }
+}

+ 241 - 87
canvas2D/src/GUI/babylon.gui.UIElement.ts

@@ -55,48 +55,64 @@
 
     export abstract class UIElement extends SmartPropertyBase {
 
-        static UIELEMENT_PROPCOUNT: number = 15;
-
-        static parentProperty         : Prim2DPropInfo;
-        static widthProperty          : Prim2DPropInfo;
-        static heightProperty         : Prim2DPropInfo;
-        static minWidthProperty       : Prim2DPropInfo;
-        static minHeightProperty      : Prim2DPropInfo;
-        static maxWidthProperty       : Prim2DPropInfo;
-        static maxHeightProperty      : Prim2DPropInfo;
-        static actualWidthProperty    : Prim2DPropInfo;
-        static actualHeightProperty   : Prim2DPropInfo;
-        static marginProperty         : Prim2DPropInfo;
-        static paddingProperty        : Prim2DPropInfo;
-        static marginAlignmentProperty: Prim2DPropInfo;
-        static isEnabledProperty      : Prim2DPropInfo;
-        static isFocusedProperty      : Prim2DPropInfo;
-        static isMouseOverProperty    : Prim2DPropInfo;
+        static get enabledState(): string {
+            return UIElement._enableState;
+        }
+
+        static get disabledState(): string {
+            return UIElement._disabledState;
+        }
+
+        static get mouseOverState(): string {
+            return UIElement._mouseOverState;
+        }
+
+        static UIELEMENT_PROPCOUNT: number = 16;
+
+        static parentProperty          : Prim2DPropInfo;
+        static widthProperty           : Prim2DPropInfo;
+        static heightProperty          : Prim2DPropInfo;
+        static minWidthProperty        : Prim2DPropInfo;
+        static minHeightProperty       : Prim2DPropInfo;
+        static maxWidthProperty        : Prim2DPropInfo;
+        static maxHeightProperty       : Prim2DPropInfo;
+        static actualWidthProperty     : Prim2DPropInfo;
+        static actualHeightProperty    : Prim2DPropInfo;
+        static marginProperty          : Prim2DPropInfo;
+        static paddingProperty         : Prim2DPropInfo;
+        static marginAlignmentProperty : Prim2DPropInfo;
+        static paddingAlignmentProperty: Prim2DPropInfo;
+        static isEnabledProperty       : Prim2DPropInfo;
+        static isFocusedProperty       : Prim2DPropInfo;
+        static isMouseOverProperty     : Prim2DPropInfo;
 
         constructor(settings: {
-            id              ?: string,
-            parent          ?: UIElement,
-            templateName    ?: string,
-            styleName       ?: string,
-            minWidth        ?: number,
-            minHeight       ?: number,
-            maxWidth        ?: number,
-            maxHeight       ?: number,
-            width           ?: number,
-            height          ?: number,
-            marginTop       ?: number | string,
-            marginLeft      ?: number | string,
-            marginRight     ?: number | string,
-            marginBottom    ?: number | string,
-            margin          ?: number | string,
-            marginHAlignment?: number,
-            marginVAlignment?: number,
-            marginAlignment ?: string,
-            paddingTop      ?: number | string,
-            paddingLeft     ?: number | string,
-            paddingRight    ?: number | string,
-            paddingBottom   ?: number | string,
-            padding         ?: string,
+            id               ?: string,
+            parent           ?: UIElement,
+            templateName     ?: string,
+            styleName        ?: string,
+            minWidth         ?: number,
+            minHeight        ?: number,
+            maxWidth         ?: number,
+            maxHeight        ?: number,
+            width            ?: number,
+            height           ?: number,
+            marginTop        ?: number | string,
+            marginLeft       ?: number | string,
+            marginRight      ?: number | string,
+            marginBottom     ?: number | string,
+            margin           ?: number | string,
+            marginHAlignment ?: number,
+            marginVAlignment ?: number,
+            marginAlignment  ?: string,
+            paddingTop       ?: number | string,
+            paddingLeft      ?: number | string,
+            paddingRight     ?: number | string,
+            paddingBottom    ?: number | string,
+            padding          ?: string,
+            paddingHAlignment?: number,
+            paddingVAlignment?: number,
+            paddingAlignment ?: string,
         }) {
             super();
 
@@ -111,7 +127,8 @@
             this._visualTemplateRoot        = null;
             this._visualChildrenPlaceholder = null;
             this._hierarchyDepth            = 0;
-            this._style                     = (settings.styleName!=null) ? UIElementStyleManager.getStyle(type, settings.styleName) : null;
+            this._renderingTemplateName     = (settings.templateName != null) ? settings.templateName : GUIManager.DefaultTemplateName;
+            this._style                     = (settings.styleName!=null) ? GUIManager.getStyle(type, settings.styleName) : null;
             this._flags                     = 0;
             this._id                        = (settings.id!=null) ? settings.id : null;
             this._uid                       = null;
@@ -124,9 +141,8 @@
             this._margin                    = null;
             this._padding                   = null;
             this._marginAlignment           = null;
-            this._isEnabled                 = true;
-            this._isFocused                 = false;
-            this._isMouseOver               = false;
+
+            this._setFlags(UIElement.flagIsVisible|UIElement.flagIsEnabled);
 
             // Default Margin Alignment for UIElement is stretch for horizontal/vertical and not left/bottom (which is the default for Canvas2D Primitives)
             //this.marginAlignment.horizontal = PrimitiveAlignment.AlignStretch;
@@ -183,7 +199,17 @@
                 this.padding.fromString(settings.padding);
             }
 
-            this._assignTemplate(settings.templateName);
+            if (settings.paddingHAlignment) {
+                this.paddingAlignment.horizontal = settings.paddingHAlignment;
+            }
+
+            if (settings.paddingVAlignment) {
+                this.paddingAlignment.vertical = settings.paddingVAlignment;
+            }
+
+            if (settings.paddingAlignment) {
+                this.paddingAlignment.fromString(settings.paddingAlignment);
+            }
 
             if (settings.parent != null) {
                 this._parent = settings.parent;
@@ -266,13 +292,27 @@
         // SizeChanged
         // ToolTipOpening/Closing
 
-        public get ownerWindows(): Window {
+        public findById(id: string): UIElement {
+            if (this._id === id) {
+                return this;
+            }
+
+            let children = this._getChildren();
+            for (let child of children) {
+                let r = child.findById(id);
+                if (r != null) {
+                    return r;
+                }
+            }
+        }
+
+        public get ownerWindow(): Window {
             return this._ownerWindow;
         }
 
         public get style(): string {
             if (!this.style) {
-                return UIElementStyleManager.DefaultStyleName;
+                return GUIManager.DefaultStyleName;
             }
             return this._style.name;
         }
@@ -284,7 +324,7 @@
 
             let newStyle: UIElementStyle = null;
             if (value) {
-                newStyle = UIElementStyleManager.getStyle(Tools.getFullClassName(this), value);
+                newStyle = GUIManager.getStyle(Tools.getFullClassName(this), value);
                 if (!newStyle) {
                     throw Error(`Couldn't find Style ${value} for UIElement ${Tools.getFullClassName(this)}`);
                 }
@@ -485,40 +525,121 @@
             return (this._marginAlignment !== null && !this._marginAlignment.isDefault);
         }
 
-        @dynamicLevelProperty(12, pi => UIElement.isEnabledProperty = pi)
+        @dynamicLevelProperty(12, pi => UIElement.paddingAlignmentProperty = pi)
+        /**
+         * You can get/set the margin alignment through this property
+         */
+        public get paddingAlignment(): PrimitiveAlignment {
+            if (!this._paddingAlignment) {
+                this._paddingAlignment = new PrimitiveAlignment();
+            }
+            return this._paddingAlignment;
+        }
+
+        public set paddingAlignment(value: PrimitiveAlignment) {
+            this.paddingAlignment.copyFrom(value);
+        }
+
+        /**
+         * Check if there a marginAlignment specified (non null and not default)
+         */
+        public get _hasPaddingAlignment(): boolean {
+            return (this._paddingAlignment !== null && !this._paddingAlignment.isDefault);
+        }
+
+        public get isVisible(): boolean {
+            return this._isFlagSet(UIElement.flagIsVisible);
+        }
+
+        public set isVisible(value: boolean) {
+            if (this.isVisible === value) {
+                return;
+            }
+
+            this._visualPlaceholder.levelVisible = value;
+
+            this._changeFlags(UIElement.flagIsVisible, value);
+        }
+
+        @dynamicLevelProperty(13, pi => UIElement.isEnabledProperty = pi)
         /**
-         * True if the UIElement is enabled, false if it's disabled
+         * True if the UIElement is enabled, false if it's disabled.
+         * User interaction is not possible if the UIElement is not enabled
          */
         public get isEnabled(): boolean {
-            return this._isEnabled;
+            return this._isFlagSet(UIElement.flagIsEnabled);
         }
 
         public set isEnabled(value: boolean) {
-            this._isEnabled = value;
+            this._changeFlags(UIElement.flagIsEnabled, value);
         }
 
-        @dynamicLevelProperty(13, pi => UIElement.isFocusedProperty = pi)
+        @dynamicLevelProperty(14, pi => UIElement.isFocusedProperty = pi)
         /**
          * True if the UIElement has the focus, false if it doesn't
          */
         public get isFocused(): boolean {
-            return this._isFocused;
+            return this._isFlagSet(UIElement.flagIsFocus);
         }
 
         public set isFocused(value: boolean) {
-            this._isFocused = value;
+            // If the UIElement doesn't accept focus, set it on its parent
+            if (!this.isFocusable) {
+                let p = this.parent;
+                if (!p) {
+                    return;
+                }
+                p.isFocused = value;
+            }
+
+            // If the focus is being set, notify the Focus Manager
+            if (value) {
+                this.ownerWindow.focusManager.setFocusOn(this, this.getFocusScope());
+            }
+
+            this._changeFlags(UIElement.flagIsFocus, value);
         }
 
-        @dynamicLevelProperty(14, pi => UIElement.isMouseOverProperty = pi)
+        @dynamicLevelProperty(15, pi => UIElement.isMouseOverProperty = pi)
         /**
          * True if the UIElement has the mouse over it
          */
         public get isMouseOver(): boolean {
-            return this._isMouseOver;
+            return this._isFlagSet(UIElement.flagIsMouseOver);
         }
 
         public set isMouseOver(value: boolean) {
-            this._isMouseOver = value;
+            this._changeFlags(UIElement.flagIsMouseOver, value);
+        }
+
+        public get isFocusScope(): boolean {
+            return this._isFlagSet(UIElement.flagIsFocusScope);
+        }
+
+        public set isFocusScope(value: boolean) {
+            this._changeFlags(UIElement.flagIsFocusScope, value);
+        }
+
+        public get isFocusable(): boolean {
+            return this._isFlagSet(UIElement.flagIsFocusable);
+        }
+
+        public set isFocusable(value: boolean) {
+            this._changeFlags(UIElement.flagIsFocusable, value);
+        }
+
+        // Look for the nearest parent which is the focus scope. Should always return something as the Window UIElement which is the root of all UI Tree is focus scope (unless the user disable it)
+        protected getFocusScope(): UIElement {
+            if (this.isFocusScope) {
+                return this;
+            }
+
+            let p = this.parent;
+            if (!p) {
+                return null;
+            }
+
+            return p.getFocusScope();
         }
 
         /**
@@ -582,38 +703,43 @@
 
         private _assignTemplate(templateName: string) {
             if (!templateName) {
-                templateName = UIElementRenderingTemplateManager.DefaultTemplateName;
+                templateName = GUIManager.DefaultTemplateName;
             }
             let className = Tools.getFullClassName(this);
             if (!className) {
                 throw Error("Couldn't access class name of this UIElement, you have to decorate the type with the className decorator");
             }
 
-            let factory = UIElementRenderingTemplateManager.getRenderingTemplate(className, templateName);
+            let factory = GUIManager.getRenderingTemplate(className, templateName);
             if (!factory) {
                 throw Error(`Couldn't get the renderingTemplate ${templateName} of class ${className}`);
             }
 
+            this._renderingTemplateName = templateName;
             this._renderingTemplate = factory();
             this._renderingTemplate.attach(this);
         }
 
         public _createVisualTree() {
-            let parentPrim: Prim2DBase = this.ownerWindows.canvas;
+            let parentPrim: Prim2DBase = this.ownerWindow.canvas;
             if (this.parent) {
                 parentPrim = this.parent.visualChildrenPlaceholder;
             }
 
-            this._visualPlaceholder = new Group2D({ parent: parentPrim, id: `GUI Visual Placeholder of ${this.id}`});
+            if (!this._renderingTemplate) {
+                this._assignTemplate(this._renderingTemplateName);               
+            }
+
+            this._visualPlaceholder = new Group2D({ parent: parentPrim, id: `GUI ${Tools.getClassName(this)} RootGroup of ${this.id}`});
             let p = this._visualPlaceholder;
             p.addExternalData<UIElement>("_GUIOwnerElement_", this);
             p.dataSource = this;
-            p.createSimpleDataBinding(Prim2DBase.widthProperty, "width", DataBinding.MODE_ONEWAY);
-            p.createSimpleDataBinding(Prim2DBase.heightProperty, "height", DataBinding.MODE_ONEWAY);
-            p.createSimpleDataBinding(Prim2DBase.actualWidthProperty, "actualWidth", DataBinding.MODE_ONEWAYTOSOURCE);
-            p.createSimpleDataBinding(Prim2DBase.actualHeightProperty, "actualHeight", DataBinding.MODE_ONEWAYTOSOURCE);
-            p.createSimpleDataBinding(Prim2DBase.marginProperty, "margin", DataBinding.MODE_ONEWAY);
-            p.createSimpleDataBinding(Prim2DBase.paddingProperty, "padding", DataBinding.MODE_ONEWAY);
+
+            p.createSimpleDataBinding(Prim2DBase.widthProperty          , "width"          , DataBinding.MODE_ONEWAY);
+            p.createSimpleDataBinding(Prim2DBase.heightProperty         , "height"         , DataBinding.MODE_ONEWAY);
+            p.createSimpleDataBinding(Prim2DBase.actualWidthProperty    , "actualWidth"    , DataBinding.MODE_ONEWAYTOSOURCE);
+            p.createSimpleDataBinding(Prim2DBase.actualHeightProperty   , "actualHeight"   , DataBinding.MODE_ONEWAYTOSOURCE);
+            p.createSimpleDataBinding(Prim2DBase.marginProperty         , "margin"         , DataBinding.MODE_ONEWAY);
             p.createSimpleDataBinding(Prim2DBase.marginAlignmentProperty, "marginAlignment", DataBinding.MODE_ONEWAY);
             this.createVisualTree();
         }
@@ -675,12 +801,19 @@
         protected get _position(): Vector2 { return null; } // TODO use abstract keyword when TS 2.0 will be approved
         protected abstract _getChildren(): Array<UIElement>;
 
-        public static flagVisualToBuild = 0x0000001;    // set if the UIElement visual must be updated
+        public static flagVisualToBuild = 0x0000001;
+        public static flagIsVisible     = 0x0000002;
+        public static flagIsFocus       = 0x0000004;
+        public static flagIsFocusScope  = 0x0000008;
+        public static flagIsFocusable   = 0x0000010;
+        public static flagIsEnabled     = 0x0000020;
+        public static flagIsMouseOver   = 0x0000040;
 
         protected _visualPlaceholder: Group2D;
         protected _visualTemplateRoot: Prim2DBase;
         protected _visualChildrenPlaceholder: Prim2DBase;
-        private _renderingTemplate: UIElementRenderingTemplateBase;
+        private _renderingTemplateName: string;
+        protected _renderingTemplate: UIElementRenderingTemplateBase;
         private _parent: UIElement;
         private _hierarchyDepth: number;
         private _flags: number;
@@ -699,9 +832,11 @@
         private _margin: PrimitiveThickness;
         private _padding: PrimitiveThickness;
         private _marginAlignment: PrimitiveAlignment;
-        private _isEnabled: boolean;
-        private _isFocused: boolean;
-        private _isMouseOver: boolean;
+        private _paddingAlignment: PrimitiveAlignment;
+
+        private static _enableState = "Enabled";
+        private static _disabledState = "Disabled";
+        private static _mouseOverState = "MouseOver";
     }
 
     export abstract class UIElementStyle {
@@ -710,9 +845,23 @@
         get name(): string { return null; } // TODO use abstract keyword when TS 2.0 will be approved
     }
 
-    export class UIElementStyleManager {
+    export class GUIManager {
+
+        /////////////////////////////////////////////////////////////////////////////////////////////////////
+        // DATA TEMPLATE MANAGER
+
+        static registerDataTemplate(className: string, factory: (parent: UIElement, dataObject: any) => UIElement) {
+            
+        }
+
+        // DATA TEMPLATE MANAGER
+        /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+        /////////////////////////////////////////////////////////////////////////////////////////////////////
+        // STYLE MANAGER
+
         static getStyle(uiElType: string, styleName: string): UIElementStyle {
-            let styles = UIElementStyleManager.stylesByUIElement.get(uiElType);
+            let styles = GUIManager.stylesByUIElement.get(uiElType);
             if (!styles) {
                 throw Error(`The type ${uiElType} is unknown, no style were registered for it.`);
             }
@@ -724,7 +873,7 @@
         }
 
         static registerStyle(uiElType: string, templateName: string, style: UIElementStyle) {
-            let templates = UIElementStyleManager.stylesByUIElement.getOrAddWithFactory(uiElType, () => new StringDictionary<UIElementStyle>());
+            let templates = GUIManager.stylesByUIElement.getOrAddWithFactory(uiElType, () => new StringDictionary<UIElementStyle>());
             if (templates.contains(templateName)) {
                 templates[templateName] = style;
             } else {
@@ -735,19 +884,20 @@
         static stylesByUIElement: StringDictionary<StringDictionary<UIElementStyle>> = new StringDictionary<StringDictionary<UIElementStyle>>();
 
         public static get DefaultStyleName(): string {
-            return UIElementStyleManager._defaultStyleName;
+            return GUIManager._defaultStyleName;
         }
 
         public static set DefaultStyleName(value: string) {
-            UIElementStyleManager._defaultStyleName = value;
+            GUIManager._defaultStyleName = value;
         }
 
-        private static _defaultStyleName = "Default";
-    }
+        // STYLE MANAGER
+        /////////////////////////////////////////////////////////////////////////////////////////////////////
 
-    export class UIElementRenderingTemplateManager {
+        /////////////////////////////////////////////////////////////////////////////////////////////////////
+        // RENDERING TEMPLATE MANAGER
         static getRenderingTemplate(uiElType: string, templateName: string): () => UIElementRenderingTemplateBase {
-            let templates = UIElementRenderingTemplateManager.renderingTemplatesByUIElement.get(uiElType);
+            let templates = GUIManager.renderingTemplatesByUIElement.get(uiElType);
             if (!templates) {
                 throw Error(`The type ${uiElType} is unknown, no Rendering Template were registered for it.`);
             }
@@ -759,7 +909,7 @@
         }
 
         static registerRenderingTemplate(uiElType: string, templateName: string, factory: () => UIElementRenderingTemplateBase) {
-            let templates = UIElementRenderingTemplateManager.renderingTemplatesByUIElement.getOrAddWithFactory(uiElType, () => new StringDictionary<() => UIElementRenderingTemplateBase>());
+            let templates = GUIManager.renderingTemplatesByUIElement.getOrAddWithFactory(uiElType, () => new StringDictionary<() => UIElementRenderingTemplateBase>());
             if (templates.contains(templateName)) {
                 templates[templateName] = factory;
             } else {
@@ -770,14 +920,18 @@
         static renderingTemplatesByUIElement: StringDictionary<StringDictionary<() => UIElementRenderingTemplateBase>> = new StringDictionary<StringDictionary<() => UIElementRenderingTemplateBase>>();
 
         public static get DefaultTemplateName(): string {
-            return UIElementRenderingTemplateManager._defaultTemplateName;
+            return GUIManager._defaultTemplateName;
         }
 
         public static set DefaultTemplateName(value: string) {
-            UIElementRenderingTemplateManager._defaultTemplateName = value;
+            GUIManager._defaultTemplateName = value;
         }
-        
+
+        // RENDERING TEMPLATE MANAGER
+        /////////////////////////////////////////////////////////////////////////////////////////////////////
+
         private static _defaultTemplateName = "Default";
+        private static _defaultStyleName = "Default";
     }
 
     export abstract class UIElementRenderingTemplateBase {
@@ -799,7 +953,7 @@
 
     export function registerWindowRenderingTemplate(uiElType: string, templateName: string, factory: () => UIElementRenderingTemplateBase): (target: Object) => void {
         return () => {
-            UIElementRenderingTemplateManager.registerRenderingTemplate(uiElType, templateName, factory);
+            GUIManager.registerRenderingTemplate(uiElType, templateName, factory);
         }
     }
 

+ 107 - 77
canvas2D/src/GUI/babylon.gui.button.ts

@@ -3,6 +3,10 @@
     @className("Button", "BABYLON")
     export class Button extends ContentControl {
 
+        static get pushedState() {
+            return Button._pushedState;
+        }
+
         static BUTTON_PROPCOUNT = ContentControl.CONTENTCONTROL_PROPCOUNT + 3;
 
         static isPushedProperty: Prim2DPropInfo;
@@ -11,25 +15,27 @@
 
         constructor(settings?: {
 
-            id              ?: string,
-            parent          ?: UIElement,
-            templateName    ?: string,
-            styleName       ?: string,
-            content         ?: any,
-            contentAlignment?: string,
-            marginTop       ?: number | string,
-            marginLeft      ?: number | string,
-            marginRight     ?: number | string,
-            marginBottom    ?: number | string,
-            margin          ?: number | string,
-            marginHAlignment?: number,
-            marginVAlignment?: number,
-            marginAlignment ?: string,
-            paddingTop      ?: number | string,
-            paddingLeft     ?: number | string,
-            paddingRight    ?: number | string,
-            paddingBottom   ?: number | string,
-            padding         ?: string,
+            id               ?: string,
+            parent           ?: UIElement,
+            templateName     ?: string,
+            styleName        ?: string,
+            content          ?: any,
+            marginTop        ?: number | string,
+            marginLeft       ?: number | string,
+            marginRight      ?: number | string,
+            marginBottom     ?: number | string,
+            margin           ?: number | string,
+            marginHAlignment ?: number,
+            marginVAlignment ?: number,
+            marginAlignment  ?: string,
+            paddingTop       ?: number | string,
+            paddingLeft      ?: number | string,
+            paddingRight     ?: number | string,
+            paddingBottom    ?: number | string,
+            padding          ?: string,
+            paddingHAlignment?: number,
+            paddingVAlignment?: number,
+            paddingAlignment ?: string,
         }) {
             if (!settings) {
                 settings = {};
@@ -37,28 +43,33 @@
 
             super(settings);
 
-            // For a button the default contentAlignemnt is center/center
-            if (settings.contentAlignment == null) {
-                this.contentAlignment.horizontal = PrimitiveAlignment.AlignCenter;
-                this.contentAlignment.vertical = PrimitiveAlignment.AlignCenter;
+            if (settings.paddingAlignment == null) {
+                this.paddingAlignment.horizontal = PrimitiveAlignment.AlignCenter;
+                this.paddingAlignment.vertical = PrimitiveAlignment.AlignCenter;
             }
-            this.normalEnabledBackground    = Canvas2D.GetSolidColorBrushFromHex("#337AB7FF");
-            this.normalDisabledBackground   = Canvas2D.GetSolidColorBrushFromHex("#7BA9D0FF");
-            this.normalMouseOverBackground  = Canvas2D.GetSolidColorBrushFromHex("#286090FF");
-            this.normalPushedBackground     = Canvas2D.GetSolidColorBrushFromHex("#1E496EFF");
-            this.normalEnabledBorder        = Canvas2D.GetSolidColorBrushFromHex("#2E6DA4FF");
-            this.normalDisabledBorder       = Canvas2D.GetSolidColorBrushFromHex("#77A0C4FF");
-            this.normalMouseOverBorder      = Canvas2D.GetSolidColorBrushFromHex("#204D74FF");
-            this.normalPushedBorder         = Canvas2D.GetSolidColorBrushFromHex("#2E5D9EFF");
-
-            this.defaultEnabledBackground   = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
-            this.defaultDisabledBackground  = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
-            this.defaultMouseOverBackground = Canvas2D.GetSolidColorBrushFromHex("#E6E6E6FF");
-            this.defaultPushedBackground    = Canvas2D.GetSolidColorBrushFromHex("#D4D4D4FF");
-            this.defaultEnabledBorder       = Canvas2D.GetSolidColorBrushFromHex("#CCCCCCFF");
-            this.defaultDisabledBorder      = Canvas2D.GetSolidColorBrushFromHex("#DEDEDEFF");
-            this.defaultMouseOverBorder     = Canvas2D.GetSolidColorBrushFromHex("#ADADADFF");
-            this.defaultPushedBorder        = Canvas2D.GetSolidColorBrushFromHex("#6C8EC5FF");
+
+            this._normalStateBackground  = new ObservableStringDictionary<IBrush2D>(false);
+            this._normalStateBorder      = new ObservableStringDictionary<IBrush2D>(false);
+            this._defaultStateBackground = new ObservableStringDictionary<IBrush2D>(false);
+            this._defaultStateBorder     = new ObservableStringDictionary<IBrush2D>(false);
+
+            this._normalStateBackground.add(UIElement.enabledState   , Canvas2D.GetSolidColorBrushFromHex("#337AB7FF"));
+            this._normalStateBackground.add(UIElement.disabledState  , Canvas2D.GetSolidColorBrushFromHex("#7BA9D0FF"));
+            this._normalStateBackground.add(UIElement.mouseOverState , Canvas2D.GetSolidColorBrushFromHex("#286090FF"));
+            this._normalStateBackground.add(Button.pushedState       , Canvas2D.GetSolidColorBrushFromHex("#1E496EFF"));
+            this._normalStateBorder.add(UIElement.enabledState       , Canvas2D.GetSolidColorBrushFromHex("#2E6DA4FF"));
+            this._normalStateBorder.add(UIElement.disabledState      , Canvas2D.GetSolidColorBrushFromHex("#77A0C4FF"));
+            this._normalStateBorder.add(UIElement.mouseOverState     , Canvas2D.GetSolidColorBrushFromHex("#204D74FF"));
+            this._normalStateBorder.add(Button.pushedState           , Canvas2D.GetSolidColorBrushFromHex("#2E5D9EFF"));
+
+            this._defaultStateBackground.add(UIElement.enabledState   , Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"));
+            this._defaultStateBackground.add(UIElement.disabledState  , Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"));
+            this._defaultStateBackground.add(UIElement.mouseOverState , Canvas2D.GetSolidColorBrushFromHex("#E6E6E6FF"));
+            this._defaultStateBackground.add(Button.pushedState       , Canvas2D.GetSolidColorBrushFromHex("#D4D4D4FF"));
+            this._defaultStateBorder.add(UIElement.enabledState       , Canvas2D.GetSolidColorBrushFromHex("#CCCCCCFF"));
+            this._defaultStateBorder.add(UIElement.disabledState      , Canvas2D.GetSolidColorBrushFromHex("#DEDEDEFF"));
+            this._defaultStateBorder.add(UIElement.mouseOverState     , Canvas2D.GetSolidColorBrushFromHex("#ADADADFF"));
+            this._defaultStateBorder.add(Button.pushedState           , Canvas2D.GetSolidColorBrushFromHex("#6C8EC5FF"));
         }
 
         @dependencyProperty(ContentControl.CONTROL_PROPCOUNT + 0, pi => Button.isPushedProperty = pi)
@@ -96,13 +107,20 @@
         }
 
         public _raiseClick() {
-            console.log("click");
+            if (this._clickObservable && this._clickObservable.hasObservers()) {
+                this._clickObservable.notifyObservers(this);
+            }
         }
 
         protected createVisualTree() {
             super.createVisualTree();
             let p = this._visualPlaceholder;
             p.pointerEventObservable.add((e, s) => {
+                // check if input must be discarded
+                if (!this.isVisible || !this.isEnabled) {
+                    return;
+                }
+
                 // We reject an event coming from the placeholder because it means it's on an empty spot, so it's not valid.
                 if (e.relatedTarget === this._visualPlaceholder) {
                     return;
@@ -113,37 +131,38 @@
                     this.isPushed = false;
                 } else if (s.mask === PrimitivePointerInfo.PointerDown) {
                     this.isPushed = true;
+                    this.isFocused = true;
                 }
 
             }, PrimitivePointerInfo.PointerUp|PrimitivePointerInfo.PointerDown);
         }
 
+        public get normalStateBackground(): ObservableStringDictionary<IBrush2D> {
+            return this._normalStateBackground;
+        }
+
+        public get defaultStateBackground(): ObservableStringDictionary<IBrush2D> {
+            return this._defaultStateBackground;
+        }
+
+        public get normalStateBorder(): ObservableStringDictionary<IBrush2D> {
+            return this._normalStateBorder;
+        }
+
+        public get defaultStateBorder(): ObservableStringDictionary<IBrush2D> {
+            return this._defaultStateBorder;
+        }
+
+        private _normalStateBackground: ObservableStringDictionary<IBrush2D>;
+        private _normalStateBorder: ObservableStringDictionary<IBrush2D>;
+        private _defaultStateBackground: ObservableStringDictionary<IBrush2D>;
+        private _defaultStateBorder: ObservableStringDictionary<IBrush2D>;
         private _isPushed: boolean;
         private _isDefault: boolean;
         private _isOutline: boolean;
         private _clickObservable: Observable<Button>;
 
-        protected get _position(): Vector2 {
-            return Vector2.Zero();
-        }
-
-        public normalEnabledBackground  : IBrush2D;
-        public normalDisabledBackground : IBrush2D;
-        public normalMouseOverBackground: IBrush2D;
-        public normalPushedBackground   : IBrush2D;
-        public normalEnabledBorder      : IBrush2D;
-        public normalDisabledBorder     : IBrush2D;
-        public normalMouseOverBorder    : IBrush2D;
-        public normalPushedBorder       : IBrush2D;
-
-        public defaultEnabledBackground  : IBrush2D;
-        public defaultDisabledBackground : IBrush2D;
-        public defaultMouseOverBackground: IBrush2D;
-        public defaultPushedBackground   : IBrush2D;
-        public defaultEnabledBorder      : IBrush2D;
-        public defaultDisabledBorder     : IBrush2D;
-        public defaultMouseOverBorder    : IBrush2D;
-        public defaultPushedBorder       : IBrush2D;
+        private static _pushedState = "Pushed";
     }
 
     @registerWindowRenderingTemplate("BABYLON.Button", "Default", () => new DefaultButtonRenderingTemplate())
@@ -166,37 +185,48 @@
                 Button.isDefaultProperty.flagId      |
                 Button.isOutlineProperty.flagId      |
                 Button.isPushedProperty.flagId);
+
+            // Register for brush change and update the Visual
+            let button = <Button>owner;
+            button.normalStateBackground.dictionaryChanged.add ((e, c) => this.stateChange());
+            button.normalStateBorder.dictionaryChanged.add     ((e, c) => this.stateChange());
+            button.defaultStateBackground.dictionaryChanged.add((e, c) => this.stateChange());
+            button.defaultStateBorder.dictionaryChanged.add    ((e, c) => this.stateChange());
         }
 
         stateChange(): void {
+            //console.log("state changed");
             let b = <Button>this.owner;
-            let bg = b.isDefault ? b.defaultEnabledBackground : b.normalEnabledBackground;
-            let bd = b.isDefault ? b.defaultEnabledBorder : b.normalEnabledBorder;
+            let state = UIElement.enabledState;
+            let bg = b.isDefault ? b.defaultStateBackground.get(state) : b.normalStateBackground.get(state);
+            let bd = b.isDefault ? b.defaultStateBorder.get(state) : b.normalStateBorder.get(state);
 
             if (b.isPushed) {
+                state = Button.pushedState;
                 if (b.isDefault) {
-                    bg = b.defaultPushedBackground;
-                    bd = b.defaultPushedBorder;
+                    bg = b.defaultStateBackground.get(state);
+                    bd = b.defaultStateBorder.get(state);
                 } else {
-                    bg = b.normalPushedBackground;
-                    bd = b.normalPushedBorder;
+                    bg = b.normalStateBackground.get(state);
+                    bd = b.normalStateBorder.get(state);
                 }
             } else if (b.isMouseOver) {
-                console.log("MouseOver Style");
+                state = UIElement.mouseOverState;
                 if (b.isDefault) {
-                    bg = b.defaultMouseOverBackground;
-                    bd = b.defaultMouseOverBorder;
+                    bg = b.defaultStateBackground.get(state);
+                    bd = b.defaultStateBorder.get(state);
                 } else {
-                    bg = b.normalMouseOverBackground;
-                    bd = b.normalMouseOverBorder;
+                    bg = b.normalStateBackground.get(state);
+                    bd = b.normalStateBorder.get(state);
                 }
             } else if (!b.isEnabled) {
+                state = UIElement.disabledState;
                 if (b.isDefault) {
-                    bg = b.defaultDisabledBackground;
-                    bd = b.defaultDisabledBorder;
+                    bg = b.defaultStateBackground.get(state);
+                    bd = b.defaultStateBorder.get(state);
                 } else {
-                    bg = b.normalDisabledBackground;
-                    bd = b.normalDisabledBorder;
+                    bg = b.normalStateBackground.get(state);
+                    bd = b.normalStateBorder.get(state);
                 }
             }
 

+ 128 - 0
canvas2D/src/GUI/babylon.gui.contentControl.ts

@@ -0,0 +1,128 @@
+module BABYLON {
+
+
+    @className("ContentControl", "BABYLON")
+    export abstract class ContentControl extends Control {
+        static CONTENTCONTROL_PROPCOUNT = Control.CONTROL_PROPCOUNT + 2;
+
+        static contentProperty: Prim2DPropInfo;
+
+        constructor(settings?: {
+            id              ?: string,
+            templateName    ?: string,
+            styleName       ?: string,
+            content         ?: any,
+        }) {
+            if (!settings) {
+                settings = {};
+            }
+
+            super(settings);
+
+            if (settings.content!=null) {
+                this._content = settings.content;
+            }
+        }
+
+        dispose(): boolean {
+            if (this.isDisposed) {
+                return false;
+            }
+
+            if (this.content && this.content.dispose) {
+                this.content.dispose();
+                this.content = null;
+            }
+
+            if (this.__contentUIElement) {
+                this.__contentUIElement.dispose();
+                this.__contentUIElement = null;
+            }
+
+            super.dispose();
+
+            return true;
+        }
+
+        @dependencyProperty(Control.CONTROL_PROPCOUNT + 0, pi => ContentControl.contentProperty = pi)
+        public get content(): any {
+            return this._content;
+        }
+
+        public set content(value: any) {
+            this._content = value;
+        }
+
+        protected get _contentUIElement(): UIElement {
+            if (!this.__contentUIElement) {
+                this._buildContentUIElement();
+            }
+
+            return this.__contentUIElement;
+        }
+
+        public _createVisualTree() {
+            // Base implementation will create the Group2D for the Visual Placeholder and its Visual Tree
+            super._createVisualTree();
+
+            // A Content Control has a Group2D, child of the visualPlaceHolder, which is the Content placeholder.
+            // The Content UIElement Tree will be create inside this placeholder.
+            this._contentPlaceholder = new Group2D({ parent: this._visualPlaceholder, id: `ContentControl Content Placeholder of ${this.id}` });
+            let p = this._contentPlaceholder;
+
+            // The UIElement padding properties (padding and paddingAlignment) are bound to the Group2D Content placeholder, we bound to the Margin properties as the Group2D acts as an inner element already, so margin of inner is padding.
+            p.dataSource = this;
+            p.createSimpleDataBinding(Prim2DBase.marginProperty, "padding", DataBinding.MODE_ONEWAY);
+            p.createSimpleDataBinding(Prim2DBase.marginAlignmentProperty, "paddingAlignment", DataBinding.MODE_ONEWAY);
+
+            // The UIElement set the childrenPlaceholder with the visual returned by the renderingTemplate.
+            // But it's not the case for a ContentControl, the placeholder of UIElement Children (the content)
+            this._visualChildrenPlaceholder = this._contentPlaceholder;
+        }
+
+        private _buildContentUIElement() {
+            let c = this._content;
+            this.__contentUIElement = null;
+
+            // Already a UIElement
+            if (c instanceof UIElement) {
+                this.__contentUIElement = c;
+            }
+
+            // Test primary types
+            else if ((typeof c === "string") || (typeof c === "boolean") || (typeof c === "number")) {
+                let l = new Label({ parent: this, id: "Content of " + this.id });
+                let binding = new DataBinding();
+                binding.propertyPathName = "content";
+                binding.stringFormat = v => `${v}`;
+                binding.dataSource = this;
+                l.createDataBinding(Label.textProperty, binding);
+
+                this.__contentUIElement = l;
+            }
+
+            // Data Template!
+            else {
+                // TODO: DataTemplate lookup and create instance
+            }
+
+            if (this.__contentUIElement) {
+                this.__contentUIElement._patchUIElement(this.ownerWindow, this);               
+            }
+        }
+
+        private _contentPlaceholder: Group2D;
+        private _content: any;
+        private __contentUIElement: UIElement;
+
+        protected _getChildren(): Array<UIElement> {
+            let children = new Array<UIElement>();
+
+            if (this.content) {
+                children.push(this._contentUIElement);
+            }
+
+            return children;
+        }
+    }
+}

+ 0 - 137
canvas2D/src/GUI/babylon.gui.control.ts

@@ -72,141 +72,4 @@
         private _fontName: string;
         private _foreground: IBrush2D;
     }
-
-
-    @className("ContentControl", "BABYLON")
-    export abstract class ContentControl extends Control {
-        static CONTENTCONTROL_PROPCOUNT = Control.CONTROL_PROPCOUNT + 2;
-
-        static contentProperty: Prim2DPropInfo;
-        static contentAlignmentProperty: Prim2DPropInfo;
-
-        constructor(settings?: {
-            id              ?: string,
-            templateName    ?: string,
-            styleName       ?: string,
-            content         ?: any,
-            contentAlignment?: string,
-        }) {
-            if (!settings) {
-                settings = {};
-            }
-
-            super(settings);
-
-            if (settings.content!=null) {
-                this._content = settings.content;
-            }
-
-            if (settings.contentAlignment != null) {
-                this.contentAlignment.fromString(settings.contentAlignment);
-            }
-        }
-
-        dispose(): boolean {
-            if (this.isDisposed) {
-                return false;
-            }
-
-            if (this.content && this.content.dispose) {
-                this.content.dispose();
-                this.content = null;
-            }
-
-            if (this.__contentUIElement) {
-                this.__contentUIElement.dispose();
-                this.__contentUIElement = null;
-            }
-
-            super.dispose();
-
-            return true;
-        }
-
-        @dependencyProperty(Control.CONTROL_PROPCOUNT + 0, pi => ContentControl.contentProperty = pi)
-        public get content(): any {
-            return this._content;
-        }
-
-        public set content(value: any) {
-            this._content = value;
-        }
-
-        @dependencyProperty(Control.CONTROL_PROPCOUNT + 1, pi => ContentControl.contentAlignmentProperty = pi)
-        public get contentAlignment(): PrimitiveAlignment {
-            if (!this._contentAlignment) {
-                this._contentAlignment = new PrimitiveAlignment();
-            }
-            return this._contentAlignment;
-        }
-
-        public set contentAlignment(value: PrimitiveAlignment) {
-            this.contentAlignment.copyFrom(value);
-        }
-
-        /**
-         * Check if there a contentAlignment specified (non null and not default)
-         */
-        public get _hasContentAlignment(): boolean {
-            return (this._contentAlignment !== null && !this._contentAlignment.isDefault);
-        }
-
-        protected get _contentUIElement(): UIElement {
-            if (!this.__contentUIElement) {
-                this._buildContentUIElement();
-            }
-
-            return this.__contentUIElement;
-        }
-
-        private _buildContentUIElement() {
-            let c = this._content;
-            this.__contentUIElement = null;
-
-            // Already a UIElement
-            if (c instanceof UIElement) {
-                this.__contentUIElement = c;
-            }
-
-            // Test primary types
-            else if ((typeof c === "string") || (typeof c === "boolean") || (typeof c === "number")) {
-                let l = new Label({ parent: this, id: "Content of " + this.id });
-                let binding = new DataBinding();
-                binding.propertyPathName = "content";
-                binding.stringFormat = v => `${v}`;
-                binding.dataSource = this;
-                l.createDataBinding(Label.textProperty, binding);
-
-                binding = new DataBinding();
-                binding.propertyPathName = "contentAlignment";
-                binding.dataSource = this;
-                l.createDataBinding(Label.marginAlignmentProperty, binding);
-
-                this.__contentUIElement = l;
-            }
-
-            // Data Template!
-            else {
-                // TODO: DataTemplate lookup and create instance
-            }
-
-            if (this.__contentUIElement) {
-                this.__contentUIElement._patchUIElement(this.ownerWindows, this);               
-            }
-        }
-
-        private _content: any;
-        private _contentAlignment: PrimitiveAlignment;
-        private __contentUIElement: UIElement;
-
-        protected _getChildren(): Array<UIElement> {
-            let children = new Array<UIElement>();
-
-            if (this.content) {
-                children.push(this._contentUIElement);
-            }
-
-            return children;
-        }
-    }
 }

+ 110 - 41
canvas2D/src/GUI/babylon.gui.window.ts

@@ -1,42 +1,98 @@
 module BABYLON {
+
+    class FocusScopeData {
+        constructor(focusScope: UIElement) {
+            this.focusScope = focusScope;
+            this.focusedElement = null;
+        }
+
+        focusScope: UIElement;
+        focusedElement: UIElement;
+    }
+
+    export class FocusManager {
+        constructor() {
+            this._focusScopes = new StringDictionary<FocusScopeData>();
+            this._rootScope = new FocusScopeData(null);
+            this._activeScope = null;
+        }
+
+        public setFocusOn(el: UIElement, focusScope: UIElement) {
+            let fsd = (focusScope != null) ? this._focusScopes.getOrAddWithFactory(focusScope.uid, k => new FocusScopeData(focusScope)) : this._rootScope;
+
+            if (fsd.focusedElement !== el) {
+                // Remove focus from current
+                if (fsd.focusedElement) {
+                    fsd.focusedElement.isFocused = false;
+                }
+
+                fsd.focusedElement = el;
+            }
+
+            if (this._activeScope !== fsd) {
+                this._activeScope = fsd;
+            }
+
+        }
+
+        private _rootScope: FocusScopeData;
+        private _focusScopes: StringDictionary<FocusScopeData>;
+        private _activeScope: FocusScopeData;
+    }
+
+    class GUISceneData {
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this.screenSpaceCanvas = new ScreenSpaceCanvas2D(scene, { id: "GUI Canvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE });
+            this.focusManager = new FocusManager();
+        }
+
+        screenSpaceCanvas: ScreenSpaceCanvas2D;
+        scene: Scene;
+        focusManager: FocusManager;
+    }
+
     @className("Window", "BABYLON")
     export class Window extends ContentControl {
-        static WINDOW_PROPCOUNT = ContentControl.CONTENTCONTROL_PROPCOUNT + 2;
+        static WINDOW_PROPCOUNT = ContentControl.CONTENTCONTROL_PROPCOUNT + 4;
 
         static leftProperty: Prim2DPropInfo;
         static bottomProperty: Prim2DPropInfo;
         static positionProperty: Prim2DPropInfo;
+        static isActiveProperty: Prim2DPropInfo;
 
         constructor(scene: Scene, settings?: {
 
-            id              ?: string,
-            templateName    ?: string,
-            styleName       ?: string,
-            content         ?: any,
-            contentAlignment?: string,
-            left            ?: number,
-            bottom          ?: number,
-            minWidth        ?: number,
-            minHeight       ?: number,
-            maxWidth        ?: number,
-            maxHeight       ?: number,
-            width           ?: number,
-            height          ?: number,
-            worldPosition   ?: Vector3,
-            worldRotation   ?: Quaternion,
-            marginTop       ?: number | string,
-            marginLeft      ?: number | string,
-            marginRight     ?: number | string,
-            marginBottom    ?: number | string,
-            margin          ?: number | string,
-            marginHAlignment?: number,
-            marginVAlignment?: number,
-            marginAlignment ?: string,
-            paddingTop      ?: number | string,
-            paddingLeft     ?: number | string,
-            paddingRight    ?: number | string,
-            paddingBottom   ?: number | string,
-            padding         ?: string,
+            id               ?: string,
+            templateName     ?: string,
+            styleName        ?: string,
+            content          ?: any,
+            left             ?: number,
+            bottom           ?: number,
+            minWidth         ?: number,
+            minHeight        ?: number,
+            maxWidth         ?: number,
+            maxHeight        ?: number,
+            width            ?: number,
+            height           ?: number,
+            worldPosition    ?: Vector3,
+            worldRotation    ?: Quaternion,
+            marginTop        ?: number | string,
+            marginLeft       ?: number | string,
+            marginRight      ?: number | string,
+            marginBottom     ?: number | string,
+            margin           ?: number | string,
+            marginHAlignment ?: number,
+            marginVAlignment ?: number,
+            marginAlignment  ?: string,
+            paddingTop       ?: number | string,
+            paddingLeft      ?: number | string,
+            paddingRight     ?: number | string,
+            paddingBottom    ?: number | string,
+            padding          ?: string,
+            paddingHAlignment?: number,
+            paddingVAlignment?: number,
+            paddingAlignment ?: string,
         }) {
 
             if (!settings) {
@@ -45,6 +101,11 @@
 
             super(settings);
 
+            // Per default a Window is focus scope
+            this.isFocusScope = true;
+
+            this.isActive = false;
+
             if (!this._UIElementVisualToBuildList) {
                 this._UIElementVisualToBuildList = new Array<UIElement>();
             }
@@ -54,7 +115,8 @@
 
             // Screen Space UI
             if (!settings.worldPosition && !settings.worldRotation) {
-                this._canvas = Window.getScreenCanvas(scene);
+                this._sceneData = Window.getSceneData(scene);
+                this._canvas = this._sceneData.screenSpaceCanvas;
                 this._isWorldSpaceCanvas = false;
                 this._left = (settings.left != null) ? settings.left : 0;
                 this._bottom = (settings.bottom != null) ? settings.bottom : 0;
@@ -116,6 +178,19 @@
             this._bottom = value.y;
         }
 
+        @dependencyProperty(ContentControl.CONTENTCONTROL_PROPCOUNT + 3, pi => Window.isActiveProperty = pi)
+        public get isActive(): boolean {
+            return this._isActive;
+        }
+
+        public set isActive(value: boolean) {
+            this._isActive = value;
+        }
+
+        public get focusManager(): FocusManager {
+            return this._sceneData.focusManager;
+        }
+
         protected get _position(): Vector2 {
             return new Vector2(this.left, this.bottom);
         }
@@ -190,28 +265,22 @@
             this._canvas.renderObservable.remove(this._renderObserver);
         }
 
+        private _sceneData: GUISceneData;
         private _canvas: Canvas2D;
         private _left: number;
         private _bottom: number;
+        private _isActive: boolean;
         private _isWorldSpaceCanvas: boolean;
         private _renderObserver: Observer<Canvas2D>;
         private _disposeObserver: Observer<SmartPropertyBase>;
         private _UIElementVisualToBuildList: Array<UIElement>;
         private _mouseOverUIElement: UIElement;
 
-        private static getScreenCanvas(scene: Scene): ScreenSpaceCanvas2D {
-            let canvas = Tools.first(Window._screenCanvasList, c => c.scene === scene);
-            if (canvas) {
-                return canvas;
-            }
-
-            canvas = new ScreenSpaceCanvas2D(scene, { id: "GUI Canvas", cachingStrategy: Canvas2D.CACHESTRATEGY_DONTCACHE });
-            Window._screenCanvasList.push(canvas);
-
-            return canvas;
+        private static getSceneData(scene: Scene): GUISceneData {
+            return Window._sceneData.getOrAddWithFactory(scene.uid, k => new GUISceneData(scene));
         }
 
-        private static _screenCanvasList: Array<ScreenSpaceCanvas2D> = new Array<ScreenSpaceCanvas2D>();
+        private static _sceneData: StringDictionary<GUISceneData> = new StringDictionary<GUISceneData>();
     }
 
     @registerWindowRenderingTemplate("BABYLON.Window", "Default", () => new DefaultWindowRenderingTemplate ())

+ 3 - 1
canvas2D/src/Tools/babylon.observableStringDictionary.ts

@@ -162,7 +162,9 @@
 
         private _removeWatchedElement(key: string, el: T) {
             let observer = this._watchedObjectList.getAndRemove(key);
-            (<IPropertyChanged><any>el).propertyChanged.remove(observer);
+            if (el["propertyChanged"]) {
+                (<IPropertyChanged><any>el).propertyChanged.remove(observer);
+            }
         }
 
         public set(key: string, value: T): boolean {

+ 19 - 1
canvas2D/src/shaders/text2d.fragment.fx

@@ -1,10 +1,28 @@
-varying vec4 vColor;
+//#extension GL_OES_standard_derivatives : enable
+
+varying vec4 vColor;
 varying vec2 vUV;
 
 // Samplers
 uniform sampler2D diffuseSampler;
 
 void main(void) {
+#ifdef SignedDistanceField
+	float dist = texture2D(diffuseSampler, vUV).r;
+	if (dist < 0.5) {
+		discard;
+	}
+
+	// Another way using derivative, commented right now because I don't know if it worth doing it
+	//float edgeDistance = 0.5;
+	//float edgeWidth = 0.7 * length(vec2(dFdx(dist), dFdy(dist)));
+	//float opacity = dist * smoothstep(edgeDistance - edgeWidth, edgeDistance + edgeWidth, dist);
+
+	//float opacity = smoothstep(0.25, 0.75, dist);
+	gl_FragColor = vec4(vColor.xyz*dist, 1.0);
+#else
 	vec4 color = texture2D(diffuseSampler, vUV);
 	gl_FragColor = color*vColor;
+#endif
+
 }

+ 38 - 0
canvas2D/tools/canvas2dinspector/README.md

@@ -0,0 +1,38 @@
+# Canvas2D Inspector
+
+![](https://raw.githubusercontent.com/Temechon/canvas2d-inspector/master/screen.jpg)
+
+[The main repo can be found here](https://github.com/Temechon/canvas2d-inspector)
+
+An extension to easily debug your Canvas2D, made with HTML/CSS.
+
+## Usage
+Insert the lib in your project: 
+```
+<script src="c2dinspector.js"></script>
+```
+
+In your code, create a new Inspector by giving it the `BABYLON.Engine`:
+
+```
+new INSPECTOR.Canvas2DInspector(engine); 
+```
+
+A right panel will be created by listing all instances of Canvas2D created in your 
+application.
+
+## Contribute
+
+```
+npm install
+grunt
+```
+
+## Create the lib from source
+
+```
+grunt dist
+```
+The library will be in the `dist` folder.
+
+

Diferenças do arquivo suprimidas por serem muito extensas
+ 153 - 70
dist/preview release/babylon.canvas2d.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 10 - 9
dist/preview release/babylon.canvas2d.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 722 - 282
dist/preview release/babylon.canvas2d.max.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 26 - 26
dist/preview release/babylon.core.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 4270 - 4211
dist/preview release/babylon.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 36 - 36
dist/preview release/babylon.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 729 - 204
dist/preview release/babylon.max.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 36 - 36
dist/preview release/babylon.noworker.js


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

@@ -8,6 +8,10 @@
 - Canvas2D moved to a separate folder in main repo. Now you need to also include babylon.cavans2d.js to get Canvas@D feature ([deltakosh](https://github.com/deltakosh))
 
 ### Updates
+- Added Bone.getAbsolutePosition and Bone.getAbsolutePositionToRef ([abow](https://github.com/abow))
+- Added Bone.setYawPitchRoll ([abow](https://github.com/abow))
+- Added Bone.rotate ([abow](https://github.com/abow))
+- Added Bone.scale ([abow](https://github.com/abow))
 - Added Node.getDirection ([abow](https://github.com/abow))
 - New ```Tools.CreateScreenshot``` function will capture all canvas data. Previous implementation is now called `CreateScreenshotUsingRenderTarget` ([deltakosh](https://github.com/deltakosh)) 
 - Cube textures are now cached by texture cache ([deltakosh](https://github.com/deltakosh)) 

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
materialsLibrary/dist/babylon.gridMaterial.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
materialsLibrary/dist/babylon.gridMaterial.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 4 - 2
materialsLibrary/dist/babylon.skyMaterial.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
materialsLibrary/dist/babylon.skyMaterial.min.js


+ 1 - 1
materialsLibrary/materials/grid/grid.fragment.fx

@@ -91,7 +91,7 @@ void main(void) {
 #endif
 
 #ifdef TRANSPARENT
-    float opacity = clamp(grid, 0.08, color.w);
+    float opacity = clamp(grid, 0.08, gridControl.w);
     gl_FragColor = vec4(color.rgb, opacity);
 #else
     // Apply the color.

+ 4 - 1
materialsLibrary/materials/sky/babylon.skyMaterial.ts

@@ -223,7 +223,10 @@ module BABYLON {
                 this._effect.setVector3("cameraPosition", this._cameraPosition);
             }
             
-            this._effect.setFloat("luminance", this.luminance);
+            if (this.luminance > 0) {
+                this._effect.setFloat("luminance", this.luminance);
+            }
+            
 			this._effect.setFloat("turbidity", this.turbidity);
 			this._effect.setFloat("rayleigh", this.rayleigh);
 			this._effect.setFloat("mieCoefficient", this.mieCoefficient);

+ 8 - 9
materialsLibrary/materials/sky/sky.fragment.fx

@@ -136,11 +136,13 @@ void main(void) {
 	float ExposureBias = fLumCompressed;
 
 	vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);
-	vec3 skyColor = curr * whiteScale;
 
-	vec3 retColor = pow(skyColor,vec3(1.0/(1.2+(1.2*sunfade))));
-	
-	vec4 baseColor = vec4(retColor, 1.0);
+	// May generate a bug so just just keep retColor = skyColor;
+	// vec3 skyColor = curr * whiteScale;
+	//vec3 retColor = pow(skyColor,vec3(1.0/(1.2+(1.2*sunfade))));
+
+	vec3 retColor = curr * whiteScale;
+
 	/**
 	*--------------------------------------------------------------------------------------------------
 	* Sky Color
@@ -151,18 +153,15 @@ void main(void) {
 	float alpha = 1.0;
 
 #ifdef VERTEXCOLOR
-	baseColor.rgb *= vColor.rgb;
+	retColor.rgb *= vColor.rgb;
 #endif
 
-	// Lighting
-	vec3 diffuseBase = vec3(1.0, 1.0, 1.0);
-
 #ifdef VERTEXALPHA
 	alpha *= vColor.a;
 #endif
 
 	// Composition
-	vec4 color = vec4(baseColor.rgb, alpha);
+	vec4 color = clamp(vec4(retColor.rgb, alpha), 0.0, 1.0);
 
     // Fog
 #include<fogFragment>

+ 1 - 0
src/Actions/babylon.actionManager.js

@@ -60,6 +60,7 @@ var BABYLON;
         function ActionManager(scene) {
             // Members
             this.actions = new Array();
+            this.hoverCursor = '';
             this._scene = scene;
             scene._actionManagers.push(this);
         }

+ 2 - 0
src/Actions/babylon.actionManager.ts

@@ -144,6 +144,8 @@
         // Members
         public actions = new Array<Action>();
 
+        public hoverCursor: string = '';
+
         private _scene: Scene;
 
         constructor(scene: Scene) {

+ 7 - 0
src/Animations/babylon.animatable.js

@@ -64,6 +64,13 @@ var BABYLON;
         };
         Animatable.prototype.goToFrame = function (frame) {
             var animations = this._animations;
+            if (animations[0]) {
+                var fps = animations[0].framePerSecond;
+                var currentFrame = animations[0].currentFrame;
+                var adjustTime = frame - currentFrame;
+                var delay = adjustTime * 1000 / fps;
+                this._localDelayOffset -= delay;
+            }
             for (var index = 0; index < animations.length; index++) {
                 animations[index].goToFrame(frame);
             }

+ 8 - 0
src/Animations/babylon.animatable.ts

@@ -74,6 +74,14 @@
         public goToFrame(frame: number): void {
             var animations = this._animations;
 
+            if (animations[0]) {
+                var fps = animations[0].framePerSecond;
+                var currentFrame = animations[0].currentFrame;
+                var adjustTime = frame - currentFrame;
+                var delay = adjustTime * 1000 / fps;
+                this._localDelayOffset -= delay;
+            }
+
             for (var index = 0; index < animations.length; index++) {
                 animations[index].goToFrame(frame);
             }

+ 256 - 0
src/Bones/babylon.bone.js

@@ -15,6 +15,26 @@ var BABYLON;
             this._worldTransform = new BABYLON.Matrix();
             this._absoluteTransform = new BABYLON.Matrix();
             this._invertedAbsoluteTransform = new BABYLON.Matrix();
+            this._scaleMatrix = BABYLON.Matrix.Identity();
+            this._scaleVector = new BABYLON.Vector3(1, 1, 1);
+            this._negateScaleChildren = new BABYLON.Vector3(1, 1, 1);
+            this._syncScaleVector = function () {
+                var lm = this.getLocalMatrix();
+                var xsq = (lm.m[0] * lm.m[0] + lm.m[1] * lm.m[1] + lm.m[2] * lm.m[2]);
+                var ysq = (lm.m[4] * lm.m[4] + lm.m[5] * lm.m[5] + lm.m[6] * lm.m[6]);
+                var zsq = (lm.m[8] * lm.m[8] + lm.m[9] * lm.m[9] + lm.m[10] * lm.m[10]);
+                var xs = lm.m[0] * lm.m[1] * lm.m[2] * lm.m[3] < 0 ? -1 : 1;
+                var ys = lm.m[4] * lm.m[5] * lm.m[6] * lm.m[7] < 0 ? -1 : 1;
+                var zs = lm.m[8] * lm.m[9] * lm.m[10] * lm.m[11] < 0 ? -1 : 1;
+                this._scaleVector.x = xs * Math.sqrt(xsq);
+                this._scaleVector.y = ys * Math.sqrt(ysq);
+                this._scaleVector.z = zs * Math.sqrt(zsq);
+                if (this._parent) {
+                    this._scaleVector.x /= this._parent._negateScaleChildren.x;
+                    this._scaleVector.y /= this._parent._negateScaleChildren.y;
+                    this._scaleVector.z /= this._parent._negateScaleChildren.z;
+                }
+            };
             this._skeleton = skeleton;
             this._matrix = matrix;
             this._baseMatrix = matrix;
@@ -138,6 +158,242 @@ var BABYLON;
             this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
             return true;
         };
+        Bone.prototype.translate = function (vec) {
+            var lm = this.getLocalMatrix();
+            lm.m[12] += vec.x;
+            lm.m[13] += vec.y;
+            lm.m[14] += vec.z;
+            this.markAsDirty();
+        };
+        Bone.prototype.setPosition = function (position) {
+            var lm = this.getLocalMatrix();
+            lm.m[12] = position.x;
+            lm.m[13] = position.y;
+            lm.m[14] = position.z;
+            this.markAsDirty();
+        };
+        Bone.prototype.setAbsolutePosition = function (position, mesh) {
+            if (mesh === void 0) { mesh = null; }
+            this._skeleton.computeAbsoluteTransforms();
+            var tmat = BABYLON.Tmp.Matrix[0];
+            var vec = BABYLON.Tmp.Vector3[0];
+            if (mesh) {
+                tmat.copyFrom(this._parent.getAbsoluteTransform());
+                tmat.multiplyToRef(mesh.getWorldMatrix(), tmat);
+            }
+            else {
+                tmat.copyFrom(this._parent.getAbsoluteTransform());
+            }
+            tmat.invert();
+            BABYLON.Vector3.TransformCoordinatesToRef(position, tmat, vec);
+            var lm = this.getLocalMatrix();
+            lm.m[12] = vec.x;
+            lm.m[13] = vec.y;
+            lm.m[14] = vec.z;
+            this.markAsDirty();
+        };
+        Bone.prototype.setScale = function (x, y, z, scaleChildren) {
+            if (scaleChildren === void 0) { scaleChildren = false; }
+            if (this.animations[0] && !this.animations[0].isStopped()) {
+                if (!scaleChildren) {
+                    this._negateScaleChildren.x = 1 / x;
+                    this._negateScaleChildren.y = 1 / y;
+                    this._negateScaleChildren.z = 1 / z;
+                }
+                this._syncScaleVector();
+            }
+            this.scale(x / this._scaleVector.x, y / this._scaleVector.y, z / this._scaleVector.z, scaleChildren);
+        };
+        Bone.prototype.scale = function (x, y, z, scaleChildren) {
+            if (scaleChildren === void 0) { scaleChildren = false; }
+            var locMat = this.getLocalMatrix();
+            var origLocMat = BABYLON.Tmp.Matrix[0];
+            origLocMat.copyFrom(locMat);
+            var origLocMatInv = BABYLON.Tmp.Matrix[1];
+            origLocMatInv.copyFrom(origLocMat);
+            origLocMatInv.invert();
+            var scaleMat = BABYLON.Tmp.Matrix[2];
+            BABYLON.Matrix.FromValuesToRef(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1, scaleMat);
+            this._scaleMatrix.multiplyToRef(scaleMat, this._scaleMatrix);
+            this._scaleVector.x *= x;
+            this._scaleVector.y *= y;
+            this._scaleVector.z *= z;
+            locMat.multiplyToRef(origLocMatInv, locMat);
+            locMat.multiplyToRef(scaleMat, locMat);
+            locMat.multiplyToRef(origLocMat, locMat);
+            var parent = this.getParent();
+            if (parent) {
+                locMat.multiplyToRef(parent.getAbsoluteTransform(), this.getAbsoluteTransform());
+            }
+            else {
+                this.getAbsoluteTransform().copyFrom(locMat);
+            }
+            var len = this.children.length;
+            scaleMat.invert();
+            for (var i = 0; i < len; i++) {
+                var child = this.children[i];
+                var cm = child.getLocalMatrix();
+                cm.multiplyToRef(scaleMat, cm);
+                var lm = child.getLocalMatrix();
+                lm.m[12] *= x;
+                lm.m[13] *= y;
+                lm.m[14] *= z;
+            }
+            this.computeAbsoluteTransforms();
+            if (scaleChildren) {
+                for (var i = 0; i < len; i++) {
+                    this.children[i].scale(x, y, z, scaleChildren);
+                }
+            }
+            this.markAsDirty();
+        };
+        Bone.prototype.setYawPitchRoll = function (yaw, pitch, roll, space, mesh) {
+            if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (mesh === void 0) { mesh = null; }
+            var rotMat = BABYLON.Tmp.Matrix[0];
+            BABYLON.Matrix.RotationYawPitchRollToRef(yaw, pitch, roll, rotMat);
+            var rotMatInv = BABYLON.Tmp.Matrix[1];
+            this._getNegativeRotationToRef(rotMatInv, space, mesh);
+            rotMatInv.multiplyToRef(rotMat, rotMat);
+            this._rotateWithMatrix(rotMat, space, mesh);
+        };
+        Bone.prototype.rotate = function (axis, amount, space, mesh) {
+            if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (mesh === void 0) { mesh = null; }
+            var rmat = BABYLON.Tmp.Matrix[0];
+            rmat.m[12] = 0;
+            rmat.m[13] = 0;
+            rmat.m[14] = 0;
+            BABYLON.Matrix.RotationAxisToRef(axis, amount, rmat);
+            this._rotateWithMatrix(rmat, space, mesh);
+        };
+        Bone.prototype.setAxisAngle = function (axis, angle, space, mesh) {
+            var rotMat = BABYLON.Tmp.Matrix[0];
+            BABYLON.Matrix.RotationAxisToRef(axis, angle, rotMat);
+            var rotMatInv = BABYLON.Tmp.Matrix[1];
+            this._getNegativeRotationToRef(rotMatInv, space, mesh);
+            rotMatInv.multiplyToRef(rotMat, rotMat);
+            this._rotateWithMatrix(rotMat, space, mesh);
+        };
+        Bone.prototype._rotateWithMatrix = function (rmat, space, mesh) {
+            if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (mesh === void 0) { mesh = null; }
+            var lmat = this.getLocalMatrix();
+            var lx = lmat.m[12];
+            var ly = lmat.m[13];
+            var lz = lmat.m[14];
+            var parent = this.getParent();
+            var parentScale = BABYLON.Tmp.Matrix[3];
+            var parentScaleInv = BABYLON.Tmp.Matrix[4];
+            if (parent) {
+                if (space == BABYLON.Space.WORLD) {
+                    if (mesh) {
+                        parentScale.copyFrom(mesh.getWorldMatrix());
+                        parent.getAbsoluteTransform().multiplyToRef(parentScale, parentScale);
+                    }
+                    else {
+                        parentScale.copyFrom(parent.getAbsoluteTransform());
+                    }
+                }
+                else {
+                    parentScale = parent._scaleMatrix;
+                }
+                parentScaleInv.copyFrom(parentScale);
+                parentScaleInv.invert();
+                lmat.multiplyToRef(parentScale, lmat);
+                lmat.multiplyToRef(rmat, lmat);
+                lmat.multiplyToRef(parentScaleInv, lmat);
+            }
+            else {
+                if (space == BABYLON.Space.WORLD && mesh) {
+                    parentScale.copyFrom(mesh.getWorldMatrix());
+                    parentScaleInv.copyFrom(parentScale);
+                    parentScaleInv.invert();
+                    lmat.multiplyToRef(parentScale, lmat);
+                    lmat.multiplyToRef(rmat, lmat);
+                    lmat.multiplyToRef(parentScaleInv, lmat);
+                }
+                else {
+                    lmat.multiplyToRef(rmat, lmat);
+                }
+            }
+            lmat.m[12] = lx;
+            lmat.m[13] = ly;
+            lmat.m[14] = lz;
+            this.computeAbsoluteTransforms();
+            this.markAsDirty();
+        };
+        Bone.prototype._getNegativeRotationToRef = function (rotMatInv, space, mesh) {
+            if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (mesh === void 0) { mesh = null; }
+            if (space == BABYLON.Space.WORLD) {
+                rotMatInv.copyFrom(this.getAbsoluteTransform());
+                if (mesh) {
+                    rotMatInv.multiplyToRef(mesh.getWorldMatrix(), rotMatInv);
+                }
+                rotMatInv.invert();
+                var scaleMatrix = BABYLON.Tmp.Matrix[2];
+                scaleMatrix.copyFrom(this._scaleMatrix);
+                scaleMatrix.m[0] *= -1;
+                rotMatInv.multiplyToRef(scaleMatrix, rotMatInv);
+            }
+            else {
+                rotMatInv.copyFrom(this.getLocalMatrix());
+                rotMatInv.invert();
+                var scaleMatrix = BABYLON.Tmp.Matrix[2];
+                scaleMatrix.copyFrom(this._scaleMatrix);
+                if (this._parent) {
+                    var pscaleMatrix = BABYLON.Tmp.Matrix[3];
+                    pscaleMatrix.copyFrom(this._parent._scaleMatrix);
+                    pscaleMatrix.invert();
+                    pscaleMatrix.multiplyToRef(rotMatInv, rotMatInv);
+                }
+                else {
+                    scaleMatrix.m[0] *= -1;
+                }
+                rotMatInv.multiplyToRef(scaleMatrix, rotMatInv);
+            }
+        };
+        Bone.prototype.getScale = function () {
+            return this._scaleVector.clone();
+        };
+        Bone.prototype.getScaleToRef = function (result) {
+            result.copyFrom(this._scaleVector);
+        };
+        Bone.prototype.getAbsolutePosition = function (mesh) {
+            if (mesh === void 0) { mesh = null; }
+            var pos = BABYLON.Vector3.Zero();
+            this.getAbsolutePositionToRef(mesh, pos);
+            return pos;
+        };
+        Bone.prototype.getAbsolutePositionToRef = function (mesh, result) {
+            if (mesh === void 0) { mesh = null; }
+            this._skeleton.computeAbsoluteTransforms();
+            var tmat = BABYLON.Tmp.Matrix[0];
+            if (mesh) {
+                tmat.copyFrom(this.getAbsoluteTransform());
+                tmat.multiplyToRef(mesh.getWorldMatrix(), tmat);
+            }
+            else {
+                tmat = this.getAbsoluteTransform();
+            }
+            result.x = tmat.m[12];
+            result.y = tmat.m[13];
+            result.z = tmat.m[14];
+        };
+        Bone.prototype.computeAbsoluteTransforms = function () {
+            if (this._parent) {
+                this._matrix.multiplyToRef(this._parent._absoluteTransform, this._absoluteTransform);
+            }
+            else {
+                this._absoluteTransform.copyFrom(this._matrix);
+            }
+            var children = this.children;
+            var len = children.length;
+            for (var i = 0; i < len; i++) {
+                children[i].computeAbsoluteTransforms();
+            }
+        };
         return Bone;
     }(BABYLON.Node));
     BABYLON.Bone = Bone;

+ 365 - 0
src/Bones/babylon.bone.ts

@@ -13,6 +13,10 @@
         private _invertedAbsoluteTransform = new Matrix();
         private _parent: Bone;
 
+        private _scaleMatrix = Matrix.Identity();
+        private _scaleVector = new Vector3(1, 1, 1);
+        private _negateScaleChildren = new Vector3(1, 1, 1);
+        
         constructor(public name: string, skeleton: Skeleton, parentBone: Bone, matrix: Matrix, restPose?: Matrix) {
             super(name, skeleton.getScene());
             this._skeleton = skeleton;
@@ -161,5 +165,366 @@
             this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
             return true;
         }
+
+        public translate (vec: Vector3): void {
+
+            var lm = this.getLocalMatrix();
+
+            lm.m[12] += vec.x;
+            lm.m[13] += vec.y;
+            lm.m[14] += vec.z;
+
+            this.markAsDirty();
+	        
+        }
+
+        public setPosition (position: Vector3): void {
+
+            var lm = this.getLocalMatrix();
+
+            lm.m[12] = position.x;
+            lm.m[13] = position.y;
+            lm.m[14] = position.z;
+
+            this.markAsDirty();
+	        
+        }
+
+        public setAbsolutePosition (position: Vector3, mesh: AbstractMesh = null): void {
+
+            this._skeleton.computeAbsoluteTransforms();
+
+            var tmat = Tmp.Matrix[0];
+            var vec = Tmp.Vector3[0];
+
+            if (mesh) {
+                tmat.copyFrom(this._parent.getAbsoluteTransform());
+                tmat.multiplyToRef(mesh.getWorldMatrix(), tmat);
+            }else {
+                tmat.copyFrom(this._parent.getAbsoluteTransform());
+            }
+
+            tmat.invert();
+			Vector3.TransformCoordinatesToRef(position, tmat, vec);
+
+			var lm = this.getLocalMatrix();
+            lm.m[12] = vec.x;
+            lm.m[13] = vec.y;
+            lm.m[14] = vec.z;
+            
+            this.markAsDirty();
+			
+	        
+        }
+
+        public setScale (x: number, y: number, z: number, scaleChildren = false): void {
+
+            if (this.animations[0] && !this.animations[0].isStopped()) {
+                if (!scaleChildren) {
+                    this._negateScaleChildren.x = 1/x;
+                    this._negateScaleChildren.y = 1/y;
+                    this._negateScaleChildren.z = 1/z;
+                }
+                this._syncScaleVector();
+            }
+
+	        this.scale(x / this._scaleVector.x, y / this._scaleVector.y, z / this._scaleVector.z, scaleChildren);
+
+        }
+
+        public scale (x: number, y: number, z: number, scaleChildren = false): void {
+	
+            var locMat = this.getLocalMatrix();
+            var origLocMat = Tmp.Matrix[0];
+            origLocMat.copyFrom(locMat);
+
+            var origLocMatInv = Tmp.Matrix[1];
+            origLocMatInv.copyFrom(origLocMat);
+            origLocMatInv.invert();
+
+            var scaleMat = Tmp.Matrix[2];
+            Matrix.FromValuesToRef(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1, scaleMat);
+            this._scaleMatrix.multiplyToRef(scaleMat, this._scaleMatrix);
+            this._scaleVector.x *= x;
+            this._scaleVector.y *= y;
+            this._scaleVector.z *= z;
+
+            locMat.multiplyToRef(origLocMatInv, locMat);
+            locMat.multiplyToRef(scaleMat, locMat);
+            locMat.multiplyToRef(origLocMat, locMat);
+
+            var parent = this.getParent();
+
+            if (parent) {
+                locMat.multiplyToRef(parent.getAbsoluteTransform(), this.getAbsoluteTransform());
+            }else {
+                this.getAbsoluteTransform().copyFrom(locMat);
+            }
+
+            var len = this.children.length;
+
+            scaleMat.invert();
+            
+            for (var i = 0; i < len; i++) {
+                var child = this.children[i];
+                var cm = child.getLocalMatrix();
+                cm.multiplyToRef(scaleMat, cm);
+                var lm = child.getLocalMatrix();
+                lm.m[12] *= x;
+                lm.m[13] *= y;
+                lm.m[14] *= z;
+            }
+
+            this.computeAbsoluteTransforms();
+            
+            if (scaleChildren) {
+                for (var i = 0; i < len; i++) {
+                    this.children[i].scale(x, y, z, scaleChildren);
+                    
+                }
+            }          
+
+            this.markAsDirty();
+
+        }
+
+        public setYawPitchRoll (yaw: number, pitch: number, roll: number, space = Space.LOCAL, mesh: AbstractMesh = null): void {
+	
+            var rotMat = Tmp.Matrix[0];
+            Matrix.RotationYawPitchRollToRef(yaw, pitch, roll, rotMat);
+            
+            var rotMatInv = Tmp.Matrix[1];
+            
+            this._getNegativeRotationToRef(rotMatInv, space, mesh);
+	
+            rotMatInv.multiplyToRef(rotMat, rotMat);
+            
+            this._rotateWithMatrix(rotMat, space, mesh);
+            
+        }
+
+        public rotate (axis: Vector3, amount: number, space = Space.LOCAL, mesh: AbstractMesh = null): void {
+            
+            var rmat = Tmp.Matrix[0];
+            rmat.m[12] = 0;
+            rmat.m[13] = 0;
+            rmat.m[14] = 0;
+            
+            Matrix.RotationAxisToRef(axis, amount, rmat);
+            
+            this._rotateWithMatrix(rmat, space, mesh);
+            
+        }
+
+        public setAxisAngle (axis: Vector3, angle: number, space = Space.LOCAL, mesh: AbstractMesh = null): void {
+
+            var rotMat = Tmp.Matrix[0];
+            Matrix.RotationAxisToRef(axis, angle, rotMat);
+            var rotMatInv = Tmp.Matrix[1];
+            
+            this._getNegativeRotationToRef(rotMatInv, space, mesh);
+            
+            rotMatInv.multiplyToRef(rotMat, rotMat);
+            this._rotateWithMatrix(rotMat, space, mesh);
+
+        }
+
+        public setRotationMatrix (rotMat: Matrix, space = Space.LOCAL, mesh: AbstractMesh = null): void {
+
+            var rotMatInv = Tmp.Matrix[1];
+            
+            this._getNegativeRotationToRef(rotMatInv, space, mesh);
+
+            rotMatInv.multiplyToRef(rotMat, rotMat);
+            
+            this._rotateWithMatrix(rotMat, space, mesh);
+
+        }
+
+        private _rotateWithMatrix (rmat: Matrix, space = Space.LOCAL, mesh: AbstractMesh = null): void {
+
+            var lmat = this.getLocalMatrix();
+            var lx = lmat.m[12];
+            var ly = lmat.m[13];
+            var lz = lmat.m[14];
+            var parent = this.getParent();
+            var parentScale = Tmp.Matrix[3];
+            var parentScaleInv = Tmp.Matrix[4];
+
+            if (parent) {
+                if (space == Space.WORLD) {
+                    if (mesh) {
+                        parentScale.copyFrom(mesh.getWorldMatrix());
+                        parent.getAbsoluteTransform().multiplyToRef(parentScale, parentScale);
+                    }else {
+                        parentScale.copyFrom(parent.getAbsoluteTransform());
+                    }
+                }else {
+                    parentScale = parent._scaleMatrix;
+                }
+                parentScaleInv.copyFrom(parentScale);
+                parentScaleInv.invert();
+                lmat.multiplyToRef(parentScale, lmat);
+                lmat.multiplyToRef(rmat, lmat);
+                lmat.multiplyToRef(parentScaleInv, lmat);
+            }else {
+                if (space == Space.WORLD && mesh) {
+                    parentScale.copyFrom(mesh.getWorldMatrix());
+                    parentScaleInv.copyFrom(parentScale);
+                    parentScaleInv.invert();
+                    lmat.multiplyToRef(parentScale, lmat);
+                    lmat.multiplyToRef(rmat, lmat);
+                    lmat.multiplyToRef(parentScaleInv, lmat);
+                }else {
+                    lmat.multiplyToRef(rmat, lmat);
+                }
+            }
+
+            lmat.m[12] = lx;
+            lmat.m[13] = ly;
+            lmat.m[14] = lz;
+
+            this.computeAbsoluteTransforms();
+
+            this.markAsDirty();
+            
+        }
+
+        private _getNegativeRotationToRef(rotMatInv: Matrix, space = Space.LOCAL, mesh: AbstractMesh = null): void {
+
+            if (space == Space.WORLD) {
+                var scaleMatrix = BABYLON.Tmp.Matrix[2];
+				scaleMatrix.copyFrom(this._scaleMatrix);
+				rotMatInv.copyFrom(this.getAbsoluteTransform());
+				if (mesh) {
+                    rotMatInv.multiplyToRef(mesh.getWorldMatrix(), rotMatInv);
+					var meshScale = BABYLON.Tmp.Matrix[3];
+					BABYLON.Matrix.ScalingToRef(mesh.scaling.x, mesh.scaling.y, mesh.scaling.z, meshScale);
+					scaleMatrix.multiplyToRef(meshScale, scaleMatrix);
+                }
+				rotMatInv.invert();
+                scaleMatrix.m[0] *= -1;
+                rotMatInv.multiplyToRef(scaleMatrix, rotMatInv);
+            } else {
+                rotMatInv.copyFrom(this.getLocalMatrix());
+                rotMatInv.invert();
+                var scaleMatrix = Tmp.Matrix[2];
+                scaleMatrix.copyFrom(this._scaleMatrix);
+                if (this._parent) {
+                    var pscaleMatrix = Tmp.Matrix[3];
+                    pscaleMatrix.copyFrom(this._parent._scaleMatrix);
+                    pscaleMatrix.invert();
+                    pscaleMatrix.multiplyToRef(rotMatInv, rotMatInv);
+                } else {
+                    scaleMatrix.m[0] *= -1;
+                }
+                rotMatInv.multiplyToRef(scaleMatrix, rotMatInv);
+            }
+
+        }
+
+        public getScale(): Vector3 {
+            
+            return this._scaleVector.clone();
+            
+        }
+
+        public getScaleToRef(result: Vector3): void {
+	
+            result.copyFrom(this._scaleVector);
+            
+        }
+
+        public getAbsolutePosition (mesh: AbstractMesh = null): Vector3 {
+
+            var pos = Vector3.Zero();
+
+            this.getAbsolutePositionToRef(mesh, pos);
+
+            return pos;
+
+        }
+
+        public getAbsolutePositionToRef (mesh: AbstractMesh = null, result: Vector3): void {
+
+            this._skeleton.computeAbsoluteTransforms();
+            
+            var tmat = Tmp.Matrix[0];
+
+            if (mesh) {
+                tmat.copyFrom(this.getAbsoluteTransform());
+                tmat.multiplyToRef(mesh.getWorldMatrix(), tmat);
+            }else{
+                tmat = this.getAbsoluteTransform();
+            }
+
+            result.x = tmat.m[12];
+            result.y = tmat.m[13];
+            result.z = tmat.m[14];
+
+        }
+
+        public computeAbsoluteTransforms (): void {
+
+            if (this._parent) {
+                this._matrix.multiplyToRef(this._parent._absoluteTransform, this._absoluteTransform);
+            } else {
+                this._absoluteTransform.copyFrom(this._matrix);
+            }
+
+            var children = this.children;
+            var len = children.length;
+
+            for (var i = 0; i < len; i++) {
+                children[i].computeAbsoluteTransforms();
+            }
+
+        }
+
+        private _syncScaleVector = function(): void{
+            
+            var lm = this.getLocalMatrix();
+            
+            var xsq = (lm.m[0] * lm.m[0] + lm.m[1] * lm.m[1] + lm.m[2] * lm.m[2]);
+            var ysq = (lm.m[4] * lm.m[4] + lm.m[5] * lm.m[5] + lm.m[6] * lm.m[6]);
+            var zsq = (lm.m[8] * lm.m[8] + lm.m[9] * lm.m[9] + lm.m[10] * lm.m[10]);
+            
+            var xs = lm.m[0] * lm.m[1] * lm.m[2] * lm.m[3] < 0 ? -1 : 1;
+            var ys = lm.m[4] * lm.m[5] * lm.m[6] * lm.m[7] < 0 ? -1 : 1;
+            var zs = lm.m[8] * lm.m[9] * lm.m[10] * lm.m[11] < 0 ? -1 : 1;
+            
+            this._scaleVector.x = xs * Math.sqrt(xsq);
+            this._scaleVector.y = ys * Math.sqrt(ysq);
+            this._scaleVector.z = zs * Math.sqrt(zsq);
+            
+            if (this._parent) {
+                this._scaleVector.x /= this._parent._negateScaleChildren.x;
+                this._scaleVector.y /= this._parent._negateScaleChildren.y;
+                this._scaleVector.z /= this._parent._negateScaleChildren.z;
+            }
+
+        }
+
+        public getDirection (localAxis: Vector3){
+
+            var result = Vector3.Zero();
+
+            this.getDirectionToRef(localAxis, result);
+            
+            return result;
+
+        }
+
+        public getDirectionToRef (localAxis: Vector3, result: Vector3) {
+
+            this._skeleton.computeAbsoluteTransforms();
+            Vector3.TransformNormalToRef(localAxis, this.getAbsoluteTransform(), result);
+            
+            if (this._scaleVector.x != 1 || this._scaleVector.y != 1 || this._scaleVector.z != 1) {
+                result.normalize();
+            }
+
+        }
+
     }
 } 

+ 9 - 0
src/Bones/babylon.skeleton.js

@@ -10,6 +10,7 @@ var BABYLON;
             this._meshesWithPoseMatrix = new Array();
             this._identity = BABYLON.Matrix.Identity();
             this._ranges = {};
+            this._lastAbsoluteTransformsUpdateId = -1;
             this.bones = [];
             this._scene = scene;
             scene.skeletons.push(this);
@@ -333,6 +334,14 @@ var BABYLON;
             }
             return skeleton;
         };
+        Skeleton.prototype.computeAbsoluteTransforms = function (forceUpdate) {
+            if (forceUpdate === void 0) { forceUpdate = false; }
+            var renderId = this._scene.getRenderId();
+            if (this._lastAbsoluteTransformsUpdateId != renderId || forceUpdate) {
+                this.bones[0].computeAbsoluteTransforms();
+                this._lastAbsoluteTransformsUpdateId = renderId;
+            }
+        };
         return Skeleton;
     }());
     BABYLON.Skeleton = Skeleton;

+ 14 - 0
src/Bones/babylon.skeleton.ts

@@ -13,6 +13,8 @@
 
         private _ranges: { [name: string]: AnimationRange; } = {};
 
+        private _lastAbsoluteTransformsUpdateId = -1;
+
         constructor(public name: string, public id: string, scene: Scene) {
             this.bones = [];
 
@@ -402,5 +404,17 @@
             }
             return skeleton;
         }
+
+        public computeAbsoluteTransforms (forceUpdate = false): void {
+
+            var renderId = this._scene.getRenderId();
+            
+            if (this._lastAbsoluteTransformsUpdateId != renderId || forceUpdate ) {
+                this.bones[0].computeAbsoluteTransforms();
+                this._lastAbsoluteTransformsUpdateId = renderId;
+            }
+            
+        }
+
     }
 }

+ 8 - 0
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts

@@ -5,6 +5,9 @@ module BABYLON {
         camera: ArcRotateCamera;
 
         @serialize()
+        public buttons = [0, 1, 2];
+
+        @serialize()
         public angularSensibilityX = 1000.0;
 
         @serialize()
@@ -38,6 +41,11 @@ module BABYLON {
 
             this._pointerInput = (p, s) => {
                 var evt = <PointerEvent>p.event;
+
+                if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
+                    return;
+                }
+
                 if (p.type === PointerEventTypes.POINTERDOWN) {
                     try {
                         evt.srcElement.setPointerCapture(evt.pointerId);

+ 7 - 0
src/Cameras/Inputs/babylon.freecamera.input.mouse.ts

@@ -3,6 +3,9 @@ module BABYLON {
         camera: FreeCamera;
 
         @serialize()
+        public buttons = [0, 1, 2];
+
+        @serialize()
         public angularSensibility = 2000.0;
 
         private _pointerInput: (p: PointerInfo, s: EventState) => void;
@@ -25,6 +28,10 @@ module BABYLON {
                         return;
                     }
 
+                    if(p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1){
+                        return;
+                    }
+
                     if (p.type === PointerEventTypes.POINTERDOWN) {
                         try {
                             evt.srcElement.setPointerCapture(evt.pointerId);

+ 13 - 1
src/Cameras/babylon.camera.js

@@ -35,6 +35,7 @@ var BABYLON;
             // Cache
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._projectionMatrix = new BABYLON.Matrix();
+            this._doNotComputeProjectionMatrix = false;
             this._postProcesses = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._webvrViewMatrix = BABYLON.Matrix.Identity();
@@ -346,8 +347,19 @@ var BABYLON;
             this._currentRenderId = this.getScene().getRenderId();
             return this._computedViewMatrix;
         };
+        Camera.prototype.freezeProjectionMatrix = function (projection) {
+            this._doNotComputeProjectionMatrix = true;
+            if (projection !== undefined) {
+                this._projectionMatrix = projection;
+            }
+        };
+        ;
+        Camera.prototype.unfreezeProjectionMatrix = function () {
+            this._doNotComputeProjectionMatrix = false;
+        };
+        ;
         Camera.prototype.getProjectionMatrix = function (force) {
-            if (!force && this._isSynchronizedProjectionMatrix()) {
+            if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
                 return this._projectionMatrix;
             }
             this._refreshFrustumPlanes = true;

+ 14 - 2
src/Cameras/babylon.camera.ts

@@ -123,6 +123,7 @@
         // Cache
         private _computedViewMatrix = Matrix.Identity();
         public _projectionMatrix = new Matrix();
+        private _doNotComputeProjectionMatrix = false;
         private _worldMatrix: Matrix;
         public _postProcesses = new Array<PostProcess>();
         private _transformMatrix = Matrix.Zero();
@@ -413,8 +414,19 @@
             return this._computedViewMatrix;
         }
 
+        public freezeProjectionMatrix(projection?: Matrix): void {
+            this._doNotComputeProjectionMatrix = true;
+            if (projection !== undefined) {
+                this._projectionMatrix = projection;
+            }
+        };
+        
+        public unfreezeProjectionMatrix(): void {
+            this._doNotComputeProjectionMatrix = false;
+        };
+        
         public getProjectionMatrix(force?: boolean): Matrix {
-            if (!force && this._isSynchronizedProjectionMatrix()) {
+            if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
                 return this._projectionMatrix;
             }
 
@@ -784,4 +796,4 @@
             return camera;
         }
     }
-}
+}

+ 10 - 4
src/Layer/babylon.highlightlayer.js

@@ -528,10 +528,16 @@ var BABYLON;
          * of the engine canvas size.
          */
         HighlightLayer.prototype.setMainTextureSize = function () {
-            this._mainTextureDesiredSize.width = this._engine.getRenderingCanvas().width * this._options.mainTextureRatio;
-            this._mainTextureDesiredSize.height = this._engine.getRenderingCanvas().height * this._options.mainTextureRatio;
-            this._mainTextureDesiredSize.width = BABYLON.Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize);
-            this._mainTextureDesiredSize.height = BABYLON.Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize);
+            if (this._options.mainTextureFixedSize) {
+                this._mainTextureDesiredSize.width = this._options.mainTextureFixedSize;
+                this._mainTextureDesiredSize.height = this._options.mainTextureFixedSize;
+            }
+            else {
+                this._mainTextureDesiredSize.width = this._engine.getRenderingCanvas().width * this._options.mainTextureRatio;
+                this._mainTextureDesiredSize.height = this._engine.getRenderingCanvas().height * this._options.mainTextureRatio;
+                this._mainTextureDesiredSize.width = BABYLON.Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize);
+                this._mainTextureDesiredSize.height = BABYLON.Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize);
+            }
         };
         /**
          * Force the stencil to the normal expected value for none glowing parts

+ 15 - 4
src/Layer/babylon.highlightlayer.ts

@@ -26,6 +26,11 @@
         mainTextureRatio?: number;
 
         /**
+         * Enforces a fixed size texture to ensure resize independant blur.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
          * Multiplication factor apply to the main texture size in the first step of the blur to reduce the size 
          * of the picture to blur (the smaller the faster).
          */
@@ -725,11 +730,17 @@
          * of the engine canvas size.
          */
         private setMainTextureSize(): void {
-            this._mainTextureDesiredSize.width = this._engine.getRenderingCanvas().width * this._options.mainTextureRatio;
-            this._mainTextureDesiredSize.height = this._engine.getRenderingCanvas().height * this._options.mainTextureRatio;
+            if (this._options.mainTextureFixedSize) {
+                this._mainTextureDesiredSize.width = this._options.mainTextureFixedSize;
+                this._mainTextureDesiredSize.height = this._options.mainTextureFixedSize;
+            }
+            else {
+                this._mainTextureDesiredSize.width = this._engine.getRenderingCanvas().width * this._options.mainTextureRatio;
+                this._mainTextureDesiredSize.height = this._engine.getRenderingCanvas().height * this._options.mainTextureRatio;
 
-            this._mainTextureDesiredSize.width = Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize);
-            this._mainTextureDesiredSize.height = Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize);
+                this._mainTextureDesiredSize.width = Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize);
+                this._mainTextureDesiredSize.height = Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize);
+            }
         }
 
         /**

+ 11 - 0
src/Materials/Textures/babylon.baseTexture.js

@@ -27,7 +27,18 @@ var BABYLON;
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NONE;
             this._scene = scene;
             this._scene.textures.push(this);
+            this._uid = null;
         }
+        Object.defineProperty(BaseTexture.prototype, "uid", {
+            get: function () {
+                if (!this._uid) {
+                    this._uid = BABYLON.Tools.RandomId();
+                }
+                return this._uid;
+            },
+            enumerable: true,
+            configurable: true
+        });
         BaseTexture.prototype.toString = function () {
             return this.name;
         };

+ 1 - 1
src/Materials/Textures/babylon.cubeTexture.js

@@ -43,7 +43,7 @@ var BABYLON;
                 if (this._texture.isReady) {
                     BABYLON.Tools.SetImmediate(function () { return onLoad(); });
                 }
-                else {
+                else if (onLoad) {
                     this._texture.onLoadedCallbacks.push(onLoad);
                 }
             }

+ 1 - 1
src/Materials/Textures/babylon.cubeTexture.ts

@@ -52,7 +52,7 @@
             } else {
                 if (this._texture.isReady) {
                     Tools.SetImmediate(() => onLoad());
-                } else {
+                } else if (onLoad) {
                     this._texture.onLoadedCallbacks.push(onLoad);
                 }
             }

+ 187 - 18
src/Materials/Textures/babylon.fontTexture.js

@@ -25,10 +25,11 @@ var BABYLON;
          * @param samplingMode the texture sampling mode
          * @param superSample if true the FontTexture will be created with a font of a size twice bigger than the given one but all properties (lineHeight, charWidth, etc.) will be according to the original size. This is made to improve the text quality.
          */
-        function FontTexture(name, font, scene, maxCharCount, samplingMode, superSample) {
+        function FontTexture(name, font, scene, maxCharCount, samplingMode, superSample, signedDistanceField) {
             if (maxCharCount === void 0) { maxCharCount = 200; }
             if (samplingMode === void 0) { samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE; }
             if (superSample === void 0) { superSample = false; }
+            if (signedDistanceField === void 0) { signedDistanceField = false; }
             _super.call(this, null, scene, true, false, samplingMode);
             this._charInfos = {};
             this._curCharCount = 0;
@@ -37,8 +38,11 @@ var BABYLON;
             this.name = name;
             this.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
+            this._sdfScale = 8;
+            this._signedDistanceField = signedDistanceField;
             this._superSample = false;
-            if (superSample) {
+            // SDF will use supersample no matter what, the resolution is otherwise too poor to produce correct result
+            if (superSample || signedDistanceField) {
                 var sfont = this.getSuperSampleFont(font);
                 if (sfont) {
                     this._superSample = true;
@@ -50,22 +54,25 @@ var BABYLON;
             this._context = this._canvas.getContext("2d");
             this._context.font = font;
             this._context.fillStyle = "white";
+            this._context.textBaseline = "top";
             this._cachedFontId = null;
             var res = this.getFontHeight(font);
-            this._lineHeightSuper = res.height;
-            this._lineHeight = this._superSample ? (this._lineHeightSuper / 2) : this._lineHeightSuper;
+            this._lineHeightSuper = res.height + 4;
+            this._lineHeight = this._superSample ? (Math.ceil(this._lineHeightSuper / 2)) : this._lineHeightSuper;
             this._offset = res.offset - 1;
+            this._xMargin = 1 + Math.ceil(this._lineHeightSuper / 15); // Right now this empiric formula seems to work...
+            this._yMargin = this._xMargin;
             var maxCharWidth = this._context.measureText("W").width;
             this._spaceWidthSuper = this._context.measureText(" ").width;
             this._spaceWidth = this._superSample ? (this._spaceWidthSuper / 2) : this._spaceWidthSuper;
             // This is an approximate size, but should always be able to fit at least the maxCharCount
-            var totalEstSurface = this._lineHeightSuper * maxCharWidth * maxCharCount;
+            var totalEstSurface = (this._lineHeightSuper + this._yMargin) * (maxCharWidth + this._xMargin) * maxCharCount;
             var edge = Math.sqrt(totalEstSurface);
             var textSize = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
             // Create the texture that will store the font characters
             this._texture = scene.getEngine().createDynamicTexture(textSize, textSize, false, samplingMode);
             var textureSize = this.getSize();
-            this.hasAlpha = true;
+            this.hasAlpha = this._signedDistanceField === false;
             // Recreate a new canvas with the final size: the one matching the texture (resizing the previous one doesn't work as one would expect...)
             this._canvas = document.createElement("canvas");
             this._canvas.width = textureSize.width;
@@ -75,6 +82,24 @@ var BABYLON;
             this._context.font = font;
             this._context.fillStyle = "white";
             this._context.imageSmoothingEnabled = false;
+            this._context.clearRect(0, 0, textureSize.width, textureSize.height);
+            // Create a canvas for the signed distance field mode, we only have to store one char, the purpose is to render a char scaled _sdfScale times
+            //  into this 2D context, then get the bitmap data, create the sdf char and push the result in the _context (which hold the whole Font Texture content)
+            // So you can see this context as an intermediate one, because it is.
+            if (this._signedDistanceField) {
+                var sdfC = document.createElement("canvas");
+                var s = this._sdfScale;
+                sdfC.width = maxCharWidth * s;
+                sdfC.height = this._lineHeightSuper * s;
+                var sdfCtx = sdfC.getContext("2d");
+                sdfCtx.scale(s, s);
+                sdfCtx.textBaseline = "top";
+                sdfCtx.font = font;
+                sdfCtx.fillStyle = "white";
+                sdfCtx.imageSmoothingEnabled = false;
+                this._sdfCanvas = sdfC;
+                this._sdfContext = sdfCtx;
+            }
             this._currentFreePosition = BABYLON.Vector2.Zero();
             // Add the basic ASCII based characters
             for (var i = 0x20; i < 0x7F; i++) {
@@ -90,6 +115,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(FontTexture.prototype, "isSignedDistanceField", {
+            get: function () {
+                return this._signedDistanceField;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(FontTexture.prototype, "spaceWidth", {
             get: function () {
                 return this._spaceWidth;
@@ -104,32 +136,34 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        FontTexture.GetCachedFontTexture = function (scene, fontName, supersample) {
+        FontTexture.GetCachedFontTexture = function (scene, fontName, supersample, signedDistanceField) {
             if (supersample === void 0) { supersample = false; }
+            if (signedDistanceField === void 0) { signedDistanceField = false; }
             var s = scene;
             if (!s.__fontTextureCache__) {
                 s.__fontTextureCache__ = new BABYLON.StringDictionary();
             }
             var dic = s.__fontTextureCache__;
-            var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS");
+            var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS") + (signedDistanceField ? "_+SDF" : "_-SDF");
             var ft = dic.get(lfn);
             if (ft) {
                 ++ft._usedCounter;
                 return ft;
             }
-            ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, BABYLON.Texture.BILINEAR_SAMPLINGMODE, supersample);
+            ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, BABYLON.Texture.BILINEAR_SAMPLINGMODE, supersample, signedDistanceField);
             ft._cachedFontId = lfn;
             dic.add(lfn, ft);
             return ft;
         };
-        FontTexture.ReleaseCachedFontTexture = function (scene, fontName, supersample) {
+        FontTexture.ReleaseCachedFontTexture = function (scene, fontName, supersample, signedDistanceField) {
             if (supersample === void 0) { supersample = false; }
+            if (signedDistanceField === void 0) { signedDistanceField = false; }
             var s = scene;
             var dic = s.__fontTextureCache__;
             if (!dic) {
                 return;
             }
-            var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS");
+            var lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS") + (signedDistanceField ? "_+SDF" : "_-SDF");
             var font = dic.get(lfn);
             if (--font._usedCounter === 0) {
                 dic.remove(lfn);
@@ -154,29 +188,164 @@ var BABYLON;
             var textureSize = this.getSize();
             // we reached the end of the current line?
             var width = Math.round(measure.width);
-            var xMargin = 1 + Math.ceil(this._lineHeightSuper / 15); // Right now this empiric formula seems to work...
-            var yMargin = xMargin;
-            if (this._currentFreePosition.x + width + xMargin > textureSize.width) {
+            if (this._currentFreePosition.x + width + this._xMargin > textureSize.width) {
                 this._currentFreePosition.x = 0;
-                this._currentFreePosition.y += this._lineHeightSuper + yMargin;
+                this._currentFreePosition.y += this._lineHeightSuper + this._yMargin;
                 // No more room?
                 if (this._currentFreePosition.y > textureSize.height) {
                     return this.getChar("!");
                 }
             }
-            // Draw the character in the texture
-            this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
+            // In sdf mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the sdf map accurately)
+            if (this._signedDistanceField) {
+                this._sdfContext.clearRect(0, 0, this._sdfCanvas.width, this._sdfCanvas.height);
+                this._sdfContext.fillText(char, 0, -this._offset);
+                var data = this._sdfContext.getImageData(0, 0, width * this._sdfScale, this._sdfCanvas.height);
+                var res = this._computeSDFChar(data);
+                this._context.putImageData(res, this._currentFreePosition.x, this._currentFreePosition.y);
+            }
+            else {
+                // Draw the character in the HTML canvas
+                this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
+            }
             // Fill the CharInfo object
             info.topLeftUV = new BABYLON.Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
             info.bottomRightUV = new BABYLON.Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + 2) / textureSize.height));
+            if (this._signedDistanceField) {
+                var off = 1 / textureSize.width;
+                info.topLeftUV.addInPlace(new BABYLON.Vector2(off, off));
+                info.bottomRightUV.addInPlace(new BABYLON.Vector2(off, off));
+            }
             info.charWidth = this._superSample ? (width / 2) : width;
             // Add the info structure
             this._charInfos[char] = info;
             this._curCharCount++;
             // Set the next position
-            this._currentFreePosition.x += width + xMargin;
+            this._currentFreePosition.x += width + this._xMargin;
             return info;
         };
+        FontTexture.prototype._computeSDFChar = function (source) {
+            var scl = this._sdfScale;
+            var sw = source.width;
+            var sh = source.height;
+            var dw = sw / scl;
+            var dh = sh / scl;
+            var roffx = 0;
+            var roffy = 0;
+            // We shouldn't look beyond half of the biggest between width and height
+            var radius = scl;
+            var br = radius - 1;
+            var lookupSrc = function (dx, dy, offX, offY, lookVis) {
+                var sx = dx * scl;
+                var sy = dy * scl;
+                // Looking out of the area? return true to make the test going on
+                if (((sx + offX) < 0) || ((sx + offX) >= sw) || ((sy + offY) < 0) || ((sy + offY) >= sh)) {
+                    return true;
+                }
+                // Get the pixel we want
+                var val = source.data[(((sy + offY) * sw) + (sx + offX)) * 4];
+                var res = (val > 0) === lookVis;
+                if (!res) {
+                    roffx = offX;
+                    roffy = offY;
+                }
+                return res;
+            };
+            var lookupArea = function (dx, dy, lookVis) {
+                // Fast rejection test, if we have the same result in N, S, W, E at a distance which is the radius-1 then it means the data will be consistent in this area. That's because we've scale the rendering of the letter "radius" times, so a letter's pixel will be at least radius wide
+                if (lookupSrc(dx, dy, 0, br, lookVis) &&
+                    lookupSrc(dx, dy, 0, -br, lookVis) &&
+                    lookupSrc(dx, dy, -br, 0, lookVis) &&
+                    lookupSrc(dx, dy, br, 0, lookVis)) {
+                    return 0;
+                }
+                for (var i = 1; i <= radius; i++) {
+                    // Quick test N, S, W, E
+                    if (!lookupSrc(dx, dy, 0, i, lookVis) || !lookupSrc(dx, dy, 0, -i, lookVis) || !lookupSrc(dx, dy, -i, 0, lookVis) || !lookupSrc(dx, dy, i, 0, lookVis)) {
+                        return i * i; // Squared Distance is simple to compute in this case
+                    }
+                    // Test the frame area (except the N, S, W, E spots) from the nearest point from the center to the further one
+                    for (var j = 1; j <= i; j++) {
+                        if (!lookupSrc(dx, dy, -j, i, lookVis) || !lookupSrc(dx, dy, j, i, lookVis) ||
+                            !lookupSrc(dx, dy, i, -j, lookVis) || !lookupSrc(dx, dy, i, j, lookVis) ||
+                            !lookupSrc(dx, dy, -j, -i, lookVis) || !lookupSrc(dx, dy, j, -i, lookVis) ||
+                            !lookupSrc(dx, dy, -i, -j, lookVis) || !lookupSrc(dx, dy, -i, j, lookVis)) {
+                            // We found the nearest texel having and opposite state, store the squared length
+                            var res_1 = (i * i) + (j * j);
+                            var count = 1;
+                            // To improve quality we will  sample the texels around this one, so it's 8 samples, we consider only the one having an opposite state, add them to the current res and will will compute the average at the end
+                            if (!lookupSrc(dx, dy, roffx - 1, roffy, lookVis)) {
+                                res_1 += (roffx - 1) * (roffx - 1) + roffy * roffy;
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx + 1, roffy, lookVis)) {
+                                res_1 += (roffx + 1) * (roffx + 1) + roffy * roffy;
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx, roffy - 1, lookVis)) {
+                                res_1 += roffx * roffx + (roffy - 1) * (roffy - 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx, roffy + 1, lookVis)) {
+                                res_1 += roffx * roffx + (roffy + 1) * (roffy + 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx - 1, roffy - 1, lookVis)) {
+                                res_1 += (roffx - 1) * (roffx - 1) + (roffy - 1) * (roffy - 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx + 1, roffy + 1, lookVis)) {
+                                res_1 += (roffx + 1) * (roffx + 1) + (roffy + 1) * (roffy + 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx + 1, roffy - 1, lookVis)) {
+                                res_1 += (roffx + 1) * (roffx + 1) + (roffy - 1) * (roffy - 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx - 1, roffy + 1, lookVis)) {
+                                res_1 += (roffx - 1) * (roffx - 1) + (roffy + 1) * (roffy + 1);
+                                ++count;
+                            }
+                            // Compute the average based on the accumulated distance
+                            return res_1 / count;
+                        }
+                    }
+                }
+                return 0;
+            };
+            var tmp = new Array(dw * dh);
+            for (var y = 0; y < dh; y++) {
+                for (var x = 0; x < dw; x++) {
+                    var curState = lookupSrc(x, y, 0, 0, true);
+                    var d = lookupArea(x, y, curState);
+                    if (d === 0) {
+                        d = radius * radius * 2;
+                    }
+                    tmp[(y * dw) + x] = curState ? d : -d;
+                }
+            }
+            var res = this._context.createImageData(dw, dh);
+            var size = dw * dh;
+            for (var j = 0; j < size; j++) {
+                var d = tmp[j];
+                var sign = (d < 0) ? -1 : 1;
+                d = Math.sqrt(Math.abs(d)) * sign;
+                d *= 127.5 / radius;
+                d += 127.5;
+                if (d < 0) {
+                    d = 0;
+                }
+                else if (d > 255) {
+                    d = 255;
+                }
+                d += 0.5;
+                res.data[j * 4 + 0] = d;
+                res.data[j * 4 + 1] = d;
+                res.data[j * 4 + 2] = d;
+                res.data[j * 4 + 3] = 255;
+            }
+            return res;
+        };
         FontTexture.prototype.measureText = function (text, tabulationSize) {
             if (tabulationSize === void 0) { tabulationSize = 4; }
             var maxWidth = 0;

+ 219 - 19
src/Materials/Textures/babylon.fontTexture.ts

@@ -26,6 +26,8 @@
         private _context: CanvasRenderingContext2D;
         private _lineHeight: number;
         private _lineHeightSuper: number;
+        private _xMargin: number;
+        private _yMargin: number;
         private _offset: number;
         private _currentFreePosition: Vector2;
         private _charInfos: ICharInfoMap = {};
@@ -35,12 +37,20 @@
         private _spaceWidthSuper;
         private _usedCounter = 1;
         private _superSample: boolean;
+        private _sdfCanvas: HTMLCanvasElement;
+        private _sdfContext: CanvasRenderingContext2D;
+        private _signedDistanceField: boolean;
         private _cachedFontId: string;
+        private _sdfScale: number;
 
         public get isSuperSampled(): boolean {
             return this._superSample;
         }
 
+        public get isSignedDistanceField(): boolean {
+            return this._signedDistanceField;
+        }
+
         public get spaceWidth(): number {
             return this._spaceWidth;
         }
@@ -49,7 +59,7 @@
             return this._lineHeight;
         }
 
-        public static GetCachedFontTexture(scene: Scene, fontName: string, supersample: boolean = false) {
+        public static GetCachedFontTexture(scene: Scene, fontName: string, supersample: boolean = false, signedDistanceField: boolean = false) {
             let s = <any>scene;
             if (!s.__fontTextureCache__) {
                 s.__fontTextureCache__ = new StringDictionary<FontTexture>();
@@ -57,28 +67,28 @@
 
             let dic = <StringDictionary<FontTexture>>s.__fontTextureCache__;
 
-            let lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS");
+            let lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS") + (signedDistanceField ? "_+SDF" : "_-SDF");
             let ft = dic.get(lfn);
             if (ft) {
                 ++ft._usedCounter;
                 return ft;
             }
 
-            ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, Texture.BILINEAR_SAMPLINGMODE, supersample);
+            ft = new FontTexture(null, fontName, scene, supersample ? 100 : 200, Texture.BILINEAR_SAMPLINGMODE, supersample, signedDistanceField);
             ft._cachedFontId = lfn;
             dic.add(lfn, ft);
 
             return ft;
         }
 
-        public static ReleaseCachedFontTexture(scene: Scene, fontName: string, supersample: boolean = false) {
+        public static ReleaseCachedFontTexture(scene: Scene, fontName: string, supersample: boolean = false, signedDistanceField: boolean = false) {
             let s = <any>scene;
             let dic = <StringDictionary<FontTexture>>s.__fontTextureCache__;
             if (!dic) {
                 return;
             }
 
-            let lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS");
+            let lfn = fontName.toLocaleLowerCase() + (supersample ? "_+SS" : "_-SS") + (signedDistanceField ? "_+SDF" : "_-SDF");
             var font = dic.get(lfn);
             if (--font._usedCounter === 0) {
                 dic.remove(lfn);
@@ -95,7 +105,7 @@
          * @param samplingMode the texture sampling mode
          * @param superSample if true the FontTexture will be created with a font of a size twice bigger than the given one but all properties (lineHeight, charWidth, etc.) will be according to the original size. This is made to improve the text quality.
          */
-        constructor(name: string, font: string, scene: Scene, maxCharCount=200, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, superSample: boolean = false) {
+        constructor(name: string, font: string, scene: Scene, maxCharCount=200, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, superSample: boolean = false, signedDistanceField: boolean = false) {
             super(null, scene, true, false, samplingMode);
 
             this.name = name;
@@ -103,8 +113,12 @@
             this.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.wrapV = Texture.CLAMP_ADDRESSMODE;
 
+            this._sdfScale = 8;
+            this._signedDistanceField = signedDistanceField;
             this._superSample = false;
-            if (superSample) {
+
+            // SDF will use supersample no matter what, the resolution is otherwise too poor to produce correct result
+            if (superSample || signedDistanceField) {
                 let sfont = this.getSuperSampleFont(font);
                 if (sfont) {
                     this._superSample = true;
@@ -117,19 +131,22 @@
             this._context = this._canvas.getContext("2d");
             this._context.font = font;
             this._context.fillStyle = "white";
+            this._context.textBaseline = "top";
             this._cachedFontId = null;
 
             var res = this.getFontHeight(font);
-            this._lineHeightSuper = res.height;
-            this._lineHeight = this._superSample ? (this._lineHeightSuper / 2) : this._lineHeightSuper;
-            this._offset = res.offset-1;
+            this._lineHeightSuper = res.height+4;
+            this._lineHeight = this._superSample ? (Math.ceil(this._lineHeightSuper / 2)) : this._lineHeightSuper;
+            this._offset = res.offset - 1;
+            this._xMargin = 1 + Math.ceil(this._lineHeightSuper / 15);    // Right now this empiric formula seems to work...
+            this._yMargin = this._xMargin;
 
             var maxCharWidth = this._context.measureText("W").width;
             this._spaceWidthSuper = this._context.measureText(" ").width;
             this._spaceWidth = this._superSample ? (this._spaceWidthSuper / 2) : this._spaceWidthSuper;
 
             // This is an approximate size, but should always be able to fit at least the maxCharCount
-            var totalEstSurface = this._lineHeightSuper * maxCharWidth * maxCharCount;
+            var totalEstSurface = (this._lineHeightSuper + this._yMargin) * (maxCharWidth + this._xMargin) * maxCharCount;
             var edge = Math.sqrt(totalEstSurface);
             var textSize = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
 
@@ -137,7 +154,7 @@
             this._texture = scene.getEngine().createDynamicTexture(textSize, textSize, false, samplingMode);
             var textureSize = this.getSize();
 
-            this.hasAlpha = true;
+            this.hasAlpha = this._signedDistanceField===false;
 
             // Recreate a new canvas with the final size: the one matching the texture (resizing the previous one doesn't work as one would expect...)
             this._canvas = document.createElement("canvas");
@@ -148,6 +165,26 @@
             this._context.font = font;
             this._context.fillStyle = "white";
             this._context.imageSmoothingEnabled = false;
+            this._context.clearRect(0, 0, textureSize.width, textureSize.height);
+
+            // Create a canvas for the signed distance field mode, we only have to store one char, the purpose is to render a char scaled _sdfScale times
+            //  into this 2D context, then get the bitmap data, create the sdf char and push the result in the _context (which hold the whole Font Texture content)
+            // So you can see this context as an intermediate one, because it is.
+            if (this._signedDistanceField) {
+                let sdfC = document.createElement("canvas");
+                let s = this._sdfScale;
+                sdfC.width = maxCharWidth * s;
+                sdfC.height = this._lineHeightSuper * s;
+                let sdfCtx = sdfC.getContext("2d");
+                sdfCtx.scale(s, s);
+                sdfCtx.textBaseline = "top";
+                sdfCtx.font = font;
+                sdfCtx.fillStyle = "white";
+                sdfCtx.imageSmoothingEnabled = false;
+
+                this._sdfCanvas = sdfC;
+                this._sdfContext = sdfCtx;
+            }
 
             this._currentFreePosition = Vector2.Zero();
 
@@ -156,6 +193,7 @@
                 var c = String.fromCharCode(i);
                 this.getChar(c);
             }
+
             this.update();
         }
 
@@ -181,11 +219,9 @@
 
             // we reached the end of the current line?
             let width = Math.round(measure.width);
-            var xMargin = 1 + Math.ceil(this._lineHeightSuper / 15);    // Right now this empiric formula seems to work...
-            var yMargin = xMargin;
-            if (this._currentFreePosition.x + width + xMargin > textureSize.width) {
+            if (this._currentFreePosition.x + width + this._xMargin > textureSize.width) {
                 this._currentFreePosition.x = 0;
-                this._currentFreePosition.y += this._lineHeightSuper + yMargin;
+                this._currentFreePosition.y += this._lineHeightSuper + this._yMargin;
 
                 // No more room?
                 if (this._currentFreePosition.y > textureSize.height) {
@@ -193,12 +229,29 @@
                 }
             }
 
-            // Draw the character in the texture
-            this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
+            // In sdf mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the sdf map accurately)
+            if (this._signedDistanceField) {
+                this._sdfContext.clearRect(0, 0, this._sdfCanvas.width, this._sdfCanvas.height);
+                this._sdfContext.fillText(char, 0, -this._offset);
+                let data = this._sdfContext.getImageData(0, 0, width*this._sdfScale, this._sdfCanvas.height);
+
+                let res = this._computeSDFChar(data);
+                this._context.putImageData(res, this._currentFreePosition.x, this._currentFreePosition.y);
+            } else {
+                // Draw the character in the HTML canvas
+                this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
+            }
 
             // Fill the CharInfo object
             info.topLeftUV = new Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
             info.bottomRightUV = new Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + 2) / textureSize.height));
+
+            if (this._signedDistanceField) {
+                let off = 1/textureSize.width;
+                info.topLeftUV.addInPlace(new Vector2(off, off));
+                info.bottomRightUV.addInPlace(new Vector2(off, off));
+            }
+
             info.charWidth = this._superSample ? (width/2) : width;
 
             // Add the info structure
@@ -206,11 +259,158 @@
             this._curCharCount++;
 
             // Set the next position
-            this._currentFreePosition.x += width + xMargin;
+            this._currentFreePosition.x += width + this._xMargin;
 
             return info;
         }
 
+        private _computeSDFChar(source: ImageData): ImageData {
+            let scl = this._sdfScale;
+            let sw = source.width;
+            let sh = source.height;
+            let dw = sw / scl;
+            let dh = sh / scl;
+            let roffx = 0;
+            let roffy = 0;
+
+            // We shouldn't look beyond half of the biggest between width and height
+            let radius = scl;
+            let br = radius - 1;
+
+            let lookupSrc = (dx: number, dy: number, offX: number, offY: number, lookVis: boolean): boolean => {
+                let sx = dx * scl;
+                let sy = dy * scl;
+
+                // Looking out of the area? return true to make the test going on
+                if (((sx + offX) < 0) || ((sx + offX) >= sw) || ((sy + offY) < 0) || ((sy + offY) >= sh)) {
+                    return true;
+                }
+
+                // Get the pixel we want
+                let val = source.data[(((sy + offY) * sw) + (sx + offX)) * 4];
+
+                let res = (val > 0) === lookVis;
+                if (!res) {
+                    roffx = offX;
+                    roffy = offY;
+                }
+                return res;
+            }
+
+            let lookupArea = (dx: number, dy: number, lookVis: boolean): number => {
+
+                // Fast rejection test, if we have the same result in N, S, W, E at a distance which is the radius-1 then it means the data will be consistent in this area. That's because we've scale the rendering of the letter "radius" times, so a letter's pixel will be at least radius wide
+                if (lookupSrc(dx, dy, 0, br, lookVis) &&
+                    lookupSrc(dx, dy, 0, -br, lookVis) &&
+                    lookupSrc(dx, dy, -br, 0, lookVis) &&
+                    lookupSrc(dx, dy, br, 0, lookVis)) {
+                    return 0;
+                }
+
+                for (let i = 1; i <= radius; i++) {
+                    // Quick test N, S, W, E
+                    if (!lookupSrc(dx, dy, 0, i, lookVis) || !lookupSrc(dx, dy, 0, -i, lookVis) || !lookupSrc(dx, dy, -i, 0, lookVis) || !lookupSrc(dx, dy, i, 0, lookVis)) {
+                        return i * i;   // Squared Distance is simple to compute in this case
+                    }
+
+                    // Test the frame area (except the N, S, W, E spots) from the nearest point from the center to the further one
+                    for (let j = 1; j <= i; j++) {
+                        if (
+                            !lookupSrc(dx, dy, -j, i, lookVis) || !lookupSrc(dx, dy, j, i, lookVis) ||
+                            !lookupSrc(dx, dy, i, -j, lookVis) || !lookupSrc(dx, dy, i, j, lookVis) ||
+                            !lookupSrc(dx, dy, -j, -i, lookVis) || !lookupSrc(dx, dy, j, -i, lookVis) ||
+                            !lookupSrc(dx, dy, -i, -j, lookVis) || !lookupSrc(dx, dy, -i, j, lookVis)) {
+
+                            // We found the nearest texel having and opposite state, store the squared length
+                            let res = (i * i) + (j * j);
+                            let count = 1;
+
+                            // To improve quality we will  sample the texels around this one, so it's 8 samples, we consider only the one having an opposite state, add them to the current res and will will compute the average at the end
+                            if (!lookupSrc(dx, dy, roffx - 1, roffy, lookVis)) {
+                                res += (roffx - 1) * (roffx - 1) + roffy * roffy;
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx + 1, roffy, lookVis)) {
+                                res += (roffx + 1) * (roffx + 1) + roffy * roffy;
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx, roffy - 1, lookVis)) {
+                                res += roffx * roffx + (roffy - 1) * (roffy - 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx, roffy + 1, lookVis)) {
+                                res += roffx * roffx + (roffy + 1) * (roffy + 1);
+                                ++count;
+                            }
+
+                            if (!lookupSrc(dx, dy, roffx - 1, roffy - 1, lookVis)) {
+                                res += (roffx - 1) * (roffx - 1) + (roffy - 1) * (roffy - 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx + 1, roffy + 1, lookVis)) {
+                                res += (roffx + 1) * (roffx + 1) + (roffy + 1) * (roffy + 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx + 1, roffy - 1, lookVis)) {
+                                res += (roffx + 1) * (roffx + 1) + (roffy - 1) * (roffy - 1);
+                                ++count;
+                            }
+                            if (!lookupSrc(dx, dy, roffx - 1, roffy + 1, lookVis)) {
+                                res += (roffx - 1) * (roffx - 1) + (roffy + 1) * (roffy + 1);
+                                ++count;
+                            }
+
+                            // Compute the average based on the accumulated distance
+                            return res / count;
+                        }
+                    }
+                }
+
+                return 0;
+            }
+
+            let tmp = new Array<number>(dw * dh);
+            for (let y = 0; y < dh; y++) {
+                for (let x = 0; x < dw; x++) {
+
+                    let curState = lookupSrc(x, y, 0, 0, true);
+
+                    let d = lookupArea(x, y, curState);
+                    if (d === 0) {
+                        d = radius * radius * 2;
+                    }
+                    tmp[(y * dw) + x] = curState ? d : -d;
+                }
+            }
+
+            let res = this._context.createImageData(dw, dh);
+
+            let size = dw * dh;
+            for (let j = 0; j < size; j++) {
+                let d = tmp[j];
+
+                let sign = (d < 0) ? -1 : 1;
+
+                d = Math.sqrt(Math.abs(d)) * sign;
+
+                d *= 127.5 / radius;
+                d += 127.5;
+                if (d < 0) {
+                    d = 0;
+                } else if (d > 255) {
+                    d = 255;
+                }
+                d += 0.5;
+
+                res.data[j*4 + 0] = d;
+                res.data[j*4 + 1] = d;
+                res.data[j*4 + 2] = d;
+                res.data[j*4 + 3] = 255;
+            }
+
+            return res;
+        }
+
         public measureText(text: string, tabulationSize: number = 4): Size {
             let maxWidth: number = 0;
             let curWidth: number = 0;

+ 1 - 1
src/Materials/Textures/babylon.hdrCubeTexture.js

@@ -300,7 +300,7 @@ var BABYLON;
             var texture = null;
             if (parsedTexture.name && !parsedTexture.isRenderTarget) {
                 var size = parsedTexture.isBABYLONPreprocessed ? null : parsedTexture.size;
-                texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size, texture.generateHarmonics, texture.useInGammaSpace, texture.usePMREMGenerator);
+                texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size, parsedTexture.generateHarmonics, parsedTexture.useInGammaSpace, parsedTexture.usePMREMGenerator);
                 texture.name = parsedTexture.name;
                 texture.hasAlpha = parsedTexture.hasAlpha;
                 texture.level = parsedTexture.level;

+ 1 - 1
src/Materials/Textures/babylon.hdrCubeTexture.ts

@@ -385,7 +385,7 @@ module BABYLON {
             if (parsedTexture.name && !parsedTexture.isRenderTarget) {
                 var size = parsedTexture.isBABYLONPreprocessed ? null : parsedTexture.size;
                 texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size,
-                    texture.generateHarmonics, texture.useInGammaSpace, texture.usePMREMGenerator);
+                    parsedTexture.generateHarmonics, parsedTexture.useInGammaSpace, parsedTexture.usePMREMGenerator);
                 texture.name = parsedTexture.name;
                 texture.hasAlpha = parsedTexture.hasAlpha;
                 texture.level = parsedTexture.level;

+ 5 - 5
src/Materials/Textures/babylon.texture.js

@@ -213,6 +213,10 @@ var BABYLON;
             return new Texture("data:" + name, scene, noMipmap, invertY, samplingMode, onLoad, onError, data);
         };
         Texture.Parse = function (parsedTexture, scene, rootUrl) {
+            if (parsedTexture.customType) {
+                var customTexture = BABYLON.Tools.Instantiate(parsedTexture.customType);
+                return customTexture.Parse(parsedTexture, scene, rootUrl);
+            }
             if (parsedTexture.isCube) {
                 return BABYLON.CubeTexture.Parse(parsedTexture, scene, rootUrl);
             }
@@ -220,11 +224,7 @@ var BABYLON;
                 return null;
             }
             var texture = BABYLON.SerializationHelper.Parse(function () {
-                if (parsedTexture.customType) {
-                    var customTexture = BABYLON.Tools.Instantiate(parsedTexture.customType);
-                    return customTexture.Parse(parsedTexture, scene, rootUrl);
-                }
-                else if (parsedTexture.mirrorPlane) {
+                if (parsedTexture.mirrorPlane) {
                     var mirrorTexture = new BABYLON.MirrorTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene);
                     mirrorTexture._waitingRenderList = parsedTexture.renderList;
                     mirrorTexture.mirrorPlane = BABYLON.Plane.FromArray(parsedTexture.mirrorPlane);

+ 6 - 4
src/Materials/Textures/babylon.texture.ts

@@ -268,6 +268,11 @@
         }
 
         public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): BaseTexture {
+            if (parsedTexture.customType) { 
+                var customTexture = Tools.Instantiate(parsedTexture.customType);
+                return customTexture.Parse(parsedTexture, scene, rootUrl);
+            }
+
             if (parsedTexture.isCube) {
                 return CubeTexture.Parse(parsedTexture, scene, rootUrl);
             }
@@ -277,10 +282,7 @@
             }
 
             var texture = SerializationHelper.Parse(() => {
-                if (parsedTexture.customType) {
-                    var customTexture = Tools.Instantiate(parsedTexture.customType);
-                    return customTexture.Parse(parsedTexture, scene, rootUrl);
-                } else if (parsedTexture.mirrorPlane) {
+                if (parsedTexture.mirrorPlane) {
                     var mirrorTexture = new MirrorTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene);
                     mirrorTexture._waitingRenderList = parsedTexture.renderList;
                     mirrorTexture.mirrorPlane = Plane.FromArray(parsedTexture.mirrorPlane);

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

@@ -133,6 +133,12 @@ var BABYLON;
                 callback(vertexCode);
                 return;
             }
+            // Base64 encoded ?
+            if (vertex.substr(0, 7) === "base64:") {
+                var vertexBinary = window.atob(vertex.substr(7));
+                callback(vertexBinary);
+                return;
+            }
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
                 callback(Effect.ShadersStore[vertex + "VertexShader"]);
@@ -155,6 +161,12 @@ var BABYLON;
                 callback(fragmentCode);
                 return;
             }
+            // Base64 encoded ?
+            if (fragment.substr(0, 7) === "base64:") {
+                var fragmentBinary = window.atob(fragment.substr(7));
+                callback(fragmentBinary);
+                return;
+            }
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
                 callback(Effect.ShadersStore[fragment + "PixelShader"]);

+ 14 - 0
src/Materials/babylon.effect.ts

@@ -177,6 +177,13 @@
                 return;
             }
 
+            // Base64 encoded ?
+            if (vertex.substr(0, 7) === "base64:") {
+            	var vertexBinary = window.atob(vertex.substr(7));
+            	callback(vertexBinary);
+            	return;
+            }
+
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
                 callback(Effect.ShadersStore[vertex + "VertexShader"]);
@@ -203,6 +210,13 @@
                 return;
             }
 
+            // Base64 encoded ?
+            if (fragment.substr(0, 7) === "base64:") {
+            	var fragmentBinary = window.atob(fragment.substr(7));
+            	callback(fragmentBinary);
+            	return;
+            }
+
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
                 callback(Effect.ShadersStore[fragment + "PixelShader"]);

+ 1 - 1
src/Materials/babylon.fresnelParameters.js

@@ -11,7 +11,7 @@ var BABYLON;
         FresnelParameters.prototype.clone = function () {
             var newFresnelParameters = new FresnelParameters();
             BABYLON.Tools.DeepCopy(this, newFresnelParameters);
-            return new FresnelParameters;
+            return newFresnelParameters;
         };
         FresnelParameters.prototype.serialize = function () {
             var serializationObject = {};

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

@@ -11,7 +11,7 @@
 
             Tools.DeepCopy(this, newFresnelParameters);
 
-            return new FresnelParameters;
+            return newFresnelParameters;
         }
 
         public serialize(): any {

+ 103 - 103
src/Materials/babylon.pbrMaterial.js

@@ -218,6 +218,10 @@ var BABYLON;
              */
             this.overloadedMicroSurfaceIntensity = 0.0;
             this._overloadedMicroSurface = new BABYLON.Vector3(this.overloadedMicroSurface, this.overloadedMicroSurfaceIntensity, this.overloadedReflectionIntensity);
+            /**
+             * AKA Occlusion Texture Intensity in other nomenclature.
+             */
+            this.ambientTextureStrength = 1.0;
             this.ambientColor = new BABYLON.Color3(0, 0, 0);
             /**
              * AKA Diffuse Color in other nomenclature.
@@ -441,114 +445,111 @@ var BABYLON;
             var needUVs = false;
             this._defines.reset();
             if (scene.texturesEnabled) {
-                // Textures
-                if (scene.texturesEnabled) {
-                    if (scene.getEngine().getCaps().textureLOD) {
-                        this._defines.LODBASEDMICROSFURACE = true;
+                if (scene.getEngine().getCaps().textureLOD) {
+                    this._defines.LODBASEDMICROSFURACE = true;
+                }
+                if (this.albedoTexture && BABYLON.StandardMaterial.DiffuseTextureEnabled) {
+                    if (!this.albedoTexture.isReady()) {
+                        return false;
                     }
-                    if (this.albedoTexture && BABYLON.StandardMaterial.DiffuseTextureEnabled) {
-                        if (!this.albedoTexture.isReady()) {
-                            return false;
-                        }
-                        else {
-                            needUVs = true;
-                            this._defines.ALBEDO = true;
-                        }
+                    else {
+                        needUVs = true;
+                        this._defines.ALBEDO = true;
                     }
-                    if (this.ambientTexture && BABYLON.StandardMaterial.AmbientTextureEnabled) {
-                        if (!this.ambientTexture.isReady()) {
-                            return false;
-                        }
-                        else {
-                            needUVs = true;
-                            this._defines.AMBIENT = true;
-                        }
+                }
+                if (this.ambientTexture && BABYLON.StandardMaterial.AmbientTextureEnabled) {
+                    if (!this.ambientTexture.isReady()) {
+                        return false;
                     }
-                    if (this.opacityTexture && BABYLON.StandardMaterial.OpacityTextureEnabled) {
-                        if (!this.opacityTexture.isReady()) {
-                            return false;
-                        }
-                        else {
-                            needUVs = true;
-                            this._defines.OPACITY = true;
-                            if (this.opacityTexture.getAlphaFromRGB) {
-                                this._defines.OPACITYRGB = true;
-                            }
+                    else {
+                        needUVs = true;
+                        this._defines.AMBIENT = true;
+                    }
+                }
+                if (this.opacityTexture && BABYLON.StandardMaterial.OpacityTextureEnabled) {
+                    if (!this.opacityTexture.isReady()) {
+                        return false;
+                    }
+                    else {
+                        needUVs = true;
+                        this._defines.OPACITY = true;
+                        if (this.opacityTexture.getAlphaFromRGB) {
+                            this._defines.OPACITYRGB = true;
                         }
                     }
-                    if (this.reflectionTexture && BABYLON.StandardMaterial.ReflectionTextureEnabled) {
-                        if (!this.reflectionTexture.isReady()) {
-                            return false;
+                }
+                if (this.reflectionTexture && BABYLON.StandardMaterial.ReflectionTextureEnabled) {
+                    if (!this.reflectionTexture.isReady()) {
+                        return false;
+                    }
+                    else {
+                        needNormals = true;
+                        this._defines.REFLECTION = true;
+                        if (this.reflectionTexture.coordinatesMode === BABYLON.Texture.INVCUBIC_MODE) {
+                            this._defines.INVERTCUBICMAP = true;
                         }
-                        else {
+                        this._defines.REFLECTIONMAP_3D = this.reflectionTexture.isCube;
+                        switch (this.reflectionTexture.coordinatesMode) {
+                            case BABYLON.Texture.CUBIC_MODE:
+                            case BABYLON.Texture.INVCUBIC_MODE:
+                                this._defines.REFLECTIONMAP_CUBIC = true;
+                                break;
+                            case BABYLON.Texture.EXPLICIT_MODE:
+                                this._defines.REFLECTIONMAP_EXPLICIT = true;
+                                break;
+                            case BABYLON.Texture.PLANAR_MODE:
+                                this._defines.REFLECTIONMAP_PLANAR = true;
+                                break;
+                            case BABYLON.Texture.PROJECTION_MODE:
+                                this._defines.REFLECTIONMAP_PROJECTION = true;
+                                break;
+                            case BABYLON.Texture.SKYBOX_MODE:
+                                this._defines.REFLECTIONMAP_SKYBOX = true;
+                                break;
+                            case BABYLON.Texture.SPHERICAL_MODE:
+                                this._defines.REFLECTIONMAP_SPHERICAL = true;
+                                break;
+                            case BABYLON.Texture.EQUIRECTANGULAR_MODE:
+                                this._defines.REFLECTIONMAP_EQUIRECTANGULAR = true;
+                                break;
+                        }
+                        if (this.reflectionTexture instanceof BABYLON.HDRCubeTexture && this.reflectionTexture) {
+                            this._defines.USESPHERICALFROMREFLECTIONMAP = true;
                             needNormals = true;
-                            this._defines.REFLECTION = true;
-                            if (this.reflectionTexture.coordinatesMode === BABYLON.Texture.INVCUBIC_MODE) {
-                                this._defines.INVERTCUBICMAP = true;
-                            }
-                            this._defines.REFLECTIONMAP_3D = this.reflectionTexture.isCube;
-                            switch (this.reflectionTexture.coordinatesMode) {
-                                case BABYLON.Texture.CUBIC_MODE:
-                                case BABYLON.Texture.INVCUBIC_MODE:
-                                    this._defines.REFLECTIONMAP_CUBIC = true;
-                                    break;
-                                case BABYLON.Texture.EXPLICIT_MODE:
-                                    this._defines.REFLECTIONMAP_EXPLICIT = true;
-                                    break;
-                                case BABYLON.Texture.PLANAR_MODE:
-                                    this._defines.REFLECTIONMAP_PLANAR = true;
-                                    break;
-                                case BABYLON.Texture.PROJECTION_MODE:
-                                    this._defines.REFLECTIONMAP_PROJECTION = true;
-                                    break;
-                                case BABYLON.Texture.SKYBOX_MODE:
-                                    this._defines.REFLECTIONMAP_SKYBOX = true;
-                                    break;
-                                case BABYLON.Texture.SPHERICAL_MODE:
-                                    this._defines.REFLECTIONMAP_SPHERICAL = true;
-                                    break;
-                                case BABYLON.Texture.EQUIRECTANGULAR_MODE:
-                                    this._defines.REFLECTIONMAP_EQUIRECTANGULAR = true;
-                                    break;
-                            }
-                            if (this.reflectionTexture instanceof BABYLON.HDRCubeTexture && this.reflectionTexture) {
-                                this._defines.USESPHERICALFROMREFLECTIONMAP = true;
-                                needNormals = true;
-                                if (this.reflectionTexture.isPMREM) {
-                                    this._defines.USEPMREMREFLECTION = true;
-                                }
+                            if (this.reflectionTexture.isPMREM) {
+                                this._defines.USEPMREMREFLECTION = true;
                             }
                         }
                     }
-                    if (this.lightmapTexture && BABYLON.StandardMaterial.LightmapTextureEnabled) {
-                        if (!this.lightmapTexture.isReady()) {
-                            return false;
-                        }
-                        else {
-                            needUVs = true;
-                            this._defines.LIGHTMAP = true;
-                            this._defines.USELIGHTMAPASSHADOWMAP = this.useLightmapAsShadowmap;
-                        }
+                }
+                if (this.lightmapTexture && BABYLON.StandardMaterial.LightmapTextureEnabled) {
+                    if (!this.lightmapTexture.isReady()) {
+                        return false;
                     }
-                    if (this.emissiveTexture && BABYLON.StandardMaterial.EmissiveTextureEnabled) {
-                        if (!this.emissiveTexture.isReady()) {
-                            return false;
-                        }
-                        else {
-                            needUVs = true;
-                            this._defines.EMISSIVE = true;
-                        }
+                    else {
+                        needUVs = true;
+                        this._defines.LIGHTMAP = true;
+                        this._defines.USELIGHTMAPASSHADOWMAP = this.useLightmapAsShadowmap;
                     }
-                    if (this.reflectivityTexture && BABYLON.StandardMaterial.SpecularTextureEnabled) {
-                        if (!this.reflectivityTexture.isReady()) {
-                            return false;
-                        }
-                        else {
-                            needUVs = true;
-                            this._defines.REFLECTIVITY = true;
-                            this._defines.MICROSURFACEFROMREFLECTIVITYMAP = this.useMicroSurfaceFromReflectivityMapAlpha;
-                            this._defines.MICROSURFACEAUTOMATIC = this.useAutoMicroSurfaceFromReflectivityMap;
-                        }
+                }
+                if (this.emissiveTexture && BABYLON.StandardMaterial.EmissiveTextureEnabled) {
+                    if (!this.emissiveTexture.isReady()) {
+                        return false;
+                    }
+                    else {
+                        needUVs = true;
+                        this._defines.EMISSIVE = true;
+                    }
+                }
+                if (this.reflectivityTexture && BABYLON.StandardMaterial.SpecularTextureEnabled) {
+                    if (!this.reflectivityTexture.isReady()) {
+                        return false;
+                    }
+                    else {
+                        needUVs = true;
+                        this._defines.REFLECTIVITY = true;
+                        this._defines.MICROSURFACEFROMREFLECTIVITYMAP = this.useMicroSurfaceFromReflectivityMapAlpha;
+                        this._defines.MICROSURFACEAUTOMATIC = this.useAutoMicroSurfaceFromReflectivityMap;
                     }
                 }
                 if (scene.getEngine().getCaps().standardDerivatives && this.bumpTexture && BABYLON.StandardMaterial.BumpTextureEnabled && !this.disableBumpMap) {
@@ -558,7 +559,7 @@ var BABYLON;
                     else {
                         needUVs = true;
                         this._defines.BUMP = true;
-                        if (this.useParallax) {
+                        if (this.useParallax && this.albedoTexture && BABYLON.StandardMaterial.DiffuseTextureEnabled) {
                             this._defines.PARALLAX = true;
                             if (this.useParallaxOcclusion) {
                                 this._defines.PARALLAXOCCLUSION = true;
@@ -771,10 +772,6 @@ var BABYLON;
                 BABYLON.MaterialHelper.PrepareAttributesForBones(attribs, mesh, this._defines, fallbacks);
                 BABYLON.MaterialHelper.PrepareAttributesForInstances(attribs, this._defines);
                 // Legacy browser patch
-                var shaderName = "pbr";
-                if (!scene.getEngine().getCaps().standardDerivatives) {
-                    shaderName = "legacypbr";
-                }
                 var join = this._defines.toString();
                 var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vEmissiveColor", "vReflectionColor",
                     "vFogInfos", "vFogColor", "pointSize",
@@ -795,7 +792,7 @@ var BABYLON;
                 BABYLON.ColorCurves.PrepareUniforms(uniforms);
                 BABYLON.ColorGradingTexture.PrepareUniformsAndSamplers(uniforms, samplers);
                 BABYLON.MaterialHelper.PrepareUniformsAndSamplersList(uniforms, samplers, this._defines, this.maxSimultaneousLights);
-                this._effect = scene.getEngine().createEffect(shaderName, attribs, uniforms, samplers, join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights });
+                this._effect = scene.getEngine().createEffect("pbr", attribs, uniforms, samplers, join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights });
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -848,7 +845,7 @@ var BABYLON;
                     }
                     if (this.ambientTexture && BABYLON.StandardMaterial.AmbientTextureEnabled) {
                         this._effect.setTexture("ambientSampler", this.ambientTexture);
-                        this._effect.setFloat2("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level);
+                        this._effect.setFloat3("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level, this.ambientTextureStrength);
                         this._effect.setMatrix("ambientMatrix", this.ambientTexture.getTextureMatrix());
                     }
                     if (this.opacityTexture && BABYLON.StandardMaterial.OpacityTextureEnabled) {
@@ -1155,6 +1152,9 @@ var BABYLON;
             BABYLON.serializeAsTexture()
         ], PBRMaterial.prototype, "ambientTexture", void 0);
         __decorate([
+            BABYLON.serialize()
+        ], PBRMaterial.prototype, "ambientTextureStrength", void 0);
+        __decorate([
             BABYLON.serializeAsTexture()
         ], PBRMaterial.prototype, "opacityTexture", void 0);
         __decorate([

+ 99 - 100
src/Materials/babylon.pbrMaterial.ts

@@ -261,6 +261,12 @@
         @serializeAsTexture()
         public ambientTexture: BaseTexture;
 
+        /**
+         * AKA Occlusion Texture Intensity in other nomenclature.
+         */
+        @serialize()
+        public ambientTextureStrength: number = 1.0;
+
         @serializeAsTexture()
         public opacityTexture: BaseTexture;
 
@@ -613,120 +619,117 @@
             this._defines.reset();
 
             if (scene.texturesEnabled) {
-                // Textures
-                if (scene.texturesEnabled) {
-                    if (scene.getEngine().getCaps().textureLOD) {
-                        this._defines.LODBASEDMICROSFURACE = true;
-                    }
+                if (scene.getEngine().getCaps().textureLOD) {
+                    this._defines.LODBASEDMICROSFURACE = true;
+                }
 
-                    if (this.albedoTexture && StandardMaterial.DiffuseTextureEnabled) {
-                        if (!this.albedoTexture.isReady()) {
-                            return false;
-                        } else {
-                            needUVs = true;
-                            this._defines.ALBEDO = true;
-                        }
+                if (this.albedoTexture && StandardMaterial.DiffuseTextureEnabled) {
+                    if (!this.albedoTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.ALBEDO = true;
                     }
+                }
 
-                    if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
-                        if (!this.ambientTexture.isReady()) {
-                            return false;
-                        } else {
-                            needUVs = true;
-                            this._defines.AMBIENT = true;
-                        }
+                if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
+                    if (!this.ambientTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.AMBIENT = true;
                     }
+                }
 
-                    if (this.opacityTexture && StandardMaterial.OpacityTextureEnabled) {
-                        if (!this.opacityTexture.isReady()) {
-                            return false;
-                        } else {
-                            needUVs = true;
-                            this._defines.OPACITY = true;
+                if (this.opacityTexture && StandardMaterial.OpacityTextureEnabled) {
+                    if (!this.opacityTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.OPACITY = true;
 
-                            if (this.opacityTexture.getAlphaFromRGB) {
-                                this._defines.OPACITYRGB = true;
-                            }
+                        if (this.opacityTexture.getAlphaFromRGB) {
+                            this._defines.OPACITYRGB = true;
                         }
                     }
+                }
 
-                    if (this.reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
-                        if (!this.reflectionTexture.isReady()) {
-                            return false;
-                        } else {
-                            needNormals = true;
-                            this._defines.REFLECTION = true;
+                if (this.reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
+                    if (!this.reflectionTexture.isReady()) {
+                        return false;
+                    } else {
+                        needNormals = true;
+                        this._defines.REFLECTION = true;
 
-                            if (this.reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) {
-                                this._defines.INVERTCUBICMAP = true;
-                            }
+                        if (this.reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) {
+                            this._defines.INVERTCUBICMAP = true;
+                        }
 
-                            this._defines.REFLECTIONMAP_3D = this.reflectionTexture.isCube;
-
-                            switch (this.reflectionTexture.coordinatesMode) {
-                                case Texture.CUBIC_MODE:
-                                case Texture.INVCUBIC_MODE:
-                                    this._defines.REFLECTIONMAP_CUBIC = true;
-                                    break;
-                                case Texture.EXPLICIT_MODE:
-                                    this._defines.REFLECTIONMAP_EXPLICIT = true;
-                                    break;
-                                case Texture.PLANAR_MODE:
-                                    this._defines.REFLECTIONMAP_PLANAR = true;
-                                    break;
-                                case Texture.PROJECTION_MODE:
-                                    this._defines.REFLECTIONMAP_PROJECTION = true;
-                                    break;
-                                case Texture.SKYBOX_MODE:
-                                    this._defines.REFLECTIONMAP_SKYBOX = true;
-                                    break;
-                                case Texture.SPHERICAL_MODE:
-                                    this._defines.REFLECTIONMAP_SPHERICAL = true;
-                                    break;
-                                case Texture.EQUIRECTANGULAR_MODE:
-                                    this._defines.REFLECTIONMAP_EQUIRECTANGULAR = true;
-                                    break;
-                            }
+                        this._defines.REFLECTIONMAP_3D = this.reflectionTexture.isCube;
+
+                        switch (this.reflectionTexture.coordinatesMode) {
+                            case Texture.CUBIC_MODE:
+                            case Texture.INVCUBIC_MODE:
+                                this._defines.REFLECTIONMAP_CUBIC = true;
+                                break;
+                            case Texture.EXPLICIT_MODE:
+                                this._defines.REFLECTIONMAP_EXPLICIT = true;
+                                break;
+                            case Texture.PLANAR_MODE:
+                                this._defines.REFLECTIONMAP_PLANAR = true;
+                                break;
+                            case Texture.PROJECTION_MODE:
+                                this._defines.REFLECTIONMAP_PROJECTION = true;
+                                break;
+                            case Texture.SKYBOX_MODE:
+                                this._defines.REFLECTIONMAP_SKYBOX = true;
+                                break;
+                            case Texture.SPHERICAL_MODE:
+                                this._defines.REFLECTIONMAP_SPHERICAL = true;
+                                break;
+                            case Texture.EQUIRECTANGULAR_MODE:
+                                this._defines.REFLECTIONMAP_EQUIRECTANGULAR = true;
+                                break;
+                        }
 
-                            if (this.reflectionTexture instanceof HDRCubeTexture && (<HDRCubeTexture>this.reflectionTexture)) {
-                                this._defines.USESPHERICALFROMREFLECTIONMAP = true;
-                                needNormals = true;
+                        if (this.reflectionTexture instanceof HDRCubeTexture && (<HDRCubeTexture>this.reflectionTexture)) {
+                            this._defines.USESPHERICALFROMREFLECTIONMAP = true;
+                            needNormals = true;
 
-                                if ((<HDRCubeTexture>this.reflectionTexture).isPMREM) {
-                                    this._defines.USEPMREMREFLECTION = true;
-                                }
+                            if ((<HDRCubeTexture>this.reflectionTexture).isPMREM) {
+                                this._defines.USEPMREMREFLECTION = true;
                             }
                         }
                     }
+                }
 
-                    if (this.lightmapTexture && StandardMaterial.LightmapTextureEnabled) {
-                        if (!this.lightmapTexture.isReady()) {
-                            return false;
-                        } else {
-                            needUVs = true;
-                            this._defines.LIGHTMAP = true;
-                            this._defines.USELIGHTMAPASSHADOWMAP = this.useLightmapAsShadowmap;
-                        }
+                if (this.lightmapTexture && StandardMaterial.LightmapTextureEnabled) {
+                    if (!this.lightmapTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.LIGHTMAP = true;
+                        this._defines.USELIGHTMAPASSHADOWMAP = this.useLightmapAsShadowmap;
                     }
+                }
 
-                    if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
-                        if (!this.emissiveTexture.isReady()) {
-                            return false;
-                        } else {
-                            needUVs = true;
-                            this._defines.EMISSIVE = true;
-                        }
+                if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
+                    if (!this.emissiveTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.EMISSIVE = true;
                     }
+                }
 
-                    if (this.reflectivityTexture && StandardMaterial.SpecularTextureEnabled) {
-                        if (!this.reflectivityTexture.isReady()) {
-                            return false;
-                        } else {
-                            needUVs = true;
-                            this._defines.REFLECTIVITY = true;
-                            this._defines.MICROSURFACEFROMREFLECTIVITYMAP = this.useMicroSurfaceFromReflectivityMapAlpha;
-                            this._defines.MICROSURFACEAUTOMATIC = this.useAutoMicroSurfaceFromReflectivityMap;
-                        }
+                if (this.reflectivityTexture && StandardMaterial.SpecularTextureEnabled) {
+                    if (!this.reflectivityTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.REFLECTIVITY = true;
+                        this._defines.MICROSURFACEFROMREFLECTIVITYMAP = this.useMicroSurfaceFromReflectivityMapAlpha;
+                        this._defines.MICROSURFACEAUTOMATIC = this.useAutoMicroSurfaceFromReflectivityMap;
                     }
                 }
 
@@ -737,7 +740,7 @@
                         needUVs = true;
                         this._defines.BUMP = true;
 
-                        if (this.useParallax) {
+                        if (this.useParallax && this.albedoTexture && StandardMaterial.DiffuseTextureEnabled) {
                             this._defines.PARALLAX = true;
                             if (this.useParallaxOcclusion) {
                                 this._defines.PARALLAXOCCLUSION = true;
@@ -1003,10 +1006,6 @@
                 MaterialHelper.PrepareAttributesForInstances(attribs, this._defines);
 
                 // Legacy browser patch
-                var shaderName = "pbr";
-                if (!scene.getEngine().getCaps().standardDerivatives) {
-                    shaderName = "legacypbr";
-                }
                 var join = this._defines.toString();
                 
                 var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vEmissiveColor", "vReflectionColor",
@@ -1031,7 +1030,7 @@
                 ColorGradingTexture.PrepareUniformsAndSamplers(uniforms, samplers); 
                 MaterialHelper.PrepareUniformsAndSamplersList(uniforms, samplers, this._defines, this.maxSimultaneousLights); 
                 
-                this._effect = scene.getEngine().createEffect(shaderName,
+                this._effect = scene.getEngine().createEffect("pbr",
                     attribs, uniforms, samplers,
                     join, fallbacks, this.onCompiled, this.onError, {maxSimultaneousLights: this.maxSimultaneousLights});
             }
@@ -1108,7 +1107,7 @@
                     if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
                         this._effect.setTexture("ambientSampler", this.ambientTexture);
 
-                        this._effect.setFloat2("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level);
+                        this._effect.setFloat3("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level, this.ambientTextureStrength);
                         this._effect.setMatrix("ambientMatrix", this.ambientTexture.getTextureMatrix());
                     }
 

+ 6 - 3
src/Math/babylon.math.js

@@ -856,9 +856,12 @@ var BABYLON;
             return result;
         };
         Vector3.TransformNormalToRef = function (vector, transformation, result) {
-            result.x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
-            result.y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
-            result.z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
+            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
+            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
+            var z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
+            result.x = x;
+            result.y = y;
+            result.z = z;
         };
         Vector3.TransformNormalFromFloatsToRef = function (x, y, z, transformation, result) {
             result.x = (x * transformation.m[0]) + (y * transformation.m[4]) + (z * transformation.m[8]);

+ 35 - 3
src/Math/babylon.math.ts

@@ -1074,9 +1074,12 @@
         }
 
         public static TransformNormalToRef(vector: Vector3, transformation: Matrix, result: Vector3): void {
-            result.x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
-            result.y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
-            result.z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
+            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
+            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
+            var z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
+            result.x = x;
+            result.y = y;
+            result.z = z;
         }
 
         public static TransformNormalFromFloatsToRef(x: number, y: number, z: number, transformation: Matrix, result: Vector3): void {
@@ -3113,6 +3116,35 @@
             result.m[14] = temp3 * plane.d;
             result.m[15] = 1.0;
         }
+
+        public static FromXYZAxesToRef(xaxis: Vector3, yaxis: Vector3, zaxis: Vector3, mat: Matrix) {
+            
+            mat.m[0] = xaxis.x;
+            mat.m[1] = xaxis.y;
+            mat.m[2] = xaxis.z;
+
+            mat.m[3] = 0;
+            
+            mat.m[4] = yaxis.x;
+            mat.m[5] = yaxis.y;
+            mat.m[6] = yaxis.z;
+            
+            mat.m[7] = 0;
+            
+            mat.m[8] = zaxis.x;
+            mat.m[9] = zaxis.y;
+            mat.m[10] = zaxis.z;
+            
+            mat.m[11] = 0;
+            
+            mat.m[12] = 0;
+            mat.m[13] = 0;
+            mat.m[14] = 0;
+            
+            mat.m[15] = 1;
+
+        }
+
     }
 
     export class Plane {

+ 1 - 1
src/Mesh/babylon.mesh.js

@@ -968,7 +968,7 @@ var BABYLON;
                 engine.setAlphaMode(effectiveMaterial.alphaMode);
             }
             // Draw
-            this._processRendering(subMesh, effect, fillMode, batch, hardwareInstancedRendering, this._onBeforeDraw);
+            this._processRendering(subMesh, effect, fillMode, batch, hardwareInstancedRendering, this._onBeforeDraw, effectiveMaterial);
             // Unbind
             effectiveMaterial.unbind();
             // Outline - step 2

+ 1 - 1
src/Mesh/babylon.mesh.ts

@@ -1067,7 +1067,7 @@
             }
 
             // Draw
-            this._processRendering(subMesh, effect, fillMode, batch, hardwareInstancedRendering, this._onBeforeDraw);
+            this._processRendering(subMesh, effect, fillMode, batch, hardwareInstancedRendering, this._onBeforeDraw, effectiveMaterial);
 
             // Unbind
             effectiveMaterial.unbind();

+ 5 - 1
src/Particles/babylon.solidParticle.js

@@ -11,7 +11,7 @@ var BABYLON;
          * `idxInShape` (integer) is the index of the particle in the current model (ex: the 10th box of addShape(box, 30))
          * `modelBoundingInfo` is the reference to the model BoundingInfo used for intersection computations.
          */
-        function SolidParticle(particleIndex, positionIndex, model, shapeId, idxInShape, modelBoundingInfo) {
+        function SolidParticle(particleIndex, positionIndex, model, shapeId, idxInShape, sps, modelBoundingInfo) {
             this.idx = 0; // particle global index
             this.color = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0); // color
             this.position = BABYLON.Vector3.Zero(); // position
@@ -29,6 +29,7 @@ var BABYLON;
             this._model = model;
             this.shapeId = shapeId;
             this.idxInShape = idxInShape;
+            this._sps = sps;
             if (modelBoundingInfo) {
                 this._modelBoundingInfo = modelBoundingInfo;
                 this._boundingInfo = new BABYLON.BoundingInfo(modelBoundingInfo.minimum, modelBoundingInfo.maximum);
@@ -69,6 +70,9 @@ var BABYLON;
             if (!this._boundingInfo || !target._boundingInfo) {
                 return false;
             }
+            if (this._sps._bSphereOnly) {
+                return BABYLON.BoundingSphere.Intersects(this._boundingInfo.boundingSphere, target._boundingInfo.boundingSphere);
+            }
             return this._boundingInfo.intersects(target._boundingInfo, false);
         };
         return SolidParticle;

+ 6 - 1
src/Particles/babylon.solidParticle.ts

@@ -17,6 +17,7 @@ module BABYLON {
         public idxInShape: number = 0;                  // index of the particle in its shape id
         public _modelBoundingInfo: BoundingInfo;        // reference to the shape model BoundingInfo object
         public _boundingInfo: BoundingInfo;             // particle BoundingInfo
+        public _sps: SolidParticleSystem;               // reference to the SPS what the particle belongs to
 
         /**
          * Creates a Solid Particle object.
@@ -28,12 +29,13 @@ module BABYLON {
          * `idxInShape` (integer) is the index of the particle in the current model (ex: the 10th box of addShape(box, 30))
          * `modelBoundingInfo` is the reference to the model BoundingInfo used for intersection computations.
          */
-        constructor(particleIndex: number, positionIndex: number, model: ModelShape, shapeId: number, idxInShape: number, modelBoundingInfo?: BoundingInfo) {
+        constructor(particleIndex: number, positionIndex: number, model: ModelShape, shapeId: number, idxInShape: number, sps: SolidParticleSystem, modelBoundingInfo?: BoundingInfo) {
             this.idx = particleIndex;
             this._pos = positionIndex;
             this._model = model;
             this.shapeId = shapeId;
             this.idxInShape = idxInShape;
+            this._sps = sps;
             if (modelBoundingInfo) {
                 this._modelBoundingInfo = modelBoundingInfo;
                 this._boundingInfo = new BoundingInfo(modelBoundingInfo.minimum, modelBoundingInfo.maximum);
@@ -71,6 +73,9 @@ module BABYLON {
             if (!this._boundingInfo || !target._boundingInfo) {
                 return false;
             }
+            if (this._sps._bSphereOnly) {
+                return BoundingSphere.Intersects(this._boundingInfo.boundingSphere, target._boundingInfo.boundingSphere);
+            }
             return this._boundingInfo.intersects(target._boundingInfo, false);
         }
     }

+ 27 - 34
src/Particles/babylon.solidParticleSystem.js

@@ -8,9 +8,12 @@ var BABYLON;
         * Creates a SPS (Solid Particle System) object.
         * `name` (String) is the SPS name, this will be the underlying mesh name.
         * `scene` (Scene) is the scene in which the SPS is added.
-        * `updatable` (default true) : if the SPS must be updatable or immutable.
-        * `isPickable` (default false) : if the solid particles must be pickable.
-        * `particleIntersection` (default false) : if the solid particle intersections must be computed
+        * `updatable` (optional boolean, default true) : if the SPS must be updatable or immutable.
+        * `isPickable` (optional boolean, default false) : if the solid particles must be pickable.
+        * `particleIntersection` (optional boolean, default false) : if the solid particle intersections must be computed.
+        * `boundingSphereOnly` (optional boolean, default false) : if the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster).
+        * `bSphereRadiusFactor` (optional float, default 1.0) : a number to multiply the boundind sphere radius by in order to reduce it for instance.
+        *  Example : bSphereRadiusFactor = 1.0 / Math.sqrt(3.0) => the bounding sphere exactly matches a spherical mesh.
         */
         function SolidParticleSystem(name, scene, options) {
             // public members
@@ -51,7 +54,7 @@ var BABYLON;
             this._isVisibilityBoxLocked = false;
             this._alwaysVisible = false;
             this._shapeCounter = 0;
-            this._copy = new BABYLON.SolidParticle(null, null, null, null, null);
+            this._copy = new BABYLON.SolidParticle(null, null, null, null, null, null);
             this._color = new BABYLON.Color4(0, 0, 0, 0);
             this._computeParticleColor = true;
             this._computeParticleTexture = true;
@@ -91,11 +94,15 @@ var BABYLON;
             this._minBbox = BABYLON.Tmp.Vector3[4];
             this._maxBbox = BABYLON.Tmp.Vector3[5];
             this._particlesIntersect = false;
+            this._bSphereOnly = false;
+            this._bSphereRadiusFactor = 1.0;
             this.name = name;
             this._scene = scene;
             this._camera = scene.activeCamera;
             this._pickable = options ? options.isPickable : false;
             this._particlesIntersect = options ? options.particleIntersection : false;
+            this._bSphereOnly = options ? options.boundingSphereOnly : false;
+            this._bSphereRadiusFactor = (options && options.bSphereRadiusFactor) ? options.bSphereRadiusFactor : 1.0;
             if (options && options.updatable) {
                 this._updatable = options.updatable;
             }
@@ -350,7 +357,7 @@ var BABYLON;
         };
         // adds a new particle object in the particles array
         SolidParticleSystem.prototype._addParticle = function (idx, idxpos, model, shapeId, idxInShape, bInfo) {
-            this.particles.push(new BABYLON.SolidParticle(idx, idxpos, model, shapeId, idxInShape, bInfo));
+            this.particles.push(new BABYLON.SolidParticle(idx, idxpos, model, shapeId, idxInShape, this, bInfo));
         };
         /**
         * Adds some particles to the SPS from the model shape. Returns the shape id.
@@ -616,9 +623,9 @@ var BABYLON;
                     var bInfo = this._particle._boundingInfo;
                     var bBox = bInfo.boundingBox;
                     var bSphere = bInfo.boundingSphere;
-                    // place, scale and rotate the particle bbox within the SPS local system
-                    for (var b = 0; b < bBox.vectors.length; b++) {
-                        if (this._particle.isVisible) {
+                    if (!this._bSphereOnly) {
+                        // place, scale and rotate the particle bbox within the SPS local system, then update it
+                        for (var b = 0; b < bBox.vectors.length; b++) {
                             this._vertex.x = this._particle._modelBoundingInfo.boundingBox.vectors[b].x * this._particle.scaling.x;
                             this._vertex.y = this._particle._modelBoundingInfo.boundingBox.vectors[b].y * this._particle.scaling.y;
                             this._vertex.z = this._particle._modelBoundingInfo.boundingBox.vectors[b].z * this._particle.scaling.z;
@@ -630,33 +637,19 @@ var BABYLON;
                             bBox.vectors[b].y = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
                             bBox.vectors[b].z = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
                         }
-                        else {
-                            bBox.vectors[b].x = this._camera.position.x;
-                            bBox.vectors[b].y = this._camera.position.y;
-                            bBox.vectors[b].z = this._camera.position.z;
-                        }
-                    }
-                    // place and scale the particle bouding sphere in the SPS local system
-                    if (this._particle.isVisible) {
-                        this._minBbox.x = this._particle._modelBoundingInfo.minimum.x * this._particle.scaling.x;
-                        this._minBbox.y = this._particle._modelBoundingInfo.minimum.y * this._particle.scaling.y;
-                        this._minBbox.z = this._particle._modelBoundingInfo.minimum.z * this._particle.scaling.z;
-                        this._maxBbox.x = this._particle._modelBoundingInfo.maximum.x * this._particle.scaling.x;
-                        this._maxBbox.y = this._particle._modelBoundingInfo.maximum.y * this._particle.scaling.y;
-                        this._maxBbox.z = this._particle._modelBoundingInfo.maximum.z * this._particle.scaling.z;
-                        bSphere.center.x = this._particle.position.x + (this._minBbox.x + this._maxBbox.x) * 0.5;
-                        bSphere.center.y = this._particle.position.y + (this._minBbox.y + this._maxBbox.y) * 0.5;
-                        bSphere.center.z = this._particle.position.z + (this._minBbox.z + this._maxBbox.z) * 0.5;
-                        bSphere.radius = BABYLON.Vector3.Distance(this._minimum, this._maximum) * 0.5;
-                    }
-                    else {
-                        bSphere.center.x = this._camera.position.x;
-                        bSphere.center.y = this._camera.position.x;
-                        bSphere.center.z = this._camera.position.x;
-                        bSphere.radius = 0.0;
+                        bBox._update(this.mesh._worldMatrix);
                     }
-                    // then update the bbox and the bsphere into the world system
-                    bBox._update(this.mesh._worldMatrix);
+                    // place and scale the particle bouding sphere in the SPS local system, then update it
+                    this._minBbox.x = this._particle._modelBoundingInfo.minimum.x * this._particle.scaling.x;
+                    this._minBbox.y = this._particle._modelBoundingInfo.minimum.y * this._particle.scaling.y;
+                    this._minBbox.z = this._particle._modelBoundingInfo.minimum.z * this._particle.scaling.z;
+                    this._maxBbox.x = this._particle._modelBoundingInfo.maximum.x * this._particle.scaling.x;
+                    this._maxBbox.y = this._particle._modelBoundingInfo.maximum.y * this._particle.scaling.y;
+                    this._maxBbox.z = this._particle._modelBoundingInfo.maximum.z * this._particle.scaling.z;
+                    bSphere.center.x = this._particle.position.x + (this._minBbox.x + this._maxBbox.x) * 0.5;
+                    bSphere.center.y = this._particle.position.y + (this._minBbox.y + this._maxBbox.y) * 0.5;
+                    bSphere.center.z = this._particle.position.z + (this._minBbox.z + this._maxBbox.z) * 0.5;
+                    bSphere.radius = this._bSphereRadiusFactor * 0.5 * Math.sqrt((this._maxBbox.x - this._minBbox.x) * (this._maxBbox.x - this._minBbox.x) + (this._maxBbox.y - this._minBbox.y) * (this._maxBbox.y - this._minBbox.y) + (this._maxBbox.z - this._minBbox.z) * (this._maxBbox.z - this._minBbox.z));
                     bSphere._update(this.mesh._worldMatrix);
                 }
                 // increment indexes for the next particle

+ 29 - 37
src/Particles/babylon.solidParticleSystem.ts

@@ -67,7 +67,7 @@
         private _isVisibilityBoxLocked = false;
         private _alwaysVisible: boolean = false;
         private _shapeCounter: number = 0;
-        private _copy: SolidParticle = new SolidParticle(null, null, null, null, null);
+        private _copy: SolidParticle = new SolidParticle(null, null, null, null, null, null);
         private _shape: Vector3[];
         private _shapeUV: number[];
         private _color: Color4 = new Color4(0, 0, 0, 0);
@@ -111,21 +111,28 @@
         private _minBbox: Vector3 = Tmp.Vector3[4];
         private _maxBbox: Vector3 = Tmp.Vector3[5];
         private _particlesIntersect: boolean = false;
+        public _bSphereOnly: boolean = false;
+        public _bSphereRadiusFactor: number = 1.0;
 
         /**
         * Creates a SPS (Solid Particle System) object.
         * `name` (String) is the SPS name, this will be the underlying mesh name.  
         * `scene` (Scene) is the scene in which the SPS is added.  
-        * `updatable` (default true) : if the SPS must be updatable or immutable.  
-        * `isPickable` (default false) : if the solid particles must be pickable.  
-        * `particleIntersection` (default false) : if the solid particle intersections must be computed
+        * `updatable` (optional boolean, default true) : if the SPS must be updatable or immutable.  
+        * `isPickable` (optional boolean, default false) : if the solid particles must be pickable.  
+        * `particleIntersection` (optional boolean, default false) : if the solid particle intersections must be computed.    
+        * `boundingSphereOnly` (optional boolean, default false) : if the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster).  
+        * `bSphereRadiusFactor` (optional float, default 1.0) : a number to multiply the boundind sphere radius by in order to reduce it for instance. 
+        *  Example : bSphereRadiusFactor = 1.0 / Math.sqrt(3.0) => the bounding sphere exactly matches a spherical mesh.  
         */
-        constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; particleIntersection?: boolean }) {
+        constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; particleIntersection?: boolean; boundingSphereOnly?: boolean; bSphereRadiusFactor?: number }) {
             this.name = name;
             this._scene = scene;
             this._camera = <TargetCamera>scene.activeCamera;
             this._pickable = options ? options.isPickable : false;
             this._particlesIntersect = options ? options.particleIntersection : false;
+            this._bSphereOnly= options ? options.boundingSphereOnly : false;
+            this._bSphereRadiusFactor = (options && options.bSphereRadiusFactor) ? options.bSphereRadiusFactor : 1.0;
             if (options && options.updatable) {
                 this._updatable = options.updatable;
             } else {
@@ -404,7 +411,7 @@
 
         // adds a new particle object in the particles array
         private _addParticle(idx: number, idxpos: number, model: ModelShape, shapeId: number, idxInShape: number, bInfo?: BoundingInfo): void {
-            this.particles.push(new SolidParticle(idx, idxpos, model, shapeId, idxInShape, bInfo));
+            this.particles.push(new SolidParticle(idx, idxpos, model, shapeId, idxInShape, this, bInfo));
         }
 
         /**
@@ -704,11 +711,10 @@
                 if (this._particlesIntersect) {
                     var bInfo = this._particle._boundingInfo;
                     var bBox = bInfo.boundingBox;
-                    var bSphere = bInfo.boundingSphere;
-                    
-                    // place, scale and rotate the particle bbox within the SPS local system
-                    for (var b = 0; b < bBox.vectors.length; b++) {
-                        if (this._particle.isVisible) {
+                    var bSphere = bInfo.boundingSphere;                   
+                    if (!this._bSphereOnly) {
+                        // place, scale and rotate the particle bbox within the SPS local system, then update it
+                        for (var b = 0; b < bBox.vectors.length; b++) {
                             this._vertex.x = this._particle._modelBoundingInfo.boundingBox.vectors[b].x * this._particle.scaling.x;
                             this._vertex.y = this._particle._modelBoundingInfo.boundingBox.vectors[b].y * this._particle.scaling.y;
                             this._vertex.z = this._particle._modelBoundingInfo.boundingBox.vectors[b].z * this._particle.scaling.z;
@@ -720,33 +726,19 @@
                             bBox.vectors[b].y = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
                             bBox.vectors[b].z = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
                         }
-                        else {
-                            bBox.vectors[b].x = this._camera.position.x;
-                            bBox.vectors[b].y = this._camera.position.y;
-                            bBox.vectors[b].z = this._camera.position.z;
-                        }
-                    }
-                    // place and scale the particle bouding sphere in the SPS local system
-                    if (this._particle.isVisible) {
-                        this._minBbox.x = this._particle._modelBoundingInfo.minimum.x * this._particle.scaling.x;
-                        this._minBbox.y = this._particle._modelBoundingInfo.minimum.y * this._particle.scaling.y;
-                        this._minBbox.z = this._particle._modelBoundingInfo.minimum.z * this._particle.scaling.z;
-                        this._maxBbox.x = this._particle._modelBoundingInfo.maximum.x * this._particle.scaling.x;
-                        this._maxBbox.y = this._particle._modelBoundingInfo.maximum.y * this._particle.scaling.y;
-                        this._maxBbox.z = this._particle._modelBoundingInfo.maximum.z * this._particle.scaling.z;
-                        bSphere.center.x = this._particle.position.x + (this._minBbox.x + this._maxBbox.x) * 0.5;
-                        bSphere.center.y = this._particle.position.y + (this._minBbox.y + this._maxBbox.y) * 0.5;
-                        bSphere.center.z = this._particle.position.z + (this._minBbox.z + this._maxBbox.z) * 0.5;
-                        bSphere.radius = Vector3.Distance(this._minimum, this._maximum) * 0.5;
-                    } else {
-                        bSphere.center.x = this._camera.position.x;
-                        bSphere.center.y = this._camera.position.x;
-                        bSphere.center.z = this._camera.position.x;
-                        bSphere.radius = 0.0;                       
+                        bBox._update(this.mesh._worldMatrix);
                     }
-
-                    // then update the bbox and the bsphere into the world system
-                    bBox._update(this.mesh._worldMatrix);
+                    // place and scale the particle bouding sphere in the SPS local system, then update it
+                    this._minBbox.x = this._particle._modelBoundingInfo.minimum.x * this._particle.scaling.x;
+                    this._minBbox.y = this._particle._modelBoundingInfo.minimum.y * this._particle.scaling.y;
+                    this._minBbox.z = this._particle._modelBoundingInfo.minimum.z * this._particle.scaling.z;
+                    this._maxBbox.x = this._particle._modelBoundingInfo.maximum.x * this._particle.scaling.x;
+                    this._maxBbox.y = this._particle._modelBoundingInfo.maximum.y * this._particle.scaling.y;
+                    this._maxBbox.z = this._particle._modelBoundingInfo.maximum.z * this._particle.scaling.z;
+                    bSphere.center.x = this._particle.position.x + (this._minBbox.x + this._maxBbox.x) * 0.5;
+                    bSphere.center.y = this._particle.position.y + (this._minBbox.y + this._maxBbox.y) * 0.5;
+                    bSphere.center.z = this._particle.position.z + (this._minBbox.z + this._maxBbox.z) * 0.5;
+                    bSphere.radius = this._bSphereRadiusFactor * 0.5 * Math.sqrt((this._maxBbox.x - this._minBbox.x) * (this._maxBbox.x - this._minBbox.x) + (this._maxBbox.y - this._minBbox.y) * (this._maxBbox.y - this._minBbox.y) + (this._maxBbox.z - this._minBbox.z) * (this._maxBbox.z - this._minBbox.z));
                     bSphere._update(this.mesh._worldMatrix);
                 }
 

+ 5 - 10
src/Shaders/ShadersInclude/bumpFragment.fx

@@ -1,24 +1,19 @@
-#ifdef BUMP
-	vec2 bumpUV = vBumpUV;
-#endif
+vec2 uvOffset = vec2(0.0, 0.0);
 
 #if defined(BUMP) || defined(PARALLAX)
-	mat3 TBN = cotangent_frame(normalW * vBumpInfos.y, -viewDirectionW, bumpUV);
+	mat3 TBN = cotangent_frame(normalW * vBumpInfos.y, -viewDirectionW, vBumpUV);
 #endif
 
 #ifdef PARALLAX
 	mat3 invTBN = transposeMat3(TBN);
 
 	#ifdef PARALLAXOCCLUSION
-		vec2 uvOffset = parallaxOcclusion(invTBN * -viewDirectionW, invTBN * normalW, bumpUV, vBumpInfos.z);
+		uvOffset = parallaxOcclusion(invTBN * -viewDirectionW, invTBN * normalW, vBumpUV, vBumpInfos.z);
 	#else
-		vec2 uvOffset = parallaxOffset(invTBN * viewDirectionW, vBumpInfos.z);
+		uvOffset = parallaxOffset(invTBN * viewDirectionW, vBumpInfos.z);
 	#endif
-
-	diffuseUV += uvOffset;
-	bumpUV += uvOffset;
 #endif
 
 #ifdef BUMP
-	normalW = perturbNormal(viewDirectionW, TBN, bumpUV);
+	normalW = perturbNormal(viewDirectionW, TBN, vBumpUV + uvOffset);
 #endif

+ 6 - 10
src/Shaders/default.fragment.fx

@@ -176,14 +176,10 @@ void main(void) {
 	vec3 normalW = vec3(1.0, 1.0, 1.0);
 #endif
 
-#ifdef DIFFUSE
-	vec2 diffuseUV = vDiffuseUV;
-#endif
-
 #include<bumpFragment>
 
 #ifdef DIFFUSE
-	baseColor = texture2D(diffuseSampler, diffuseUV);
+	baseColor = texture2D(diffuseSampler, vDiffuseUV + uvOffset);
 
 #ifdef ALPHATEST
 	if (baseColor.a < 0.4)
@@ -205,7 +201,7 @@ void main(void) {
 	vec3 baseAmbientColor = vec3(1., 1., 1.);
 
 #ifdef AMBIENT
-	baseAmbientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
+	baseAmbientColor = texture2D(ambientSampler, vAmbientUV + uvOffset).rgb * vAmbientInfos.y;
 #endif
 
 	// Specular map
@@ -214,7 +210,7 @@ void main(void) {
 	vec3 specularColor = vSpecularColor.rgb;
 
 #ifdef SPECULAR
-	vec4 specularMapColor = texture2D(specularSampler, vSpecularUV);
+	vec4 specularMapColor = texture2D(specularSampler, vSpecularUV + uvOffset);
 	specularColor = specularMapColor.rgb;
 #ifdef GLOSSINESS
 	glossiness = glossiness * specularMapColor.a;
@@ -233,7 +229,7 @@ void main(void) {
 	float shadow = 1.;
 
 #ifdef LIGHTMAP
-	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV).rgb * vLightmapInfos.y;
+	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb * vLightmapInfos.y;
 #endif
 
 #include<lightFragment>[0..maxSimultaneousLights]
@@ -319,7 +315,7 @@ void main(void) {
 #endif
 
 #ifdef OPACITY
-	vec4 opacityMap = texture2D(opacitySampler, vOpacityUV);
+	vec4 opacityMap = texture2D(opacitySampler, vOpacityUV + uvOffset);
 
 #ifdef OPACITYRGB
 	opacityMap.rgb = opacityMap.rgb * vec3(0.3, 0.59, 0.11);
@@ -343,7 +339,7 @@ void main(void) {
 	// Emissive
 	vec3 emissiveColor = vEmissiveColor;
 #ifdef EMISSIVE
-	emissiveColor += texture2D(emissiveSampler, vEmissiveUV).rgb * vEmissiveInfos.y;
+	emissiveColor += texture2D(emissiveSampler, vEmissiveUV + uvOffset).rgb * vEmissiveInfos.y;
 #endif
 
 #ifdef EMISSIVEFRESNEL

+ 0 - 226
src/Shaders/legacydefault.fragment.fx

@@ -1,226 +0,0 @@
-#define MAP_PROJECTION	4.
-
-// Constants
-uniform vec3 vEyePosition;
-uniform vec3 vAmbientColor;
-uniform vec4 vDiffuseColor;
-#ifdef SPECULARTERM
-uniform vec4 vSpecularColor;
-#endif
-uniform vec3 vEmissiveColor;
-
-// Input
-varying vec3 vPositionW;
-varying vec3 vNormalW;
-
-#ifdef VERTEXCOLOR
-varying vec4 vColor;
-#endif
-
-// Lights
-#include<lightFragmentDeclaration>[0..3]
-
-#include<lightsFragmentFunctions>
-#include<shadowsFragmentFunctions>
-
-// Samplers
-#ifdef DIFFUSE
-varying vec2 vDiffuseUV;
-uniform sampler2D diffuseSampler;
-uniform vec2 vDiffuseInfos;
-#endif
-
-#ifdef AMBIENT
-varying vec2 vAmbientUV;
-uniform sampler2D ambientSampler;
-uniform vec2 vAmbientInfos;
-#endif
-
-#ifdef OPACITY	
-varying vec2 vOpacityUV;
-uniform sampler2D opacitySampler;
-uniform vec2 vOpacityInfos;
-#endif
-
-#ifdef REFLECTION
-varying vec3 vReflectionUVW;
-#ifdef REFLECTIONMAP_3D
-uniform samplerCube reflectionCubeSampler;
-#else
-uniform sampler2D reflection2DSampler;
-#endif
-uniform vec2 vReflectionInfos;
-#endif
-
-#ifdef EMISSIVE
-varying vec2 vEmissiveUV;
-uniform vec2 vEmissiveInfos;
-uniform sampler2D emissiveSampler;
-#endif
-
-#if defined(SPECULAR) && defined(SPECULARTERM)
-varying vec2 vSpecularUV;
-uniform vec2 vSpecularInfos;
-uniform sampler2D specularSampler;
-#endif
-
-// Fresnel
-#include<fresnelFunction>
-
-#ifdef DIFFUSEFRESNEL
-uniform vec4 diffuseLeftColor;
-uniform vec4 diffuseRightColor;
-#endif
-
-#ifdef OPACITYFRESNEL
-uniform vec4 opacityParts;
-#endif
-
-#ifdef REFLECTIONFRESNEL
-uniform vec4 reflectionLeftColor;
-uniform vec4 reflectionRightColor;
-#endif
-
-#ifdef EMISSIVEFRESNEL
-uniform vec4 emissiveLeftColor;
-uniform vec4 emissiveRightColor;
-#endif
-
-#include<clipPlaneFragmentDeclaration>
-#include<fogFragmentDeclaration>
-
-void main(void) {
-#include<clipPlaneFragment>
-
-	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
-
-	// Base color
-	vec4 baseColor = vec4(1., 1., 1., 1.);
-	vec3 diffuseColor = vDiffuseColor.rgb;
-
-#ifdef DIFFUSE
-	baseColor = texture2D(diffuseSampler, vDiffuseUV);
-
-#ifdef ALPHATEST
-	if (baseColor.a < 0.4)
-		discard;
-#endif
-
-	baseColor.rgb *= vDiffuseInfos.y;
-#endif
-
-#ifdef VERTEXCOLOR
-	baseColor.rgb *= vColor.rgb;
-#endif
-
-	// Bump
-	vec3 normalW = normalize(vNormalW);
-
-	// Ambient color
-	vec3 baseAmbientColor = vec3(1., 1., 1.);
-
-#ifdef AMBIENT
-	baseAmbientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
-#endif
-
-	// Lighting
-	vec3 diffuseBase = vec3(0., 0., 0.);
-	lightingInfo info;
-	float glossiness = 0.;
-#ifdef SPECULARTERM
-	vec3 specularBase = vec3(0., 0., 0.);
-	glossiness = vSpecularColor.a;
-#endif
-	float shadow = 1.;
-
-#include<lightFragment>[0..3]
-
-	// Reflection
-	vec3 reflectionColor = vec3(0., 0., 0.);
-
-#ifdef REFLECTION
-#ifdef REFLECTIONMAP_3D
-		reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW).rgb * vReflectionInfos.x;
-#else
-		vec2 coords = vReflectionUVW.xy;
-
-#ifdef REFLECTIONMAP_PROJECTION
-		coords /= vReflectionUVW.z;
-#endif
-
-		coords.y = 1.0 - coords.y;
-
-		reflectionColor = texture2D(reflection2DSampler, coords).rgb * vReflectionInfos.x;
-#endif
-
-#ifdef REFLECTIONFRESNEL
-	float reflectionFresnelTerm = computeFresnelTerm(viewDirectionW, normalW, reflectionRightColor.a, reflectionLeftColor.a);
-
-	reflectionColor *= reflectionLeftColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
-#endif
-#endif
-
-	// Alpha
-	float alpha = vDiffuseColor.a;
-
-#ifdef OPACITY
-	vec4 opacityMap = texture2D(opacitySampler, vOpacityUV);
-#ifdef OPACITYRGB
-	opacityMap.rgb = opacityMap.rgb * vec3(0.3, 0.59, 0.11);
-	alpha *= (opacityMap.x + opacityMap.y + opacityMap.z)* vOpacityInfos.y;
-#else
-	alpha *= opacityMap.a * vOpacityInfos.y;
-#endif
-#endif
-
-#ifdef VERTEXALPHA
-	alpha *= vColor.a;
-#endif
-
-#ifdef OPACITYFRESNEL
-	float opacityFresnelTerm = computeFresnelTerm(viewDirectionW, normalW, opacityParts.z, opacityParts.w);
-
-	alpha += opacityParts.x * (1.0 - opacityFresnelTerm) + opacityFresnelTerm * opacityParts.y;
-#endif
-
-	// Emissive
-	vec3 emissiveColor = vEmissiveColor;
-#ifdef EMISSIVE
-	emissiveColor += texture2D(emissiveSampler, vEmissiveUV).rgb * vEmissiveInfos.y;
-#endif
-
-#ifdef EMISSIVEFRESNEL
-	float emissiveFresnelTerm = computeFresnelTerm(viewDirectionW, normalW, emissiveRightColor.a, emissiveLeftColor.a);
-
-	emissiveColor *= emissiveLeftColor.rgb * (1.0 - emissiveFresnelTerm) + emissiveFresnelTerm * emissiveRightColor.rgb;
-#endif
-
-	// Specular map
-#ifdef SPECULARTERM
-	vec3 specularColor = vSpecularColor.rgb;
-#ifdef SPECULAR
-	specularColor = texture2D(specularSampler, vSpecularUV).rgb * vSpecularInfos.y;
-#endif
-#endif
-
-	// Fresnel
-#ifdef DIFFUSEFRESNEL
-	float diffuseFresnelTerm = computeFresnelTerm(viewDirectionW, normalW, diffuseRightColor.a, diffuseLeftColor.a);
-
-	diffuseBase *= diffuseLeftColor.rgb * (1.0 - diffuseFresnelTerm) + diffuseFresnelTerm * diffuseRightColor.rgb;
-#endif
-
-	// Composition
-	vec3 finalDiffuse = clamp(diffuseBase * diffuseColor + emissiveColor + vAmbientColor, 0.0, 1.0) * baseColor.rgb;
-#ifdef SPECULARTERM
-	vec3 finalSpecular = specularBase * specularColor;
-#else
-	vec3 finalSpecular = vec3(0.0);
-#endif
-
-	vec4 color = vec4(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor, alpha);
-
-#include<fogFragment>
-
-	gl_FragColor = color;
-}

+ 0 - 208
src/Shaders/legacydefault.vertex.fx

@@ -1,208 +0,0 @@
-// Attributes
-attribute vec3 position;
-attribute vec3 normal;
-#ifdef UV1
-attribute vec2 uv;
-#endif
-#ifdef UV2
-attribute vec2 uv2;
-#endif
-#ifdef VERTEXCOLOR
-attribute vec4 color;
-#endif
-#include<bonesDeclaration>
-
-// Uniforms
-uniform mat4 world;
-uniform mat4 view;
-uniform mat4 viewProjection;
-
-#ifdef DIFFUSE
-varying vec2 vDiffuseUV;
-uniform mat4 diffuseMatrix;
-uniform vec2 vDiffuseInfos;
-#endif
-
-#ifdef AMBIENT
-varying vec2 vAmbientUV;
-uniform mat4 ambientMatrix;
-uniform vec2 vAmbientInfos;
-#endif
-
-#ifdef OPACITY
-varying vec2 vOpacityUV;
-uniform mat4 opacityMatrix;
-uniform vec2 vOpacityInfos;
-#endif
-
-#ifdef EMISSIVE
-varying vec2 vEmissiveUV;
-uniform vec2 vEmissiveInfos;
-uniform mat4 emissiveMatrix;
-#endif
-
-#if defined(SPECULAR) && defined(SPECULARTERM)
-varying vec2 vSpecularUV;
-uniform vec2 vSpecularInfos;
-uniform mat4 specularMatrix;
-#endif
-
-#ifdef BUMP
-varying vec2 vBumpUV;
-uniform vec2 vBumpInfos;
-uniform mat4 bumpMatrix;
-#endif
-
-// Output
-varying vec3 vPositionW;
-varying vec3 vNormalW;
-
-#ifdef VERTEXCOLOR
-varying vec4 vColor;
-#endif
-
-#include<clipPlaneVertexDeclaration>
-#include<fogVertexDeclaration>
-#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
-
-#ifdef REFLECTION
-uniform vec3 vEyePosition;
-varying vec3 vReflectionUVW;
-uniform mat4 reflectionMatrix;
-
-vec3 computeReflectionCoords(vec4 worldPos, vec3 worldNormal)
-{
-#ifdef REFLECTIONMAP_SPHERICAL
-	vec3 coords = vec3(view * vec4(worldNormal, 0.0));
-
-	return vec3(reflectionMatrix * vec4(coords, 1.0));
-#endif
-
-#ifdef REFLECTIONMAP_PLANAR
-	vec3 viewDir = worldPos.xyz - vEyePosition;
-	vec3 coords = normalize(reflect(viewDir, worldNormal));
-
-	return vec3(reflectionMatrix * vec4(coords, 1));
-#endif
-
-#ifdef REFLECTIONMAP_CUBIC
-	vec3 viewDir = worldPos.xyz - vEyePosition;
-	vec3 coords = reflect(viewDir, worldNormal);
-#ifdef INVERTCUBICMAP
-	coords.y = 1.0 - coords.y;
-#endif
-	return vec3(reflectionMatrix * vec4(coords, 0));
-#endif
-
-#ifdef REFLECTIONMAP_PROJECTION
-	return vec3(reflectionMatrix * (view * worldPos));
-#endif
-
-#ifdef REFLECTIONMAP_SKYBOX
-	return position;
-#endif
-
-#ifdef REFLECTIONMAP_EXPLICIT
-	return vec3(0, 0, 0);
-#endif
-}
-#endif
-
-void main(void) {
-	mat4 finalWorld = world;
-
-#include<bonesVertex>
-
-	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
-
-	vec4 worldPos = finalWorld * vec4(position, 1.0);
-	vPositionW = vec3(worldPos);
-	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
-
-	// Texture coordinates
-#ifndef UV1
-	vec2 uv = vec2(0., 0.);
-#endif
-#ifndef UV2
-	vec2 uv2 = vec2(0., 0.);
-#endif
-
-#ifdef DIFFUSE
-	if (vDiffuseInfos.x == 0.)
-	{
-		vDiffuseUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#ifdef AMBIENT
-	if (vAmbientInfos.x == 0.)
-	{
-		vAmbientUV = vec2(ambientMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vAmbientUV = vec2(ambientMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#ifdef OPACITY
-	if (vOpacityInfos.x == 0.)
-	{
-		vOpacityUV = vec2(opacityMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vOpacityUV = vec2(opacityMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-	
-#ifdef REFLECTION
-	vReflectionUVW = computeReflectionCoords(vec4(vPositionW, 1.0), vNormalW);
-#endif
-
-#ifdef EMISSIVE
-	if (vEmissiveInfos.x == 0.)
-	{
-		vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vEmissiveUV = vec2(emissiveMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#if defined(SPECULAR) && defined(SPECULARTERM)
-	if (vSpecularInfos.x == 0.)
-	{
-		vSpecularUV = vec2(specularMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vSpecularUV = vec2(specularMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#ifdef BUMP
-	if (vBumpInfos.x == 0.)
-	{
-		vBumpUV = vec2(bumpMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vBumpUV = vec2(bumpMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#include<clipPlaneVertex>
-#include<fogVertex>
-#include<shadowsVertex>[0..maxSimultaneousLights]
-
-	// Vertex color
-#ifdef VERTEXCOLOR
-	vColor = color;
-#endif
-}

+ 0 - 331
src/Shaders/legacypbr.fragment.fx

@@ -1,331 +0,0 @@
-precision mediump float;
-
-// Constants
-#define RECIPROCAL_PI2 0.15915494
-#define FRESNEL_MAXIMUM_ON_ROUGH 0.25
-
-uniform vec3 vEyePosition;
-uniform vec3 vAmbientColor;
-uniform vec4 vAlbedoColor;
-uniform vec3 vReflectionColor;
-
-// CUSTOM CONTROLS
-uniform vec4 vLightingIntensity;
-uniform vec4 vCameraInfos;
-
-#ifdef OVERLOADEDVALUES
-uniform vec4 vOverloadedIntensity;
-uniform vec3 vOverloadedAmbient;
-uniform vec3 vOverloadedAlbedo;
-uniform vec3 vOverloadedReflectivity;
-uniform vec3 vOverloadedEmissive;
-uniform vec3 vOverloadedReflection;
-uniform vec3 vOverloadedMicroSurface;
-#endif
-
-#ifdef OVERLOADEDSHADOWVALUES
-uniform vec4 vOverloadedShadowIntensity;
-#endif
-
-uniform vec4 vReflectivityColor;
-uniform vec3 vEmissiveColor;
-
-// Input
-varying vec3 vPositionW;
-
-#ifdef NORMAL
-varying vec3 vNormalW;
-#endif
-
-#ifdef VERTEXCOLOR
-varying vec4 vColor;
-#endif
-
-// Lights
-#include<lightFragmentDeclaration>[0..maxSimultaneousLights]
-
-// Samplers
-#ifdef ALBEDO
-varying vec2 vAlbedoUV;
-uniform sampler2D albedoSampler;
-uniform vec2 vAlbedoInfos;
-#endif
-
-#ifdef AMBIENT
-varying vec2 vAmbientUV;
-uniform sampler2D ambientSampler;
-uniform vec2 vAmbientInfos;
-#endif
-
-#ifdef OPACITY	
-varying vec2 vOpacityUV;
-uniform sampler2D opacitySampler;
-uniform vec2 vOpacityInfos;
-#endif
-
-#ifdef EMISSIVE
-varying vec2 vEmissiveUV;
-uniform vec2 vEmissiveInfos;
-uniform sampler2D emissiveSampler;
-#endif
-
-#ifdef LIGHTMAP
-varying vec2 vLightmapUV;
-uniform vec2 vLightmapInfos;
-uniform sampler2D lightmapSampler;
-#endif
-
-#if defined(REFLECTIVITY)
-varying vec2 vReflectivityUV;
-uniform vec2 vReflectivityInfos;
-uniform sampler2D reflectivitySampler;
-#endif
-
-#include<clipPlaneFragmentDeclaration>
-
-#ifdef CAMERACOLORGRADING
-    uniform sampler2D cameraColorGrading2DSampler;
-    uniform vec4 vCameraColorGradingInfos;
-    uniform vec4 vCameraColorGradingScaleOffset;
-#endif
-
-// PBR
-#include<pbrFunctions>
-#include<harmonicsFunctions>
-#include<pbrLightFunctions>
-
-void main(void) {
-#include<clipPlaneFragment>
-
-    vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
-
-    // Base color
-    vec4 surfaceAlbedo = vec4(1., 1., 1., 1.);
-    vec3 surfaceAlbedoContribution = vAlbedoColor.rgb;
-    
-    // Alpha
-    float alpha = vAlbedoColor.a;
-
-    #ifdef ALBEDO
-        surfaceAlbedo = texture2D(albedoSampler, vAlbedoUV);
-        surfaceAlbedo = vec4(toLinearSpace(surfaceAlbedo.rgb), surfaceAlbedo.a);
-
-        #ifdef ALPHATEST
-            if (baseColor.a < 0.4)
-                discard;
-        #endif
-
-        #ifdef ALPHAFROMALBEDO
-            alpha *= surfaceAlbedo.a;
-        #endif
-
-        surfaceAlbedo.rgb *= vAlbedoInfos.y;
-    #else
-        // No Albedo texture.
-        surfaceAlbedo.rgb = surfaceAlbedoContribution;
-        surfaceAlbedoContribution = vec3(1., 1., 1.);
-    #endif
-
-    #ifdef VERTEXCOLOR
-        baseColor.rgb *= vColor.rgb;
-    #endif
-
-    #ifdef OVERLOADEDVALUES
-        surfaceAlbedo.rgb = mix(surfaceAlbedo.rgb, vOverloadedAlbedo, vOverloadedIntensity.y);
-    #endif
-
-    // Bump
-    #ifdef NORMAL
-        vec3 normalW = normalize(vNormalW);
-    #else
-        vec3 normalW = vec3(1.0, 1.0, 1.0);
-    #endif
-
-    // Ambient color
-    vec3 ambientColor = vec3(1., 1., 1.);
-
-    #ifdef AMBIENT
-        ambientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
-        
-        #ifdef OVERLOADEDVALUES
-            ambientColor.rgb = mix(ambientColor.rgb, vOverloadedAmbient, vOverloadedIntensity.x);
-        #endif
-    #endif
-
-    // Reflectivity map
-    float microSurface = vReflectivityColor.a;
-    vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
-    
-    #ifdef OVERLOADEDVALUES
-        surfaceReflectivityColor.rgb = mix(surfaceReflectivityColor.rgb, vOverloadedReflectivity, vOverloadedIntensity.z);
-    #endif
-
-    #ifdef REFLECTIVITY
-        vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV);
-        surfaceReflectivityColor = surfaceReflectivityColorMap.rgb;
-        surfaceReflectivityColor = toLinearSpace(surfaceReflectivityColor);
-
-        #ifdef OVERLOADEDVALUES
-            surfaceReflectivityColor = mix(surfaceReflectivityColor, vOverloadedReflectivity, vOverloadedIntensity.z);
-        #endif
-
-        #ifdef MICROSURFACEFROMREFLECTIVITYMAP
-            microSurface = surfaceReflectivityColorMap.a;
-        #else
-            #ifdef MICROSURFACEAUTOMATIC
-                microSurface = computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
-            #endif
-        #endif
-    #endif
-
-    #ifdef OVERLOADEDVALUES
-        microSurface = mix(microSurface, vOverloadedMicroSurface.x, vOverloadedMicroSurface.y);
-    #endif
-
-    // Compute N dot V.
-    float NdotV = max(0.00000000001, dot(normalW, viewDirectionW));
-
-    // Adapt microSurface.
-    microSurface = clamp(microSurface, 0., 1.) * 0.98;
-
-    // Compute roughness.
-    float roughness = clamp(1. - microSurface, 0.000001, 1.0);
-    
-    // Lighting
-    vec3 lightDiffuseContribution = vec3(0., 0., 0.);
-
-#ifdef OVERLOADEDSHADOWVALUES
-    vec3 shadowedOnlyLightDiffuseContribution = vec3(1., 1., 1.);
-#endif
-
-#ifdef SPECULARTERM
-    vec3 lightSpecularContribution= vec3(0., 0., 0.);
-#endif
-    float notShadowLevel = 1.; // 1 - shadowLevel
-    float NdotL = -1.;
-    lightingInfo info;
-
-#include<pbrLightFunctionsCall>[0..maxSimultaneousLights]
-
-#ifdef SPECULARTERM
-    lightSpecularContribution *= vLightingIntensity.w;
-#endif
-
-#ifdef OPACITY
-    vec4 opacityMap = texture2D(opacitySampler, vOpacityUV);
-
-    #ifdef OPACITYRGB
-        opacityMap.rgb = opacityMap.rgb * vec3(0.3, 0.59, 0.11);
-        alpha *= (opacityMap.x + opacityMap.y + opacityMap.z)* vOpacityInfos.y;
-    #else
-        alpha *= opacityMap.a * vOpacityInfos.y;
-    #endif
-
-#endif
-
-#ifdef VERTEXALPHA
-    alpha *= vColor.a;
-#endif
-
-// Reflection
-vec3 environmentRadiance = vReflectionColor.rgb;
-vec3 environmentIrradiance = vReflectionColor.rgb;
-
-#ifdef OVERLOADEDVALUES
-    environmentIrradiance = mix(environmentIrradiance, vOverloadedReflection, vOverloadedMicroSurface.z);
-    environmentRadiance = mix(environmentRadiance, vOverloadedReflection, vOverloadedMicroSurface.z);
-#endif
-
-environmentRadiance *= vLightingIntensity.z;
-environmentIrradiance *= vLightingIntensity.z;
-
-// Compute reflection reflectivity fresnel
-vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
-vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0);
-vec3 specularEnvironmentReflectance = FresnelSchlickEnvironmentGGX(clamp(NdotV, 0., 1.), specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
-
-// Apply Energy Conservation taking in account the environment level only if the environment is present.
-float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
-surfaceAlbedo.rgb = (1. - reflectance) * surfaceAlbedo.rgb;
-environmentRadiance *= specularEnvironmentReflectance;
-
-// Emissive
-vec3 surfaceEmissiveColor = vEmissiveColor;
-#ifdef EMISSIVE
-    vec3 emissiveColorTex = texture2D(emissiveSampler, vEmissiveUV).rgb;
-    surfaceEmissiveColor = toLinearSpace(emissiveColorTex.rgb) * surfaceEmissiveColor * vEmissiveInfos.y;
-#endif
-
-#ifdef OVERLOADEDVALUES
-    surfaceEmissiveColor = mix(surfaceEmissiveColor, vOverloadedEmissive, vOverloadedIntensity.w);
-#endif
-
-// Composition
-#ifdef EMISSIVEASILLUMINATION
-    vec3 finalDiffuse = max(lightDiffuseContribution * surfaceAlbedoContribution + vAmbientColor, 0.0) * surfaceAlbedo.rgb;
-    
-    #ifdef OVERLOADEDSHADOWVALUES
-        shadowedOnlyLightDiffuseContribution = max(shadowedOnlyLightDiffuseContribution * surfaceAlbedoContribution + vAmbientColor, 0.0) * surfaceAlbedo.rgb;
-    #endif
-#else
-    #ifdef LINKEMISSIVEWITHALBEDO
-        vec3 finalDiffuse = max((lightDiffuseContribution + surfaceEmissiveColor) * surfaceAlbedoContribution + vAmbientColor, 0.0) * surfaceAlbedo.rgb;
-
-        #ifdef OVERLOADEDSHADOWVALUES
-            shadowedOnlyLightDiffuseContribution = max((shadowedOnlyLightDiffuseContribution + surfaceEmissiveColor) * surfaceAlbedoContribution + vAmbientColor, 0.0) * surfaceAlbedo.rgb;
-        #endif
-    #else
-        vec3 finalDiffuse = max(lightDiffuseContribution * surfaceAlbedoContribution + surfaceEmissiveColor + vAmbientColor, 0.0) * surfaceAlbedo.rgb;
-
-        #ifdef OVERLOADEDSHADOWVALUES
-            shadowedOnlyLightDiffuseContribution = max(shadowedOnlyLightDiffuseContribution * surfaceAlbedoContribution + surfaceEmissiveColor + vAmbientColor, 0.0) * surfaceAlbedo.rgb;
-        #endif
-    #endif
-#endif
-
-#ifdef OVERLOADEDSHADOWVALUES
-    finalDiffuse = mix(finalDiffuse, shadowedOnlyLightDiffuseContribution, (1.0 - vOverloadedShadowIntensity.y));
-#endif
-
-#ifdef SPECULARTERM
-    vec3 finalSpecular = lightSpecularContribution * surfaceReflectivityColor;
-#else
-    vec3 finalSpecular = vec3(0.0);
-#endif
-
-#ifdef SPECULAROVERALPHA
-    alpha = clamp(alpha + getLuminance(finalSpecular), 0., 1.);
-#endif
-
-#ifdef RADIANCEOVERALPHA
-    alpha = clamp(alpha + getLuminance(environmentRadiance), 0., 1.);
-#endif
-
-// Composition
-// Reflection already includes the environment intensity.
-#ifdef EMISSIVEASILLUMINATION
-    vec4 finalColor = vec4(finalDiffuse * ambientColor * vLightingIntensity.x + surfaceAlbedo.rgb * environmentIrradiance + finalSpecular * vLightingIntensity.x + environmentRadiance + surfaceEmissiveColor * vLightingIntensity.y, alpha);
-#else
-    vec4 finalColor = vec4(finalDiffuse * ambientColor * vLightingIntensity.x + surfaceAlbedo.rgb * environmentIrradiance + finalSpecular * vLightingIntensity.x + environmentRadiance, alpha);
-#endif
-
-    finalColor = max(finalColor, 0.0);
-
-#ifdef CAMERATONEMAP
-    finalColor.rgb = toneMaps(finalColor.rgb);
-#endif
-
-    finalColor.rgb = toGammaSpace(finalColor.rgb);
-
-#ifdef CAMERACONTRAST
-    finalColor = contrasts(finalColor);
-#endif
-
-    finalColor.rgb = clamp(finalColor.rgb, 0., 1.);
-    
-#ifdef CAMERACOLORGRADING
-    finalColor = colorGrades(finalColor, cameraColorGrading2DSampler, vCameraColorGradingInfos, vCameraColorGradingScaleOffset);
-#endif
-
-    gl_FragColor = finalColor;
-}

+ 0 - 142
src/Shaders/legacypbr.vertex.fx

@@ -1,142 +0,0 @@
-precision mediump float;
-
-// Attributes
-attribute vec3 position;
-attribute vec3 normal;
-#ifdef UV1
-attribute vec2 uv;
-#endif
-#ifdef UV2
-attribute vec2 uv2;
-#endif
-#ifdef VERTEXCOLOR
-attribute vec4 color;
-#endif
-#include<bonesDeclaration>
-
-// Uniforms
-uniform mat4 world;
-uniform mat4 view;
-uniform mat4 viewProjection;
-
-#ifdef ALBEDO
-varying vec2 vAlbedoUV;
-uniform mat4 albedoMatrix;
-uniform vec2 vAlbedoInfos;
-#endif
-
-#ifdef AMBIENT
-varying vec2 vAmbientUV;
-uniform mat4 ambientMatrix;
-uniform vec2 vAmbientInfos;
-#endif
-
-#ifdef OPACITY
-varying vec2 vOpacityUV;
-uniform mat4 opacityMatrix;
-uniform vec2 vOpacityInfos;
-#endif
-
-#ifdef EMISSIVE
-varying vec2 vEmissiveUV;
-uniform vec2 vEmissiveInfos;
-uniform mat4 emissiveMatrix;
-#endif
-
-#if defined(REFLECTIVITY)
-varying vec2 vReflectivityUV;
-uniform vec2 vReflectivityInfos;
-uniform mat4 reflectivityMatrix;
-#endif
-
-// Output
-varying vec3 vPositionW;
-varying vec3 vNormalW;
-
-#ifdef VERTEXCOLOR
-varying vec4 vColor;
-#endif
-
-#include<clipPlaneVertexDeclaration>
-
-void main(void) {
-    mat4 finalWorld = world;
-
-#include<bonesVertex>
-
-	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
-
-	vec4 worldPos = finalWorld * vec4(position, 1.0);
-	vPositionW = vec3(worldPos);
-	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
-
-	// Texture coordinates
-#ifndef UV1
-	vec2 uv = vec2(0., 0.);
-#endif
-#ifndef UV2
-	vec2 uv2 = vec2(0., 0.);
-#endif
-
-#ifdef ALBEDO
-	if (vAlbedoInfos.x == 0.)
-	{
-		vAlbedoUV = vec2(albedoMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vAlbedoUV = vec2(albedoMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#ifdef AMBIENT
-	if (vAmbientInfos.x == 0.)
-	{
-		vAmbientUV = vec2(ambientMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vAmbientUV = vec2(ambientMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#ifdef OPACITY
-	if (vOpacityInfos.x == 0.)
-	{
-		vOpacityUV = vec2(opacityMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vOpacityUV = vec2(opacityMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#ifdef EMISSIVE
-	if (vEmissiveInfos.x == 0.)
-	{
-		vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vEmissiveUV = vec2(emissiveMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#if defined(REFLECTIVITY)
-	if (vReflectivityInfos.x == 0.)
-	{
-		vReflectivityUV = vec2(reflectivityMatrix * vec4(uv, 1.0, 0.0));
-	}
-	else
-	{
-		vReflectivityUV = vec2(reflectivityMatrix * vec4(uv2, 1.0, 0.0));
-	}
-#endif
-
-#include<clipPlaneVertex>
-
-	// Vertex color
-#ifdef VERTEXCOLOR
-	vColor = color;
-#endif
-}

+ 18 - 16
src/Shaders/pbr.fragment.fx

@@ -66,7 +66,7 @@ uniform vec2 vAlbedoInfos;
 #ifdef AMBIENT
 varying vec2 vAmbientUV;
 uniform sampler2D ambientSampler;
-uniform vec2 vAmbientInfos;
+uniform vec3 vAmbientInfos;
 #endif
 
 #ifdef OPACITY	
@@ -171,6 +171,7 @@ uniform mat4 reflectionMatrix;
 #include<harmonicsFunctions>
 #include<pbrLightFunctions>
 
+#include<helperFunctions>
 #include<bumpFragmentFunctions>
 #include<clipPlaneFragmentDeclaration>
 #include<logDepthDeclaration>
@@ -183,6 +184,15 @@ void main(void) {
 
 	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
 
+	// Bump
+	#ifdef NORMAL
+		vec3 normalW = normalize(vNormalW);
+	#else
+		vec3 normalW = vec3(1.0, 1.0, 1.0);
+	#endif
+
+	#include<bumpFragment>
+
 	// Albedo
 	vec4 surfaceAlbedo = vec4(1., 1., 1., 1.);
 	vec3 surfaceAlbedoContribution = vAlbedoColor.rgb;
@@ -191,7 +201,7 @@ void main(void) {
 	float alpha = vAlbedoColor.a;
 
 #ifdef ALBEDO
-	surfaceAlbedo = texture2D(albedoSampler, vAlbedoUV);
+	surfaceAlbedo = texture2D(albedoSampler, vAlbedoUV + uvOffset);
 	surfaceAlbedo = vec4(toLinearSpace(surfaceAlbedo.rgb), surfaceAlbedo.a);
 
 #ifndef LINKREFRACTIONTOTRANSPARENCY
@@ -220,20 +230,12 @@ void main(void) {
 	surfaceAlbedo.rgb = mix(surfaceAlbedo.rgb, vOverloadedAlbedo, vOverloadedIntensity.y);
 #endif
 
-	// Bump
-#ifdef NORMAL
-	vec3 normalW = normalize(vNormalW);
-#else
-	vec3 normalW = vec3(1.0, 1.0, 1.0);
-#endif
-
-#include<bumpFragment>
-
 	// Ambient color
 	vec3 ambientColor = vec3(1., 1., 1.);
 
 #ifdef AMBIENT
-	ambientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
+	ambientColor = texture2D(ambientSampler, vAmbientUV + uvOffset).rgb * vAmbientInfos.y;
+	ambientColor = vec3(1., 1., 1.) - ((vec3(1., 1., 1.) - ambientColor) * vAmbientInfos.z);
 
 #ifdef OVERLOADEDVALUES
 	ambientColor.rgb = mix(ambientColor.rgb, vOverloadedAmbient, vOverloadedIntensity.x);
@@ -249,7 +251,7 @@ void main(void) {
 #endif
 
 #ifdef REFLECTIVITY
-	vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV);
+	vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
 	surfaceReflectivityColor = surfaceReflectivityColorMap.rgb;
 	surfaceReflectivityColor = toLinearSpace(surfaceReflectivityColor);
 
@@ -293,7 +295,7 @@ void main(void) {
 	float notShadowLevel = 1.; // 1 - shadowLevel
 
 	#ifdef LIGHTMAP
-  		vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV).rgb * vLightmapInfos.y;
+  		vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb * vLightmapInfos.y;
   	#endif
 
 	float NdotL = -1.;
@@ -315,7 +317,7 @@ void main(void) {
 #endif
 
 #ifdef OPACITY
-	vec4 opacityMap = texture2D(opacitySampler, vOpacityUV);
+	vec4 opacityMap = texture2D(opacitySampler, vOpacityUV + uvOffset);
 
 #ifdef OPACITYRGB
 	opacityMap.rgb = opacityMap.rgb * vec3(0.3, 0.59, 0.11);
@@ -531,7 +533,7 @@ void main(void) {
 	// Emissive
 	vec3 surfaceEmissiveColor = vEmissiveColor;
 #ifdef EMISSIVE
-	vec3 emissiveColorTex = texture2D(emissiveSampler, vEmissiveUV).rgb;
+	vec3 emissiveColorTex = texture2D(emissiveSampler, vEmissiveUV + uvOffset).rgb;
 	surfaceEmissiveColor = toLinearSpace(emissiveColorTex.rgb) * surfaceEmissiveColor * vEmissiveInfos.y;
 #endif
 

+ 1 - 1
src/Shaders/pbr.vertex.fx

@@ -32,7 +32,7 @@ uniform vec2 vAlbedoInfos;
 #ifdef AMBIENT
 varying vec2 vAmbientUV;
 uniform mat4 ambientMatrix;
-uniform vec2 vAmbientInfos;
+uniform vec3 vAmbientInfos;
 #endif
 
 #ifdef OPACITY

+ 6 - 3
src/Sprites/babylon.spriteManager.js

@@ -2,6 +2,7 @@ var BABYLON;
 (function (BABYLON) {
     var SpriteManager = (function () {
         function SpriteManager(name, imgUrl, capacity, cellSize, scene, epsilon, samplingMode) {
+            if (epsilon === void 0) { epsilon = 0.01; }
             if (samplingMode === void 0) { samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE; }
             this.name = name;
             this.sprites = new Array();
@@ -19,15 +20,17 @@ var BABYLON;
             this._spriteTexture = new BABYLON.Texture(imgUrl, scene, true, false, samplingMode);
             this._spriteTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this._spriteTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
-            this._epsilon = epsilon === undefined ? 0.01 : epsilon;
-            if (cellSize.width) {
+            if (cellSize.width && cellSize.height) {
                 this.cellWidth = cellSize.width;
                 this.cellHeight = cellSize.height;
             }
-            else {
+            else if (cellSize !== undefined) {
                 this.cellWidth = cellSize;
                 this.cellHeight = cellSize;
             }
+            else {
+                return;
+            }
             this._scene = scene;
             this._scene.spriteManagers.push(this);
             var indices = [];

+ 6 - 6
src/Sprites/babylon.spriteManager.ts

@@ -43,20 +43,20 @@
             this._spriteTexture = value;
         }
 
-        constructor(public name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
+        constructor(public name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon: number = 0.01, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
             this._capacity = capacity;
             this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
             this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this._spriteTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
 
-            this._epsilon = epsilon === undefined ? 0.01 : epsilon;
-
-            if (cellSize.width) {
+            if (cellSize.width && cellSize.height) {
                 this.cellWidth = cellSize.width;
                 this.cellHeight = cellSize.height;
-            } else {
+            } else if(cellSize !== undefined) {
                 this.cellWidth = cellSize;
                 this.cellHeight = cellSize;
+            } else {
+               return;   
             }
 
             this._scene = scene;
@@ -283,4 +283,4 @@
             this.onDisposeObservable.clear();
         }
     }
-} 
+} 

+ 4 - 0
src/States/babylon.alphaCullingState.js

@@ -3,11 +3,15 @@ var BABYLON;
     var Internals;
     (function (Internals) {
         var _AlphaState = (function () {
+            /**
+             * Initializes the state.
+             */
             function _AlphaState() {
                 this._isAlphaBlendDirty = false;
                 this._isBlendFunctionParametersDirty = false;
                 this._alphaBlend = false;
                 this._blendFunctionParameters = new Array(4);
+                this.reset();
             }
             Object.defineProperty(_AlphaState.prototype, "isDirty", {
                 get: function () {

+ 7 - 0
src/States/babylon.alphaCullingState.ts

@@ -5,6 +5,13 @@
         private _alphaBlend = false;
         private _blendFunctionParameters = new Array<number>(4);
 
+        /**
+         * Initializes the state.
+         */
+        public constructor() {
+            this.reset();
+        }
+
         public get isDirty(): boolean {
             return this._isAlphaBlendDirty || this._isBlendFunctionParametersDirty;
         }

+ 4 - 0
src/States/babylon.depthCullingState.js

@@ -3,6 +3,9 @@ var BABYLON;
     var Internals;
     (function (Internals) {
         var _DepthCullingState = (function () {
+            /**
+             * Initializes the state.
+             */
             function _DepthCullingState() {
                 this._isDepthTestDirty = false;
                 this._isDepthMaskDirty = false;
@@ -10,6 +13,7 @@ var BABYLON;
                 this._isCullFaceDirty = false;
                 this._isCullDirty = false;
                 this._isZOffsetDirty = false;
+                this.reset();
             }
             Object.defineProperty(_DepthCullingState.prototype, "isDirty", {
                 get: function () {

+ 7 - 0
src/States/babylon.depthCullingState.ts

@@ -14,6 +14,13 @@
         private _cullFace: number;
         private _zOffset: number;
 
+        /**
+         * Initializes the state.
+         */
+        public constructor() {
+            this.reset();
+        }
+
         public get isDirty(): boolean {
             return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty || this._isZOffsetDirty;
         }

+ 13 - 2
src/Tools/babylon.rectPackingMap.js

@@ -15,6 +15,11 @@ var BABYLON;
             this._size = size;
             this._root = root;
             this._parent = parent;
+            this._contentSize = null;
+            this._bottomNode = null;
+            this._leftNode = null;
+            this._initialSize = null;
+            this._rightNode = null;
         }
         Object.defineProperty(PackedRect.prototype, "pos", {
             /**
@@ -106,8 +111,13 @@ var BABYLON;
                     resNode = this._bottomNode.findNode(size);
                 }
             }
-            else if (this._initialSize && (size.width <= this._initialSize.width) && (size.height <= this._initialSize.height)) {
-                resNode = this;
+            else if (this._initialSize) {
+                if ((size.width <= this._initialSize.width) && (size.height <= this._initialSize.height)) {
+                    resNode = this;
+                }
+                else {
+                    return null;
+                }
             }
             else if ((size.width <= this._size.width) && (size.height <= this._size.height)) {
                 resNode = this;
@@ -117,6 +127,7 @@ var BABYLON;
         PackedRect.prototype.splitNode = function (contentSize) {
             // If there's no contentSize but an initialSize it means this node were previously allocated, but freed, we need to create a _leftNode as subNode and use to allocate the space we need (and this node will have a right/bottom subNode for the space left as this._initialSize may be greater than contentSize)
             if (!this._contentSize && this._initialSize) {
+                this._contentSize = contentSize.clone();
                 this._leftNode = new PackedRect(this._root, this, new BABYLON.Vector2(this._pos.x, this._pos.y), new BABYLON.Size(this._initialSize.width, this._initialSize.height));
                 return this._leftNode.splitNode(contentSize);
             }

+ 17 - 6
src/Tools/babylon.rectPackingMap.ts

@@ -6,10 +6,15 @@
   */
     export class PackedRect {
         constructor(root: PackedRect, parent: PackedRect, pos: Vector2, size: Size) {
-            this._pos = pos;
-            this._size = size;
-            this._root = root;
-            this._parent = parent;
+            this._pos         = pos;
+            this._size        = size;
+            this._root        = root;
+            this._parent      = parent;
+            this._contentSize = null;
+            this._bottomNode  = null;
+            this._leftNode    = null;
+            this._initialSize = null;
+            this._rightNode   = null;
         }
 
         /**
@@ -104,8 +109,13 @@
             }
 
             // The node is free, but was previously allocated (_initialSize is set), rely on initialSize to make the test as it's the space we have
-            else if (this._initialSize && (size.width <= this._initialSize.width) && (size.height <= this._initialSize.height)) {
-                resNode = this;
+            else if (this._initialSize) {
+                if ((size.width <= this._initialSize.width) && (size.height <= this._initialSize.height))
+                {
+                    resNode = this;
+                } else {
+                    return null;
+                }
             }
 
             // The node is free and empty, rely on its size for the test
@@ -118,6 +128,7 @@
         private splitNode(contentSize: Size): PackedRect {
             // If there's no contentSize but an initialSize it means this node were previously allocated, but freed, we need to create a _leftNode as subNode and use to allocate the space we need (and this node will have a right/bottom subNode for the space left as this._initialSize may be greater than contentSize)
             if (!this._contentSize && this._initialSize) {
+                this._contentSize = contentSize.clone();
                 this._leftNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y), new Size(this._initialSize.width, this._initialSize.height));
                 return this._leftNode.splitNode(contentSize);
             } else {

+ 3 - 0
src/babylon.engine.js

@@ -214,6 +214,9 @@ var BABYLON;
             //    // Do nothing
             //}
             if (!this._gl) {
+                if (!canvas) {
+                    throw new Error("The provided canvas is null or undefined.");
+                }
                 try {
                     this._gl = (canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options));
                 }

+ 3 - 0
src/babylon.engine.ts

@@ -518,6 +518,9 @@
             //}
 
             if (!this._gl) {
+                if (!canvas) {
+                    throw new Error("The provided canvas is null or undefined.");
+                }
                 try {
                     this._gl = <WebGLRenderingContext>(canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options));
                 } catch (e) {

+ 42 - 22
src/babylon.scene.js

@@ -696,7 +696,12 @@ var BABYLON;
                     _this.setPointerOverSprite(null);
                     _this.setPointerOverMesh(pickResult.pickedMesh);
                     if (_this._pointerOverMesh.actionManager && _this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                        canvas.style.cursor = _this.hoverCursor;
+                        if (_this._pointerOverMesh.actionManager.hoverCursor) {
+                            canvas.style.cursor = _this._pointerOverMesh.actionManager.hoverCursor;
+                        }
+                        else {
+                            canvas.style.cursor = _this.hoverCursor;
+                        }
                     }
                     else {
                         canvas.style.cursor = "";
@@ -707,8 +712,13 @@ var BABYLON;
                     // Sprites
                     pickResult = _this.pickSprite(_this._unTranslatedPointerX, _this._unTranslatedPointerY, spritePredicate, false, _this.cameraToUseForPointers);
                     if (pickResult.hit && pickResult.pickedSprite) {
-                        canvas.style.cursor = _this.hoverCursor;
                         _this.setPointerOverSprite(pickResult.pickedSprite);
+                        if (_this._pointerOverSprite.actionManager && _this._pointerOverSprite.actionManager.hoverCursor) {
+                            canvas.style.cursor = _this._pointerOverSprite.actionManager.hoverCursor;
+                        }
+                        else {
+                            canvas.style.cursor = _this.hoverCursor;
+                        }
                     }
                     else {
                         _this.setPointerOverSprite(null);
@@ -1779,6 +1789,7 @@ var BABYLON;
             }
             // Render targets
             this._renderTargetsDuration.beginMonitoring();
+            var needsRestoreFrameBuffer = false;
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
@@ -1794,7 +1805,33 @@ var BABYLON;
                 BABYLON.Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 this._intermediateRendering = false;
                 this._renderId++;
-                engine.restoreDefaultFramebuffer(); // Restore back buffer
+                needsRestoreFrameBuffer = true; // Restore back buffer
+            }
+            // Render HighlightLayer Texture
+            var stencilState = this._engine.getStencilBuffer();
+            var renderhighlights = false;
+            if (this.renderTargetsEnabled && this.highlightLayers && this.highlightLayers.length > 0) {
+                this._intermediateRendering = true;
+                for (var i = 0; i < this.highlightLayers.length; i++) {
+                    var highlightLayer = this.highlightLayers[i];
+                    if (highlightLayer.shouldRender() &&
+                        (!highlightLayer.camera ||
+                            (highlightLayer.camera.cameraRigMode === BABYLON.Camera.RIG_MODE_NONE && camera === highlightLayer.camera) ||
+                            (highlightLayer.camera.cameraRigMode !== BABYLON.Camera.RIG_MODE_NONE && highlightLayer.camera._rigCameras.indexOf(camera) > -1))) {
+                        renderhighlights = true;
+                        var renderTarget = highlightLayer._mainTexture;
+                        if (renderTarget._shouldRender()) {
+                            this._renderId++;
+                            renderTarget.render(false, false);
+                            needsRestoreFrameBuffer = true;
+                        }
+                    }
+                }
+                this._intermediateRendering = false;
+                this._renderId++;
+            }
+            if (needsRestoreFrameBuffer) {
+                engine.restoreDefaultFramebuffer();
             }
             this._renderTargetsDuration.endMonitoring(false);
             // Prepare Frame
@@ -1816,17 +1853,8 @@ var BABYLON;
             // Render
             BABYLON.Tools.StartPerformanceCounter("Main render");
             // Activate HighlightLayer stencil
-            var stencilState = this._engine.getStencilBuffer();
-            var renderhighlights = false;
-            if (this.highlightLayers && this.highlightLayers.length > 0) {
-                for (var i = 0; i < this.highlightLayers.length; i++) {
-                    var highlightLayer = this.highlightLayers[i];
-                    if ((!highlightLayer.camera || camera == highlightLayer.camera) && highlightLayer.shouldRender()) {
-                        renderhighlights = true;
-                        this._engine.setStencilBuffer(true);
-                        break;
-                    }
-                }
+            if (renderhighlights) {
+                this._engine.setStencilBuffer(true);
             }
             this._renderingManager.render(null, null, true, true);
             // Restore HighlightLayer stencil
@@ -2021,14 +2049,6 @@ var BABYLON;
             if (this._depthRenderer) {
                 this._renderTargets.push(this._depthRenderer.getDepthMap());
             }
-            // HighlightLayer
-            if (this.highlightLayers && this.highlightLayers.length > 0) {
-                for (var i = 0; i < this.highlightLayers.length; i++) {
-                    if (this.highlightLayers[i].shouldRender()) {
-                        this._renderTargets.push(this.highlightLayers[i]._mainTexture);
-                    }
-                }
-            }
             // RenderPipeline
             this.postProcessRenderPipelineManager.update();
             // Multi-cameras?

+ 50 - 23
src/babylon.scene.ts

@@ -819,7 +819,11 @@
                     this.setPointerOverMesh(pickResult.pickedMesh);
 
                     if (this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                        canvas.style.cursor = this.hoverCursor;
+                        if(this._pointerOverMesh.actionManager.hoverCursor){
+                            canvas.style.cursor = this._pointerOverMesh.actionManager.hoverCursor;
+                        } else {
+                            canvas.style.cursor = this.hoverCursor;
+                        }
                     } else {
                         canvas.style.cursor = "";
                     }
@@ -829,8 +833,12 @@
                     pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, spritePredicate, false, this.cameraToUseForPointers);
 
                     if (pickResult.hit && pickResult.pickedSprite) {
-                        canvas.style.cursor = this.hoverCursor;
                         this.setPointerOverSprite(pickResult.pickedSprite);
+                        if (this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
+                            canvas.style.cursor = this._pointerOverSprite.actionManager.hoverCursor;
+                        } else {
+                            canvas.style.cursor = this.hoverCursor;
+                        }
                     } else {
                         this.setPointerOverSprite(null);
                         // Restore pointer
@@ -2120,6 +2128,8 @@
 
             // Render targets
             this._renderTargetsDuration.beginMonitoring();
+            var needsRestoreFrameBuffer = false;
+
             var beforeRenderTargetDate = Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
@@ -2136,14 +2146,49 @@
 
                 this._intermediateRendering = false;
                 this._renderId++;
-                engine.restoreDefaultFramebuffer(); // Restore back buffer
+
+                needsRestoreFrameBuffer = true; // Restore back buffer
+            }
+
+            // Render HighlightLayer Texture
+            var stencilState = this._engine.getStencilBuffer();
+            var renderhighlights = false;
+            if (this.renderTargetsEnabled && this.highlightLayers && this.highlightLayers.length > 0) {
+                this._intermediateRendering = true;
+                for (let i = 0; i < this.highlightLayers.length; i++) {
+                    let highlightLayer = this.highlightLayers[i];
+
+                    if (highlightLayer.shouldRender() &&
+                        (!highlightLayer.camera ||
+                            (highlightLayer.camera.cameraRigMode === Camera.RIG_MODE_NONE && camera === highlightLayer.camera) ||
+                            (highlightLayer.camera.cameraRigMode !== Camera.RIG_MODE_NONE && highlightLayer.camera._rigCameras.indexOf(camera) > -1))) {
+
+                        renderhighlights = true;
+
+                        var renderTarget = (<RenderTargetTexture>(<any>highlightLayer)._mainTexture);
+                        if (renderTarget._shouldRender()) {
+                            this._renderId++;
+                            renderTarget.render(false, false);
+                            needsRestoreFrameBuffer = true;
+                        }
+                    }
+                }
+
+                this._intermediateRendering = false;
+                this._renderId++;
             }
+
+            if (needsRestoreFrameBuffer) {
+                engine.restoreDefaultFramebuffer();
+            }
+
             this._renderTargetsDuration.endMonitoring(false);
 
             // Prepare Frame
             this.postProcessManager._prepareFrame();
 
             this._renderDuration.beginMonitoring();
+
             // Backgrounds
             var layerIndex;
             var layer;
@@ -2162,17 +2207,8 @@
             Tools.StartPerformanceCounter("Main render");
 
             // Activate HighlightLayer stencil
-            var stencilState = this._engine.getStencilBuffer();
-            var renderhighlights = false;
-            if (this.highlightLayers && this.highlightLayers.length > 0) {
-                for (let i = 0; i < this.highlightLayers.length; i++) {
-                    let highlightLayer = this.highlightLayers[i];
-                    if ((!highlightLayer.camera || camera == highlightLayer.camera) && highlightLayer.shouldRender()) {
-                        renderhighlights = true;
-                        this._engine.setStencilBuffer(true);
-                        break;
-                    }
-                }
+            if (renderhighlights) {
+                this._engine.setStencilBuffer(true);
             }
 
             this._renderingManager.render(null, null, true, true);
@@ -2412,15 +2448,6 @@
                 this._renderTargets.push(this._depthRenderer.getDepthMap());
             }
 
-            // HighlightLayer
-            if (this.highlightLayers && this.highlightLayers.length > 0) {
-                for (let i = 0; i < this.highlightLayers.length; i++) {
-                    if (this.highlightLayers[i].shouldRender()) {
-                        this._renderTargets.push((<any>this.highlightLayers[i])._mainTexture);
-                    }
-                }
-            }
-
             // RenderPipeline
             this.postProcessRenderPipelineManager.update();