Procházet zdrojové kódy

Merge pull request #2 from BabylonJS/master

update fork
jahow před 10 roky
rodič
revize
da310a268e
37 změnil soubory, kde provedl 1781 přidání a 539 odebrání
  1. 4 1
      Babylon/Audio/babylon.sound.js
  2. 4 1
      Babylon/Audio/babylon.sound.ts
  3. 4 1
      Babylon/Bones/babylon.skeleton.js
  4. 5 1
      Babylon/Bones/babylon.skeleton.ts
  5. 7 2
      Babylon/Collisions/babylon.pickingInfo.js
  6. 6 2
      Babylon/Collisions/babylon.pickingInfo.ts
  7. 1 1
      Babylon/Debug/babylon.debugLayer.js
  8. 1 1
      Babylon/Debug/babylon.debugLayer.js.map
  9. 2 1
      Babylon/Debug/babylon.debugLayer.ts
  10. 2 1
      Babylon/Materials/babylon.material.js
  11. 4 2
      Babylon/Materials/babylon.material.ts
  12. 25 3
      Babylon/Materials/babylon.shaderMaterial.js
  13. 30 3
      Babylon/Materials/babylon.shaderMaterial.ts
  14. 1 1
      Babylon/Materials/babylon.standardMaterial.js
  15. 2 2
      Babylon/Materials/babylon.standardMaterial.ts
  16. 62 16
      Babylon/Math/babylon.math.js
  17. 64 21
      Babylon/Math/babylon.math.ts
  18. 171 0
      Babylon/Mesh/babylon.mesh.js
  19. 222 10
      Babylon/Mesh/babylon.mesh.ts
  20. 7 1
      Babylon/Mesh/babylon.mesh.vertexData.js
  21. 7 2
      Babylon/Mesh/babylon.mesh.vertexData.ts
  22. 71 39
      Babylon/PostProcess/babylon.lensRenderingPipeline.js
  23. 124 82
      Babylon/PostProcess/babylon.lensRenderingPipeline.ts
  24. 10 3
      Babylon/PostProcess/babylon.postProcessManager.js
  25. 10 2
      Babylon/PostProcess/babylon.postProcessManager.ts
  26. 15 3
      Babylon/Rendering/babylon.boundingBoxRenderer.js
  27. 19 3
      Babylon/Rendering/babylon.boundingBoxRenderer.ts
  28. 10 12
      Babylon/Shaders/chromaticAberration.fragment.fx
  29. 95 165
      Babylon/Shaders/depthOfField.fragment.fx
  30. 140 0
      Babylon/Shaders/lensHighlights.fragment.fx
  31. 42 5
      Babylon/babylon.engine.js
  32. 45 7
      Babylon/babylon.engine.ts
  33. 1 0
      Babylon/babylon.node.ts
  34. 39 1
      Babylon/babylon.scene.js
  35. 46 1
      Babylon/babylon.scene.ts
  36. 462 122
      babylon.2.1-alpha.debug.js
  37. 21 21
      babylon.2.1-alpha.js

+ 4 - 1
Babylon/Audio/babylon.sound.js

@@ -96,8 +96,11 @@ var BABYLON;
                     BABYLON.Tools.Error("Web Audio is not supported by your browser.");
                     BABYLON.Engine.audioEngine.WarnedWebAudioUnsupported = true;
                 }
+                // Simulating a ready to play event to avoid breaking code for non web audio browsers
                 if (this._readyToPlayCallback) {
-                    this._readyToPlayCallback();
+                    window.setTimeout(function () {
+                        _this._readyToPlayCallback();
+                    }, 1000);
                 }
             }
         }

+ 4 - 1
Babylon/Audio/babylon.sound.ts

@@ -108,8 +108,11 @@
                     BABYLON.Tools.Error("Web Audio is not supported by your browser.");
                     Engine.audioEngine.WarnedWebAudioUnsupported = true;
                 }
+                // Simulating a ready to play event to avoid breaking code for non web audio browsers
                 if (this._readyToPlayCallback) {
-                    this._readyToPlayCallback();
+                    window.setTimeout(() => {
+                        this._readyToPlayCallback();
+                    }, 1000);
                 }
             }
         }

+ 4 - 1
Babylon/Bones/babylon.skeleton.js

@@ -10,6 +10,9 @@ var BABYLON;
             this.bones = [];
             this._scene = scene;
             scene.skeletons.push(this);
+            this.prepare():
+            //make sure it will recalculate the matrix next time prepare is called.
+            this._isDirty = true;
         }
         // Members
         Skeleton.prototype.getTransformMatrices = function () {
@@ -68,4 +71,4 @@ var BABYLON;
     })();
     BABYLON.Skeleton = Skeleton;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.skeleton.js.map
+//# sourceMappingURL=babylon.skeleton.js.map

+ 5 - 1
Babylon/Bones/babylon.skeleton.ts

@@ -14,6 +14,10 @@
             this._scene = scene;
 
             scene.skeletons.push(this);
+            
+            this.prepare();
+            //make sure it will recalculate the matrix next time prepare is called.
+            this._isDirty = true; 
         }
 
         // Members
@@ -86,4 +90,4 @@
             return result;
         }
     }
-}
+}

+ 7 - 2
Babylon/Collisions/babylon.pickingInfo.js

@@ -23,7 +23,8 @@ var BABYLON;
             this.subMeshId = 0;
         }
         // Methods
-        PickingInfo.prototype.getNormal = function () {
+        PickingInfo.prototype.getNormal = function (useWorldCoordinates) {
+            if (useWorldCoordinates === void 0) { useWorldCoordinates = false; }
             if (!this.pickedMesh || !this.pickedMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
                 return null;
             }
@@ -35,7 +36,11 @@ var BABYLON;
             normal0 = normal0.scale(this.bu);
             normal1 = normal1.scale(this.bv);
             normal2 = normal2.scale(1.0 - this.bu - this.bv);
-            return new BABYLON.Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            var result = new BABYLON.Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            if (useWorldCoordinates) {
+                result = BABYLON.Vector3.TransformNormal(result, this.pickedMesh.getWorldMatrix());
+            }
+            return result;
         };
         PickingInfo.prototype.getTextureCoordinates = function () {
             if (!this.pickedMesh || !this.pickedMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {

+ 6 - 2
Babylon/Collisions/babylon.pickingInfo.ts

@@ -18,7 +18,7 @@
         public subMeshId = 0;
 
         // Methods
-        public getNormal(): Vector3 {
+        public getNormal(useWorldCoordinates = false): Vector3 {
             if (!this.pickedMesh || !this.pickedMesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                 return null;
             }
@@ -34,7 +34,11 @@
             normal1 = normal1.scale(this.bv);
             normal2 = normal2.scale(1.0 - this.bu - this.bv);
 
-            return new Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            var result = new Vector3(normal0.x + normal1.x + normal2.x, normal0.y + normal1.y + normal2.y, normal0.z + normal1.z + normal2.z);
+            if (useWorldCoordinates) {
+                result = Vector3.TransformNormal(result, this.pickedMesh.getWorldMatrix());
+            }
+            return result;
         }
 
         public getTextureCoordinates(): Vector2 {

+ 1 - 1
Babylon/Debug/babylon.debugLayer.js

@@ -269,13 +269,13 @@ var BABYLON;
             if (this._enabled) {
                 return;
             }
+            this._enabled = true;
             if (camera) {
                 this._camera = camera;
             }
             else {
                 this._camera = this._scene.activeCamera;
             }
-            this._enabled = true;
             this._showUI = showUI;
             var engine = this._scene.getEngine();
             this._globalDiv = document.createElement("div");

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
Babylon/Debug/babylon.debugLayer.js.map


+ 2 - 1
Babylon/Debug/babylon.debugLayer.ts

@@ -373,13 +373,14 @@
                 return;
             }
 
+            this._enabled = true;
+
             if (camera) {
                 this._camera = camera;
             } else {
                 this._camera = this._scene.activeCamera;
             }
 
-            this._enabled = true;
             this._showUI = showUI;
 
             var engine = this._scene.getEngine();

+ 2 - 1
Babylon/Materials/babylon.material.js

@@ -11,6 +11,7 @@ var BABYLON;
             this._wasPreviouslyReady = false;
             this._fillMode = Material.TriangleFillMode;
             this.pointSize = 1.0;
+            this.zOffset = 0;
             this.id = name;
             this._scene = scene;
             if (!doNotAdd) {
@@ -91,7 +92,7 @@ var BABYLON;
         Material.prototype._preBind = function () {
             var engine = this._scene.getEngine();
             engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling);
+            engine.setState(this.backFaceCulling, this.zOffset);
         };
         Material.prototype.bind = function (world, mesh) {
             this._scene._cachedMaterial = this;

+ 4 - 2
Babylon/Materials/babylon.material.ts

@@ -35,6 +35,8 @@
 
         public pointSize = 1.0;
 
+        public zOffset = 0;
+
         public get wireframe(): boolean {
             return this._fillMode === Material.WireFrameFillMode;
         }
@@ -100,10 +102,10 @@
             var engine = this._scene.getEngine();
 
             engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling);
+            engine.setState(this.backFaceCulling, this.zOffset);
         }
 
-        public bind(world: Matrix, mesh: Mesh): void {
+        public bind(world: Matrix, mesh?: Mesh): void {
             this._scene._cachedMaterial = this;
 
             if (this.onBind) {

+ 25 - 3
Babylon/Materials/babylon.shaderMaterial.js

@@ -80,7 +80,7 @@ var BABYLON;
             this._matrices[name] = value;
             return this;
         };
-        ShaderMaterial.prototype.isReady = function () {
+        ShaderMaterial.prototype.isReady = function (mesh, useInstances) {
             var scene = this.getScene();
             var engine = scene.getEngine();
             if (!this.checkReadyOnEveryCall) {
@@ -88,8 +88,26 @@ var BABYLON;
                     return true;
                 }
             }
+            // Instances
+            var defines = [];
+            var fallbacks = new BABYLON.EffectFallbacks();
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+            }
+            // Bones
+            if (mesh && mesh.useBones) {
+                defines.push("#define BONES");
+                defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
+                defines.push("#define BONES4");
+                fallbacks.addFallback(0, "BONES4");
+            }
+            // Alpha test
+            if (engine.getAlphaTesting()) {
+                defines.push("#define ALPHATEST");
+            }
             var previousEffect = this._effect;
-            this._effect = engine.createEffect(this._shaderPath, this._options.attributes, this._options.uniforms, this._options.samplers, "", null, this.onCompiled, this.onError);
+            var join = defines.join("\n");
+            this._effect = engine.createEffect(this._shaderPath, this._options.attributes, this._options.uniforms, this._options.samplers, join, fallbacks, this.onCompiled, this.onError);
             if (!this._effect.isReady()) {
                 return false;
             }
@@ -112,7 +130,7 @@ var BABYLON;
                 this._effect.setMatrix("worldViewProjection", world.multiply(scene.getTransformMatrix()));
             }
         };
-        ShaderMaterial.prototype.bind = function (world) {
+        ShaderMaterial.prototype.bind = function (world, mesh) {
             // Std values
             this.bindOnlyWorldMatrix(world);
             if (this.getScene().getCachedMaterial() !== this) {
@@ -125,6 +143,10 @@ var BABYLON;
                 if (this._options.uniforms.indexOf("viewProjection") !== -1) {
                     this._effect.setMatrix("viewProjection", this.getScene().getTransformMatrix());
                 }
+                // Bones
+                if (mesh.useBones) {
+                    this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
+                }
                 for (var name in this._textures) {
                     this._effect.setTexture(name, this._textures[name]);
                 }

+ 30 - 3
Babylon/Materials/babylon.shaderMaterial.ts

@@ -98,7 +98,7 @@
             return this;
         }
 
-        public isReady(): boolean {
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
             var scene = this.getScene();
             var engine = scene.getEngine();
 
@@ -108,12 +108,34 @@
                 }
             }
 
+            // Instances
+            var defines = [];
+            var fallbacks = new EffectFallbacks();
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+            }
+
+            // Bones
+            if (mesh && mesh.useBones) {
+                defines.push("#define BONES");
+                defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
+                defines.push("#define BONES4");
+                fallbacks.addFallback(0, "BONES4");
+            }
+
+            // Alpha test
+            if (engine.getAlphaTesting()) {
+                defines.push("#define ALPHATEST");
+            }
+
             var previousEffect = this._effect;
+            var join = defines.join("\n");
+
             this._effect = engine.createEffect(this._shaderPath,
                 this._options.attributes,
                 this._options.uniforms,
                 this._options.samplers,
-                "", null, this.onCompiled, this.onError);
+                join, fallbacks, this.onCompiled, this.onError);
 
             if (!this._effect.isReady()) {
                 return false;
@@ -145,7 +167,7 @@
             }
         }
 
-        public bind(world: Matrix): void {
+        public bind(world: Matrix, mesh?: Mesh): void {
             // Std values
             this.bindOnlyWorldMatrix(world);
 
@@ -162,6 +184,11 @@
                     this._effect.setMatrix("viewProjection", this.getScene().getTransformMatrix());
                 }
 
+                // Bones
+                if (mesh.useBones) {
+                    this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
+                }
+
                 // Texture
                 for (var name in this._textures) {
                     this._effect.setTexture(name, this._textures[name]);

+ 1 - 1
Babylon/Materials/babylon.standardMaterial.js

@@ -351,7 +351,7 @@ var BABYLON;
             this.bindOnlyWorldMatrix(world);
             this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
             // Bones
-            if (mesh.useBones) {
+            if (mesh && mesh.useBones) {
                 this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
             }
             if (scene.getCachedMaterial() !== this) {

+ 2 - 2
Babylon/Materials/babylon.standardMaterial.ts

@@ -425,7 +425,7 @@
             this._effect.setMatrix("world", world);
         }
 
-        public bind(world: Matrix, mesh: Mesh): void {
+        public bind(world: Matrix, mesh?: Mesh): void {
             var scene = this.getScene();
 
             // Matrices        
@@ -433,7 +433,7 @@
             this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
 
             // Bones
-            if (mesh.useBones) {
+            if (mesh && mesh.useBones) {
                 this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
             }
 

+ 62 - 16
Babylon/Math/babylon.math.js

@@ -580,6 +580,12 @@ var BABYLON;
             return this;
         };
         // Statics
+        Vector3.GetClipFactor = function (vector0, vector1, axis, size) {
+            var d0 = Vector3.Dot(vector0, axis) - size;
+            var d1 = Vector3.Dot(vector1, axis) - size;
+            var s = d0 / (d0 - d1);
+            return s;
+        };
         Vector3.FromArray = function (array, offset) {
             if (!offset) {
                 offset = 0;
@@ -2619,7 +2625,33 @@ var BABYLON;
             this._tangents = new Array();
             this._normals = new Array();
             this._binormals = new Array();
-            this._curve = path.slice(); // copy array         
+            this._curve = path.slice(); // copy array  
+            this._compute();
+        }
+        Path3D.prototype.getCurve = function () {
+            return this._curve;
+        };
+        Path3D.prototype.getTangents = function () {
+            return this._tangents;
+        };
+        Path3D.prototype.getNormals = function () {
+            return this._normals;
+        };
+        Path3D.prototype.getBinormals = function () {
+            return this._binormals;
+        };
+        Path3D.prototype.getDistances = function () {
+            return this._distances;
+        };
+        Path3D.prototype.update = function (path) {
+            for (var i = 0; i < path.length; i++) {
+                this._curve[i] = path[i];
+            }
+            this._compute();
+            return this;
+        };
+        // private function compute() : computes tangents, normals and binormals
+        Path3D.prototype._compute = function () {
             var l = this._curve.length;
             // first and last tangents
             this._tangents[0] = this._curve[1].subtract(this._curve[0]);
@@ -2659,21 +2691,6 @@ var BABYLON;
                 this._binormals[i] = Vector3.Cross(curTang, this._normals[i]);
                 this._binormals[i].normalize();
             }
-        }
-        Path3D.prototype.getCurve = function () {
-            return this._curve;
-        };
-        Path3D.prototype.getTangents = function () {
-            return this._tangents;
-        };
-        Path3D.prototype.getNormals = function () {
-            return this._normals;
-        };
-        Path3D.prototype.getBinormals = function () {
-            return this._binormals;
-        };
-        Path3D.prototype.getDistances = function () {
-            return this._distances;
         };
         // private function normalVector(v0, vt) :
         // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
@@ -2742,6 +2759,35 @@ var BABYLON;
         return Curve3;
     })();
     BABYLON.Curve3 = Curve3;
+    // Vertex formats
+    var PositionNormalVertex = (function () {
+        function PositionNormalVertex(position, normal) {
+            if (position === void 0) { position = Vector3.Zero(); }
+            if (normal === void 0) { normal = Vector3.Up(); }
+            this.position = position;
+            this.normal = normal;
+        }
+        PositionNormalVertex.prototype.clone = function () {
+            return new PositionNormalVertex(this.position.clone(), this.normal.clone());
+        };
+        return PositionNormalVertex;
+    })();
+    BABYLON.PositionNormalVertex = PositionNormalVertex;
+    var PositionNormalTextureVertex = (function () {
+        function PositionNormalTextureVertex(position, normal, uv) {
+            if (position === void 0) { position = Vector3.Zero(); }
+            if (normal === void 0) { normal = Vector3.Up(); }
+            if (uv === void 0) { uv = Vector2.Zero(); }
+            this.position = position;
+            this.normal = normal;
+            this.uv = uv;
+        }
+        PositionNormalTextureVertex.prototype.clone = function () {
+            return new PositionNormalTextureVertex(this.position.clone(), this.normal.clone(), this.uv.clone());
+        };
+        return PositionNormalTextureVertex;
+    })();
+    BABYLON.PositionNormalTextureVertex = PositionNormalTextureVertex;
     // SIMD
     if (window.SIMD !== undefined) {
         // Replace functions

+ 64 - 21
Babylon/Math/babylon.math.ts

@@ -716,6 +716,15 @@
         }
 
         // Statics
+        public static GetClipFactor(vector0: Vector3, vector1: Vector3, axis: Vector3, size) {
+            var d0 = Vector3.Dot(vector0, axis) - size;
+            var d1 = Vector3.Dot(vector1, axis) - size;
+
+            var s = d0 / (d0 - d1);
+
+            return s;
+        }
+
         public static FromArray(array: number[], offset?: number): Vector3 {
             if (!offset) {
                 offset = 0;
@@ -3285,7 +3294,40 @@
         private _binormals = new Array<Vector3>();
 
         constructor(public path: Vector3[]) {
-            this._curve = path.slice();   // copy array         
+            this._curve = path.slice();   // copy array  
+            this._compute();       
+        }
+
+        public getCurve(): Vector3[] {
+            return this._curve;
+        }
+
+        public getTangents(): Vector3[] {
+            return this._tangents;
+        }
+
+        public getNormals(): Vector3[] {
+            return this._normals;
+        }
+
+        public getBinormals(): Vector3[] {
+            return this._binormals;
+        }
+
+        public getDistances(): number[] {
+            return this._distances;
+        }
+
+        public update(path: Vector3[]): Path3D {
+            for(var i = 0; i < path.length; i++) {
+                this._curve[i] = path[i];
+            }
+            this._compute();
+            return this;
+        }
+
+        // private function compute() : computes tangents, normals and binormals
+        private _compute() {
             var l = this._curve.length;
 
             // first and last tangents
@@ -3332,26 +3374,6 @@
             }
         }
 
-        public getCurve(): Vector3[] {
-            return this._curve;
-        }
-
-        public getTangents(): Vector3[] {
-            return this._tangents;
-        }
-
-        public getNormals(): Vector3[] {
-            return this._normals;
-        }
-
-        public getBinormals(): Vector3[] {
-            return this._binormals;
-        }
-
-        public getDistances(): number[] {
-            return this._distances;
-        }
-
         // private function normalVector(v0, vt) :
         // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
         private _normalVector(v0: Vector3, vt: Vector3): Vector3 {
@@ -3424,6 +3446,27 @@
         }
     }
 
+    // Vertex formats
+    export class PositionNormalVertex {
+        constructor(public position: Vector3 = Vector3.Zero(), public normal: Vector3 = Vector3.Up()) {
+            
+        }
+
+        public clone(): PositionNormalVertex {
+            return new PositionNormalVertex(this.position.clone(), this.normal.clone());
+        }
+    }
+
+    export class PositionNormalTextureVertex {
+        constructor(public position: Vector3 = Vector3.Zero(), public normal: Vector3 = Vector3.Up(), public uv: Vector2 = Vector2.Zero()) {
+
+        }
+
+        public clone(): PositionNormalTextureVertex {
+            return new PositionNormalTextureVertex(this.position.clone(), this.normal.clone(), this.uv.clone());
+        }
+    }
+
     // SIMD
     if (window.SIMD !== undefined) {
         // Replace functions

+ 171 - 0
Babylon/Mesh/babylon.mesh.js

@@ -169,6 +169,9 @@ var BABYLON;
             }
             var distanceToCamera = (boundingSphere ? boundingSphere : this.getBoundingInfo().boundingSphere).centerWorld.subtract(camera.position).length();
             if (this._LODLevels[this._LODLevels.length - 1].distance > distanceToCamera) {
+                if (this.onLODLevelSelection) {
+                    this.onLODLevelSelection(distanceToCamera, this, this._LODLevels[this._LODLevels.length - 1].mesh);
+                }
                 return this;
             }
             for (var index = 0; index < this._LODLevels.length; index++) {
@@ -178,9 +181,15 @@ var BABYLON;
                         level.mesh._preActivate();
                         level.mesh._updateSubMeshesBoundingInfo(this.worldMatrixFromCache);
                     }
+                    if (this.onLODLevelSelection) {
+                        this.onLODLevelSelection(distanceToCamera, this, level.mesh);
+                    }
                     return level.mesh;
                 }
             }
+            if (this.onLODLevelSelection) {
+                this.onLODLevelSelection(distanceToCamera, this, this);
+            }
             return this;
         };
         Object.defineProperty(Mesh.prototype, "geometry", {
@@ -1115,6 +1124,168 @@ var BABYLON;
             var tube = Mesh.CreateRibbon(name, circlePaths, false, true, 0, scene, updatable, sideOrientation);
             return tube;
         };
+        // Decals
+        Mesh.CreateDecal = function (name, sourceMesh, position, normal, size, angle) {
+            if (angle === void 0) { angle = 0; }
+            var indices = sourceMesh.getIndices();
+            var positions = sourceMesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+            var normals = sourceMesh.getVerticesData(BABYLON.VertexBuffer.NormalKind);
+            // Getting correct rotation
+            if (!normal) {
+                var target = new BABYLON.Vector3(0, 0, 1);
+                var camera = sourceMesh.getScene().activeCamera;
+                var cameraWorldTarget = BABYLON.Vector3.TransformCoordinates(target, camera.getWorldMatrix());
+                normal = camera.globalPosition.subtract(cameraWorldTarget);
+            }
+            var yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
+            var len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
+            var pitch = Math.atan2(normal.y, len);
+            // Matrix
+            var decalWorldMatrix = BABYLON.Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(BABYLON.Matrix.Translation(position.x, position.y, position.z));
+            var inverseDecalWorldMatrix = BABYLON.Matrix.Invert(decalWorldMatrix);
+            var meshWorldMatrix = sourceMesh.getWorldMatrix();
+            var transformMatrix = meshWorldMatrix.multiply(inverseDecalWorldMatrix);
+            var vertexData = new BABYLON.VertexData();
+            vertexData.indices = [];
+            vertexData.positions = [];
+            vertexData.normals = [];
+            vertexData.uvs = [];
+            var currentVertexDataIndex = 0;
+            var extractDecalVector3 = function (indexId) {
+                var vertexId = indices[indexId];
+                var result = new BABYLON.PositionNormalVertex();
+                result.position = new BABYLON.Vector3(positions[vertexId * 3], positions[vertexId * 3 + 1], positions[vertexId * 3 + 2]);
+                // Send vector to decal local world
+                result.position = BABYLON.Vector3.TransformCoordinates(result.position, transformMatrix);
+                // Get normal
+                result.normal = new BABYLON.Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
+                return result;
+            };
+            // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
+            var clip = function (vertices, axis) {
+                if (vertices.length === 0) {
+                    return vertices;
+                }
+                var clipSize = 0.5 * Math.abs(BABYLON.Vector3.Dot(size, axis));
+                var clipVertices = function (v0, v1) {
+                    var clipFactor = BABYLON.Vector3.GetClipFactor(v0.position, v1.position, axis, clipSize);
+                    return new BABYLON.PositionNormalVertex(BABYLON.Vector3.Lerp(v0.position, v1.position, clipFactor), BABYLON.Vector3.Lerp(v0.normal, v1.normal, clipFactor));
+                };
+                var result = new Array();
+                for (var index = 0; index < vertices.length; index += 3) {
+                    var v1Out;
+                    var v2Out;
+                    var v3Out;
+                    var total = 0;
+                    var nV1, nV2, nV3, nV4;
+                    var d1 = BABYLON.Vector3.Dot(vertices[index].position, axis) - clipSize;
+                    var d2 = BABYLON.Vector3.Dot(vertices[index + 1].position, axis) - clipSize;
+                    var d3 = BABYLON.Vector3.Dot(vertices[index + 2].position, axis) - clipSize;
+                    v1Out = d1 > 0;
+                    v2Out = d2 > 0;
+                    v3Out = d3 > 0;
+                    total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
+                    switch (total) {
+                        case 0:
+                            result.push(vertices[index]);
+                            result.push(vertices[index + 1]);
+                            result.push(vertices[index + 2]);
+                            break;
+                        case 1:
+                            if (v1Out) {
+                                nV1 = vertices[index + 1];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index], nV1);
+                                nV4 = clipVertices(vertices[index], nV2);
+                            }
+                            if (v2Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index + 1], nV1);
+                                nV4 = clipVertices(vertices[index + 1], nV2);
+                                result.push(nV3);
+                                result.push(nV2.clone());
+                                result.push(nV1.clone());
+                                result.push(nV2.clone());
+                                result.push(nV3.clone());
+                                result.push(nV4);
+                                break;
+                            }
+                            if (v3Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 1];
+                                nV3 = clipVertices(vertices[index + 2], nV1);
+                                nV4 = clipVertices(vertices[index + 2], nV2);
+                            }
+                            result.push(nV1.clone());
+                            result.push(nV2.clone());
+                            result.push(nV3);
+                            result.push(nV4);
+                            result.push(nV3.clone());
+                            result.push(nV2.clone());
+                            break;
+                        case 2:
+                            if (!v1Out) {
+                                nV1 = vertices[index].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 1]);
+                                nV3 = clipVertices(nV1, vertices[index + 2]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v2Out) {
+                                nV1 = vertices[index + 1].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 2]);
+                                nV3 = clipVertices(nV1, vertices[index]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v3Out) {
+                                nV1 = vertices[index + 2].clone();
+                                nV2 = clipVertices(nV1, vertices[index]);
+                                nV3 = clipVertices(nV1, vertices[index + 1]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            break;
+                        case 3:
+                            break;
+                    }
+                }
+                return result;
+            };
+            for (var index = 0; index < indices.length; index += 3) {
+                var faceVertices = new Array();
+                faceVertices.push(extractDecalVector3(index));
+                faceVertices.push(extractDecalVector3(index + 1));
+                faceVertices.push(extractDecalVector3(index + 2));
+                // Clip
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(1, 0, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(-1, 0, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, 1, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, -1, 0));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, 0, 1));
+                faceVertices = clip(faceVertices, new BABYLON.Vector3(0, 0, -1));
+                if (faceVertices.length === 0) {
+                    continue;
+                }
+                for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
+                    var vertex = faceVertices[vIndex];
+                    vertexData.indices.push(currentVertexDataIndex);
+                    BABYLON.Vector3.TransformCoordinates(vertex.position, decalWorldMatrix).toArray(vertexData.positions, currentVertexDataIndex * 3);
+                    vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
+                    vertexData.uvs.push(0.5 + vertex.position.x / size.x);
+                    vertexData.uvs.push(0.5 + vertex.position.y / size.y);
+                    currentVertexDataIndex++;
+                }
+            }
+            // Return mesh
+            var decal = new Mesh(name, sourceMesh.getScene());
+            vertexData.applyToMesh(decal);
+            return decal;
+        };
         // Tools
         Mesh.MinMax = function (meshes) {
             var minVector = null;

+ 222 - 10
Babylon/Mesh/babylon.mesh.ts

@@ -28,13 +28,13 @@
             return Mesh._DEFAULTSIDE;
         }
 
-
         // Members
         public delayLoadState = Engine.DELAYLOADSTATE_NONE;
         public instances = new Array<InstancedMesh>();
         public delayLoadingFile: string;
         public _binaryInfo: any;
         private _LODLevels = new Array<Internals.MeshLODLevel>();
+        public onLODLevelSelection: (distance: number, mesh: Mesh, selectedLevel: Mesh) => void;
 
         // Private
         public _geometry: Geometry;
@@ -187,6 +187,9 @@
             var distanceToCamera = (boundingSphere ? boundingSphere : this.getBoundingInfo().boundingSphere).centerWorld.subtract(camera.position).length();
 
             if (this._LODLevels[this._LODLevels.length - 1].distance > distanceToCamera) {
+                if (this.onLODLevelSelection) {
+                    this.onLODLevelSelection(distanceToCamera, this, this._LODLevels[this._LODLevels.length - 1].mesh);
+                }
                 return this;
             }
 
@@ -194,15 +197,21 @@
                 var level = this._LODLevels[index];
 
                 if (level.distance < distanceToCamera) {
-
                     if (level.mesh) {
                         level.mesh._preActivate();
                         level.mesh._updateSubMeshesBoundingInfo(this.worldMatrixFromCache);
                     }
+
+                    if (this.onLODLevelSelection) {
+                        this.onLODLevelSelection(distanceToCamera, this, level.mesh);
+                    }
                     return level.mesh;
                 }
             }
 
+            if (this.onLODLevelSelection) {
+                this.onLODLevelSelection(distanceToCamera, this, this);
+            }
             return this;
         }
 
@@ -766,7 +775,7 @@
 
                     this.delayLoadState = Engine.DELAYLOADSTATE_LOADED;
                     scene._removePendingData(this);
-                },() => { }, scene.database, getBinaryData);
+                }, () => { }, scene.database, getBinaryData);
             }
         }
 
@@ -923,7 +932,7 @@
                 }
             };
 
-            Tools.LoadImage(url, onload,() => { }, scene.database);
+            Tools.LoadImage(url, onload, () => { }, scene.database);
         }
 
         public applyDisplacementMapFromBuffer(buffer: Uint8Array, heightMapWidth: number, heightMapHeight: number, minHeight: number, maxHeight: number): void {
@@ -944,7 +953,7 @@
             for (var index = 0; index < positions.length; index += 3) {
                 Vector3.FromArrayToRef(positions, index, position);
                 Vector3.FromArrayToRef(normals, index, normal);
-                Vector2.FromArrayToRef(uvs,(index / 3) * 2, uv);
+                Vector2.FromArrayToRef(uvs, (index / 3) * 2, uv);
 
                 // Compute height
                 var u = ((Math.abs(uv.x) * heightMapWidth) % heightMapWidth) | 0;
@@ -1025,8 +1034,8 @@
                 indices[index + 2] = index + 2;
 
                 var p1 = Vector3.FromArray(positions, index * 3);
-                var p2 = Vector3.FromArray(positions,(index + 1) * 3);
-                var p3 = Vector3.FromArray(positions,(index + 2) * 3);
+                var p2 = Vector3.FromArray(positions, (index + 1) * 3);
+                var p3 = Vector3.FromArray(positions, (index + 2) * 3);
 
                 var p1p2 = p1.subtract(p2);
                 var p3p2 = p3.subtract(p2);
@@ -1105,7 +1114,7 @@
             }
             var dupes = [];
 
-            AsyncLoop.SyncAsyncForLoop(vectorPositions.length, 40,(iteration) => {
+            AsyncLoop.SyncAsyncForLoop(vectorPositions.length, 40, (iteration) => {
                 var realPos = vectorPositions.length - 1 - iteration;
                 var testedPosition = vectorPositions[realPos];
                 for (var j = 0; j < realPos; ++j) {
@@ -1115,7 +1124,7 @@
                         break;
                     }
                 }
-            },() => {
+            }, () => {
                     for (var i = 0; i < indices.length; ++i) {
                         indices[i] = dupes[indices[i]] || indices[i];
                     }
@@ -1317,7 +1326,7 @@
                 }
             };
 
-            Tools.LoadImage(url, onload,() => { }, scene.database);
+            Tools.LoadImage(url, onload, () => { }, scene.database);
 
             return ground;
         }
@@ -1353,6 +1362,209 @@
             return tube;
         }
 
+        // Decals
+        public static CreateDecal(name: string, sourceMesh: AbstractMesh, position: Vector3, normal: Vector3, size: Vector3, angle: number = 0) {
+            var indices = sourceMesh.getIndices();
+            var positions = sourceMesh.getVerticesData(VertexBuffer.PositionKind);
+            var normals = sourceMesh.getVerticesData(VertexBuffer.NormalKind);
+
+            // Getting correct rotation
+            if (!normal) {
+                var target = new Vector3(0, 0, 1);
+                var camera = sourceMesh.getScene().activeCamera;
+                var cameraWorldTarget = Vector3.TransformCoordinates(target, camera.getWorldMatrix());
+
+                normal = camera.globalPosition.subtract(cameraWorldTarget);
+            }
+
+            var yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
+            var len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
+            var pitch = Math.atan2(normal.y, len);
+
+            // Matrix
+            var decalWorldMatrix = Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(Matrix.Translation(position.x, position.y, position.z));
+            var inverseDecalWorldMatrix = Matrix.Invert(decalWorldMatrix);
+            var meshWorldMatrix = sourceMesh.getWorldMatrix();
+            var transformMatrix = meshWorldMatrix.multiply(inverseDecalWorldMatrix);
+
+            var vertexData = new VertexData();
+            vertexData.indices = [];
+            vertexData.positions = [];
+            vertexData.normals = [];
+            vertexData.uvs = [];
+
+            var currentVertexDataIndex = 0;
+
+            var extractDecalVector3 = (indexId: number): PositionNormalVertex => {
+                var vertexId = indices[indexId];
+                var result = new PositionNormalVertex();
+                result.position = new Vector3(positions[vertexId * 3], positions[vertexId * 3 + 1], positions[vertexId * 3 + 2]);
+
+                // Send vector to decal local world
+                result.position = Vector3.TransformCoordinates(result.position, transformMatrix);
+
+                // Get normal
+                result.normal = new Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
+
+                return result;
+            }
+
+            
+            // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
+            var clip = (vertices: PositionNormalVertex[], axis: Vector3): PositionNormalVertex[]=> {
+                if (vertices.length === 0) {
+                    return vertices;
+                }
+
+                var clipSize = 0.5 * Math.abs(Vector3.Dot(size, axis));
+
+                var clipVertices = (v0: PositionNormalVertex, v1: PositionNormalVertex): PositionNormalVertex => {
+                    var clipFactor = Vector3.GetClipFactor(v0.position, v1.position, axis, clipSize);
+
+                    return new PositionNormalVertex(
+                        Vector3.Lerp(v0.position, v1.position, clipFactor),
+                        Vector3.Lerp(v0.normal, v1.normal, clipFactor)
+                    );
+                }
+
+                var result = new Array<PositionNormalVertex>();
+
+                for (var index = 0; index < vertices.length; index += 3) {
+                    var v1Out: boolean;
+                    var v2Out: boolean;
+                    var v3Out: boolean;
+                    var total = 0;
+                    var nV1: PositionNormalVertex, nV2: PositionNormalVertex, nV3: PositionNormalVertex, nV4: PositionNormalVertex;
+
+                    var d1 = Vector3.Dot(vertices[index].position, axis) - clipSize;
+                    var d2 = Vector3.Dot(vertices[index + 1].position, axis) - clipSize;
+                    var d3 = Vector3.Dot(vertices[index + 2].position, axis) - clipSize;
+
+                    v1Out = d1 > 0;
+                    v2Out = d2 > 0;
+                    v3Out = d3 > 0;
+
+                    total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
+
+                    switch (total) {
+                        case 0:
+                            result.push(vertices[index]);
+                            result.push(vertices[index + 1]);
+                            result.push(vertices[index + 2]);
+                            break;
+                        case 1:
+
+                            if (v1Out) {
+                                nV1 = vertices[index + 1];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index], nV1);
+                                nV4 = clipVertices(vertices[index], nV2);
+                            }
+
+                            if (v2Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 2];
+                                nV3 = clipVertices(vertices[index + 1], nV1);
+                                nV4 = clipVertices(vertices[index + 1], nV2);
+
+                                result.push(nV3);
+                                result.push(nV2.clone());
+                                result.push(nV1.clone());
+
+                                result.push(nV2.clone());
+                                result.push(nV3.clone());
+                                result.push(nV4);
+                                break;
+                            }
+                            if (v3Out) {
+                                nV1 = vertices[index];
+                                nV2 = vertices[index + 1];
+                                nV3 = clipVertices(vertices[index + 2], nV1);
+                                nV4 = clipVertices(vertices[index + 2], nV2);
+                            }
+
+                            result.push(nV1.clone());
+                            result.push(nV2.clone());
+                            result.push(nV3);
+
+                            result.push(nV4);
+                            result.push(nV3.clone());
+                            result.push(nV2.clone());
+                            break;
+                        case 2:
+                            if (!v1Out) {
+                                nV1 = vertices[index].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 1]);
+                                nV3 = clipVertices(nV1, vertices[index + 2]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v2Out) {
+                                nV1 = vertices[index + 1].clone();
+                                nV2 = clipVertices(nV1, vertices[index + 2]);
+                                nV3 = clipVertices(nV1, vertices[index]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            if (!v3Out) {
+                                nV1 = vertices[index + 2].clone();
+                                nV2 = clipVertices(nV1, vertices[index]);
+                                nV3 = clipVertices(nV1, vertices[index + 1]);
+                                result.push(nV1);
+                                result.push(nV2);
+                                result.push(nV3);
+                            }
+                            break;
+                        case 3:
+                            break;
+                    }
+                }
+
+                return result;
+            }
+
+            for (var index = 0; index < indices.length; index += 3) {
+                var faceVertices = new Array<PositionNormalVertex>();
+
+                faceVertices.push(extractDecalVector3(index));
+                faceVertices.push(extractDecalVector3(index + 1));
+                faceVertices.push(extractDecalVector3(index + 2));
+
+                // Clip
+                faceVertices = clip(faceVertices, new Vector3(1, 0, 0));
+                faceVertices = clip(faceVertices, new Vector3(-1, 0, 0));
+                faceVertices = clip(faceVertices, new Vector3(0, 1, 0));
+                faceVertices = clip(faceVertices, new Vector3(0, -1, 0));
+                faceVertices = clip(faceVertices, new Vector3(0, 0, 1));
+                faceVertices = clip(faceVertices, new Vector3(0, 0, -1));
+
+                if (faceVertices.length === 0) {
+                    continue;
+                }
+                
+                // Add UVs and get back to world
+                for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
+                    var vertex = faceVertices[vIndex];
+
+                    vertexData.indices.push(currentVertexDataIndex);
+                    Vector3.TransformCoordinates(vertex.position, decalWorldMatrix).toArray(vertexData.positions, currentVertexDataIndex * 3);
+                    vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
+                    vertexData.uvs.push(0.5 + vertex.position.x / size.x);
+                    vertexData.uvs.push(0.5 + vertex.position.y / size.y);
+
+                    currentVertexDataIndex++;
+                }
+            }
+
+            // Return mesh
+            var decal = new Mesh(name, sourceMesh.getScene());
+            vertexData.applyToMesh(decal);
+
+            return decal;
+        }
+
         // Tools
         public static MinMax(meshes: AbstractMesh[]): { min: Vector3; max: Vector3 } {
             var minVector: Vector3 = null;

+ 7 - 1
Babylon/Mesh/babylon.mesh.vertexData.js

@@ -884,6 +884,11 @@ var BABYLON;
             return vertexData;
         };
         // Tools
+        /**
+         * @param {any} - positions (number[] or Float32Array)
+         * @param {any} - indices   (number[] or Uint16Array)
+         * @param {any} - normals   (number[] or Float32Array)
+         */
         VertexData.ComputeNormals = function (positions, indices, normals) {
             var positionVectors = [];
             var facesOfVertices = [];
@@ -968,4 +973,5 @@ var BABYLON;
     })();
     BABYLON.VertexData = VertexData;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.mesh.vertexData.js.map
+
+//# sourceMappingURL=../Mesh/babylon.mesh.vertexData.js.map

+ 7 - 2
Babylon/Mesh/babylon.mesh.vertexData.ts

@@ -1113,13 +1113,18 @@
         }
 
         // Tools
-        public static ComputeNormals(positions: number[], indices: number[], normals: number[]) {
+        /**
+         * @param {any} - positions (number[] or Float32Array)
+         * @param {any} - indices   (number[] or Uint16Array)
+         * @param {any} - normals   (number[] or Float32Array)
+         */
+        public static ComputeNormals(positions: any, indices: any, normals: any) {
             var positionVectors = [];
             var facesOfVertices = [];
             var index;
 
             for (index = 0; index < positions.length; index += 3) {
-                var vector3 = new Vector3(positions[index], positions[index + 1], positions[index + 2]);
+                var vector3 = new Vector3(<number> positions[index], <number> positions[index + 1], <number> positions[index + 2]);
                 positionVectors.push(vector3);
                 facesOfVertices.push([]);
             }

+ 71 - 39
Babylon/PostProcess/babylon.lensRenderingPipeline.js

@@ -10,28 +10,28 @@ var BABYLON;
         __extends(LensRenderingPipeline, _super);
         /**
          * @constructor
+         *
+         * Effect parameters are as follow:
+         * {
+         *      chromatic_aberration: number;       // from 0 to x (1 for realism)
+         *      edge_blur: number;                  // from 0 to x (1 for realism)
+         *      distortion: number;                 // from 0 to x (1 for realism)
+         *      grain_amount: number;               // from 0 to 1
+         *      grain_texture: BABYLON.Texture;     // texture to use for grain effect; if unset, use random B&W noise
+         *      dof_focus_depth: number;            // depth-of-field: focus depth; unset to disable (disabled by default)
+         *      dof_aperture: number;               // depth-of-field: focus blur bias (default: 1)
+         *      dof_pentagon: boolean;              // depth-of-field: makes a pentagon-like "bokeh" effect
+         *      dof_gain: number;                   // depth-of-field: depthOfField gain; unset to disable (disabled by default)
+         *      dof_threshold: number;              // depth-of-field: depthOfField threshold (default: 1)
+         *      blur_noise: boolean;                // add a little bit of noise to the blur (default: true)
+         * }
+         * Note: if an effect parameter is unset, effect is disabled
+         *
          * @param {string} name - The rendering pipeline name
-         * @param {object} parameters - An object containing all parameters (see below)
+         * @param {object} parameters - An object containing all parameters (see above)
          * @param {BABYLON.Scene} scene - The scene linked to this pipeline
          * @param {number} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
          * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
-
-            Effect parameters are as follow:
-            {
-                chromatic_aberration: number;		// from 0 to x (1 for realism)
-                edge_blur: number;					// from 0 to x (1 for realism)
-                distortion: number;					// from 0 to x (1 for realism)
-                grain_amount: number;				// from 0 to 1
-                grain_texture: BABYLON.Texture;		// texture to use for grain effect; if unset, use random B&W noise
-                dof_focus_depth: number;			// depth-of-field: focus depth; unset to disable
-                dof_aperture: number;				// depth-of-field: focus blur bias (default: 1)
-                dof_pentagon: boolean;				// depth-of-field: makes a pentagon-like "bokeh" effect
-                dof_gain: boolean;					// depth-of-field: depthOfField gain (default: 1)
-                dof_threshold: boolean;				// depth-of-field: depthOfField threshold (default: 1)
-                blur_noise: boolean;				// add a little bit of noise to the blur (default: true)
-            }
-
-            Note: if an effect parameter is unset, effect is disabled
          */
         function LensRenderingPipeline(name, parameters, scene, ratio, cameras) {
             var _this = this;
@@ -41,8 +41,9 @@ var BABYLON;
             // - chromatic aberration (slight shift of RGB colors)
             // - blur on the edge of the lens
             // - lens distortion
-            // - depth-of-field 'bokeh' effect (shapes appearing in blured areas, stronger highlights)
-            // - grain/dust-on-lens effect
+            // - depth-of-field blur & highlights enhancing
+            // - depth-of-field 'bokeh' effect (shapes appearing in blurred areas)
+            // - grain effect (noise or custom texture)
             // Two additional texture samplers are needed:
             // - depth map (for depth-of-field)
             // - grain texture
@@ -52,6 +53,11 @@ var BABYLON;
             */
             this.LensChromaticAberrationEffect = "LensChromaticAberrationEffect";
             /**
+            * The highlights enhancing PostProcess id in the pipeline
+            * @type {string}
+            */
+            this.HighlightsEnhancingEffect = "HighlightsEnhancingEffect";
+            /**
             * The depth-of-field PostProcess id in the pipeline
             * @type {string}
             */
@@ -70,7 +76,7 @@ var BABYLON;
             this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
             this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
             this._distortion = parameters.distortion ? parameters.distortion : 0;
-            this._highlightsGain = parameters.dof_gain ? parameters.dof_gain : 1;
+            this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
             this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
             this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
             this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
@@ -78,21 +84,28 @@ var BABYLON;
             this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
             // Create effects
             this._createChromaticAberrationPostProcess(ratio);
+            this._createHighlightsPostProcess(ratio);
             this._createDepthOfFieldPostProcess(ratio);
             // Set up pipeline
             this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, function () {
                 return _this._chromaticAberrationPostProcess;
             }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.HighlightsEnhancingEffect, function () {
+                return _this._highlightsPostProcess;
+            }, true));
             this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.LensDepthOfFieldEffect, function () {
                 return _this._depthOfFieldPostProcess;
             }, true));
+            if (this._highlightsGain == -1) {
+                this._disableEffect(this.HighlightsEnhancingEffect, null);
+            }
             // Finish
             scene.postProcessRenderPipelineManager.addPipeline(this);
             if (cameras) {
                 scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
             }
         }
-        // public methods
+        // public methods (self explanatory)
         LensRenderingPipeline.prototype.setEdgeBlur = function (amount) {
             this._edgeBlur = amount;
         };
@@ -117,12 +130,6 @@ var BABYLON;
         LensRenderingPipeline.prototype.disableEdgeDistortion = function () {
             this._distortion = 0;
         };
-        LensRenderingPipeline.prototype.setHighlightsGain = function (amount) {
-            this._highlightsGain = amount;
-        };
-        LensRenderingPipeline.prototype.setHighlightsThreshold = function (amount) {
-            this._highlightsThreshold = amount;
-        };
         LensRenderingPipeline.prototype.setFocusDepth = function (amount) {
             this._dofDepth = amount;
         };
@@ -144,6 +151,18 @@ var BABYLON;
         LensRenderingPipeline.prototype.disableNoiseBlur = function () {
             this._blurNoise = false;
         };
+        LensRenderingPipeline.prototype.setHighlightsGain = function (amount) {
+            this._highlightsGain = amount;
+        };
+        LensRenderingPipeline.prototype.setHighlightsThreshold = function (amount) {
+            if (this._highlightsGain == -1) {
+                this._highlightsGain = 1.0;
+            }
+            this._highlightsThreshold = amount;
+        };
+        LensRenderingPipeline.prototype.disableHighlights = function () {
+            this._highlightsGain = -1;
+        };
         /**
          * Removes the internal pipeline assets and detaches the pipeline from the scene cameras
          */
@@ -151,6 +170,7 @@ var BABYLON;
             if (disableDepthRender === void 0) { disableDepthRender = false; }
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
             this._chromaticAberrationPostProcess = undefined;
+            this._highlightsPostProcess = undefined;
             this._depthOfFieldPostProcess = undefined;
             this._grainTexture.dispose();
             if (disableDepthRender)
@@ -162,16 +182,27 @@ var BABYLON;
             this._chromaticAberrationPostProcess = new BABYLON.PostProcess("LensChromaticAberration", "chromaticAberration", ["chromatic_aberration", "screen_width", "screen_height"], [], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
             this._chromaticAberrationPostProcess.onApply = function (effect) {
                 effect.setFloat('chromatic_aberration', _this._chromaticAberration);
-                effect.setFloat('screen_width', _this._scene.getEngine().getRenderWidth());
-                effect.setFloat('screen_height', _this._scene.getEngine().getRenderHeight());
+                effect.setFloat('screen_width', _this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', _this._scene.getEngine().getRenderingCanvas().height);
+            };
+        };
+        // highlights enhancing
+        LensRenderingPipeline.prototype._createHighlightsPostProcess = function (ratio) {
+            var _this = this;
+            this._highlightsPostProcess = new BABYLON.PostProcess("LensHighlights", "lensHighlights", ["pentagon", "gain", "threshold", "screen_width", "screen_height"], [], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
+            this._highlightsPostProcess.onApply = function (effect) {
+                effect.setFloat('gain', _this._highlightsGain);
+                effect.setFloat('threshold', _this._highlightsThreshold);
+                effect.setBool('pentagon', _this._dofPentagon);
+                effect.setTextureFromPostProcess("textureSampler", _this._chromaticAberrationPostProcess);
+                effect.setFloat('screen_width', _this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', _this._scene.getEngine().getRenderingCanvas().height);
             };
         };
         // colors shifting and distortion
         LensRenderingPipeline.prototype._createDepthOfFieldPostProcess = function (ratio) {
             var _this = this;
             this._depthOfFieldPostProcess = new BABYLON.PostProcess("LensDepthOfField", "depthOfField", [
-                "gain",
-                "threshold",
                 "focus_depth",
                 "aperture",
                 "pentagon",
@@ -182,23 +213,24 @@ var BABYLON;
                 "blur_noise",
                 "grain_amount",
                 "screen_width",
-                "screen_height"
-            ], ["depthSampler", "grainSampler"], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
+                "screen_height",
+                "highlights"
+            ], ["depthSampler", "grainSampler", "highlightsSampler"], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false);
             this._depthOfFieldPostProcess.onApply = function (effect) {
-                effect.setBool('pentagon', _this._dofPentagon);
                 effect.setBool('blur_noise', _this._blurNoise);
                 effect.setFloat('maxZ', _this._scene.activeCamera.maxZ);
                 effect.setFloat('grain_amount', _this._grainAmount);
                 effect.setTexture("depthSampler", _this._depthTexture);
                 effect.setTexture("grainSampler", _this._grainTexture);
-                effect.setFloat('screen_width', _this._scene.getEngine().getRenderWidth());
-                effect.setFloat('screen_height', _this._scene.getEngine().getRenderHeight());
+                effect.setTextureFromPostProcess("textureSampler", _this._highlightsPostProcess);
+                effect.setTextureFromPostProcess("highlightsSampler", _this._depthOfFieldPostProcess);
+                effect.setFloat('screen_width', _this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', _this._scene.getEngine().getRenderingCanvas().height);
                 effect.setFloat('distortion', _this._distortion);
                 effect.setFloat('focus_depth', _this._dofDepth);
                 effect.setFloat('aperture', _this._dofAperture);
-                effect.setFloat('gain', _this._highlightsGain);
-                effect.setFloat('threshold', _this._highlightsThreshold);
                 effect.setFloat('edge_blur', _this._edgeBlur);
+                effect.setBool('highlights', (_this._highlightsGain != -1));
             };
         };
         // creates a black and white random noise texture, 512x512

+ 124 - 82
Babylon/PostProcess/babylon.lensRenderingPipeline.ts

@@ -1,16 +1,17 @@
 module BABYLON {
     export class LensRenderingPipeline extends PostProcessRenderPipeline {
 
-    	// Lens effects can be of the following:
-    	// - chromatic aberration (slight shift of RGB colors)
-    	// - blur on the edge of the lens
-    	// - lens distortion
-    	// - depth-of-field 'bokeh' effect (shapes appearing in blured areas, stronger highlights)
-    	// - grain/dust-on-lens effect
-
-    	// Two additional texture samplers are needed:
-    	// - depth map (for depth-of-field)
-    	// - grain texture
+        // Lens effects can be of the following:
+        // - chromatic aberration (slight shift of RGB colors)
+        // - blur on the edge of the lens
+        // - lens distortion
+        // - depth-of-field blur & highlights enhancing
+        // - depth-of-field 'bokeh' effect (shapes appearing in blurred areas)
+        // - grain effect (noise or custom texture)
+
+        // Two additional texture samplers are needed:
+        // - depth map (for depth-of-field)
+        // - grain texture
 
         /**
         * The chromatic aberration PostProcess id in the pipeline
@@ -18,6 +19,11 @@ module BABYLON {
         */
         public LensChromaticAberrationEffect: string = "LensChromaticAberrationEffect";
         /**
+        * The highlights enhancing PostProcess id in the pipeline
+        * @type {string}
+        */
+        public HighlightsEnhancingEffect: string = "HighlightsEnhancingEffect";
+        /**
         * The depth-of-field PostProcess id in the pipeline
         * @type {string}
         */
@@ -28,6 +34,7 @@ module BABYLON {
         private _grainTexture: Texture;
 
         private _chromaticAberrationPostProcess: PostProcess;
+        private _highlightsPostProcess: PostProcess;
         private _depthOfFieldPostProcess: PostProcess;
 
         private _edgeBlur: number;
@@ -41,72 +48,77 @@ module BABYLON {
         private _dofPentagon: boolean;
         private _blurNoise: boolean;
 
+
         /**
          * @constructor
+         *
+         * Effect parameters are as follow:
+         * {
+         *      chromatic_aberration: number;       // from 0 to x (1 for realism)
+         *      edge_blur: number;                  // from 0 to x (1 for realism)
+         *      distortion: number;                 // from 0 to x (1 for realism)
+         *      grain_amount: number;               // from 0 to 1
+         *      grain_texture: BABYLON.Texture;     // texture to use for grain effect; if unset, use random B&W noise
+         *      dof_focus_depth: number;            // depth-of-field: focus depth; unset to disable (disabled by default)
+         *      dof_aperture: number;               // depth-of-field: focus blur bias (default: 1)
+         *      dof_pentagon: boolean;              // depth-of-field: makes a pentagon-like "bokeh" effect
+         *      dof_gain: number;                   // depth-of-field: depthOfField gain; unset to disable (disabled by default)
+         *      dof_threshold: number;              // depth-of-field: depthOfField threshold (default: 1)
+         *      blur_noise: boolean;                // add a little bit of noise to the blur (default: true)
+         * }
+         * Note: if an effect parameter is unset, effect is disabled
+         *
          * @param {string} name - The rendering pipeline name
-         * @param {object} parameters - An object containing all parameters (see below)
+         * @param {object} parameters - An object containing all parameters (see above)
          * @param {BABYLON.Scene} scene - The scene linked to this pipeline
          * @param {number} ratio - The size of the postprocesses (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
          * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
-
-         	Effect parameters are as follow:
-		 	{
-				chromatic_aberration: number;		// from 0 to x (1 for realism)
-				edge_blur: number;					// from 0 to x (1 for realism)
-				distortion: number;					// from 0 to x (1 for realism)
-				grain_amount: number;				// from 0 to 1
-				grain_texture: BABYLON.Texture;		// texture to use for grain effect; if unset, use random B&W noise
-				dof_focus_depth: number;			// depth-of-field: focus depth; unset to disable
-				dof_aperture: number;				// depth-of-field: focus blur bias (default: 1)
-				dof_pentagon: boolean;				// depth-of-field: makes a pentagon-like "bokeh" effect
-				dof_gain: boolean;					// depth-of-field: depthOfField gain (default: 1)
-				dof_threshold: boolean;				// depth-of-field: depthOfField threshold (default: 1)
-				blur_noise: boolean;				// add a little bit of noise to the blur (default: true)
-			}
-
-			Note: if an effect parameter is unset, effect is disabled
          */
         constructor(name: string, parameters: any, scene: Scene, ratio: number = 1.0, cameras?: Camera[]) {
-			super(scene.getEngine(), name);
-
-			this._scene = scene;
-
-			// Fetch texture samplers
-			this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
-			if (parameters.grain_texture) {
-			    this._grainTexture = parameters.grain_texture;
-			} else {
-			     this._createGrainTexture();
-			}
-
-			// save parameters
-			this._edgeBlur = parameters.edge_blur ? parameters.edge_blur : 0;
-			this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
-			this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
-			this._distortion = parameters.distortion ? parameters.distortion : 0;
-			this._highlightsGain = parameters.dof_gain ? parameters.dof_gain : 1;
-			this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
-			this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
-			this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
-			this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
-			this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
-
-			// Create effects
+            super(scene.getEngine(), name);
+
+            this._scene = scene;
+
+            // Fetch texture samplers
+            this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
+            if (parameters.grain_texture) { this._grainTexture = parameters.grain_texture; }
+            else { this._createGrainTexture(); }
+
+            // save parameters
+            this._edgeBlur = parameters.edge_blur ? parameters.edge_blur : 0;
+            this._grainAmount = parameters.grain_amount ? parameters.grain_amount : 0;
+            this._chromaticAberration = parameters.chromatic_aberration ? parameters.chromatic_aberration : 0;
+            this._distortion = parameters.distortion ? parameters.distortion : 0;
+            this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
+            this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
+            this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
+            this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
+            this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
+            this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
+
+            // Create effects
             this._createChromaticAberrationPostProcess(ratio);
+            this._createHighlightsPostProcess(ratio);
             this._createDepthOfFieldPostProcess(ratio);
 
             // Set up pipeline
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, () => { return this._chromaticAberrationPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.HighlightsEnhancingEffect, () => { return this._highlightsPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensDepthOfFieldEffect, () => { return this._depthOfFieldPostProcess; }, true));
 
+            if (this._highlightsGain == -1) {
+                this._disableEffect(this.HighlightsEnhancingEffect, null);
+            }
+
             // Finish
             scene.postProcessRenderPipelineManager.addPipeline(this);
-            if(cameras) {
+            if (cameras) {
                 scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
             }
         }
 
-        // public methods
+        // public methods (self explanatory)
+
         public setEdgeBlur(amount: number) { this._edgeBlur = amount; }
         public disableEdgeBlur() { this._edgeBlur = 0; }
         public setGrainAmount(amount: number) { this._grainAmount = amount; }
@@ -115,8 +127,6 @@ module BABYLON {
         public disableChromaticAberration() { this._chromaticAberration = 0; }
         public setEdgeDistortion(amount: number) { this._distortion = amount; }
         public disableEdgeDistortion() { this._distortion = 0; }
-        public setHighlightsGain(amount: number) { this._highlightsGain = amount; }
-        public setHighlightsThreshold(amount: number) { this._highlightsThreshold = amount; }
         public setFocusDepth(amount: number) { this._dofDepth = amount; }
         public disableDepthOfField() { this._dofDepth = -1; }
         public setAperture(amount: number) { this._dofAperture = amount; }
@@ -124,6 +134,18 @@ module BABYLON {
         public disablePentagonBokeh() { this._dofPentagon = false; }
         public enableNoiseBlur() { this._blurNoise = true; }
         public disableNoiseBlur() { this._blurNoise = false; }
+        public setHighlightsGain(amount: number) {
+            this._highlightsGain = amount;
+        }
+        public setHighlightsThreshold(amount: number) {
+            if (this._highlightsGain == -1) {
+                this._highlightsGain = 1.0;
+            }
+            this._highlightsThreshold = amount;
+        }
+        public disableHighlights() {
+            this._highlightsGain = -1;
+        }
 
         /**
          * Removes the internal pipeline assets and detaches the pipeline from the scene cameras
@@ -132,6 +154,7 @@ module BABYLON {
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
 
             this._chromaticAberrationPostProcess = undefined;
+            this._highlightsPostProcess = undefined;
             this._depthOfFieldPostProcess = undefined;
 
             this._grainTexture.dispose();
@@ -143,49 +166,68 @@ module BABYLON {
         // colors shifting and distortion
         private _createChromaticAberrationPostProcess(ratio: number): void {
             this._chromaticAberrationPostProcess = new PostProcess("LensChromaticAberration", "chromaticAberration",
-            	["chromatic_aberration", "screen_width", "screen_height"],		// uniforms
-            	[],											// samplers
+                ["chromatic_aberration", "screen_width", "screen_height"],		// uniforms
+                [],											// samplers
                 ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
                 this._scene.getEngine(), false);
 
             this._chromaticAberrationPostProcess.onApply = (effect: Effect) => {
-            	effect.setFloat('chromatic_aberration', this._chromaticAberration);
-                effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
-                effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
+                effect.setFloat('chromatic_aberration', this._chromaticAberration);
+                effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
+            };
+        }
+
+        // highlights enhancing
+        private _createHighlightsPostProcess(ratio: number): void {
+            this._highlightsPostProcess = new PostProcess("LensHighlights", "lensHighlights",
+                ["pentagon", "gain", "threshold", "screen_width", "screen_height"],      // uniforms
+                [],     // samplers
+                ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
+                this._scene.getEngine(), false);
+
+            this._highlightsPostProcess.onApply = (effect: Effect) => {
+                effect.setFloat('gain', this._highlightsGain);
+                effect.setFloat('threshold', this._highlightsThreshold);
+                effect.setBool('pentagon', this._dofPentagon);
+                effect.setTextureFromPostProcess("textureSampler", this._chromaticAberrationPostProcess);
+                effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
             };
         }
 
         // colors shifting and distortion
         private _createDepthOfFieldPostProcess(ratio: number): void {
             this._depthOfFieldPostProcess = new PostProcess("LensDepthOfField", "depthOfField",
-            	[
-            		"gain", "threshold", "focus_depth", "aperture", "pentagon", "maxZ", "edge_blur",
-            		"chromatic_aberration", "distortion", "blur_noise", "grain_amount", "screen_width", "screen_height"
-            	],
-            	["depthSampler", "grainSampler"],
+                [
+                    "focus_depth", "aperture", "pentagon", "maxZ", "edge_blur", "chromatic_aberration",
+                    "distortion", "blur_noise", "grain_amount", "screen_width", "screen_height", "highlights"
+                ],
+                ["depthSampler", "grainSampler", "highlightsSampler"],
                 ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
                 this._scene.getEngine(), false);
 
             this._depthOfFieldPostProcess.onApply = (effect: Effect) => {
-	            effect.setBool('pentagon', this._dofPentagon);
-            	effect.setBool('blur_noise', this._blurNoise);
-            	effect.setFloat('maxZ', this._scene.activeCamera.maxZ);
-            	effect.setFloat('grain_amount', this._grainAmount);
+                effect.setBool('blur_noise', this._blurNoise);
+                effect.setFloat('maxZ', this._scene.activeCamera.maxZ);
+                effect.setFloat('grain_amount', this._grainAmount);
+
+                effect.setTexture("depthSampler", this._depthTexture);
+                effect.setTexture("grainSampler", this._grainTexture);
+                effect.setTextureFromPostProcess("textureSampler", this._highlightsPostProcess);
+                effect.setTextureFromPostProcess("highlightsSampler", this._depthOfFieldPostProcess);
 
-            	effect.setTexture("depthSampler", this._depthTexture);
-            	effect.setTexture("grainSampler", this._grainTexture);
+                effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
+                effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
 
-            	effect.setFloat('screen_width', this._scene.getEngine().getRenderWidth());
-            	effect.setFloat('screen_height', this._scene.getEngine().getRenderHeight());
+                effect.setFloat('distortion', this._distortion);
 
-            	effect.setFloat('distortion', this._distortion);
+                effect.setFloat('focus_depth', this._dofDepth);
+                effect.setFloat('aperture', this._dofAperture);
 
-	            effect.setFloat('focus_depth', this._dofDepth);
-	            effect.setFloat('aperture', this._dofAperture);
-            	effect.setFloat('gain', this._highlightsGain);
-            	effect.setFloat('threshold', this._highlightsThreshold);
+                effect.setFloat('edge_blur', this._edgeBlur);
 
-            	effect.setFloat('edge_blur', this._edgeBlur);
+                effect.setBool('highlights', (this._highlightsGain != -1));
             };
         }
 
@@ -206,7 +248,7 @@ module BABYLON {
             var value;
             for (var x = 0; x < size; x++) {
                 for (var y = 0; y < size; y++) {
-                    value = Math.floor(rand(0.42,0.58)*255);
+                    value = Math.floor(rand(0.42, 0.58) * 255);
                     context.fillStyle = 'rgb(' + value + ', ' + value + ', ' + value + ')';
                     context.fillRect(x, y, 1, 1);
                 }

+ 10 - 3
Babylon/PostProcess/babylon.postProcessManager.js

@@ -5,13 +5,18 @@ var BABYLON;
             this._vertexDeclaration = [2];
             this._vertexStrideSize = 2 * 4;
             this._scene = scene;
+        }
+        PostProcessManager.prototype._prepareBuffers = function () {
+            if (this._vertexBuffer) {
+                return;
+            }
             // VBO
             var vertices = [];
             vertices.push(1, 1);
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
             // Indices
             var indices = [];
             indices.push(0);
@@ -20,8 +25,8 @@ var BABYLON;
             indices.push(0);
             indices.push(2);
             indices.push(3);
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
-        }
+            this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
+        };
         // Methods
         PostProcessManager.prototype._prepareFrame = function (sourceTexture) {
             var postProcesses = this._scene.activeCamera._postProcesses;
@@ -53,6 +58,7 @@ var BABYLON;
                         pp.onBeforeRender(effect);
                     }
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
                     // Draw order
                     engine.draw(true, 0, 6);
@@ -91,6 +97,7 @@ var BABYLON;
                         pp.onBeforeRender(effect);
                     }
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
                     // Draw order
                     engine.draw(true, 0, 6);

+ 10 - 2
Babylon/PostProcess/babylon.postProcessManager.ts

@@ -8,6 +8,12 @@
 
         constructor(scene: Scene) {
             this._scene = scene;
+        }
+
+        private _prepareBuffers(): void {
+            if (this._vertexBuffer) {
+                return;
+            }
 
             // VBO
             var vertices = [];
@@ -15,7 +21,7 @@
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
 
             // Indices
             var indices = [];
@@ -27,7 +33,7 @@
             indices.push(2);
             indices.push(3);
 
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
         }
 
         // Methods
@@ -67,6 +73,7 @@
                     }
 
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
 
                     // Draw order
@@ -111,6 +118,7 @@
                     }
 
                     // VBOs
+                    this._prepareBuffers();
                     engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
 
                     // Draw order

+ 15 - 3
Babylon/Rendering/babylon.boundingBoxRenderer.js

@@ -7,7 +7,12 @@ var BABYLON;
             this.showBackLines = true;
             this.renderList = new BABYLON.SmartArray(32);
             this._scene = scene;
-            this._colorShader = new BABYLON.ShaderMaterial("colorShader", scene, "color", {
+        }
+        BoundingBoxRenderer.prototype._prepareRessources = function () {
+            if (this._colorShader) {
+                return;
+            }
+            this._colorShader = new BABYLON.ShaderMaterial("colorShader", this._scene, "color", {
                 attributes: ["position"],
                 uniforms: ["worldViewProjection", "color"]
             });
@@ -15,12 +20,16 @@ var BABYLON;
             var boxdata = BABYLON.VertexData.CreateBox(1.0);
             this._vb = new BABYLON.VertexBuffer(engine, boxdata.positions, BABYLON.VertexBuffer.PositionKind, false);
             this._ib = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
-        }
+        };
         BoundingBoxRenderer.prototype.reset = function () {
             this.renderList.reset();
         };
         BoundingBoxRenderer.prototype.render = function () {
-            if (this.renderList.length === 0 || !this._colorShader.isReady()) {
+            if (this.renderList.length === 0) {
+                return;
+            }
+            this._prepareRessources();
+            if (!this._colorShader.isReady()) {
                 return;
             }
             var engine = this._scene.getEngine();
@@ -57,6 +66,9 @@ var BABYLON;
             engine.setDepthWrite(true);
         };
         BoundingBoxRenderer.prototype.dispose = function () {
+            if (!this._colorShader) {
+                return;
+            }
             this._colorShader.dispose();
             this._vb.dispose();
             this._scene.getEngine()._releaseBuffer(this._ib);

+ 19 - 3
Babylon/Rendering/babylon.boundingBoxRenderer.ts

@@ -11,9 +11,15 @@
         private _ib: WebGLBuffer;
 
         constructor(scene: Scene) {
-
             this._scene = scene;
-            this._colorShader = new ShaderMaterial("colorShader", scene, "color",
+        }
+
+        private _prepareRessources(): void {
+            if (this._colorShader) {
+                return;
+            }
+
+            this._colorShader = new ShaderMaterial("colorShader", this._scene, "color",
                 {
                     attributes: ["position"],
                     uniforms: ["worldViewProjection", "color"]
@@ -31,7 +37,13 @@
         }
 
         public render(): void {
-            if (this.renderList.length === 0 || !this._colorShader.isReady()) {
+            if (this.renderList.length === 0) {
+                return;
+            }
+
+            this._prepareRessources();
+
+            if (!this._colorShader.isReady()) {
                 return;
             }
 
@@ -78,6 +90,10 @@
         }
 
         public dispose(): void {
+            if (!this._colorShader) {
+                return;
+            }
+
             this._colorShader.dispose();
             this._vb.dispose();
             this._scene.getEngine()._releaseBuffer(this._ib);

+ 10 - 12
Babylon/Shaders/chromaticAberration.fragment.fx

@@ -1,9 +1,7 @@
-/*
-	BABYLON.JS Chromatic Aberration GLSL Shader
-	Author: Olivier Guyot
-	Separates very slightly R, G and B colors on the edges of the screen
-	Inspired by Francois Tarlier & Martins Upitis	
-*/
+// BABYLON.JS Chromatic Aberration GLSL Shader
+// Author: Olivier Guyot
+// Separates very slightly R, G and B colors on the edges of the screen
+// Inspired by Francois Tarlier & Martins Upitis
 
 #ifdef GL_ES
 precision highp float;
@@ -22,18 +20,18 @@ varying vec2 vUV;
 
 void main(void)
 {
-	vec2 centered_screen_pos = vec2(vUV.x-0.5, vUV.y-0.5);
+	vec2 centered_screen_pos = vec2(vUV.x - 0.5, vUV.y - 0.5);
 	float radius2 = centered_screen_pos.x*centered_screen_pos.x
-					+ centered_screen_pos.y*centered_screen_pos.y;
+		+ centered_screen_pos.y*centered_screen_pos.y;
 	float radius = sqrt(radius2);
 
 	vec4 original = texture2D(textureSampler, vUV);
 
-	if(chromatic_aberration > 0.0) {
+	if (chromatic_aberration > 0.0) {
 		//index of refraction of each color channel, causing chromatic dispersion
-		vec3 ref_indices = vec3(0.6, 0.3, 0.0);
-		float ref_shiftX = chromatic_aberration * radius * 12.0 / screen_width;
-		float ref_shiftY = chromatic_aberration * radius * 12.0 / screen_height;
+		vec3 ref_indices = vec3(-0.3, 0.0, 0.3);
+		float ref_shiftX = chromatic_aberration * radius * 17.0 / screen_width;
+		float ref_shiftY = chromatic_aberration * radius * 17.0 / screen_height;
 
 		// shifts for red, green & blue
 		vec2 ref_coords_r = vec2(vUV.x + ref_indices.r*ref_shiftX, vUV.y + ref_indices.r*ref_shiftY*0.5);

+ 95 - 165
Babylon/Shaders/depthOfField.fragment.fx

@@ -1,9 +1,7 @@
-/*
-	BABYLON.JS Depth-of-field GLSL Shader
-	Author: Olivier Guyot
-	Does depth-of-field blur, edge blur, highlights enhancing
-	Inspired by Francois Tarlier & Martins Upitis
-*/
+// BABYLON.JS Depth-of-field GLSL Shader
+// Author: Olivier Guyot
+// Does depth-of-field blur, edge blur
+// Inspired by Francois Tarlier & Martins Upitis
 
 #ifdef GL_ES
 precision highp float;
@@ -12,12 +10,12 @@ precision highp float;
 
 // samplers
 uniform sampler2D textureSampler;
+uniform sampler2D highlightsSampler;
 uniform sampler2D depthSampler;
 uniform sampler2D grainSampler;
 
 // uniforms
 uniform float grain_amount;
-uniform bool pentagon;
 uniform float maxZ;
 uniform bool blur_noise;
 uniform float screen_width;
@@ -25,24 +23,14 @@ uniform float screen_height;
 uniform float distortion;
 uniform float focus_depth;
 uniform float aperture;
-uniform float gain;
-uniform float threshold;
 uniform float edge_blur;
+uniform bool highlights;
 
 // varyings
 varying vec2 vUV;
 
 // constants
 #define PI 3.14159265
-const int RING_1_SAMPLES = 4;
-const int RING_2_SAMPLES = 6;
-const int RING_3_SAMPLES = 9;
-const int RING_4_SAMPLES = 12;
-const int RING_5_SAMPLES = 16;
-//const int RING_6_SAMPLES = 15;
-const float RING_STEP_DIST = 0.4;			// a new blur ring is added each time this distance is passed
-const float PENTAGON_ANGLE_SUB = 1.2566;		// 2PI / 5
-const float PENTAGON_ANGLE_SUB_HALF = 0.6283;	// 2PI / 10
 
 // common calculations
 vec2 centered_screen_pos;
@@ -53,7 +41,7 @@ float radius;
 // applies edge distortion on texture coords
 vec2 getDistortedCoords(vec2 coords) {
 
-	if(distortion == 0.0) { return coords; }
+	if (distortion == 0.0) { return coords; }
 
 	vec2 direction = 1.0 * normalize(centered_screen_pos);
 	vec2 dist_coords = vec2(0.5, 0.5);
@@ -66,153 +54,83 @@ vec2 getDistortedCoords(vec2 coords) {
 	return dist_coords;
 }
 
-// picks either original screen color or highlights only
-vec4 getColor(vec2 coords, bool highlight) {
-
-	vec4 color = texture2D(textureSampler, coords);
-
-	if(highlight) {
-		float luminance = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));
-		float lum_threshold;
-		if(threshold > 1.0) { lum_threshold = 0.94 + 0.01 * threshold; }
-		else { lum_threshold = 0.5 + 0.44 * threshold; }
-		if(luminance < lum_threshold) {
-			color.rgb = vec3(0.0, 0.0, 0.0);
-			color.a = 1.0;
-		}
+// returns original screen color after blur
+vec4 getBlurColor(vec2 coords, float size) {
+
+	vec4 col = texture2D(textureSampler, coords);
+	if (size == 0.0) { return col; }
+
+	// there are max. 30 samples; the number of samples chosen is dependant on the blur size
+	// there can be 10, 20 or 30 samples chosen; levels of blur are then 1, 2 or 3
+	float blur_level = min(3.0, ceil(size / 1.0));
+
+	float w = (size / screen_width);
+	float h = (size / screen_height);
+	float total_weight = 1.0;
+
+	col += texture2D(textureSampler, coords + vec2(-0.53*w, 0.15*h))*0.93;
+	col += texture2D(textureSampler, coords + vec2(0.42*w, -0.69*h))*0.90;
+	col += texture2D(textureSampler, coords + vec2(0.20*w, 1.00*h))*0.87;
+	col += texture2D(textureSampler, coords + vec2(-0.97*w, -0.72*h))*0.85;
+	col += texture2D(textureSampler, coords + vec2(1.37*w, -0.14*h))*0.83;
+	col += texture2D(textureSampler, coords + vec2(-1.02*w, 1.16*h))*0.80;
+	col += texture2D(textureSampler, coords + vec2(-0.03*w, -1.69*h))*0.78;
+	col += texture2D(textureSampler, coords + vec2(1.27*w, 1.34*h))*0.76;
+	col += texture2D(textureSampler, coords + vec2(-1.98*w, -0.14*h))*0.74;
+	col += texture2D(textureSampler, coords + vec2(1.66*w, -1.32*h))*0.72;
+	total_weight += 8.18;
+
+	if (blur_level > 1.0) {
+		col += texture2D(textureSampler, coords + vec2(-0.35*w, 2.22*h))*0.70;
+		col += texture2D(textureSampler, coords + vec2(-1.31*w, -1.98*h))*0.67;
+		col += texture2D(textureSampler, coords + vec2(2.42*w, 0.61*h))*0.65;
+		col += texture2D(textureSampler, coords + vec2(-2.31*w, 1.25*h))*0.63;
+		col += texture2D(textureSampler, coords + vec2(0.90*w, -2.59*h))*0.61;
+		col += texture2D(textureSampler, coords + vec2(1.14*w, 2.62*h))*0.59;
+		col += texture2D(textureSampler, coords + vec2(-2.72*w, -1.21*h))*0.56;
+		col += texture2D(textureSampler, coords + vec2(2.93*w, -0.98*h))*0.54;
+		col += texture2D(textureSampler, coords + vec2(-1.56*w, 2.80*h))*0.52;
+		col += texture2D(textureSampler, coords + vec2(-0.77*w, -3.22*h))*0.49;
+		total_weight += 5.96;
 	}
 
-	return color;
-}
-
-// returns a modifier to be applied on the radius, in order to simulate a pentagon
-float pentagonShape(float angle) {
-    float a1 = mod(angle, PENTAGON_ANGLE_SUB) / PENTAGON_ANGLE_SUB - 0.5;
-    float a2 = 0.5 - a1 * a1;
-    return 1.35 - 0.94 * a2;
-}
-
-// returns original screen color after blur
-vec4 getBlurColor(vec2 coords, float size, bool highlight) {
-
-	float w = (size/screen_width);
-	float h = (size/screen_height);
-
-	vec4 col = getColor(coords, highlight);
-	if(size == 0.0) { return col; }
-
-	float s = 1.0;
-	float pw;			// sample x relative coord
-	float ph;			// sample y relative coord
-	float bias = 0.65;	// inner/outer ring bias
-	if(highlight) { bias = 0.95; }
-	float sample_angle;
-	float ratio_rings;
-	float ring_radius;
-	float penta;		// pentagon shape modifier
-
-	int ring_count;
-	if(size >= 6.0 * RING_STEP_DIST) { ring_count = 6; }
-	else if(size >= 5.0 * RING_STEP_DIST) { ring_count = 5; }
-	else if(size >= 4.0 * RING_STEP_DIST) { ring_count = 4; }
-	else if(size >= 3.0 * RING_STEP_DIST) { ring_count = 3; }
-	else if(size >= 2.0 * RING_STEP_DIST) { ring_count = 2; }
-	else { ring_count = 1; }
-	
-	// RING 1
-	if(size > RING_STEP_DIST) {
-		ring_radius = size / float(ring_count);
-		ratio_rings = 1.0 / float(ring_count);
-		for(int i = 0; i < RING_1_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_1_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);
-		}
-	}	
-
-	// RING 2
-	if(size > RING_STEP_DIST * 2.0) {
-		ring_radius = 2.0 * size / float(ring_count);
-		ratio_rings = 2.0 / float(ring_count);
-		for(int i = 0; i < RING_2_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_2_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
-
-	// RING 3
-	if(size > RING_STEP_DIST * 3.0) {
-		ring_radius = 3.0 * size / float(ring_count);
-		ratio_rings = 3.0 / float(ring_count);
-		for(int i = 0; i < RING_3_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_3_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
-
-	// RING 4
-	if(size > RING_STEP_DIST * 4.0) {
-		ring_radius = 4.0 * size / float(ring_count);
-		ratio_rings = 4.0 / float(ring_count);
-		for(int i = 0; i < RING_4_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_4_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
-
-	// RING 5
-	if(size > RING_STEP_DIST * 5.0) {
-		ring_radius = 5.0 * size / float(ring_count);
-		ratio_rings = 5.0 / float(ring_count);
-		for(int i = 0; i < RING_5_SAMPLES; i++) {
-			sample_angle = PI *2.0 * float(i) / float(RING_5_SAMPLES);
-			if(pentagon) { penta = pentagonShape(sample_angle); }
-			else { penta = 1.0; }
-			pw = cos( sample_angle ) * penta * ring_radius;
-			ph = sin( sample_angle ) * penta * ring_radius;
-			col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
-			s += 1.0 * mix(1.0, ratio_rings, bias);  
-		}
-	}	
+	if (blur_level > 2.0) {
+		col += texture2D(textureSampler, coords + vec2(2.83*w, 1.92*h))*0.46;
+		col += texture2D(textureSampler, coords + vec2(-3.49*w, 0.51*h))*0.44;
+		col += texture2D(textureSampler, coords + vec2(2.30*w, -2.82*h))*0.41;
+		col += texture2D(textureSampler, coords + vec2(0.22*w, 3.74*h))*0.38;
+		col += texture2D(textureSampler, coords + vec2(-2.76*w, -2.68*h))*0.34;
+		col += texture2D(textureSampler, coords + vec2(3.95*w, 0.11*h))*0.31;
+		col += texture2D(textureSampler, coords + vec2(-3.07*w, 2.65*h))*0.26;
+		col += texture2D(textureSampler, coords + vec2(0.48*w, -4.13*h))*0.22;
+		col += texture2D(textureSampler, coords + vec2(2.49*w, 3.46*h))*0.15;
+		total_weight += 2.97;
+	}
 
-	col /= s;		// scales color according to samples taken
+	col /= total_weight;		// scales color according to weights
 	col.a = 1.0;
 
+	// blur levels debug
+	// if(blur_level == 1.0) { col.b = 0.0; }
+	// if(blur_level == 2.0) { col.r = 0.0; }
+	// if(blur_level == 3.0) { col.g = 0.0; }
+
 	return col;
 }
 
 // on-the-fly constant noise
 vec2 rand(vec2 co)
 {
-	float noise1 = (fract(sin(dot(co ,vec2(12.9898,78.233))) * 43758.5453));
-	float noise2 = (fract(sin(dot(co ,vec2(12.9898,78.233)*2.0)) * 43758.5453));
-	return clamp(vec2(noise1,noise2),0.0,1.0);
+	float noise1 = (fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453));
+	float noise2 = (fract(sin(dot(co, vec2(12.9898, 78.233)*2.0)) * 43758.5453));
+	return clamp(vec2(noise1, noise2), 0.0, 1.0);
 }
 
 void main(void)
 {
 
 	// Common calc
-	centered_screen_pos = vec2(vUV.x-0.5, vUV.y-0.5);
+	centered_screen_pos = vec2(vUV.x - 0.5, vUV.y - 0.5);
 	radius2 = centered_screen_pos.x*centered_screen_pos.x + centered_screen_pos.y*centered_screen_pos.y;
 	radius = sqrt(radius2);
 
@@ -222,41 +140,53 @@ void main(void)
 
 	// blur from depth of field effect
 	float dof_blur_amount = 0.0;
-	if(focus_depth != -1.0) {
+	float depth_bias = 0.0;		// positive if the pixel is further than focus depth; negative if closer
+	if (focus_depth != -1.0) {
 		vec4 depth_sample = texture2D(depthSampler, distorted_coords);
 		float depth = depth_sample.r;
-		dof_blur_amount = abs(depth - focus_depth) * aperture * 3.5;
-		if(dof_blur_amount < 0.05) { dof_blur_amount = 0.0; }				// no blur at all
-		else if( depth - focus_depth < 0.0 ) { dof_blur_amount *= 2.0; }	// blur more when close to camera
-		dof_blur_amount = clamp(dof_blur_amount, 0.0, 1.0);
+		depth_bias = depth - focus_depth;
+
+		// compute blur amount with distance
+		if (depth_bias > 0.0) { dof_blur_amount = depth_bias * aperture * 2.2; }
+		else { dof_blur_amount = depth_bias * depth_bias * aperture * 30.0; }
+
+		if (dof_blur_amount < 0.05) { dof_blur_amount = 0.0; }	// no blur at all
 	}
 
 	// blur from edge blur effect
 	float edge_blur_amount = 0.0;
-	if(edge_blur > 0.0) {
-		edge_blur_amount = clamp( ( radius*2.0 - 1.0 + 0.15*edge_blur ) * 1.5 , 0.0 , 1.0 ) * 1.3;
+	if (edge_blur > 0.0) {
+		edge_blur_amount = clamp((radius*2.0 - 1.0 + 0.15*edge_blur) * 1.5, 0.0, 1.0) * 1.3;
 	}
 
 	// total blur amount
 	float blur_amount = max(edge_blur_amount, dof_blur_amount);
 
 	// apply blur if necessary
-	if(blur_amount == 0.0) {
-		gl_FragColor = getColor(distorted_coords, false);
-	} else {
-		gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7, false)
-					   + gain * blur_amount*getBlurColor(distorted_coords, blur_amount * 2.75, true);
+	if (blur_amount == 0.0) {
+		gl_FragColor = texture2D(textureSampler, distorted_coords);
+	}
+	else {
+
+		// add blurred color
+		gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7);
+
+		// if further than focus depth & we have computed highlights: enhance highlights
+		if (depth_bias > 0.0 && highlights) {
+			gl_FragColor += clamp(dof_blur_amount, 0.0, 1.0)*texture2D(highlightsSampler, distorted_coords);
+		}
 
-		if(blur_noise) {
+		if (blur_noise) {
 			// we put a slight amount of noise in the blurred color
 			vec2 noise = rand(distorted_coords) * 0.01 * blur_amount;
 			vec2 blurred_coord = vec2(distorted_coords.x + noise.x, distorted_coords.y + noise.y);
-			gl_FragColor = 0.04 * getColor(blurred_coord, false) + 0.96 * gl_FragColor;
+			gl_FragColor = 0.04 * texture2D(textureSampler, blurred_coord) + 0.96 * gl_FragColor;
 		}
 	}
 
-	if(grain_amount > 0.0) {
+	// apply grain
+	if (grain_amount > 0.0) {
 		vec4 grain_color = texture2D(grainSampler, texels_coords*0.003);
-		gl_FragColor.rgb += ( -0.5 + grain_color.rgb ) * 0.20;
+		gl_FragColor.rgb += (-0.5 + grain_color.rgb) * 0.20;
 	}
 }

+ 140 - 0
Babylon/Shaders/lensHighlights.fragment.fx

@@ -0,0 +1,140 @@
+#ifdef GL_ES
+precision highp float;
+#endif
+
+// samplers
+uniform sampler2D textureSampler;	// original color
+
+// uniforms
+uniform float gain;
+uniform float threshold;
+uniform bool pentagon;
+uniform float screen_width;
+uniform float screen_height;
+
+// varyings
+varying vec2 vUV;
+
+// apply luminance filter
+vec4 highlightColor(vec4 color) {
+	vec4 highlight = color;
+	float luminance = dot(highlight.rgb, vec3(0.2125, 0.7154, 0.0721));
+	float lum_threshold;
+	if (threshold > 1.0) { lum_threshold = 0.94 + 0.01 * threshold; }
+	else { lum_threshold = 0.5 + 0.44 * threshold; }
+
+	luminance = clamp((luminance - lum_threshold) * (1.0 / (1.0 - lum_threshold)), 0.0, 1.0);
+
+	highlight *= luminance * gain;
+	highlight.a = 1.0;
+
+	return highlight;
+}
+
+void main(void)
+{
+	vec4 original = texture2D(textureSampler, vUV);
+
+	// quick exit if no highlight computing
+	if (gain == -1.0) {
+		gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
+		return;
+	}
+
+	float w = 2.0 / screen_width;
+	float h = 2.0 / screen_height;
+
+	float weight = 1.0;
+
+	// compute blurred color
+	vec4 blurred = vec4(0.0, 0.0, 0.0, 0.0);
+
+	if (pentagon) {
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.84*w, 0.43*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.48*w, -1.29*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.61*w, 1.51*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.55*w, -0.74*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.71*w, -0.52*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.94*w, 1.59*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.40*w, -1.87*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.62*w, 1.16*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.09*w, 0.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.46*w, -1.71*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.08*w, 2.42*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.85*w, -1.89*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.89*w, 0.16*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.29*w, 1.88*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.40*w, -2.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.54*w, 2.26*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.60*w, -0.61*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.31*w, -1.30*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.83*w, 2.53*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.12*w, -2.48*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.60*w, 1.11*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.82*w, 0.99*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.50*w, -2.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.85*w, 3.33*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.94*w, -1.92*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.27*w, -0.53*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.95*w, 2.48*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.23*w, -3.04*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.17*w, 2.05*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.97*w, -0.04*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.25*w, -2.00*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.31*w, 3.08*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.94*w, -2.59*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.37*w, 0.64*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-3.13*w, 1.93*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.03*w, -3.65*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.60*w, 3.17*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-3.14*w, -1.19*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.00*w, -1.19*h)));
+	}
+	else {
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.85*w, 0.36*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.52*w, -1.14*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.46*w, 1.42*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.46*w, -0.83*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.79*w, -0.42*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.11*w, 1.62*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.29*w, -2.07*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.69*w, 1.39*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.28*w, 0.12*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.65*w, -1.69*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.08*w, 2.44*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.63*w, -1.90*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.55*w, 0.31*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.13*w, 1.52*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.56*w, -2.61*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.38*w, 2.34*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.64*w, -0.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.53*w, -1.21*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.06*w, 2.63*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.00*w, -2.69*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.59*w, 1.32*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.82*w, 0.78*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.57*w, -2.50*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(0.54*w, 2.93*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.39*w, -1.81*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.01*w, -0.28*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.04*w, 2.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.02*w, -3.05*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.09*w, 2.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-3.07*w, -0.25*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.44*w, -1.90*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-0.52*w, 3.05*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-1.68*w, -2.61*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(3.01*w, 0.79*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.76*w, 1.46*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.05*w, -2.94*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(1.21*w, 2.88*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(-2.84*w, -1.30*h)));
+		blurred += highlightColor(texture2D(textureSampler, vUV + vec2(2.98*w, -0.96*h)));
+	}
+
+	blurred /= 39.0;
+
+	gl_FragColor = blurred;
+
+	//if(vUV.x > 0.5) { gl_FragColor.rgb *= 0.0; }
+}

+ 42 - 5
Babylon/babylon.engine.js

@@ -7,10 +7,25 @@ var BABYLON;
             this._isDepthFuncDirty = false;
             this._isCullFaceDirty = false;
             this._isCullDirty = false;
+            this._isZOffsetDirty = false;
         }
         Object.defineProperty(_DepthCullingState.prototype, "isDirty", {
             get: function () {
-                return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty;
+                return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty || this._isZOffsetDirty;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(_DepthCullingState.prototype, "zOffset", {
+            get: function () {
+                return this._zOffset;
+            },
+            set: function (value) {
+                if (this._zOffset === value) {
+                    return;
+                }
+                this._zOffset = value;
+                this._isZOffsetDirty = true;
             },
             enumerable: true,
             configurable: true
@@ -91,11 +106,13 @@ var BABYLON;
             this._depthFunc = null;
             this._cull = null;
             this._cullFace = null;
+            this._zOffset = 0;
             this._isDepthTestDirty = true;
             this._isDepthMaskDirty = true;
             this._isDepthFuncDirty = false;
             this._isCullFaceDirty = false;
             this._isCullDirty = false;
+            this._isZOffsetDirty = false;
         };
         _DepthCullingState.prototype.apply = function (gl) {
             if (!this.isDirty) {
@@ -136,6 +153,17 @@ var BABYLON;
                 gl.depthFunc(this.depthFunc);
                 this._isDepthFuncDirty = false;
             }
+            // zOffset
+            if (this._isZOffsetDirty) {
+                if (this.zOffset) {
+                    gl.enable(gl.POLYGON_OFFSET_FILL);
+                    gl.polygonOffset(this.zOffset, 0);
+                }
+                else {
+                    gl.disable(gl.POLYGON_OFFSET_FILL);
+                }
+                this._isZOffsetDirty = false;
+            }
         };
         return _DepthCullingState;
     })();
@@ -370,9 +398,6 @@ var BABYLON;
             };
             window.addEventListener("blur", this._onBlur);
             window.addEventListener("focus", this._onFocus);
-            // Textures
-            this._workingCanvas = document.createElement("canvas");
-            this._workingContext = this._workingCanvas.getContext("2d");
             // Viewport
             this._hardwareScalingLevel = 1.0 / (window.devicePixelRatio || 1.0);
             this.resize();
@@ -551,6 +576,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Engine.prototype._prepareWorkingCanvas = function () {
+            if (this._workingCanvas) {
+                return;
+            }
+            this._workingCanvas = document.createElement("canvas");
+            this._workingContext = this._workingCanvas.getContext("2d");
+        };
         Engine.prototype.getGlInfo = function () {
             return {
                 vendor: this._glVendor,
@@ -1105,7 +1137,8 @@ var BABYLON;
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         };
         // States
-        Engine.prototype.setState = function (culling, force) {
+        Engine.prototype.setState = function (culling, zOffset, force) {
+            if (zOffset === void 0) { zOffset = 0; }
             // Culling        
             if (this._depthCullingState.cull !== culling || force) {
                 if (culling) {
@@ -1116,6 +1149,8 @@ var BABYLON;
                     this._depthCullingState.cull = false;
                 }
             }
+            // Z offset
+            this._depthCullingState.zOffset = zOffset;
         };
         Engine.prototype.setDepthBuffer = function (enable) {
             this._depthCullingState.depthTest = enable;
@@ -1259,6 +1294,7 @@ var BABYLON;
                     prepareWebGLTexture(texture, _this._gl, scene, img.width, img.height, invertY, noMipmap, false, function (potWidth, potHeight) {
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         if (!isPot) {
+                            _this._prepareWorkingCanvas();
                             _this._workingCanvas.width = potWidth;
                             _this._workingCanvas.height = potHeight;
                             if (samplingMode === BABYLON.Texture.NEAREST_SAMPLINGMODE) {
@@ -1494,6 +1530,7 @@ var BABYLON;
                 cascadeLoad(rootUrl, scene, function (imgs) {
                     var width = BABYLON.Tools.GetExponantOfTwo(imgs[0].width, _this._caps.maxCubemapTextureSize);
                     var height = width;
+                    _this._prepareWorkingCanvas();
                     _this._workingCanvas.width = width;
                     _this._workingCanvas.height = height;
                     var faces = [

+ 45 - 7
Babylon/babylon.engine.ts

@@ -5,15 +5,30 @@
         private _isDepthFuncDirty = false;
         private _isCullFaceDirty = false;
         private _isCullDirty = false;
+        private _isZOffsetDirty = false;
 
         private _depthTest: boolean;
         private _depthMask: boolean;
         private _depthFunc: number;
         private _cull: boolean;
         private _cullFace: number;
+        private _zOffset: number;
 
         public get isDirty(): boolean {
-            return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty;
+            return this._isDepthFuncDirty || this._isDepthTestDirty || this._isDepthMaskDirty || this._isCullFaceDirty || this._isCullDirty || this._isZOffsetDirty;
+        }
+
+        public get zOffset(): number {
+            return this._zOffset;
+        }
+
+        public set zOffset(value: number) {
+            if (this._zOffset === value) {
+                return;
+            }
+
+            this._zOffset = value;
+            this._isZOffsetDirty = true;
         }
 
         public get cullFace(): number {
@@ -87,12 +102,14 @@
             this._depthFunc = null;
             this._cull = null;
             this._cullFace = null;
+            this._zOffset = 0;
 
             this._isDepthTestDirty = true;
             this._isDepthMaskDirty = true;
             this._isDepthFuncDirty = false;
             this._isCullFaceDirty = false;
             this._isCullDirty = false;
+            this._isZOffsetDirty = false;
         }
 
         public apply(gl: WebGLRenderingContext) {
@@ -139,6 +156,18 @@
                 gl.depthFunc(this.depthFunc);
                 this._isDepthFuncDirty = false;
             }
+
+            // zOffset
+            if (this._isZOffsetDirty) {
+                if (this.zOffset) {
+                    gl.enable(gl.POLYGON_OFFSET_FILL);
+                    gl.polygonOffset(this.zOffset, 0);
+                } else {
+                    gl.disable(gl.POLYGON_OFFSET_FILL);
+                }
+
+                this._isZOffsetDirty = false;
+            }
         }
     }
 
@@ -540,11 +569,6 @@
             window.addEventListener("blur", this._onBlur);
             window.addEventListener("focus", this._onFocus);
 
-
-            // Textures
-            this._workingCanvas = document.createElement("canvas");
-            this._workingContext = this._workingCanvas.getContext("2d");
-
             // Viewport
             this._hardwareScalingLevel = 1.0 / (window.devicePixelRatio || 1.0);
             this.resize();
@@ -638,6 +662,15 @@
             Tools.Log("Babylon.js engine (v" + Engine.Version + ") launched");
         }
 
+        private _prepareWorkingCanvas(): void {
+            if (this._workingCanvas) {
+                return;
+            }
+
+            this._workingCanvas = document.createElement("canvas");
+            this._workingContext = this._workingCanvas.getContext("2d");
+        }
+
         public getGlInfo() {
             return {
                 vendor: this._glVendor,
@@ -1335,7 +1368,7 @@
         }
 
         // States
-        public setState(culling: boolean, force?: boolean): void {
+        public setState(culling: boolean, zOffset: number = 0, force?: boolean): void {
             // Culling        
             if (this._depthCullingState.cull !== culling || force) {
                 if (culling) {
@@ -1345,6 +1378,9 @@
                     this._depthCullingState.cull = false;
                 }
             }
+
+            // Z offset
+            this._depthCullingState.zOffset = zOffset;
         }
 
         public setDepthBuffer(enable: boolean): void {
@@ -1517,6 +1553,7 @@
                     prepareWebGLTexture(texture, this._gl, scene, img.width, img.height, invertY, noMipmap, false, (potWidth, potHeight) => {
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         if (!isPot) {
+                            this._prepareWorkingCanvas();
                             this._workingCanvas.width = potWidth;
                             this._workingCanvas.height = potHeight;
 
@@ -1808,6 +1845,7 @@
                     var width = Tools.GetExponantOfTwo(imgs[0].width, this._caps.maxCubemapTextureSize);
                     var height = width;
 
+                    this._prepareWorkingCanvas();
                     this._workingCanvas.width = width;
                     this._workingCanvas.height = height;
 

+ 1 - 0
Babylon/babylon.node.ts

@@ -7,6 +7,7 @@
         public parent: Node;
         public name: string;
         public id: string;
+        public uniqueId: number;
         public state = "";
 
         public animations = new Array<Animation>();

+ 39 - 1
Babylon/babylon.scene.js

@@ -130,6 +130,7 @@ var BABYLON;
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._scaledPosition = BABYLON.Vector3.Zero();
             this._scaledVelocity = BABYLON.Vector3.Zero();
+            this._uniqueIdCounter = 0;
             this._engine = engine;
             engine.scenes.push(this);
             this._renderingManager = new BABYLON.RenderingManager(this);
@@ -528,6 +529,7 @@ var BABYLON;
         };
         // Methods
         Scene.prototype.addMesh = function (newMesh) {
+            newMesh.uniqueId = this._uniqueIdCounter++;
             var position = this.meshes.push(newMesh);
             if (this.onNewMeshAdded) {
                 this.onNewMeshAdded(newMesh, position, this);
@@ -567,12 +569,14 @@ var BABYLON;
             return index;
         };
         Scene.prototype.addLight = function (newLight) {
+            newLight.uniqueId = this._uniqueIdCounter++;
             var position = this.lights.push(newLight);
             if (this.onNewLightAdded) {
                 this.onNewLightAdded(newLight, position, this);
             }
         };
         Scene.prototype.addCamera = function (newCamera) {
+            newCamera.uniqueId = this._uniqueIdCounter++;
             var position = this.cameras.push(newCamera);
             if (this.onNewCameraAdded) {
                 this.onNewCameraAdded(newCamera, position, this);
@@ -640,6 +644,14 @@ var BABYLON;
             }
             return null;
         };
+        Scene.prototype.getCameraByUniqueID = function (uniqueId) {
+            for (var index = 0; index < this.cameras.length; index++) {
+                if (this.cameras[index].uniqueId === uniqueId) {
+                    return this.cameras[index];
+                }
+            }
+            return null;
+        };
         /**
          * get a camera using its name
          * @param {string} the camera's name
@@ -680,6 +692,19 @@ var BABYLON;
             return null;
         };
         /**
+         * get a light node using its scene-generated unique ID
+         * @param {number} the light's unique id
+         * @return {BABYLON.Light|null} the light or null if none found.
+         */
+        Scene.prototype.getLightByUniqueID = function (uniqueId) {
+            for (var index = 0; index < this.lights.length; index++) {
+                if (this.lights[index].uniqueId === uniqueId) {
+                    return this.lights[index];
+                }
+            }
+            return null;
+        };
+        /**
          * get a geometry using its ID
          * @param {string} the geometry's id
          * @return {BABYLON.Geometry|null} the geometry or null if none found.
@@ -709,7 +734,7 @@ var BABYLON;
             return this._geometries;
         };
         /**
-         * Get a the first added mesh found of a given ID
+         * Get the first added mesh found of a given ID
          * @param {string} id - the id to search for
          * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.
          */
@@ -722,6 +747,19 @@ var BABYLON;
             return null;
         };
         /**
+         * Get a mesh with its auto-generated unique id
+         * @param {number} uniqueId - the unique id to search for
+         * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.
+         */
+        Scene.prototype.getMeshByUniqueID = function (uniqueId) {
+            for (var index = 0; index < this.meshes.length; index++) {
+                if (this.meshes[index].uniqueId === uniqueId) {
+                    return this.meshes[index];
+                }
+            }
+            return null;
+        };
+        /**
          * Get a the last added mesh found of a given ID
          * @param {string} id - the id to search for
          * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.

+ 46 - 1
Babylon/babylon.scene.ts

@@ -261,6 +261,8 @@
 
         private _depthRenderer: DepthRenderer;
 
+        private _uniqueIdCounter = 0;
+
         /**
          * @constructor
          * @param {BABYLON.Engine} engine - the engine to be used to render this scene.
@@ -719,6 +721,7 @@
         // Methods
 
         public addMesh(newMesh: AbstractMesh) {
+            newMesh.uniqueId = this._uniqueIdCounter++;
             var position = this.meshes.push(newMesh);
             if (this.onNewMeshAdded) {
                 this.onNewMeshAdded(newMesh, position, this);
@@ -762,6 +765,7 @@
         }
 
         public addLight(newLight: Light) {
+            newLight.uniqueId = this._uniqueIdCounter++;
             var position = this.lights.push(newLight);
             if (this.onNewLightAdded) {
                 this.onNewLightAdded(newLight, position, this);
@@ -769,6 +773,7 @@
         }
 
         public addCamera(newCamera: Camera) {
+            newCamera.uniqueId = this._uniqueIdCounter++;
             var position = this.cameras.push(newCamera);
             if (this.onNewCameraAdded) {
                 this.onNewCameraAdded(newCamera, position, this);
@@ -849,6 +854,16 @@
             return null;
         }
 
+        public getCameraByUniqueID(uniqueId: number): Camera {
+            for (var index = 0; index < this.cameras.length; index++) {
+                if (this.cameras[index].uniqueId === uniqueId) {
+                    return this.cameras[index];
+                }
+            }
+
+            return null;
+        }
+
         /**
          * get a camera using its name
          * @param {string} the camera's name
@@ -895,6 +910,21 @@
         }
 
         /**
+         * get a light node using its scene-generated unique ID
+         * @param {number} the light's unique id
+         * @return {BABYLON.Light|null} the light or null if none found.
+         */
+        public getLightByUniqueID(uniqueId: number): Light {
+            for (var index = 0; index < this.lights.length; index++) {
+                if (this.lights[index].uniqueId === uniqueId) {
+                    return this.lights[index];
+                }
+            }
+
+            return null;
+        }
+
+        /**
          * get a geometry using its ID
          * @param {string} the geometry's id
          * @return {BABYLON.Geometry|null} the geometry or null if none found.
@@ -930,7 +960,7 @@
         }
 
         /**
-         * Get a the first added mesh found of a given ID
+         * Get the first added mesh found of a given ID
          * @param {string} id - the id to search for
          * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.
          */
@@ -945,6 +975,21 @@
         }
 
         /**
+         * Get a mesh with its auto-generated unique id
+         * @param {number} uniqueId - the unique id to search for
+         * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.
+         */
+        public getMeshByUniqueID(uniqueId: number): AbstractMesh {
+            for (var index = 0; index < this.meshes.length; index++) {
+                if (this.meshes[index].uniqueId === uniqueId) {
+                    return this.meshes[index];
+                }
+            }
+
+            return null;
+        }
+
+        /**
          * Get a the last added mesh found of a given ID
          * @param {string} id - the id to search for
          * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 462 - 122
babylon.2.1-alpha.debug.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 21 - 21
babylon.2.1-alpha.js