Просмотр исходного кода

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

nockawa 8 лет назад
Родитель
Сommit
8582242607
55 измененных файлов с 7336 добавлено и 14278 удалено
  1. 26 0
      canvas2D/src/Engine/babylon.canvas2d.ts
  2. 10 3
      canvas2D/src/Engine/babylon.prim2dBase.ts
  3. 16 2
      canvas2D/src/Engine/babylon.smartPropertyPrim.ts
  4. 672 660
      dist/preview release/babylon.canvas2d.d.ts
  5. 8 8
      dist/preview release/babylon.canvas2d.js
  6. 35 3
      dist/preview release/babylon.canvas2d.max.js
  7. 23 23
      dist/preview release/babylon.core.js
  8. 1629 1536
      dist/preview release/babylon.d.ts
  9. 35 35
      dist/preview release/babylon.js
  10. 198 29
      dist/preview release/babylon.max.js
  11. 34 34
      dist/preview release/babylon.noworker.js
  12. 1 0
      dist/preview release/what's new.md
  13. 1 1
      materialsLibrary/dist/babylon.fireMaterial.js
  14. 1 1
      materialsLibrary/dist/babylon.fireMaterial.min.js
  15. 1 1
      materialsLibrary/dist/babylon.furMaterial.js
  16. 1 1
      materialsLibrary/dist/babylon.furMaterial.min.js
  17. 1 1
      materialsLibrary/dist/babylon.gradientMaterial.js
  18. 1 1
      materialsLibrary/dist/babylon.gradientMaterial.min.js
  19. 17 4
      materialsLibrary/dist/babylon.gridMaterial.js
  20. 1 1
      materialsLibrary/dist/babylon.gridMaterial.min.js
  21. 6 4
      materialsLibrary/dist/babylon.lavaMaterial.js
  22. 1 1
      materialsLibrary/dist/babylon.lavaMaterial.min.js
  23. 1 1
      materialsLibrary/dist/babylon.normalMaterial.js
  24. 1 1
      materialsLibrary/dist/babylon.normalMaterial.min.js
  25. 1 2
      materialsLibrary/dist/babylon.simpleMaterial.js
  26. 1 1
      materialsLibrary/dist/babylon.simpleMaterial.min.js
  27. 1 1
      materialsLibrary/dist/babylon.terrainMaterial.js
  28. 1 1
      materialsLibrary/dist/babylon.terrainMaterial.min.js
  29. 1 1
      materialsLibrary/dist/babylon.triPlanarMaterial.js
  30. 1 1
      materialsLibrary/dist/babylon.triPlanarMaterial.min.js
  31. 81 4
      materialsLibrary/dist/babylon.waterMaterial.js
  32. 1 1
      materialsLibrary/dist/babylon.waterMaterial.min.js
  33. 1 0
      materialsLibrary/dist/dts/babylon.lavaMaterial.d.ts
  34. 23 1
      materialsLibrary/dist/dts/babylon.waterMaterial.d.ts
  35. 44 26
      materialsLibrary/materials/grid/babylon.gridmaterial.ts
  36. 10 4
      materialsLibrary/materials/grid/grid.fragment.fx
  37. 11 0
      materialsLibrary/materials/grid/grid.vertex.fx
  38. 3987 11819
      materialsLibrary/test/refs/babylon.max.js
  39. 1 3
      postProcessLibrary/postProcesses/asciiArt/babylon.asciiArtPostProcess.ts
  40. 1 3
      postProcessLibrary/postProcesses/digitalRain/babylon.digitalRainPostProcess.ts
  41. 11 7
      src/Cameras/VR/babylon.webVRCamera.js
  42. 15 8
      src/Cameras/VR/babylon.webVRCamera.ts
  43. 25 8
      src/Cameras/babylon.camera.js
  44. 27 8
      src/Cameras/babylon.camera.ts
  45. 9 0
      src/Materials/Textures/babylon.baseTexture.ts
  46. 9 0
      src/Math/babylon.math.js
  47. 12 0
      src/Math/babylon.math.ts
  48. 14 13
      src/Particles/babylon.solidParticleSystem.js
  49. 14 15
      src/Particles/babylon.solidParticleSystem.ts
  50. 44 0
      src/Rendering/babylon.renderingManager.js
  51. 52 0
      src/Rendering/babylon.renderingManager.ts
  52. 8 0
      src/babylon.node.js
  53. 12 0
      src/babylon.node.ts
  54. 86 0
      src/babylon.scene.js
  55. 112 0
      src/babylon.scene.ts

+ 26 - 0
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -76,6 +76,7 @@
             isScreenSpace?: boolean,
             cachingStrategy?: number,
             enableInteraction?: boolean,
+            allow3DEventBelowCanvas?: boolean,
             origin?: Vector2,
             isVisible?: boolean,
             backgroundRoundRadius?: number,
@@ -169,6 +170,8 @@
             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;
@@ -487,6 +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 {
@@ -1062,6 +1071,21 @@
             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;
@@ -1817,6 +1841,7 @@
          *  - 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.
@@ -1846,6 +1871,7 @@
             cachingStrategy?: number,
             cacheBehavior?: number,
             enableInteraction?: boolean,
+            allow3DEventBelowCanvas?: boolean,
             isVisible?: boolean,
             backgroundRoundRadius?: number,
             backgroundFill?: IBrush2D | string,

+ 10 - 3
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1785,7 +1785,8 @@
 
         @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
+         * 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.
+         * BEWARE: don't change this value, it's read-only!
          */
         public get actualPosition(): Vector2 {
             if (this._actualPosition != null) {
@@ -1841,7 +1842,10 @@
          */
         @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 3, pi => Prim2DBase.positionProperty = pi, false, false, true)
         public get position(): Vector2 {
-            return this._position || Prim2DBase._nullPosition;
+            if (!this._position) {
+                this._position = Vector2.Zero();
+            }
+            return this._position;
         }
 
         public set position(value: Vector2) {
@@ -2419,7 +2423,10 @@
          * The setter should only be called by a Layout Engine class.
          */
         public get layoutAreaPos(): Vector2 {
-            return this._layoutAreaPos || Prim2DBase._nullPosition;
+            if (!this._layoutAreaPos) {
+                this._layoutAreaPos = Vector2.Zero();
+            }
+            return this._layoutAreaPos;
         }
 
         public set layoutAreaPos(val: Vector2) {

+ 16 - 2
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 + ";";
                 }
             });
 
@@ -1265,7 +1278,8 @@
         public static flagActualScaleDirty        = 0x0040000;    // set if the actualScale property needs to be recomputed
         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 = 0x0100000;    // set if the layout bounding info is dirty
+        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;

Разница между файлами не показана из-за своего большого размера
+ 672 - 660
dist/preview release/babylon.canvas2d.d.ts


Разница между файлами не показана из-за своего большого размера
+ 8 - 8
dist/preview release/babylon.canvas2d.js


+ 35 - 3
dist/preview release/babylon.canvas2d.max.js

@@ -2635,7 +2635,8 @@ var BABYLON;
         SmartPropertyPrim.flagActualScaleDirty = 0x0040000; // set if the actualScale property needs to be recomputed
         SmartPropertyPrim.flagDontInheritParentScale = 0x0080000; // set if the actualScale must not use its parent's scale to be computed
         SmartPropertyPrim.flagGlobalTransformDirty = 0x0100000; // set if the global transform must be recomputed due to a local transform change
-        SmartPropertyPrim.flagLayoutBoundingInfoDirty = 0x0100000; // set if the layout bounding info is dirty
+        SmartPropertyPrim.flagLayoutBoundingInfoDirty = 0x0200000; // set if the layout bounding info is dirty
+        SmartPropertyPrim.flagAllow3DEventsBelowCanvas = 0x0400000; // set if pointer events should be sent to 3D Engine when the pointer is over the Canvas
         SmartPropertyPrim = __decorate([
             BABYLON.className("SmartPropertyPrim", "BABYLON")
         ], SmartPropertyPrim);
@@ -4236,7 +4237,10 @@ var BABYLON;
              * Setting this property may have no effect is specific alignment are in effect.
              */
             get: function () {
-                return this._position || Prim2DBase._nullPosition;
+                if (!this._position) {
+                    this._position = BABYLON.Vector2.Zero();
+                }
+                return this._position;
             },
             set: function (value) {
                 if (!this._checkPositionChange()) {
@@ -4813,7 +4817,10 @@ var BABYLON;
              * The setter should only be called by a Layout Engine class.
              */
             get: function () {
-                return this._layoutAreaPos || Prim2DBase._nullPosition;
+                if (!this._layoutAreaPos) {
+                    this._layoutAreaPos = BABYLON.Vector2.Zero();
+                }
+                return this._layoutAreaPos;
             },
             set: function (val) {
                 if (this._layoutAreaPos && this._layoutAreaPos.equals(val)) {
@@ -11596,6 +11603,7 @@ var BABYLON;
             this._trackedGroups = new Array();
             this._maxAdaptiveWorldSpaceCanvasSize = null;
             this._groupCacheMaps = new BABYLON.StringDictionary();
+            this._changeFlags(BABYLON.SmartPropertyPrim.flagAllow3DEventsBelowCanvas, (settings.allow3DEventBelowCanvas != null) && settings.allow3DEventBelowCanvas);
             this._patchHierarchy(this);
             var enableInteraction = (settings.enableInteraction == null) ? true : settings.enableInteraction;
             this._fitRenderingDevice = !settings.size;
@@ -11872,6 +11880,12 @@ var BABYLON;
                 skip = !this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData);
             }
             eventState.skipNextObservers = skip;
+            if (!skip && (this._isFlagSet(BABYLON.SmartPropertyPrim.flagAllow3DEventsBelowCanvas) === false)) {
+                eventState.skipNextObservers = true;
+                if (eventData instanceof BABYLON.PointerInfoPre) {
+                    eventData.skipOnPointerObservable = true;
+                }
+            }
         };
         Canvas2D.prototype._updatePointerInfo = function (eventData, localPosition) {
             var s = this.scale;
@@ -12426,6 +12440,23 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Canvas2D.prototype, "allow3DEventBelowCanvas", {
+            /**
+             * 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
+             */
+            get: function () {
+                return this._isFlagSet(BABYLON.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
+             */
+            set: function (value) {
+                this._changeFlags(BABYLON.SmartPropertyPrim.flagAllow3DEventsBelowCanvas, value);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Canvas2D.prototype.createCanvasProfileInfoCanvas = function () {
             if (this._profilingCanvas) {
                 return this._profilingCanvas;
@@ -13047,6 +13078,7 @@ var BABYLON;
          *  - 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.

Разница между файлами не показана из-за своего большого размера
+ 23 - 23
dist/preview release/babylon.core.js


Разница между файлами не показана из-за своего большого размера
+ 1629 - 1536
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 35 - 35
dist/preview release/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 198 - 29
dist/preview release/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 34 - 34
dist/preview release/babylon.noworker.js


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

@@ -8,6 +8,7 @@
 - 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 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)) 
 - Added onAnimationEnd callback for `sprite.playAnimation` ([deltakosh](https://github.com/deltakosh)) 

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.fireMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.fireMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.furMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.furMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.gradientMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.gradientMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 17 - 4
materialsLibrary/dist/babylon.gridMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.gridMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 6 - 4
materialsLibrary/dist/babylon.lavaMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.lavaMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.normalMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.normalMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 2
materialsLibrary/dist/babylon.simpleMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.simpleMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.terrainMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.terrainMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.triPlanarMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.triPlanarMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 81 - 4
materialsLibrary/dist/babylon.waterMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
materialsLibrary/dist/babylon.waterMaterial.min.js


+ 1 - 0
materialsLibrary/dist/dts/babylon.lavaMaterial.d.ts

@@ -11,6 +11,7 @@ declare module BABYLON {
         private _lastTime;
         diffuseColor: Color3;
         disableLighting: boolean;
+        maxSimultaneousLights: number;
         private _worldViewProjectionMatrix;
         private _scaledDiffuse;
         private _renderId;

+ 23 - 1
materialsLibrary/dist/dts/babylon.waterMaterial.d.ts

@@ -26,7 +26,19 @@ declare module BABYLON {
         */
         bumpHeight: number;
         /**
-        * @param {number}: The water color blended with the reflection and refraction samplers
+         * @param {boolean}: Add a smaller moving bump to less steady waves.
+         */
+        bumpSuperimpose: boolean;
+        /**
+         * @param {boolean}: Color refraction and reflection differently with .waterColor2 and .colorBlendFactor2. Non-linear (physically correct) fresnel.
+         */
+        fresnelSeparate: boolean;
+        /**
+         * @param {boolean}: bump Waves modify the reflection.
+         */
+        bumpAffectsReflection: boolean;
+        /**
+        * @param {number}: The water color blended with the refraction (near)
         */
         waterColor: Color3;
         /**
@@ -34,6 +46,14 @@ declare module BABYLON {
         */
         colorBlendFactor: number;
         /**
+         * @param {number}: The water color blended with the reflection (far)
+         */
+        waterColor2: Color3;
+        /**
+         * @param {number}: The blend factor related to the water color (reflection, far)
+         */
+        colorBlendFactor2: number;
+        /**
         * @param {number}: Represents the maximum length of a wave
         */
         waveLength: number;
@@ -50,10 +70,12 @@ declare module BABYLON {
         private _renderId;
         private _defines;
         private _cachedDefines;
+        private _useLogarithmicDepth;
         /**
         * Constructor
         */
         constructor(name: string, scene: Scene, renderTargetSize?: Vector2);
+        useLogarithmicDepth: boolean;
         refractionTexture: RenderTargetTexture;
         reflectionTexture: RenderTargetTexture;
         addToRenderList(node: any): void;

+ 44 - 26
materialsLibrary/materials/grid/babylon.gridmaterial.ts

@@ -4,60 +4,62 @@ module BABYLON {
     class GRIDMaterialDefines extends MaterialDefines {
         public TRANSPARENT = false;
 
+        public FOG = false;
+
         constructor() {
             super();
             this._keys = Object.keys(this);
         }
     }
-    
+
     /**
      * The grid materials allows you to wrap any shape with a grid.
      * Colors are customizable.
      */
     export class GridMaterial extends BABYLON.Material {
-        
+
         /**
          * Main color of the grid (e.g. between lines)
          */
         @serializeAsColor3()
         public mainColor = Color3.White();
-        
+
         /**
          * Color of the grid lines.
          */
         @serializeAsColor3()
         public lineColor = Color3.Black();
-        
+
         /**
          * The scale of the grid compared to unit.
          */
         @serialize()
         public gridRatio = 1.0;
-        
+
         /**
          * The frequency of thicker lines.
          */
         @serialize()
         public majorUnitFrequency = 10;
-        
+
         /**
          * The visibility of minor units in the grid.
          */
         @serialize()
         public minorUnitVisibility = 0.33;
-        
+
         /**
          * The grid opacity outside of the lines.
          */
         @serialize()
         public opacity = 1.0;
-        
+
         private _gridControl: Vector4 = new Vector4(this.gridRatio, this.majorUnitFrequency, this.minorUnitVisibility, this.opacity);
-        
+
         private _renderId: number;
         private _defines = new GRIDMaterialDefines();
         private _cachedDefines = new GRIDMaterialDefines();
-        
+
         /**
          * constructor
          * @param name The name given to the material in order to identify it afterwards.
@@ -66,14 +68,14 @@ module BABYLON {
         constructor(name: string, scene: Scene) {
             super(name, scene);
         }
-        
+
         /**
          * Returns wehter or not the grid requires alpha blending.
          */
         public needAlphaBlending(): boolean {
             return this.opacity < 1.0;
         }
-          
+
         private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
             if (!mesh) {
                 return true;
@@ -112,6 +114,11 @@ module BABYLON {
                 this._defines.TRANSPARENT = true;
             }
 
+            // Fog
+            if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
+                this._defines.FOG = true;
+            }
+
             // Get correct effect      
             if (!this._effect || !this._defines.isEqual(this._cachedDefines)) {
                 this._defines.cloneTo(this._cachedDefines);
@@ -122,19 +129,19 @@ module BABYLON {
 
                 // Effect
                 var shaderName = scene.getEngine().getCaps().standardDerivatives ? "grid" : "legacygrid";
-                
+
                 // Defines
                 var join = this._defines.toString();
                 this._effect = scene.getEngine().createEffect(shaderName,
                     attribs,
-                    ["worldViewProjection", "mainColor", "lineColor", "gridControl"],
+                    ["worldViewProjection", "mainColor", "lineColor", "gridControl", "vFogInfos", "vFogColor", "world", "view"],
                     [],
-                    join, 
-                    null, 
-                    this.onCompiled, 
+                    join,
+                    null,
+                    this.onCompiled,
                     this.onError);
             }
-            
+
             if (!this._effect.isReady()) {
                 return false;
             }
@@ -144,44 +151,55 @@ module BABYLON {
 
             return true;
         }
-        
+
         public bindOnlyWorldMatrix(world: Matrix): void {
             var scene = this.getScene();
 
             this._effect.setMatrix("worldViewProjection", world.multiply(scene.getTransformMatrix()));
+            this._effect.setMatrix("world", world);
+            this._effect.setMatrix("view", scene.getViewMatrix());
         }
 
         public bind(world: Matrix, mesh?: Mesh): void {
             var scene = this.getScene();
 
-            // Matrices        
+            // Matrices
             this.bindOnlyWorldMatrix(world);
-            
+
             // Uniforms
             if (scene.getCachedMaterial() !== (<BABYLON.Material>this)) {
                 this._effect.setColor3("mainColor", this.mainColor);
                 this._effect.setColor3("lineColor", this.lineColor);
-                
+
                 this._gridControl.x = this.gridRatio;
                 this._gridControl.y = Math.round(this.majorUnitFrequency);
                 this._gridControl.z = this.minorUnitVisibility;
                 this._gridControl.w = this.opacity;
                 this._effect.setVector4("gridControl", this._gridControl);
             }
+
+            // View
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setMatrix("view", scene.getViewMatrix());
+            }
+
+            // Fog
+            MaterialHelper.BindFogParameters(scene, mesh, this._effect);
+
             super.bind(world, mesh);
         }
-        
+
         public dispose(forceDisposeEffect?: boolean): void {
             super.dispose(forceDisposeEffect);
         }
-        
+
         public clone(name: string): GridMaterial {
             return SerializationHelper.Clone(() => new GridMaterial(name, this.getScene()), this);
         }
 
         public serialize(): any {
-            var serializationObject = SerializationHelper.Serialize(this); 
-            serializationObject.customType = "BABYLON.GridMaterial"; 
+            var serializationObject = SerializationHelper.Serialize(this);
+            serializationObject.customType = "BABYLON.GridMaterial";
             return serializationObject;
         }
 

+ 10 - 4
materialsLibrary/materials/grid/grid.fragment.fx

@@ -13,6 +13,8 @@ uniform vec4 gridControl;
 varying vec3 vPosition;
 varying vec3 vNormal;
 
+#include<fogFragmentDeclaration>
+
 float getVisibility(float position) {
     // Major grid line every Frequency defined in material.
     float majorGridFrequency = gridControl.y;
@@ -82,13 +84,17 @@ void main(void) {
     float grid = clamp(x + y + z, 0., 1.);
     
     // Create the color.
-    vec3 gridColor = mix(mainColor, lineColor, grid);
+    vec3 color = mix(mainColor, lineColor, grid);
+
+#ifdef FOG
+    #include<fogFragment>
+#endif
 
 #ifdef TRANSPARENT
-    float opacity = clamp(grid, 0.08, gridControl.w);
-    gl_FragColor = vec4(gridColor.rgb, opacity);
+    float opacity = clamp(grid, 0.08, color.w);
+    gl_FragColor = vec4(color.rgb, opacity);
 #else
     // Apply the color.
-    gl_FragColor = vec4(gridColor.rgb, 1.0);
+    gl_FragColor = vec4(color.rgb, 1.0);
 #endif
 }

+ 11 - 0
materialsLibrary/materials/grid/grid.vertex.fx

@@ -6,12 +6,23 @@ attribute vec3 normal;
 
 // Uniforms
 uniform mat4 worldViewProjection;
+uniform mat4 world;
+uniform mat4 view;
 
 // Varying
 varying vec3 vPosition;
 varying vec3 vNormal;
 
+#include<fogVertexDeclaration>
+
 void main(void) {
+
+    #ifdef FOG
+    vec4 worldPos = world * vec4(position, 1.0);
+    #endif
+
+    #include<fogVertex>
+
     gl_Position = worldViewProjection * vec4(position, 1.0);
 
     vPosition = position;

Разница между файлами не показана из-за своего большого размера
+ 3987 - 11819
materialsLibrary/test/refs/babylon.max.js


+ 1 - 3
postProcessLibrary/postProcesses/asciiArt/babylon.asciiArtPostProcess.ts

@@ -1,6 +1,4 @@
- /// <reference path="../../../dist/preview release/babylon.d.ts"/>
- 
-module BABYLON {
+module BABYLON {
 
     /**
      * AsciiArtFontTexture is the helper class used to easily create your ascii art font texture.

+ 1 - 3
postProcessLibrary/postProcesses/digitalRain/babylon.digitalRainPostProcess.ts

@@ -1,6 +1,4 @@
- /// <reference path="../../../dist/preview release/babylon.d.ts"/>
- 
-module BABYLON {
+module BABYLON {
 
     /**
      * DigitalRainFontTexture is the helper class used to easily create your digital rain font texture.

+ 11 - 7
src/Cameras/VR/babylon.webVRCamera.js

@@ -16,6 +16,7 @@ var BABYLON;
             this._vrDevice = null;
             this._cacheState = null;
             this._vrEnabled = false;
+            this._attached = false;
             //enable VR
             this.getEngine().initWebVR();
             if (!this.getEngine().vrDisplaysPromise) {
@@ -24,6 +25,7 @@ var BABYLON;
             else {
                 //TODO get the metrics updated using the device's eye parameters!
                 //TODO also check that the device has the right capabilities!
+                this._frameData = new VRFrameData();
                 this.getEngine().vrDisplaysPromise.then(function (devices) {
                     if (devices.length > 0) {
                         _this._vrEnabled = true;
@@ -47,7 +49,10 @@ var BABYLON;
                             _this._vrDevice = devices[0];
                         }
                         //reset the rig parameters.
-                        _this.setCameraRigMode(BABYLON.Camera.RIG_MODE_WEBVR, { vrDisplay: _this._vrDevice });
+                        _this.setCameraRigMode(BABYLON.Camera.RIG_MODE_WEBVR, { vrDisplay: _this._vrDevice, frameData: _this._frameData });
+                        if (_this._attached) {
+                            _this.getEngine().enableVR(_this._vrDevice);
+                        }
                     }
                     else {
                         BABYLON.Tools.Error("No WebVR devices found!");
@@ -58,25 +63,23 @@ var BABYLON;
             this._quaternionCache = new BABYLON.Quaternion();
         }
         WebVRFreeCamera.prototype._checkInputs = function () {
-            if (this._vrEnabled) {
-                var currentPost = this._vrDevice.getPose();
+            if (this._vrEnabled && this._vrDevice.getFrameData(this._frameData)) {
+                var currentPost = this._frameData.pose;
                 //make sure we have data
                 if (currentPost && currentPost.orientation) {
                     this._cacheState = currentPost;
-                    this.rotationQuaternion.copyFromFloats(this._cacheState.orientation[0], this._cacheState.orientation[1], this._cacheState.orientation[2], this._cacheState.orientation[3]);
+                    this.rotationQuaternion.copyFromFloats(this._cacheState.orientation[0], this._cacheState.orientation[1], -this._cacheState.orientation[2], -this._cacheState.orientation[3]);
                     if (this.webVROptions.trackPosition && this._cacheState.position) {
                         this.position.copyFromFloats(this._cacheState.position[0], this._cacheState.position[1], -this._cacheState.position[2]);
                         this.webVROptions.positionScale && this.position.scaleInPlace(this.webVROptions.positionScale);
                     }
-                    //Flip in XY plane
-                    this.rotationQuaternion.z *= -1;
-                    this.rotationQuaternion.w *= -1;
                 }
             }
             _super.prototype._checkInputs.call(this);
         };
         WebVRFreeCamera.prototype.attachControl = function (element, noPreventDefault) {
             _super.prototype.attachControl.call(this, element, noPreventDefault);
+            this._attached = true;
             noPreventDefault = BABYLON.Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
             if (this._vrEnabled) {
                 this.getEngine().enableVR(this._vrDevice);
@@ -85,6 +88,7 @@ var BABYLON;
         WebVRFreeCamera.prototype.detachControl = function (element) {
             _super.prototype.detachControl.call(this, element);
             this._vrEnabled = false;
+            this._attached = false;
             this.getEngine().disableVR();
         };
         WebVRFreeCamera.prototype.requestVRFullscreen = function (requestPointerlock) {

+ 15 - 8
src/Cameras/VR/babylon.webVRCamera.ts

@@ -1,5 +1,6 @@
 declare var HMDVRDevice;
 declare var VRDisplay;
+declare var VRFrameData;
 
 module BABYLON {
 
@@ -13,10 +14,13 @@ module BABYLON {
         public _vrDevice = null;
         private _cacheState = null;
         private _vrEnabled = false;
+        private _attached: boolean = false;
 
         private _oldSize: BABYLON.Size;
         private _oldHardwareScaleFactor: number;
 
+        private _frameData;
+
         private _quaternionCache: Quaternion;
 
         constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = false, private webVROptions: WebVROptions = {}) {
@@ -30,6 +34,7 @@ module BABYLON {
             } else {
                 //TODO get the metrics updated using the device's eye parameters!
                 //TODO also check that the device has the right capabilities!
+                this._frameData = new VRFrameData();
                 this.getEngine().vrDisplaysPromise.then((devices) => {
                     if (devices.length > 0) {
                         this._vrEnabled = true;
@@ -52,7 +57,11 @@ module BABYLON {
                         }
 
                         //reset the rig parameters.
-                        this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { vrDisplay: this._vrDevice });
+                        this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { vrDisplay: this._vrDevice, frameData: this._frameData });
+
+                        if (this._attached) {
+                            this.getEngine().enableVR(this._vrDevice)
+                        }
                     } else {
                         Tools.Error("No WebVR devices found!");
                     }
@@ -64,21 +73,17 @@ module BABYLON {
         }
 
         public _checkInputs(): void {
-            if (this._vrEnabled) {
-                var currentPost = this._vrDevice.getPose();
+            if (this._vrEnabled && this._vrDevice.getFrameData(this._frameData)) {
+                var currentPost = this._frameData.pose;
                 //make sure we have data
                 if (currentPost && currentPost.orientation) {
                     this._cacheState = currentPost;
-                    this.rotationQuaternion.copyFromFloats(this._cacheState.orientation[0], this._cacheState.orientation[1], this._cacheState.orientation[2], this._cacheState.orientation[3]);
+                    this.rotationQuaternion.copyFromFloats(this._cacheState.orientation[0], this._cacheState.orientation[1], -this._cacheState.orientation[2], -this._cacheState.orientation[3]);
                     if (this.webVROptions.trackPosition && this._cacheState.position) {
                         this.position.copyFromFloats(this._cacheState.position[0], this._cacheState.position[1], -this._cacheState.position[2]);
                         this.webVROptions.positionScale && this.position.scaleInPlace(this.webVROptions.positionScale)
                     }
-                    //Flip in XY plane
-                    this.rotationQuaternion.z *= -1;
-                    this.rotationQuaternion.w *= -1;
                 }
-
             }
 
             super._checkInputs();
@@ -86,6 +91,7 @@ module BABYLON {
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
             super.attachControl(element, noPreventDefault);
+            this._attached = true;
 
             noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
 
@@ -97,6 +103,7 @@ module BABYLON {
         public detachControl(element: HTMLElement): void {
             super.detachControl(element);
             this._vrEnabled = false;
+            this._attached = false;
             this.getEngine().disableVR();
         }
 

+ 25 - 8
src/Cameras/babylon.camera.js

@@ -37,6 +37,7 @@ var BABYLON;
             this._projectionMatrix = new BABYLON.Matrix();
             this._postProcesses = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
+            this._webvrViewMatrix = BABYLON.Matrix.Identity();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._globalPosition = BABYLON.Vector3.Zero();
             this._refreshFrustumPlanes = true;
@@ -462,16 +463,18 @@ var BABYLON;
                     break;
                 case Camera.RIG_MODE_WEBVR:
                     if (rigParams.vrDisplay) {
-                        var leftEye = rigParams.vrDisplay.getEyeParameters('left');
-                        var rightEye = rigParams.vrDisplay.getEyeParameters('right');
+                        //var leftEye = rigParams.vrDisplay.getEyeParameters('left');
+                        //var rightEye = rigParams.vrDisplay.getEyeParameters('right');
                         this._rigCameras[0].viewport = new BABYLON.Viewport(0, 0, 0.5, 1.0);
-                        this._rigCameras[0].setCameraRigParameter("vrFieldOfView", leftEye.fieldOfView);
-                        this._rigCameras[0].setCameraRigParameter("vrOffsetMatrix", BABYLON.Matrix.Translation(-leftEye.offset[0], leftEye.offset[1], -leftEye.offset[2]));
+                        this._rigCameras[0].setCameraRigParameter("left", true);
+                        this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
+                        //this._rigCameras[0].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-leftEye.offset[0], leftEye.offset[1], -leftEye.offset[2]));
                         this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new BABYLON.Matrix();
                         this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
+                        //this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
                         this._rigCameras[1].viewport = new BABYLON.Viewport(0.5, 0, 0.5, 1.0);
-                        this._rigCameras[1].setCameraRigParameter("vrFieldOfView", rightEye.fieldOfView);
-                        this._rigCameras[1].setCameraRigParameter("vrOffsetMatrix", BABYLON.Matrix.Translation(-rightEye.offset[0], rightEye.offset[1], -rightEye.offset[2]));
+                        this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
+                        //this._rigCameras[1].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-rightEye.offset[0], rightEye.offset[1], -rightEye.offset[2]));
                         this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new BABYLON.Matrix();
                         this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
                     }
@@ -487,10 +490,24 @@ var BABYLON;
             return this._projectionMatrix;
         };
         Camera.prototype._getWebVRProjectionMatrix = function () {
-            BABYLON.Matrix.PerspectiveFovWebVRToRef(this._cameraRigParams['vrFieldOfView'], this.minZ, this.maxZ, this._cameraRigParams.vrWorkMatrix);
-            this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams['vrOffsetMatrix'], this._projectionMatrix);
+            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
+            //babylon compatible matrix
+            [8, 9, 10, 11].forEach(function (num) {
+                projectionArray[num] *= -1;
+            });
+            BABYLON.Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
             return this._projectionMatrix;
         };
+        //Can be used, but we'll use the free camera's view matrix calculation
+        Camera.prototype._getWebVRViewMatrix = function () {
+            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftViewMatrix : this._cameraRigParams["frameData"].rightViewMatrix;
+            //babylon compatible matrix
+            [8, 9, 10, 11].forEach(function (num) {
+                projectionArray[num] *= -1;
+            });
+            BABYLON.Matrix.FromArrayToRef(projectionArray, 0, this._webvrViewMatrix);
+            return this._webvrViewMatrix;
+        };
         Camera.prototype.setCameraRigParameter = function (name, value) {
             if (!this._cameraRigParams) {
                 this._cameraRigParams = {};

+ 27 - 8
src/Cameras/babylon.camera.ts

@@ -126,6 +126,7 @@
         private _worldMatrix: Matrix;
         public _postProcesses = new Array<PostProcess>();
         private _transformMatrix = Matrix.Zero();
+        private _webvrViewMatrix = Matrix.Identity();
 
         public _activeMeshes = new SmartArray<Mesh>(256);
 
@@ -574,18 +575,21 @@
                     break;
                 case Camera.RIG_MODE_WEBVR:
                     if (rigParams.vrDisplay) {
-                        var leftEye = rigParams.vrDisplay.getEyeParameters('left');
-                        var rightEye = rigParams.vrDisplay.getEyeParameters('right');
+                        //var leftEye = rigParams.vrDisplay.getEyeParameters('left');
+                        //var rightEye = rigParams.vrDisplay.getEyeParameters('right');
                         this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
-                        this._rigCameras[0].setCameraRigParameter("vrFieldOfView", leftEye.fieldOfView);
-                        this._rigCameras[0].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-leftEye.offset[0], leftEye.offset[1], -leftEye.offset[2]));
+                        this._rigCameras[0].setCameraRigParameter("left", true);
+                        this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
+                        //this._rigCameras[0].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-leftEye.offset[0], leftEye.offset[1], -leftEye.offset[2]));
                         this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
+                        //this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
                         this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
-                        this._rigCameras[1].setCameraRigParameter("vrFieldOfView", rightEye.fieldOfView);
-                        this._rigCameras[1].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-rightEye.offset[0], rightEye.offset[1], -rightEye.offset[2]));
+                        this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
+                        //this._rigCameras[1].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-rightEye.offset[0], rightEye.offset[1], -rightEye.offset[2]));
                         this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
+                        //this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
                     }
                     break;
 
@@ -603,11 +607,26 @@
         }
 
         private _getWebVRProjectionMatrix(): Matrix {
-            Matrix.PerspectiveFovWebVRToRef(this._cameraRigParams['vrFieldOfView'], this.minZ, this.maxZ, this._cameraRigParams.vrWorkMatrix);
-            this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams['vrOffsetMatrix'], this._projectionMatrix);
+            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
+            //babylon compatible matrix
+            [8, 9, 10, 11].forEach(function (num) {
+                projectionArray[num] *= -1;
+            });
+            Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
             return this._projectionMatrix;
         }
 
+        //Can be used, but we'll use the free camera's view matrix calculation
+        private _getWebVRViewMatrix(): Matrix {
+            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftViewMatrix : this._cameraRigParams["frameData"].rightViewMatrix;
+            //babylon compatible matrix
+            [8, 9, 10, 11].forEach(function (num) {
+                projectionArray[num] *= -1;
+            });
+            Matrix.FromArrayToRef(projectionArray, 0, this._webvrViewMatrix);
+            return this._webvrViewMatrix;
+        }
+
         public setCameraRigParameter(name: string, value: any) {
             if (!this._cameraRigParams) {
                 this._cameraRigParams = {};

+ 9 - 0
src/Materials/Textures/babylon.baseTexture.ts

@@ -33,6 +33,13 @@
         @serialize()
         public isRenderTarget = false;
 
+        public get uid(): string {
+            if (!this._uid) {
+                this._uid = Tools.RandomId();
+            }
+            return this._uid;
+        }
+
         public toString(): string {
             return this.name;
         }
@@ -59,10 +66,12 @@
 
         private _scene: Scene;
         public _texture: WebGLTexture;
+        private _uid: string;
 
         constructor(scene: Scene) {
             this._scene = scene;
             this._scene.textures.push(this);
+            this._uid = null;
         }
 
         public getScene(): Scene {

+ 9 - 0
src/Math/babylon.math.js

@@ -818,6 +818,15 @@ var BABYLON;
         Vector3.Up = function () {
             return new Vector3(0, 1.0, 0);
         };
+        Vector3.Forward = function () {
+            return new Vector3(0, 0, 1.0);
+        };
+        Vector3.Right = function () {
+            return new Vector3(1.0, 0, 0);
+        };
+        Vector3.Left = function () {
+            return new Vector3(-1.0, 0, 0);
+        };
         Vector3.TransformCoordinates = function (vector, transformation) {
             var result = Vector3.Zero();
             Vector3.TransformCoordinatesToRef(vector, transformation, result);

+ 12 - 0
src/Math/babylon.math.ts

@@ -1023,6 +1023,18 @@
             return new Vector3(0, 1.0, 0);
         }
 
+        public static Forward(): Vector3 {
+            return new Vector3(0, 0, 1.0);
+        }
+
+        public static Right(): Vector3 {
+            return new Vector3(1.0, 0, 0);
+        }
+
+        public static Left(): Vector3 {
+            return new Vector3(-1.0, 0, 0);
+        }
+
         public static TransformCoordinates(vector: Vector3, transformation: Matrix): Vector3 {
             var result = Vector3.Zero();
 

+ 14 - 13
src/Particles/babylon.solidParticleSystem.js

@@ -88,6 +88,8 @@ var BABYLON;
             this._maximum = BABYLON.Tmp.Vector3[1];
             this._scale = BABYLON.Tmp.Vector3[2];
             this._translation = BABYLON.Tmp.Vector3[3];
+            this._minBbox = BABYLON.Tmp.Vector3[4];
+            this._maxBbox = BABYLON.Tmp.Vector3[5];
             this._particlesIntersect = false;
             this.name = name;
             this._scene = scene;
@@ -137,7 +139,6 @@ var BABYLON;
             vertexData.applyToMesh(mesh, this._updatable);
             this.mesh = mesh;
             this.mesh.isPickable = this._pickable;
-            this._wm = this.mesh.getWorldMatrix();
             // free memory
             this._positions = null;
             this._normals = null;
@@ -469,7 +470,7 @@ var BABYLON;
             // if the particles will always face the camera
             if (this.billboard) {
                 // compute the camera position and un-rotate it by the current mesh rotation
-                if (this._wm.decompose(this._scale, this._quaternion, this._translation)) {
+                if (this.mesh._worldMatrix.decompose(this._scale, this._quaternion, this._translation)) {
                     this._quaternionToRotationMatrix();
                     this._rotMatrix.invertToRef(this._invertMatrix);
                     this._camera._currentTarget.subtractToRef(this._camera.globalPosition, this._camDir);
@@ -637,15 +638,15 @@ var BABYLON;
                     }
                     // place and scale the particle bouding sphere in the SPS local system
                     if (this._particle.isVisible) {
-                        this._minimum.x = this._particle._modelBoundingInfo.minimum.x * this._particle.scaling.x;
-                        this._minimum.y = this._particle._modelBoundingInfo.minimum.y * this._particle.scaling.y;
-                        this._minimum.z = this._particle._modelBoundingInfo.minimum.z * this._particle.scaling.z;
-                        this._maximum.x = this._particle._modelBoundingInfo.maximum.x * this._particle.scaling.x;
-                        this._maximum.y = this._particle._modelBoundingInfo.maximum.y * this._particle.scaling.y;
-                        this._maximum.z = this._particle._modelBoundingInfo.maximum.z * this._particle.scaling.z;
-                        bSphere.center.x = this._particle.position.x + (this._minimum.x + this._maximum.x) * 0.5;
-                        bSphere.center.y = this._particle.position.y + (this._minimum.y + this._maximum.y) * 0.5;
-                        bSphere.center.z = this._particle.position.z + (this._minimum.z + this._maximum.z) * 0.5;
+                        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 {
@@ -655,8 +656,8 @@ var BABYLON;
                         bSphere.radius = 0.0;
                     }
                     // then update the bbox and the bsphere into the world system
-                    bBox._update(this._wm);
-                    bSphere._update(this._wm);
+                    bBox._update(this.mesh._worldMatrix);
+                    bSphere._update(this.mesh._worldMatrix);
                 }
                 // increment indexes for the next particle
                 index = idx + 3;

+ 14 - 15
src/Particles/babylon.solidParticleSystem.ts

@@ -108,9 +108,9 @@
         private _maximum: Vector3 = Tmp.Vector3[1];
         private _scale: Vector3 = Tmp.Vector3[2];
         private _translation: Vector3 = Tmp.Vector3[3];
+        private _minBbox: Vector3 = Tmp.Vector3[4];
+        private _maxBbox: Vector3 = Tmp.Vector3[5];
         private _particlesIntersect: boolean = false;
-        private _wm: Matrix;
-
 
         /**
         * Creates a SPS (Solid Particle System) object.
@@ -168,7 +168,6 @@
             vertexData.applyToMesh(mesh, this._updatable);
             this.mesh = mesh;
             this.mesh.isPickable = this._pickable;
-            this._wm = this.mesh.getWorldMatrix();
 
             // free memory
             this._positions = null;
@@ -541,7 +540,7 @@
             // if the particles will always face the camera
             if (this.billboard) {
                 // compute the camera position and un-rotate it by the current mesh rotation
-                if (this._wm.decompose(this._scale, this._quaternion, this._translation)) {
+                if (this.mesh._worldMatrix.decompose(this._scale, this._quaternion, this._translation)) {
                     this._quaternionToRotationMatrix();
                     this._rotMatrix.invertToRef(this._invertMatrix);
                     this._camera._currentTarget.subtractToRef(this._camera.globalPosition, this._camDir);
@@ -729,15 +728,15 @@
                     }
                     // place and scale the particle bouding sphere in the SPS local system
                     if (this._particle.isVisible) {
-                        this._minimum.x = this._particle._modelBoundingInfo.minimum.x * this._particle.scaling.x;
-                        this._minimum.y = this._particle._modelBoundingInfo.minimum.y * this._particle.scaling.y;
-                        this._minimum.z = this._particle._modelBoundingInfo.minimum.z * this._particle.scaling.z;
-                        this._maximum.x = this._particle._modelBoundingInfo.maximum.x * this._particle.scaling.x;
-                        this._maximum.y = this._particle._modelBoundingInfo.maximum.y * this._particle.scaling.y;
-                        this._maximum.z = this._particle._modelBoundingInfo.maximum.z * this._particle.scaling.z;
-                        bSphere.center.x = this._particle.position.x + (this._minimum.x + this._maximum.x) * 0.5;
-                        bSphere.center.y = this._particle.position.y + (this._minimum.y + this._maximum.y) * 0.5;
-                        bSphere.center.z = this._particle.position.z + (this._minimum.z + this._maximum.z) * 0.5;
+                        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;
@@ -747,8 +746,8 @@
                     }
 
                     // then update the bbox and the bsphere into the world system
-                    bBox._update(this._wm);
-                    bSphere._update(this._wm);
+                    bBox._update(this.mesh._worldMatrix);
+                    bSphere._update(this.mesh._worldMatrix);
                 }
 
                 // increment indexes for the next particle

+ 44 - 0
src/Rendering/babylon.renderingManager.js

@@ -7,6 +7,7 @@ var BABYLON;
             this._customOpaqueSortCompareFn = {};
             this._customAlphaTestSortCompareFn = {};
             this._customTransparentSortCompareFn = {};
+            this._renderinGroupInfo = null;
             this._scene = scene;
             for (var i = RenderingManager.MIN_RENDERINGGROUPS; i < RenderingManager.MAX_RENDERINGGROUPS; i++) {
                 this._autoClearDepthStencil[i] = true;
@@ -66,6 +67,17 @@ var BABYLON;
             }
         };
         RenderingManager.prototype.render = function (customRenderFunction, activeMeshes, renderParticles, renderSprites) {
+            // Check if there's at least on observer on the onRenderingGroupObservable and initialize things to fire it
+            var observable = this._scene.onRenderingGroupObservable.hasObservers() ? this._scene.onRenderingGroupObservable : null;
+            var info = null;
+            if (observable) {
+                if (!this._renderinGroupInfo) {
+                    this._renderinGroupInfo = new BABYLON.RenderingGroupInfo();
+                }
+                info = this._renderinGroupInfo;
+                info.scene = this._scene;
+                info.camera = this._scene.activeCamera;
+            }
             this._currentActiveMeshes = activeMeshes;
             this._currentRenderParticles = renderParticles;
             this._currentRenderSprites = renderSprites;
@@ -75,20 +87,52 @@ var BABYLON;
                 var needToStepBack = false;
                 this._currentIndex = index;
                 if (renderingGroup) {
+                    var renderingGroupMask = 0;
+                    // Fire PRECLEAR stage
+                    if (observable) {
+                        renderingGroupMask = Math.pow(2, index);
+                        info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRECLEAR;
+                        info.renderingGroupId = index;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
+                    // Clear depth/stencil if needed
                     if (this._autoClearDepthStencil[index]) {
                         this._clearDepthStencilBuffer();
                     }
+                    // Fire PREOPAQUE stage
+                    if (observable) {
+                        info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PREOPAQUE;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
                     if (!renderingGroup.onBeforeTransparentRendering) {
                         renderingGroup.onBeforeTransparentRendering = this._renderSpritesAndParticles.bind(this);
                     }
+                    // Fire PRETRANSPARENT stage
+                    if (observable) {
+                        info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRETRANSPARENT;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
                     if (!renderingGroup.render(customRenderFunction)) {
                         this._renderingGroups.splice(index, 1);
                         needToStepBack = true;
                         this._renderSpritesAndParticles();
                     }
+                    // Fire POSTTRANSPARENT stage
+                    if (observable) {
+                        info.renderStage = BABYLON.RenderingGroupInfo.STAGE_POSTTRANSPARENT;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
                 }
                 else {
                     this._renderSpritesAndParticles();
+                    if (observable) {
+                        var renderingGroupMask = Math.pow(2, index);
+                        info.renderStage = BABYLON.RenderingGroupInfo.STAGE_PRECLEAR;
+                        info.renderingGroupId = index;
+                        observable.notifyObservers(info, renderingGroupMask);
+                        info.renderStage = BABYLON.RenderingGroupInfo.STAGE_POSTTRANSPARENT;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
                 }
                 if (needToStepBack) {
                     index--;

+ 52 - 0
src/Rendering/babylon.renderingManager.ts

@@ -23,6 +23,7 @@
         private _customOpaqueSortCompareFn: { [id:number]: (a: SubMesh, b: SubMesh) => number } = {};
         private _customAlphaTestSortCompareFn: { [id:number]: (a: SubMesh, b: SubMesh) => number } = {};
         private _customTransparentSortCompareFn: { [id:number]: (a: SubMesh, b: SubMesh) => number } = {};
+        private _renderinGroupInfo: RenderingGroupInfo = null;
 
         constructor(scene: Scene) {
             this._scene = scene;
@@ -101,6 +102,18 @@
         public render(customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>) => void,
             activeMeshes: AbstractMesh[], renderParticles: boolean, renderSprites: boolean): void {
 
+            // Check if there's at least on observer on the onRenderingGroupObservable and initialize things to fire it
+            let observable = this._scene.onRenderingGroupObservable.hasObservers() ? this._scene.onRenderingGroupObservable : null;
+            let info: RenderingGroupInfo = null;
+            if (observable) {
+                if (!this._renderinGroupInfo) {
+                    this._renderinGroupInfo = new RenderingGroupInfo();
+                }
+                info = this._renderinGroupInfo;
+                info.scene = this._scene;
+                info.camera = this._scene.activeCamera;
+            }
+
             this._currentActiveMeshes = activeMeshes;
             this._currentRenderParticles = renderParticles;
             this._currentRenderSprites = renderSprites;
@@ -113,21 +126,60 @@
                 this._currentIndex = index;
 
                 if (renderingGroup) {
+                    let renderingGroupMask = 0;
+
+                    // Fire PRECLEAR stage
+                    if (observable) {
+                        renderingGroupMask = Math.pow(2, index);
+                        info.renderStage = RenderingGroupInfo.STAGE_PRECLEAR;
+                        info.renderingGroupId = index;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
+
+                    // Clear depth/stencil if needed
                     if (this._autoClearDepthStencil[index]) {
                         this._clearDepthStencilBuffer();
                     }
 
+                    // Fire PREOPAQUE stage
+                    if (observable) {
+                        info.renderStage = RenderingGroupInfo.STAGE_PREOPAQUE;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
+
                     if (!renderingGroup.onBeforeTransparentRendering) {
                         renderingGroup.onBeforeTransparentRendering = this._renderSpritesAndParticles.bind(this);
                     }
 
+                    // Fire PRETRANSPARENT stage
+                    if (observable) {
+                        info.renderStage = RenderingGroupInfo.STAGE_PRETRANSPARENT;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
+
                     if (!renderingGroup.render(customRenderFunction)) {
                         this._renderingGroups.splice(index, 1);
                         needToStepBack = true;
                         this._renderSpritesAndParticles();
                     }
+
+                    // Fire POSTTRANSPARENT stage
+                    if (observable) {
+                        info.renderStage = RenderingGroupInfo.STAGE_POSTTRANSPARENT;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
                 } else {
                     this._renderSpritesAndParticles();
+
+                    if (observable) {
+                        let renderingGroupMask = Math.pow(2, index);
+                        info.renderStage = RenderingGroupInfo.STAGE_PRECLEAR;
+                        info.renderingGroupId = index;
+                        observable.notifyObservers(info, renderingGroupMask);
+
+                        info.renderStage = RenderingGroupInfo.STAGE_POSTTRANSPARENT;
+                        observable.notifyObservers(info, renderingGroupMask);
+                    }
                 }
 
                 if (needToStepBack) {

+ 8 - 0
src/babylon.node.js

@@ -273,6 +273,14 @@ var BABYLON;
         Node.prototype.dispose = function () {
             this.parent = null;
         };
+        Node.prototype.getDirection = function (localAxis) {
+            var result = BABYLON.Vector3.Zero();
+            this.getDirectionToRef(localAxis, result);
+            return result;
+        };
+        Node.prototype.getDirectionToRef = function (localAxis, result) {
+            BABYLON.Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
+        };
         Node.ParseAnimationRanges = function (node, parsedNode, scene) {
             if (parsedNode.ranges) {
                 for (var index = 0; index < parsedNode.ranges.length; index++) {

+ 12 - 0
src/babylon.node.ts

@@ -343,6 +343,18 @@
             this.parent = null;
         }
 
+        public getDirection(localAxis:BABYLON.Vector3): BABYLON.Vector3 {
+            var result = BABYLON.Vector3.Zero();
+
+            this.getDirectionToRef(localAxis, result);
+            
+            return result;
+        }
+
+        public getDirectionToRef(localAxis:BABYLON.Vector3, result:BABYLON.Vector3): void {
+            BABYLON.Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
+        }
+
         public static ParseAnimationRanges(node: Node, parsedNode: any, scene: Scene): void {
             if (parsedNode.ranges) {
                 for (var index = 0; index < parsedNode.ranges.length; index++) {

+ 86 - 0
src/babylon.scene.js

@@ -87,6 +87,35 @@ var BABYLON;
     }(PointerInfoBase));
     BABYLON.PointerInfo = PointerInfo;
     /**
+     * This class is used by the onRenderingGroupObservable
+     */
+    var RenderingGroupInfo = (function () {
+        function RenderingGroupInfo() {
+        }
+        /**
+         * Stage corresponding to the very first hook in the renderingGroup phase: before the render buffer may be cleared
+         * This stage will be fired no matter what
+         */
+        RenderingGroupInfo.STAGE_PRECLEAR = 1;
+        /**
+         * Called before opaque object are rendered.
+         * This stage will be fired only if there's 3D Opaque content to render
+         */
+        RenderingGroupInfo.STAGE_PREOPAQUE = 2;
+        /**
+         * Called after the opaque objects are rendered and before the transparent ones
+         * This stage will be fired only if there's 3D transparent content to render
+         */
+        RenderingGroupInfo.STAGE_PRETRANSPARENT = 3;
+        /**
+         * Called after the transparent object are rendered, last hook of the renderingGroup phase
+         * This stage will be fired no matter what
+         */
+        RenderingGroupInfo.STAGE_POSTTRANSPARENT = 4;
+        return RenderingGroupInfo;
+    }());
+    BABYLON.RenderingGroupInfo = RenderingGroupInfo;
+    /**
      * Represents a scene to be rendered by the engine.
      * @see http://doc.babylonjs.com/page.php?p=21911
      */
@@ -178,6 +207,12 @@ var BABYLON;
             * @type {BABYLON.Observable}
             */
             this.onMeshRemovedObservable = new BABYLON.Observable();
+            /**
+             * This Observable will be triggered for each stage of each renderingGroup of each rendered camera.
+             * The RenderinGroupInfo class contains all the information about the context in which the observable is called
+             * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
+             */
+            this.onRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
             /**
@@ -311,6 +346,8 @@ var BABYLON;
             this._uniqueIdCounter = 0;
             this._engine = engine;
             engine.scenes.push(this);
+            this._externalData = new BABYLON.StringDictionary();
+            this._uid = null;
             this._renderingManager = new BABYLON.RenderingManager(this);
             this.postProcessManager = new BABYLON.PostProcessManager(this);
             this.postProcessRenderPipelineManager = new BABYLON.PostProcessRenderPipelineManager();
@@ -1538,6 +1575,55 @@ var BABYLON;
         Scene.prototype.isActiveMesh = function (mesh) {
             return (this._activeMeshes.indexOf(mesh) !== -1);
         };
+        Object.defineProperty(Scene.prototype, "uid", {
+            /**
+             * Return a unique id as a string which can serve as an identifier for the scene
+             */
+            get: function () {
+                if (!this._uid) {
+                    this._uid = BABYLON.Tools.RandomId();
+                }
+                return this._uid;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Add an externaly attached data from its key.
+         * This method call will fail and return false, if such key already exists.
+         * If you don't care and just want to get the data no matter what, use the more convenient getOrAddExternalDataWithFactory() method.
+         * @param key the unique key that identifies the data
+         * @param data the data object to associate to the key for this Engine instance
+         * @return true if no such key were already present and the data was added successfully, false otherwise
+         */
+        Scene.prototype.addExternalData = function (key, data) {
+            return this._externalData.add(key, data);
+        };
+        /**
+         * Get an externaly attached data from its key
+         * @param key the unique key that identifies the data
+         * @return the associated data, if present (can be null), or undefined if not present
+         */
+        Scene.prototype.getExternalData = function (key) {
+            return this._externalData.get(key);
+        };
+        /**
+         * Get an externaly attached data from its key, create it using a factory if it's not already present
+         * @param key the unique key that identifies the data
+         * @param factory the factory that will be called to create the instance if and only if it doesn't exists
+         * @return the associated data, can be null if the factory returned null.
+         */
+        Scene.prototype.getOrAddExternalDataWithFactory = function (key, factory) {
+            return this._externalData.getOrAddWithFactory(key, factory);
+        };
+        /**
+         * Remove an externaly attached data from the Engine instance
+         * @param key the unique key that identifies the data
+         * @return true if the data was successfully removed, false if it doesn't exist
+         */
+        Scene.prototype.removeExternalData = function (key) {
+            return this._externalData.remove(key);
+        };
         Scene.prototype._evaluateSubMesh = function (subMesh, mesh) {
             if (mesh.alwaysSelectAsActiveMesh || mesh.subMeshes.length === 1 || subMesh.isInFrustum(this._frustumPlanes)) {
                 var material = subMesh.getMaterial();

+ 112 - 0
src/babylon.scene.ts

@@ -63,6 +63,55 @@
     }
 
     /**
+     * This class is used by the onRenderingGroupObservable
+     */
+    export class RenderingGroupInfo {
+        /**
+         * The Scene that being rendered
+         */
+        scene: Scene;
+
+        /**
+         * The camera currently used for the rendering pass
+         */
+        camera: Camera;
+
+        /**
+         * The ID of the renderingGroup being processed
+         */
+        renderingGroupId: number;
+
+        /**
+         * The rendering stage, can be either STAGE_PRECLEAR, STAGE_PREOPAQUE, STAGE_PRETRANSPARENT, STAGE_POSTTRANSPARENT
+         */
+        renderStage: number;
+
+        /**
+         * Stage corresponding to the very first hook in the renderingGroup phase: before the render buffer may be cleared
+         * This stage will be fired no matter what
+         */
+        static STAGE_PRECLEAR = 1;
+
+        /**
+         * Called before opaque object are rendered.
+         * This stage will be fired only if there's 3D Opaque content to render
+         */
+        static STAGE_PREOPAQUE = 2;
+
+        /**
+         * Called after the opaque objects are rendered and before the transparent ones
+         * This stage will be fired only if there's 3D transparent content to render
+         */
+        static STAGE_PRETRANSPARENT = 3;
+
+        /**
+         * Called after the transparent object are rendered, last hook of the renderingGroup phase
+         * This stage will be fired no matter what
+         */
+        static STAGE_POSTTRANSPARENT = 4;
+    }
+
+    /**
      * Represents a scene to be rendered by the engine.
      * @see http://doc.babylonjs.com/page.php?p=21911
      */
@@ -234,6 +283,13 @@
         */
         public onMeshRemovedObservable = new Observable<AbstractMesh>();
 
+        /**
+         * This Observable will be triggered for each stage of each renderingGroup of each rendered camera.
+         * The RenderinGroupInfo class contains all the information about the context in which the observable is called
+         * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
+         */
+        public onRenderingGroupObservable = new Observable<RenderingGroupInfo>();
+
         // Animations
         public animations: Animation[] = [];
 
@@ -496,6 +552,9 @@
 
         private _pickedDownMesh: AbstractMesh;
         private _pickedDownSprite: Sprite;
+        private _externalData: StringDictionary<Object>;
+        private _uid: string;
+
 
         /**
          * @constructor
@@ -506,6 +565,9 @@
 
             engine.scenes.push(this);
 
+            this._externalData = new StringDictionary<Object>();
+            this._uid = null;
+
             this._renderingManager = new RenderingManager(this);
 
             this.postProcessManager = new PostProcessManager(this);
@@ -1812,6 +1874,56 @@
             return (this._activeMeshes.indexOf(mesh) !== -1);
         }
 
+        /**
+         * Return a unique id as a string which can serve as an identifier for the scene
+         */
+        public get uid(): string {
+            if (!this._uid) {
+                this._uid = Tools.RandomId();
+            }
+            return this._uid;
+        }
+
+        /**
+         * Add an externaly attached data from its key.
+         * This method call will fail and return false, if such key already exists.
+         * If you don't care and just want to get the data no matter what, use the more convenient getOrAddExternalDataWithFactory() method.
+         * @param key the unique key that identifies the data
+         * @param data the data object to associate to the key for this Engine instance
+         * @return true if no such key were already present and the data was added successfully, false otherwise
+         */
+        public addExternalData<T>(key: string, data: T): boolean {
+            return this._externalData.add(key, data);
+        }
+
+        /**
+         * Get an externaly attached data from its key
+         * @param key the unique key that identifies the data
+         * @return the associated data, if present (can be null), or undefined if not present
+         */
+        public getExternalData<T>(key: string): T {
+            return <T>this._externalData.get(key);
+        }
+
+        /**
+         * Get an externaly attached data from its key, create it using a factory if it's not already present
+         * @param key the unique key that identifies the data
+         * @param factory the factory that will be called to create the instance if and only if it doesn't exists
+         * @return the associated data, can be null if the factory returned null.
+         */
+        public getOrAddExternalDataWithFactory<T>(key: string, factory: (k: string) => T): T {
+            return <T>this._externalData.getOrAddWithFactory(key, factory);
+        }
+
+        /**
+         * Remove an externaly attached data from the Engine instance
+         * @param key the unique key that identifies the data
+         * @return true if the data was successfully removed, false if it doesn't exist
+         */
+        public removeExternalData(key): boolean {
+            return this._externalData.remove(key);
+        }
+
         private _evaluateSubMesh(subMesh: SubMesh, mesh: AbstractMesh): void {
             if (mesh.alwaysSelectAsActiveMesh || mesh.subMeshes.length === 1 || subMesh.isInFrustum(this._frustumPlanes)) {
                 var material = subMesh.getMaterial();