Przeglądaj źródła

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

David Catuhe 7 lat temu
rodzic
commit
81a075bf0e

+ 3 - 1
Tools/Gulp/config.json

@@ -1043,7 +1043,9 @@
             "files": [
                 "../../src/Rendering/babylon.outlineRenderer.js",
                 "../../src/Rendering/babylon.edgesRenderer.js",
-                "../../src/Layer/babylon.highlightlayer.js"
+                "../../src/Layer/babylon.effectLayer.js",
+                "../../src/Layer/babylon.highlightLayer.js",
+                "../../src/Layer/babylon.glowLayer.js"
             ],
             "dependUpon": [
                 "shaderMaterial"

+ 0 - 41
dist/preview release/typedocValidationBaseline.json

@@ -14674,42 +14674,6 @@
         }
       }
     },
-    "HighlightLayer": {
-      "Property": {
-        "name": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "glowingMeshStencilReference": {
-          "Naming": {
-            "NotPascalCase": true
-          }
-        },
-        "neutralColor": {
-          "Naming": {
-            "NotPascalCase": true
-          }
-        },
-        "normalMeshStencilReference": {
-          "Naming": {
-            "NotPascalCase": true
-          }
-        }
-      },
-      "Method": {
-        "_rebuild": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "shouldRender": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        }
-      }
-    },
     "HighlightsPostProcess": {
       "Class": {
         "Comments": {
@@ -27406,11 +27370,6 @@
             "MissingText": true
           }
         },
-        "highlightLayers": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "hoverCursor": {
           "Comments": {
             "MissingText": true

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

@@ -11,6 +11,7 @@
 - Introduced [Projection Texture on SpotLight](http://doc.babylonjs.com/babylon101/lights#projection-texture) ([lostink](https://github.com/lostink))
 - Introduced support for [local cubemaps](http://doc.babylonjs.com/how_to/reflect#using-local-cubemap-mode) ([deltakosh](https://github.com/deltakosh))
 - Added [VideoDome](http://doc.babylonjs.com/how_to/360videodome) class to easily support 360 videos ([DavidHGillen](https://github.com/DavidHGillen))
+- Added [GlowLayer](https://doc.babylonjs.com/how_to/glow_layer) to easily support glow from emissive materials ([sebavan](https://github.com/sebavan))
 
 ## Updates
 

+ 639 - 0
src/Layer/babylon.effectLayer.ts

@@ -0,0 +1,639 @@
+module BABYLON {
+    /**
+     * Effect layer options. This helps customizing the behaviour
+     * of the effect layer.
+     */
+    export interface IEffectLayerOptions {
+        /**
+         * Multiplication factor apply to the canvas size to compute the render target size
+         * used to generated the objects (the smaller the faster).
+         */
+        mainTextureRatio: number;
+
+        /**
+         * Enforces a fixed size texture to ensure effect stability across devices.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
+         * Alpha blending mode used to apply the blur. Default depends of the implementation.
+         */
+        alphaBlendingMode: number;
+
+        /**
+         * The camera attached to the layer.
+         */
+        camera: Nullable<Camera>;
+    }
+
+    /**
+     * The effect layer Helps adding post process effect blended with the main pass.
+     * 
+     * This can be for instance use to generate glow or higlight effects on the scene.
+     * 
+     * The effect layer class can not be used directly and is intented to inherited from to be 
+     * customized per effects.
+     */
+    export abstract class EffectLayer {
+
+        private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
+        private _indexBuffer: Nullable<WebGLBuffer>;
+        private _cachedDefines: string;
+        private _effectLayerMapGenerationEffect: Effect;
+        private _effectLayerOptions: IEffectLayerOptions;
+        private _mergeEffect: Effect;
+
+        protected _scene: Scene;
+        protected _engine: Engine;
+        protected _maxSize: number = 0;
+        protected _mainTextureDesiredSize: ISize = { width: 0, height: 0 };
+        protected _mainTexture: RenderTargetTexture;
+        protected _shouldRender = true;
+        protected _postProcesses: PostProcess[] = [];
+        protected _textures: BaseTexture[] = [];
+        protected _emissiveTextureAndColor: { texture: Nullable<BaseTexture>, color: Color4 } = { texture: null, color: new Color4() };
+
+        /**
+         * The clear color of the texture used to generate the glow map.
+         */
+        public neutralColor: Color4 = new Color4();
+
+        /**
+         * Specifies wether the highlight layer is enabled or not.
+         */
+        public isEnabled: boolean = true;
+
+        /**
+         * Gets the camera attached to the layer.
+         */
+        public get camera(): Nullable<Camera> {
+            return this._effectLayerOptions.camera;
+        }
+
+        /**
+         * An event triggered when the effect layer has been disposed.
+         */
+        public onDisposeObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the effect layer is about rendering the main texture with the glowy parts.
+         */
+        public onBeforeRenderMainTextureObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the generated texture is being merged in the scene.
+         */
+        public onBeforeComposeObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the generated texture has been merged in the scene.
+         */
+        public onAfterComposeObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the efffect layer changes its size.
+         */
+        public onSizeChangedObservable = new Observable<EffectLayer>();
+
+        /**
+         * Instantiates a new effect Layer and references it in the scene.
+         * @param name The name of the layer
+         * @param scene The scene to use the layer in
+         */
+        constructor(
+            /** The Friendly of the effect in the scene */
+            public name: string, 
+            scene: Scene) {
+            this._scene = scene || Engine.LastCreatedScene;
+            this._engine = scene.getEngine();
+            this._maxSize = this._engine.getCaps().maxTextureSize;
+            this._scene.effectLayers.push(this);
+
+            // Generate Buffers
+            this._generateIndexBuffer();
+            this._genrateVertexBuffer();
+        }
+
+        /**
+         * Get the effect name of the layer.
+         * @return The effect name
+         */ 
+        public abstract getEffectName(): string;
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @return true if ready otherwise, false
+         */ 
+        public abstract isReady(subMesh: SubMesh, useInstances: boolean): boolean;
+
+        /**
+         * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
+         * @returns true if the effect requires stencil during the main canvas render pass.
+         */
+        public abstract needStencil(): boolean;
+
+        /**
+         * Create the merge effect. This is the shader use to blit the information back
+         * to the main canvas at the end of the scene rendering.
+         * @returns The effect containing the shader used to merge the effect on the  main canvas
+         */
+        protected abstract _createMergeEffect(): Effect;
+        
+        /**
+         * Creates the render target textures and post processes used in the effect layer.
+         */
+        protected abstract _createTextureAndPostProcesses(): void;
+        
+        /**
+         * Implementation specific of rendering the generating effect on the main canvas.
+         * @param effect The effect used to render through
+         */
+        protected abstract _internalRender(effect: Effect): void;
+
+        /**
+         * Sets the required values for both the emissive texture and and the main color.
+         */
+        protected abstract _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void;
+
+        /**
+         * Free any resources and references associated to a mesh.
+         * Internal use
+         * @param mesh The mesh to free.
+         */
+        public abstract _disposeMesh(mesh: Mesh): void;
+
+        /**
+         * Initializes the effect layer with the required options.
+         * @param options Sets of none mandatory options to use with the layer (see IEffectLayerOptions for more information)
+         */
+        protected _init(options: Partial<IEffectLayerOptions>): void {
+            // Adapt options
+            this._effectLayerOptions = {
+                mainTextureRatio: 0.5,
+                alphaBlendingMode: Engine.ALPHA_COMBINE,
+                camera: null,
+                ...options,
+            };
+
+            this._setMainTextureSize();
+            this._createMainTexture();
+            this._createTextureAndPostProcesses();
+            this._mergeEffect = this._createMergeEffect();
+        }
+
+        /**
+         * Generates the index buffer of the full screen quad blending to the main canvas.
+         */
+        private _generateIndexBuffer(): void {
+            // Indices
+            var indices = [];
+            indices.push(0);
+            indices.push(1);
+            indices.push(2);
+
+            indices.push(0);
+            indices.push(2);
+            indices.push(3);
+
+            this._indexBuffer = this._engine.createIndexBuffer(indices);
+        }
+
+        /**
+         * Generates the vertex buffer of the full screen quad blending to the main canvas.
+         */
+        private _genrateVertexBuffer(): void {
+            // VBO
+            var vertices = [];
+            vertices.push(1, 1);
+            vertices.push(-1, 1);
+            vertices.push(-1, -1);
+            vertices.push(1, -1);
+
+            var vertexBuffer = new VertexBuffer(this._engine, vertices, VertexBuffer.PositionKind, false, false, 2);
+            this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
+        }
+
+        /**
+         * Sets the main texture desired size which is the closest power of two
+         * of the engine canvas size.
+         */
+        private _setMainTextureSize(): void {
+            if (this._effectLayerOptions.mainTextureFixedSize) {
+                this._mainTextureDesiredSize.width = this._effectLayerOptions.mainTextureFixedSize;
+                this._mainTextureDesiredSize.height = this._effectLayerOptions.mainTextureFixedSize;
+            }
+            else {
+                this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._effectLayerOptions.mainTextureRatio;
+                this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._effectLayerOptions.mainTextureRatio;
+
+                this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
+                this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;
+            }
+
+            this._mainTextureDesiredSize.width = Math.floor(this._mainTextureDesiredSize.width);
+            this._mainTextureDesiredSize.height = Math.floor(this._mainTextureDesiredSize.height);
+        }
+
+        /**
+         * Creates the main texture for the effect layer.
+         */
+        protected _createMainTexture(): void {
+            this._mainTexture = new RenderTargetTexture("HighlightLayerMainRTT",
+                {
+                    width: this._mainTextureDesiredSize.width,
+                    height: this._mainTextureDesiredSize.height
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_UNSIGNED_INT);
+            this._mainTexture.activeCamera = this._effectLayerOptions.camera;
+            this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._mainTexture.anisotropicFilteringLevel = 1;
+            this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            this._mainTexture.renderParticles = false;
+            this._mainTexture.renderList = null;
+            this._mainTexture.ignoreCameraViewport = true;
+
+            // Custom render function
+            this._mainTexture.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
+                this.onBeforeRenderMainTextureObservable.notifyObservers(this);
+
+                var index: number;
+
+                let engine = this._scene.getEngine();
+
+                if (depthOnlySubMeshes.length) {
+                    engine.setColorWrite(false);
+                    for (index = 0; index < depthOnlySubMeshes.length; index++) {
+                        this._renderSubMesh(depthOnlySubMeshes.data[index]);
+                    }
+                    engine.setColorWrite(true);
+                }
+
+                for (index = 0; index < opaqueSubMeshes.length; index++) {
+                    this._renderSubMesh(opaqueSubMeshes.data[index]);
+                }
+
+                for (index = 0; index < alphaTestSubMeshes.length; index++) {
+                    this._renderSubMesh(alphaTestSubMeshes.data[index]);
+                }
+
+                for (index = 0; index < transparentSubMeshes.length; index++) {
+                    this._renderSubMesh(transparentSubMeshes.data[index]);
+                }
+            };
+
+            this._mainTexture.onClearObservable.add((engine: Engine) => {
+                engine.clear(this.neutralColor, true, true, true);
+            });
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        protected _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<BaseTexture>): boolean {
+            let material = subMesh.getMaterial();
+
+            if (!material) {
+                return false;
+            }
+
+            if (!material.isReady(subMesh.getMesh(), useInstances)) {
+                return false;
+            }
+
+            var defines = [];
+
+            var attribs = [VertexBuffer.PositionKind];
+
+            var mesh = subMesh.getMesh();
+            var uv1 = false;
+            var uv2 = false;
+
+            // Alpha test
+            if (material && material.needAlphaTesting()) {
+                var alphaTexture = material.getAlphaTestTexture();
+                if (alphaTexture) {
+                    defines.push("#define ALPHATEST");
+                    if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
+                        alphaTexture.coordinatesIndex === 1) {
+                        defines.push("#define DIFFUSEUV2");
+                        uv2 = true;
+                    }
+                    else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
+                        defines.push("#define DIFFUSEUV1");
+                        uv1 = true;
+                    }
+                }
+            }
+
+            // Emissive
+            if (emissiveTexture) {
+                defines.push("#define EMISSIVE");
+                if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
+                    emissiveTexture.coordinatesIndex === 1) {
+                    defines.push("#define EMISSIVEUV2");
+                    uv2 = true;
+                }
+                else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
+                    defines.push("#define EMISSIVEUV1");
+                    uv1 = true;
+                }
+            }
+
+            if (uv1) {
+                attribs.push(VertexBuffer.UVKind);
+                defines.push("#define UV1");
+            }
+            if (uv2) {
+                attribs.push(VertexBuffer.UV2Kind);
+                defines.push("#define UV2");
+            }
+
+            // Bones
+            if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                attribs.push(VertexBuffer.MatricesIndicesKind);
+                attribs.push(VertexBuffer.MatricesWeightsKind);
+                if (mesh.numBoneInfluencers > 4) {
+                    attribs.push(VertexBuffer.MatricesIndicesExtraKind);
+                    attribs.push(VertexBuffer.MatricesWeightsExtraKind);
+                }
+                defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
+                defines.push("#define BonesPerMesh " + (mesh.skeleton ? (mesh.skeleton.bones.length + 1) : 0));
+            } else {
+                defines.push("#define NUM_BONE_INFLUENCERS 0");
+            }
+
+            // Instances
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+                attribs.push("world0");
+                attribs.push("world1");
+                attribs.push("world2");
+                attribs.push("world3");
+            }
+
+            // Get correct effect
+            var join = defines.join("\n");
+            if (this._cachedDefines !== join) {
+                this._cachedDefines = join;
+                this._effectLayerMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
+                    attribs,
+                    ["world", "mBones", "viewProjection", "diffuseMatrix", "color", "emissiveMatrix"],
+                    ["diffuseSampler", "emissiveSampler"], join);
+            }
+
+            return this._effectLayerMapGenerationEffect.isReady();
+        }
+
+        /**
+         * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene.
+         */
+        public render(): void {
+            var currentEffect = this._mergeEffect;
+
+            // Check
+            if (!currentEffect.isReady())
+                return;
+
+            for (var i = 0; i < this._postProcesses.length; i++) {
+                if (!this._postProcesses[i].isReady()) {
+                    return;
+                }
+            }
+
+            var engine = this._scene.getEngine();
+
+            this.onBeforeComposeObservable.notifyObservers(this);
+
+            // Render
+            engine.enableEffect(currentEffect);
+            engine.setState(false);
+            
+            // VBOs
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
+
+            // Cache
+            var previousAlphaMode = engine.getAlphaMode();
+
+            // Go Blend.
+            engine.setAlphaMode(this._effectLayerOptions.alphaBlendingMode);
+
+            // Blends the map on the main canvas.
+            this._internalRender(currentEffect);
+
+            // Restore Alpha
+            engine.setAlphaMode(previousAlphaMode);
+
+            this.onAfterComposeObservable.notifyObservers(this);
+
+            // Handle size changes.
+            var size = this._mainTexture.getSize();
+            this._setMainTextureSize();
+            if (size.width !== this._mainTextureDesiredSize.width || size.height !== this._mainTextureDesiredSize.height) {
+                // Recreate RTT and post processes on size change.
+                this.onSizeChangedObservable.notifyObservers(this);
+                this._disposeTextureAndPostProcesses();
+                this._createMainTexture();
+                this._createTextureAndPostProcesses();
+            }
+        }
+
+        /**
+         * Determine if a given mesh will be used in the current effect.
+         * @param mesh mesh to test
+         * @returns true if the mesh will be used
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            return true;
+        }
+
+        /**
+         * Returns true if the layer contains information to display, otherwise false.
+         * @returns true if the glow layer should be rendered
+         */
+        public shouldRender(): boolean {
+            return this.isEnabled && this._shouldRender;
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderMesh(mesh: Mesh): boolean {
+            return true;
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderEmissiveTextureForMesh(mesh: Mesh): boolean {
+            return true;
+        }
+
+        /**
+         * Renders the submesh passed in parameter to the generation map.
+         */
+        protected _renderSubMesh(subMesh: SubMesh): void {
+            if (!this.shouldRender()) {
+                return;
+            }
+
+            var material = subMesh.getMaterial();
+            var mesh = subMesh.getRenderingMesh();
+            var scene = this._scene;
+            var engine = scene.getEngine();
+
+            if (!material) {
+                return;
+            }
+
+            // Do not block in blend mode.
+            if (material.needAlphaBlendingForMesh(mesh)) {
+                return;
+            }
+
+            // Culling
+            engine.setState(material.backFaceCulling);
+
+            // Managing instances
+            var batch = mesh._getInstancesRenderList(subMesh._id);
+            if (batch.mustReturn) {
+                return;
+            }
+
+            // Early Exit per mesh
+            if (!this._shouldRenderMesh(mesh)) {
+                return;
+            }
+
+            var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
+
+            this._setEmissiveTextureAndColor(mesh, subMesh, material);
+
+            if (this._isReady(subMesh, hardwareInstancedRendering, this._emissiveTextureAndColor.texture)) {
+                engine.enableEffect(this._effectLayerMapGenerationEffect);
+                mesh._bind(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode);
+
+                this._effectLayerMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
+
+                this._effectLayerMapGenerationEffect.setFloat4("color",
+                    this._emissiveTextureAndColor.color.r,
+                    this._emissiveTextureAndColor.color.g,
+                    this._emissiveTextureAndColor.color.b,
+                    this._emissiveTextureAndColor.color.a);
+
+                // Alpha test
+                if (material && material.needAlphaTesting()) {
+                    var alphaTexture = material.getAlphaTestTexture();
+                    if (alphaTexture) {
+                        this._effectLayerMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
+                        let textureMatrix = alphaTexture.getTextureMatrix();
+
+                        if (textureMatrix) {
+                            this._effectLayerMapGenerationEffect.setMatrix("diffuseMatrix", textureMatrix);
+                        }
+                    }
+                }
+
+                // Glow emissive only
+                if (this._emissiveTextureAndColor.texture) {
+                    this._effectLayerMapGenerationEffect.setTexture("emissiveSampler", this._emissiveTextureAndColor.texture);
+                    this._effectLayerMapGenerationEffect.setMatrix("emissiveMatrix", this._emissiveTextureAndColor.texture.getTextureMatrix());
+                }
+
+                // Bones
+                if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
+                    this._effectLayerMapGenerationEffect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                }
+
+                // Draw
+                mesh._processRendering(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
+                    (isInstance, world) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
+            } else {
+                // Need to reset refresh rate of the shadowMap
+                this._mainTexture.resetRefreshCounter();
+            }
+        }
+
+        /**
+         * Rebuild the required buffers.
+         * @ignore Internal use only.
+         */
+        public _rebuild(): void {
+            let vb = this._vertexBuffers[VertexBuffer.PositionKind];
+
+            if (vb) {
+                vb._rebuild();
+            }
+
+            this._generateIndexBuffer();
+        }
+
+        /**
+         * Dispose only the render target textures and post process.
+         */
+        private _disposeTextureAndPostProcesses(): void {
+            this._mainTexture.dispose();
+
+            for (var i = 0; i < this._postProcesses.length; i++) {
+                if (this._postProcesses[i]) {
+                    this._postProcesses[i].dispose();
+                }
+            }
+            this._postProcesses = [];
+
+            for (var i = 0; i < this._textures.length; i++) {
+                if (this._textures[i]) {
+                    this._textures[i].dispose();
+                }
+            }
+            this._textures = [];
+        }
+
+        /**
+         * Dispose the highlight layer and free resources.
+         */
+        public dispose(): void {
+            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
+            }
+
+            if (this._indexBuffer) {
+                this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+                this._indexBuffer = null;
+            }
+
+            // Clean textures and post processes
+            this._disposeTextureAndPostProcesses();
+
+            // Remove from scene
+            var index = this._scene.effectLayers.indexOf(this, 0);
+            if (index > -1) {
+                this._scene.effectLayers.splice(index, 1);
+            }
+
+            // Callback
+            this.onDisposeObservable.notifyObservers(this);
+
+            this.onDisposeObservable.clear();
+            this.onBeforeRenderMainTextureObservable.clear();
+            this.onBeforeComposeObservable.clear();
+            this.onAfterComposeObservable.clear();
+            this.onSizeChangedObservable.clear();
+        }
+    }
+} 

+ 419 - 0
src/Layer/babylon.glowLayer.ts

@@ -0,0 +1,419 @@
+module BABYLON {
+    /**
+     * Glow layer options. This helps customizing the behaviour
+     * of the glow layer.
+     */
+    export interface IGlowLayerOptions {
+        /**
+         * Multiplication factor apply to the canvas size to compute the render target size
+         * used to generated the glowing objects (the smaller the faster).
+         */
+        mainTextureRatio: number;
+
+        /**
+         * Enforces a fixed size texture to ensure resize independant blur.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
+         * How big is the kernel of the blur texture.
+         */
+        blurKernelSize: number;
+
+        /**
+         * The camera attached to the layer.
+         */
+        camera: Nullable<Camera>;
+
+        /**
+         * Enable MSAA by chosing the number of samples.
+         */
+        mainTextureSamples?: number;
+    }
+
+    /**
+     * The glow layer Helps adding a glow effect around the emissive parts of a mesh.
+     * 
+     * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
+     * glowy meshes to your scene.
+     * 
+     * Documentation: https://doc.babylonjs.com/how_to/glow_layer
+     */
+    export class GlowLayer extends EffectLayer {
+        /**
+         * Effect Name of the layer.
+         */
+        public static readonly EffectName = "GlowLayer";
+
+        /**
+         * The default blur kernel size used for the glow.
+         */
+        public static DefaultBlurKernelSize = 32;
+
+        /**
+         * The default texture size ratio used for the glow.
+         */
+        public static DefaultTextureRatio = 0.5;
+
+        /**
+         * Sets the kernel size of the blur.
+         */
+        public set blurKernelSize(value: number) {
+            this._horizontalBlurPostprocess1.kernel = value;
+            this._verticalBlurPostprocess1.kernel = value;
+            this._horizontalBlurPostprocess2.kernel = value;
+            this._verticalBlurPostprocess2.kernel = value;
+        }
+
+        /**
+         * Gets the kernel size of the blur.
+         */
+        public get blurKernelSize(): number {
+            return this._horizontalBlurPostprocess1.kernel;
+        }
+
+        /**
+         * Sets the glow intensity.
+         */
+        public set intensity(value: number) {
+            this._intensity = value;
+        }
+
+        /**
+         * Gets the glow intensity.
+         */
+        public get intensity(): number {
+            return this._intensity;
+        }
+
+        private _options: IGlowLayerOptions;
+        private _intensity: number = 1.0;
+        private _horizontalBlurPostprocess1: BlurPostProcess;
+        private _verticalBlurPostprocess1: BlurPostProcess;
+        private _horizontalBlurPostprocess2: BlurPostProcess;
+        private _verticalBlurPostprocess2: BlurPostProcess;
+        private _blurTexture1: RenderTargetTexture;
+        private _blurTexture2: RenderTargetTexture;
+        private _postProcesses1: PostProcess[];
+        private _postProcesses2: PostProcess[];
+
+        private _includedOnlyMeshes: number[] = [];
+        private _excludedMeshes: number[] = [];
+
+        /**
+         * Instantiates a new glow Layer and references it to the scene.
+         * @param name The name of the layer
+         * @param scene The scene to use the layer in
+         * @param options Sets of none mandatory options to use with the layer (see IGlowLayerOptions for more information)
+         */
+        constructor(public name: string, scene: Scene, options?: Partial<IGlowLayerOptions>) {
+            super(name, scene);
+            this.neutralColor = new Color4(0, 0, 0, 1);
+
+            // Adapt options
+            this._options = {
+                mainTextureRatio: GlowLayer.DefaultTextureRatio,
+                blurKernelSize: 32,
+                mainTextureFixedSize: undefined,
+                camera: null,
+                mainTextureSamples: 1,
+                ...options,
+            };
+
+            // Initialize the layer
+            this._init({
+                alphaBlendingMode: Engine.ALPHA_ADD,
+                camera: this._options.camera,
+                mainTextureFixedSize: this._options.mainTextureFixedSize,
+                mainTextureRatio: this._options.mainTextureRatio
+            });
+        }
+
+        /**
+         * Get the effect name of the layer.
+         * @return The effect name
+         */ 
+        public getEffectName(): string {
+            return GlowLayer.EffectName;
+        }
+
+        /**
+         * Create the merge effect. This is the shader use to blit the information back
+         * to the main canvas at the end of the scene rendering.
+         */
+        protected _createMergeEffect(): Effect {
+             // Effect
+             return this._engine.createEffect("glowMapMerge",
+                [VertexBuffer.PositionKind],
+                ["offset"],
+                ["textureSampler", "textureSampler2"],
+                "#define EMISSIVE \n");
+
+        }
+
+        /**
+         * Creates the render target textures and post processes used in the glow layer.
+         */
+        protected _createTextureAndPostProcesses(): void {
+            var blurTextureWidth = this._mainTextureDesiredSize.width;
+            var blurTextureHeight = this._mainTextureDesiredSize.height;
+            blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
+            blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
+
+            this._blurTexture1 = new RenderTargetTexture("GlowLayerBlurRTT",
+                {
+                    width: blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_HALF_FLOAT);
+            this._blurTexture1.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture1.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture1.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            this._blurTexture1.renderParticles = false;
+            this._blurTexture1.ignoreCameraViewport = true;
+
+            var blurTextureWidth2 = Math.floor(blurTextureWidth / 2);
+            var blurTextureHeight2 = Math.floor(blurTextureHeight / 2);
+
+            this._blurTexture2 = new RenderTargetTexture("GlowLayerBlurRTT2",
+                {
+                    width: blurTextureWidth2,
+                    height: blurTextureHeight2
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_HALF_FLOAT);
+            this._blurTexture2.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture2.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            this._blurTexture2.renderParticles = false;
+            this._blurTexture2.ignoreCameraViewport = true;
+
+            this._textures = [ this._blurTexture1, this._blurTexture2 ];
+
+            this._horizontalBlurPostprocess1 = new BlurPostProcess("GlowLayerHBP1", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+            this._horizontalBlurPostprocess1.width = blurTextureWidth;
+            this._horizontalBlurPostprocess1.height = blurTextureHeight;
+            this._horizontalBlurPostprocess1.onApplyObservable.add(effect => {
+                effect.setTexture("textureSampler", this._mainTexture);
+            });
+
+            this._verticalBlurPostprocess1 = new BlurPostProcess("GlowLayerVBP1", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+
+            this._horizontalBlurPostprocess2 = new BlurPostProcess("GlowLayerHBP2", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth2,
+                    height: blurTextureHeight2
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+            this._horizontalBlurPostprocess2.width = blurTextureWidth2;
+            this._horizontalBlurPostprocess2.height = blurTextureHeight2;
+            this._horizontalBlurPostprocess2.onApplyObservable.add(effect => {
+                effect.setTexture("textureSampler", this._blurTexture1);
+            });
+
+            this._verticalBlurPostprocess2 = new BlurPostProcess("GlowLayerVBP2", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth2,
+                    height: blurTextureHeight2
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+
+            this._postProcesses = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1, this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
+            this._postProcesses1 = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1 ];
+            this._postProcesses2 = [ this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
+
+            this._mainTexture.samples = this._options.mainTextureSamples!;
+            this._mainTexture.onAfterUnbindObservable.add(() => {
+                let internalTexture = this._blurTexture1.getInternalTexture();
+                if (internalTexture) {
+                    this._scene.postProcessManager.directRender(
+                        this._postProcesses1,
+                        internalTexture, 
+                        true);
+
+                        internalTexture = this._blurTexture2.getInternalTexture();
+                        if (internalTexture) {
+                            this._scene.postProcessManager.directRender(
+                                this._postProcesses2,
+                                internalTexture, 
+                                true);
+                        }
+                }
+            });
+
+            // Prevent autoClear.
+            this._postProcesses.map(pp => { pp.autoClear = false; });
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+            let material = subMesh.getMaterial();
+            let mesh = subMesh.getRenderingMesh();
+
+            if (!material || !mesh) {
+                return false;
+            }
+
+            let emissiveTexture = (<any>material).emissiveTexture;
+            return super._isReady(subMesh, useInstances, emissiveTexture);
+        }
+
+        /**
+         * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
+         */
+        public needStencil(): boolean {
+            return false;
+        }
+
+        /**
+         * Implementation specific of rendering the generating effect on the main canvas.
+         * @param effect The effect used to render through
+         */
+        protected _internalRender(effect: Effect): void {
+            // Texture
+            effect.setTexture("textureSampler", this._blurTexture1);
+            effect.setTexture("textureSampler2", this._blurTexture2);
+            effect.setFloat("offset", this._intensity);
+
+            // Cache
+            var engine = this._engine;
+            var previousStencilBuffer = engine.getStencilBuffer();
+                
+            // Draw order
+            engine.setStencilBuffer(false);
+
+            engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+
+            // Draw order
+            engine.setStencilBuffer(previousStencilBuffer);
+        }
+
+        /**
+         * Sets the required values for both the emissive texture and and the main color.
+         */
+        protected _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void {
+            var textureLevel = 1.0;
+            if (material) {
+                this._emissiveTextureAndColor.texture = (<any>material).emissiveTexture;
+                if (this._emissiveTextureAndColor.texture) {
+                    textureLevel = this._emissiveTextureAndColor.texture.level;
+                }
+            }
+            else {
+                this._emissiveTextureAndColor.texture = null;
+            }
+
+            if ((<any>material).emissiveColor) {
+                this._emissiveTextureAndColor.color.set(
+                    (<any>material).emissiveColor.r * textureLevel,
+                    (<any>material).emissiveColor.g * textureLevel,
+                    (<any>material).emissiveColor .b * textureLevel,
+                    1.0);
+            }
+            else {
+                this._emissiveTextureAndColor.color.set(
+                    this.neutralColor.r,
+                    this.neutralColor.g,
+                    this.neutralColor.b,
+                    this.neutralColor.a);
+            }
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderMesh(mesh: Mesh): boolean {
+            return this.hasMesh(mesh);
+        }
+
+        /**
+         * Add a mesh in the exclusion list to prevent it to impact or being impacted by the glow layer.
+         * @param mesh The mesh to exclude from the glow layer
+         */
+        public addExcludedMesh(mesh: Mesh): void {
+            if (this._excludedMeshes.indexOf(mesh.uniqueId) !== -1) {
+                this._excludedMeshes.push(mesh.uniqueId);
+            }
+        }
+
+        /**
+          * Remove a mesh from the exclusion list to let it impact or being impacted by the glow layer.
+          * @param mesh The mesh to remove
+          */
+        public removeExcludedMesh(mesh: Mesh): void {
+            var index = this._excludedMeshes.indexOf(mesh.uniqueId);
+            if (index !== -1) {
+                this._excludedMeshes.splice(index, 1);
+            } 
+        }
+
+        /**
+         * Add a mesh in the inclusion list to impact or being impacted by the glow layer.
+         * @param mesh The mesh to include in the glow layer
+         */
+        public addIncludedOnlyMesh(mesh: Mesh): void {
+            if (this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1) {
+                this._includedOnlyMeshes.push(mesh.uniqueId);
+            }
+        }
+
+        /**
+          * Remove a mesh from the Inclusion list to prevent it to impact or being impacted by the glow layer.
+          * @param mesh The mesh to remove
+          */
+        public removeIncludedOnlyMesh(mesh: Mesh): void {
+            var index = this._includedOnlyMeshes.indexOf(mesh.uniqueId);
+            if (index !== -1) {
+                this._includedOnlyMeshes.splice(index, 1);
+            } 
+        }
+
+        /**
+         * Determine if a given mesh will be used in the glow layer
+         * @param mesh The mesh to test
+         * @returns true if the mesh will be highlighted by the current glow layer
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            // Included Mesh
+            if (this._includedOnlyMeshes.length) {
+                return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1;
+            };
+
+            // Excluded Mesh
+            if (this._excludedMeshes.length) {
+                return this._excludedMeshes.indexOf(mesh.uniqueId) === -1;
+            };
+
+            return true;
+        }
+
+        /**
+         * Free any resources and references associated to a mesh.
+         * Internal use
+         * @param mesh The mesh to free.
+         */
+        public _disposeMesh(mesh: Mesh): void {
+            this.removeIncludedOnlyMesh(mesh);
+            this.removeExcludedMesh(mesh);
+        }
+    }
+} 

+ 667 - 0
src/Layer/babylon.highlightLayer.ts

@@ -0,0 +1,667 @@
+module BABYLON {
+    /**
+     * Special Glow Blur post process only blurring the alpha channel
+     * It enforces keeping the most luminous color in the color channel.
+     */
+    class GlowBlurPostProcess extends PostProcess {
+        constructor(name: string, public direction: Vector2, public kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
+            super(name, "glowBlurPostProcess", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
+            
+            this.onApplyObservable.add((effect: Effect) => {
+                effect.setFloat2("screenSize", this.width, this.height);
+                effect.setVector2("direction", this.direction);
+                effect.setFloat("blurWidth", this.kernel);
+            });
+        }
+    }
+
+    /**
+     * Highlight layer options. This helps customizing the behaviour
+     * of the highlight layer.
+     */
+    export interface IHighlightLayerOptions {
+        /**
+         * Multiplication factor apply to the canvas size to compute the render target size
+         * used to generated the glowing objects (the smaller the faster).
+         */
+        mainTextureRatio: number;
+
+        /**
+         * Enforces a fixed size texture to ensure resize independant blur.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
+         * Multiplication factor apply to the main texture size in the first step of the blur to reduce the size 
+         * of the picture to blur (the smaller the faster).
+         */
+        blurTextureSizeRatio: number;
+
+        /**
+         * How big in texel of the blur texture is the vertical blur.
+         */
+        blurVerticalSize: number;
+
+        /**
+         * How big in texel of the blur texture is the horizontal blur.
+         */
+        blurHorizontalSize: number;
+
+        /**
+         * Alpha blending mode used to apply the blur. Default is combine.
+         */
+        alphaBlendingMode: number
+
+        /**
+         * The camera attached to the layer.
+         */
+        camera: Nullable<Camera>;
+
+        /**
+         * Should we display highlight as a solid stroke?
+         */
+        isStroke?: boolean;
+    }
+
+    /**
+     * Storage interface grouping all the information required for glowing a mesh.
+     */
+    interface IHighlightLayerMesh {
+        /** 
+         * The glowy mesh
+         */
+        mesh: Mesh;
+        /**
+         * The color of the glow
+         */
+        color: Color3;
+        /**
+         * The mesh render callback use to insert stencil information
+         */
+        observerHighlight: Nullable<Observer<Mesh>>;
+        /**
+         * The mesh render callback use to come to the default behavior
+         */
+        observerDefault: Nullable<Observer<Mesh>>;
+        /**
+         * If it exists, the emissive color of the material will be used to generate the glow.
+         * Else it falls back to the current color.
+         */
+        glowEmissiveOnly: boolean;
+    }
+
+    /**
+     * Storage interface grouping all the information required for an excluded mesh.
+     */
+    interface IHighlightLayerExcludedMesh {
+        /** 
+         * The glowy mesh
+         */
+        mesh: Mesh;
+        /**
+         * The mesh render callback use to prevent stencil use
+         */
+        beforeRender: Nullable<Observer<Mesh>>;
+        /**
+         * The mesh render callback use to restore previous stencil use
+         */
+        afterRender: Nullable<Observer<Mesh>>;
+    }
+
+    /**
+     * The highlight layer Helps adding a glow effect around a mesh.
+     * 
+     * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
+     * glowy meshes to your scene.
+     * 
+     * !!! THIS REQUIRES AN ACTIVE STENCIL BUFFER ON THE CANVAS !!!
+     */
+    export class HighlightLayer extends EffectLayer {
+        /**
+         * Effect Name of the highlight layer.
+         */
+        public static readonly EffectName = "HighlightLayer";
+
+        /**
+         * The neutral color used during the preparation of the glow effect.
+         * This is black by default as the blend operation is a blend operation. 
+         */
+        public static NeutralColor: Color4 = new Color4(0, 0, 0, 0);
+
+        /**
+         * Stencil value used for glowing meshes.
+         */
+        public static GlowingMeshStencilReference = 0x02;
+
+        /**
+         * Stencil value used for the other meshes in the scene.
+         */
+        public static NormalMeshStencilReference = 0x01;
+
+        /**
+         * Specifies whether or not the inner glow is ACTIVE in the layer.
+         */
+        public innerGlow: boolean = true;
+
+        /**
+         * Specifies whether or not the outer glow is ACTIVE in the layer.
+         */
+        public outerGlow: boolean = true;
+
+        /**
+         * Specifies the horizontal size of the blur.
+         */
+        public set blurHorizontalSize(value: number) {
+            this._horizontalBlurPostprocess.kernel = value;
+        }
+
+        /**
+         * Specifies the vertical size of the blur.
+         */
+        public set blurVerticalSize(value: number) {
+            this._verticalBlurPostprocess.kernel = value;
+        }
+
+        /**
+         * Gets the horizontal size of the blur.
+         */
+        public get blurHorizontalSize(): number {
+            return this._horizontalBlurPostprocess.kernel
+        }
+
+        /**
+         * Gets the vertical size of the blur.
+         */
+        public get blurVerticalSize(): number {
+            return this._verticalBlurPostprocess.kernel;
+        }
+
+        /**
+         * An event triggered when the highlight layer is being blurred.
+         */
+        public onBeforeBlurObservable = new Observable<HighlightLayer>();
+
+        /**
+         * An event triggered when the highlight layer has been blurred.
+         */
+        public onAfterBlurObservable = new Observable<HighlightLayer>();
+
+        private _instanceGlowingMeshStencilReference = HighlightLayer.GlowingMeshStencilReference++;
+
+        private _options: IHighlightLayerOptions;
+        private _downSamplePostprocess: PassPostProcess;
+        private _horizontalBlurPostprocess: GlowBlurPostProcess;
+        private _verticalBlurPostprocess: GlowBlurPostProcess;
+        private _blurTexture: RenderTargetTexture;
+
+        private _meshes: Nullable<{ [id: string]: Nullable<IHighlightLayerMesh> }> = {};
+        private _excludedMeshes: Nullable<{ [id: string]: Nullable<IHighlightLayerExcludedMesh> }> = {};
+
+        /**
+         * Instantiates a new highlight Layer and references it to the scene..
+         * @param name The name of the layer
+         * @param scene The scene to use the layer in
+         * @param options Sets of none mandatory options to use with the layer (see IHighlightLayerOptions for more information)
+         */
+        constructor(public name: string, scene: Scene, options?: Partial<IHighlightLayerOptions>) {
+            super(name, scene);
+            this.neutralColor = HighlightLayer.NeutralColor;
+
+            // Warn on stencil
+            if (!this._engine.isStencilEnable) {
+                Tools.Warn("Rendering the Highlight Layer requires the stencil to be active on the canvas. var engine = new BABYLON.Engine(canvas, antialias, { stencil: true }");
+            }
+
+            // Adapt options
+            this._options = {
+                mainTextureRatio: 0.5,
+                blurTextureSizeRatio: 0.5,
+                blurHorizontalSize: 1.0,
+                blurVerticalSize: 1.0,
+                alphaBlendingMode: Engine.ALPHA_COMBINE,
+                camera: null,
+                ...options,
+            };
+
+            // Initialize the layer
+            this._init({
+                alphaBlendingMode: this._options.alphaBlendingMode,
+                camera: this._options.camera,
+                mainTextureFixedSize: this._options.mainTextureFixedSize,
+                mainTextureRatio: this._options.mainTextureRatio
+            });
+        }
+
+        /**
+         * Get the effect name of the layer.
+         * @return The effect name
+         */ 
+        public getEffectName(): string {
+            return HighlightLayer.EffectName;
+        }
+
+        /**
+         * Create the merge effect. This is the shader use to blit the information back
+         * to the main canvas at the end of the scene rendering.
+         */
+        protected _createMergeEffect(): Effect {
+             // Effect
+             return this._engine.createEffect("glowMapMerge",
+                [VertexBuffer.PositionKind],
+                ["offset"],
+                ["textureSampler"],
+                this._options.isStroke ? "#define STROKE \n" : undefined);
+
+        }
+
+        /**
+         * Creates the render target textures and post processes used in the highlight layer.
+         */
+        protected _createTextureAndPostProcesses(): void {
+            var blurTextureWidth = this._mainTextureDesiredSize.width * this._options.blurTextureSizeRatio;
+            var blurTextureHeight = this._mainTextureDesiredSize.height * this._options.blurTextureSizeRatio;
+            blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
+            blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
+
+            this._blurTexture = new RenderTargetTexture("HighlightLayerBlurRTT",
+                {
+                    width: blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_HALF_FLOAT);
+            this._blurTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture.anisotropicFilteringLevel = 16;
+            this._blurTexture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
+            this._blurTexture.renderParticles = false;
+            this._blurTexture.ignoreCameraViewport = true;
+
+            this._textures = [ this._blurTexture ];
+
+            if (this._options.alphaBlendingMode === Engine.ALPHA_COMBINE) {
+                this._downSamplePostprocess = new PassPostProcess("HighlightLayerPPP", this._options.blurTextureSizeRatio,
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                this._downSamplePostprocess.onApplyObservable.add(effect => {
+                    effect.setTexture("textureSampler", this._mainTexture);
+                });
+
+                this._horizontalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
+                });
+
+                this._verticalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, 1,
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                this._verticalBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
+                });
+
+                this._postProcesses = [this._downSamplePostprocess, this._horizontalBlurPostprocess, this._verticalBlurPostprocess];
+            }
+            else {
+                this._horizontalBlurPostprocess = new BlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize / 2, {
+                        width:  blurTextureWidth,
+                        height: blurTextureHeight
+                    },
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+                this._horizontalBlurPostprocess.width = blurTextureWidth;
+                this._horizontalBlurPostprocess.height = blurTextureHeight;
+                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setTexture("textureSampler", this._mainTexture);
+                });
+
+                this._verticalBlurPostprocess = new BlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize / 2, {
+                        width:  blurTextureWidth,
+                        height: blurTextureHeight
+                    },
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+
+                this._postProcesses = [this._horizontalBlurPostprocess, this._verticalBlurPostprocess];
+            }
+
+            this._mainTexture.onAfterUnbindObservable.add(() => {
+                this.onBeforeBlurObservable.notifyObservers(this);
+
+                let internalTexture = this._blurTexture.getInternalTexture();
+                if (internalTexture) {
+                    this._scene.postProcessManager.directRender(
+                        this._postProcesses,
+                        internalTexture, 
+                        true);
+                }
+
+                this.onAfterBlurObservable.notifyObservers(this);
+            });
+
+            // Prevent autoClear.
+            this._postProcesses.map(pp => { pp.autoClear = false; });
+        }
+
+        /**
+         * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
+         */
+        public needStencil(): boolean {
+            return true;
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+            let material = subMesh.getMaterial();
+            let mesh = subMesh.getRenderingMesh();
+
+            if (!material || !mesh || !this._meshes) {
+                return false;
+            }
+
+            let emissiveTexture: Nullable<Texture> = null;
+            let highlightLayerMesh = this._meshes[mesh.uniqueId];
+
+            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
+                emissiveTexture = (<any>material).emissiveTexture;
+            }
+            return super._isReady(subMesh, useInstances, emissiveTexture);
+        }
+
+        /**
+         * Implementation specific of rendering the generating effect on the main canvas.
+         * @param effect The effect used to render through
+         */
+        protected _internalRender(effect: Effect): void {
+            // Texture
+            effect.setTexture("textureSampler", this._blurTexture);
+
+            // Cache
+            var engine = this._engine;
+            var previousStencilBuffer = engine.getStencilBuffer();
+            var previousStencilFunction = engine.getStencilFunction();
+            var previousStencilMask = engine.getStencilMask();
+            var previousStencilOperationPass = engine.getStencilOperationPass();
+            var previousStencilOperationFail = engine.getStencilOperationFail();
+            var previousStencilOperationDepthFail = engine.getStencilOperationDepthFail();
+
+            // Stencil operations
+            engine.setStencilOperationPass(Engine.REPLACE);
+            engine.setStencilOperationFail(Engine.KEEP);
+            engine.setStencilOperationDepthFail(Engine.KEEP);
+
+            // Draw order
+            engine.setStencilMask(0x00);
+            engine.setStencilBuffer(true);
+            engine.setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
+
+            // 2 passes inner outer
+            if (this.outerGlow) {
+                effect.setFloat("offset", 0);
+                engine.setStencilFunction(Engine.NOTEQUAL);
+                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+            }
+            if (this.innerGlow) {
+                effect.setFloat("offset", 1);
+                engine.setStencilFunction(Engine.EQUAL);
+                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+            }
+
+            // Restore Cache
+            engine.setStencilFunction(previousStencilFunction);
+            engine.setStencilMask(previousStencilMask);
+            engine.setStencilBuffer(previousStencilBuffer);
+            engine.setStencilOperationPass(previousStencilOperationPass);
+            engine.setStencilOperationFail(previousStencilOperationFail);
+            engine.setStencilOperationDepthFail(previousStencilOperationDepthFail);
+        }
+
+        /**
+         * Returns true if the layer contains information to display, otherwise false.
+         */
+        public shouldRender(): boolean {
+            if (super.shouldRender()) {
+                return this._meshes ? true : false;
+            }
+
+            return false;
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderMesh(mesh: Mesh): boolean {
+            // Excluded Mesh
+            if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
+                return false;
+            };
+
+            return true;
+        }
+
+        /**
+         * Sets the required values for both the emissive texture and and the main color.
+         */
+        protected _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void {
+            var highlightLayerMesh = this._meshes![mesh.uniqueId];
+            if (highlightLayerMesh) {
+                this._emissiveTextureAndColor.color.set(
+                    highlightLayerMesh.color.r,
+                    highlightLayerMesh.color.g,
+                    highlightLayerMesh.color.b,
+                    1.0);
+            }
+            else {
+                this._emissiveTextureAndColor.color.set(
+                    this.neutralColor.r,
+                    this.neutralColor.g,
+                    this.neutralColor.b,
+                    this.neutralColor.a);
+            }
+
+            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
+                this._emissiveTextureAndColor.texture = (<any>material).emissiveTexture;
+                this._emissiveTextureAndColor.color.set(
+                    1.0,
+                    1.0,
+                    1.0,
+                    1.0);
+            }
+            else {
+                this._emissiveTextureAndColor.texture = null;
+            }
+
+        }
+
+        /**
+         * Add a mesh in the exclusion list to prevent it to impact or being impacted by the highlight layer.
+         * @param mesh The mesh to exclude from the highlight layer
+         */
+        public addExcludedMesh(mesh: Mesh) {
+            if (!this._excludedMeshes) {
+                return;
+            }
+
+            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
+            if (!meshExcluded) {
+                this._excludedMeshes[mesh.uniqueId] = {
+                    mesh: mesh,
+                    beforeRender: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
+                        mesh.getEngine().setStencilBuffer(false);
+                    }),
+                    afterRender: mesh.onAfterRenderObservable.add((mesh: Mesh) => {
+                        mesh.getEngine().setStencilBuffer(true);
+                    }),
+                }
+            }
+        }
+
+        /**
+          * Remove a mesh from the exclusion list to let it impact or being impacted by the highlight layer.
+          * @param mesh The mesh to highlight
+          */
+        public removeExcludedMesh(mesh: Mesh) {
+            if (!this._excludedMeshes) {
+                return;
+            }
+
+            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
+            if (meshExcluded) {
+                if (meshExcluded.beforeRender) {
+                    mesh.onBeforeRenderObservable.remove(meshExcluded.beforeRender);
+                }
+
+                if (meshExcluded.afterRender) {
+                    mesh.onAfterRenderObservable.remove(meshExcluded.afterRender);
+                }
+            }
+
+            this._excludedMeshes[mesh.uniqueId] = null;
+        }
+
+        /**
+         * Determine if a given mesh will be highlighted by the current HighlightLayer
+         * @param mesh mesh to test
+         * @returns true if the mesh will be highlighted by the current HighlightLayer
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            if (!this._meshes) {
+                return false;
+            }
+
+            return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
+        }
+
+        /**
+         * Add a mesh in the highlight layer in order to make it glow with the chosen color.
+         * @param mesh The mesh to highlight
+         * @param color The color of the highlight
+         * @param glowEmissiveOnly Extract the glow from the emissive texture
+         */
+        public addMesh(mesh: Mesh, color: Color3, glowEmissiveOnly = false) {
+            if (!this._meshes) {
+                return;
+            }
+
+            var meshHighlight = this._meshes[mesh.uniqueId];
+            if (meshHighlight) {
+                meshHighlight.color = color;
+            }
+            else {
+                this._meshes[mesh.uniqueId] = {
+                    mesh: mesh,
+                    color: color,
+                    // Lambda required for capture due to Observable this context
+                    observerHighlight: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
+                        if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
+                            this._defaultStencilReference(mesh);
+                        }
+                        else {
+                            mesh.getScene().getEngine().setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
+                        }
+                    }),
+                    observerDefault: mesh.onAfterRenderObservable.add(this._defaultStencilReference),
+                    glowEmissiveOnly: glowEmissiveOnly
+                };
+            }
+
+            this._shouldRender = true;
+        }
+
+        /**
+         * Remove a mesh from the highlight layer in order to make it stop glowing.
+         * @param mesh The mesh to highlight
+         */
+        public removeMesh(mesh: Mesh) {
+            if (!this._meshes) {
+                return;
+            }
+
+            var meshHighlight = this._meshes[mesh.uniqueId];
+            if (meshHighlight) {
+
+                if (meshHighlight.observerHighlight) {
+                    mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
+                }
+
+                if (meshHighlight.observerDefault) {
+                    mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
+                }
+                delete this._meshes[mesh.uniqueId];
+            }
+
+            this._shouldRender = false;
+            for (var meshHighlightToCheck in this._meshes) {
+                if (this._meshes[meshHighlightToCheck]) {
+                    this._shouldRender = true;
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Force the stencil to the normal expected value for none glowing parts
+         */
+        private _defaultStencilReference(mesh: Mesh) {
+            mesh.getScene().getEngine().setStencilFunctionReference(HighlightLayer.NormalMeshStencilReference);
+        }
+
+        /**
+         * Free any resources and references associated to a mesh.
+         * Internal use
+         * @param mesh The mesh to free.
+         */
+        public _disposeMesh(mesh: Mesh): void {
+            this.removeMesh(mesh);
+            this.removeExcludedMesh(mesh);
+        }
+
+        /**
+         * Dispose the highlight layer and free resources.
+         */
+        public dispose(): void {
+            if (this._meshes) {
+                // Clean mesh references 
+                for (let id in this._meshes) {
+                    let meshHighlight = this._meshes[id];
+                    if (meshHighlight && meshHighlight.mesh) {
+
+                        if (meshHighlight.observerHighlight) {
+                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
+                        }
+
+                        if (meshHighlight.observerDefault) {
+                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
+                        }
+                    }
+                }
+                this._meshes = null;
+            }
+
+            if (this._excludedMeshes) {
+                for (let id in this._excludedMeshes) {
+                    let meshHighlight = this._excludedMeshes[id];
+                    if (meshHighlight) {
+
+                        if (meshHighlight.beforeRender) {
+                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.beforeRender);
+                        }
+
+                        if (meshHighlight.afterRender) {
+                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.afterRender);
+                        }
+                    }
+                }
+                this._excludedMeshes = null;
+            }
+
+            super.dispose();
+        }
+    }
+} 

+ 0 - 996
src/Layer/babylon.highlightlayer.ts

@@ -1,996 +0,0 @@
-module BABYLON {
-    /**
-     * Special Glow Blur post process only blurring the alpha channel
-     * It enforces keeping the most luminous color in the color channel.
-     */
-    class GlowBlurPostProcess extends PostProcess {
-        constructor(name: string, public direction: Vector2, public kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
-            super(name, "glowBlurPostProcess", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
-            
-            this.onApplyObservable.add((effect: Effect) => {
-                effect.setFloat2("screenSize", this.width, this.height);
-                effect.setVector2("direction", this.direction);
-                effect.setFloat("blurWidth", this.kernel);
-            });
-        }
-    }
-
-    /**
-     * Highlight layer options. This helps customizing the behaviour
-     * of the highlight layer.
-     */
-    export interface IHighlightLayerOptions {
-        /**
-         * Multiplication factor apply to the canvas size to compute the render target size
-         * used to generated the glowing objects (the smaller the faster).
-         */
-        mainTextureRatio: number;
-
-        /**
-         * Enforces a fixed size texture to ensure resize independant blur.
-         */
-        mainTextureFixedSize?: number;
-
-        /**
-         * Multiplication factor apply to the main texture size in the first step of the blur to reduce the size 
-         * of the picture to blur (the smaller the faster).
-         */
-        blurTextureSizeRatio: number;
-
-        /**
-         * How big in texel of the blur texture is the vertical blur.
-         */
-        blurVerticalSize: number;
-
-        /**
-         * How big in texel of the blur texture is the horizontal blur.
-         */
-        blurHorizontalSize: number;
-
-        /**
-         * Alpha blending mode used to apply the blur. Default is combine.
-         */
-        alphaBlendingMode: number
-
-        /**
-         * The camera attached to the layer.
-         */
-        camera: Nullable<Camera>;
-
-        /**
-         * Should we display highlight as a solid stroke?
-         */
-        isStroke?: boolean;
-    }
-
-    /**
-     * Storage interface grouping all the information required for glowing a mesh.
-     */
-    interface IHighlightLayerMesh {
-        /** 
-         * The glowy mesh
-         */
-        mesh: Mesh;
-        /**
-         * The color of the glow
-         */
-        color: Color3;
-        /**
-         * The mesh render callback use to insert stencil information
-         */
-        observerHighlight: Nullable<Observer<Mesh>>;
-        /**
-         * The mesh render callback use to come to the default behavior
-         */
-        observerDefault: Nullable<Observer<Mesh>>;
-        /**
-         * If it exists, the emissive color of the material will be used to generate the glow.
-         * Else it falls back to the current color.
-         */
-        glowEmissiveOnly: boolean;
-    }
-
-    /**
-     * Storage interface grouping all the information required for an excluded mesh.
-     */
-    interface IHighlightLayerExcludedMesh {
-        /** 
-         * The glowy mesh
-         */
-        mesh: Mesh;
-        /**
-         * The mesh render callback use to prevent stencil use
-         */
-        beforeRender: Nullable<Observer<Mesh>>;
-        /**
-         * The mesh render callback use to restore previous stencil use
-         */
-        afterRender: Nullable<Observer<Mesh>>;
-    }
-
-    /**
-     * The highlight layer Helps adding a glow effect around a mesh.
-     * 
-     * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
-     * glowy meshes to your scene.
-     * 
-     * !!! THIS REQUIRES AN ACTIVE STENCIL BUFFER ON THE CANVAS !!!
-     */
-    export class HighlightLayer {
-        /**
-         * The neutral color used during the preparation of the glow effect.
-         * This is black by default as the blend operation is a blend operation. 
-         */
-        public static neutralColor: Color4 = new Color4(0, 0, 0, 0);
-
-        /**
-         * Stencil value used for glowing meshes.
-         */
-        public static glowingMeshStencilReference = 0x02;
-
-        /**
-         * Stencil value used for the other meshes in the scene.
-         */
-        public static normalMeshStencilReference = 0x01;
-
-        private _scene: Scene;
-        private _engine: Engine;
-        private _options: IHighlightLayerOptions;
-        private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
-        private _indexBuffer: Nullable<WebGLBuffer>;
-        private _downSamplePostprocess: PassPostProcess;
-        private _horizontalBlurPostprocess: GlowBlurPostProcess;
-        private _verticalBlurPostprocess: GlowBlurPostProcess;
-        private _cachedDefines: string;
-        private _glowMapGenerationEffect: Effect;
-        private _glowMapMergeEffect: Effect;
-        private _blurTexture: RenderTargetTexture;
-        private _mainTexture: RenderTargetTexture;
-        private _mainTextureDesiredSize: ISize = { width: 0, height: 0 };
-        private _meshes: Nullable<{ [id: string]: Nullable<IHighlightLayerMesh> }> = {};
-        private _maxSize: number = 0;
-        private _shouldRender = false;
-        private _instanceGlowingMeshStencilReference = HighlightLayer.glowingMeshStencilReference++;
-        private _excludedMeshes: Nullable<{ [id: string]: Nullable<IHighlightLayerExcludedMesh> }> = {};
-
-        /**
-         * Specifies whether or not the inner glow is ACTIVE in the layer.
-         */
-        public innerGlow: boolean = true;
-
-        /**
-         * Specifies whether or not the outer glow is ACTIVE in the layer.
-         */
-        public outerGlow: boolean = true;
-
-        /**
-         * Specifies wether the highlight layer is enabled or not.
-         */
-        public isEnabled: boolean = true;
-
-        /**
-         * Specifies the horizontal size of the blur.
-         */
-        public set blurHorizontalSize(value: number) {
-            this._horizontalBlurPostprocess.kernel = value;
-        }
-
-        /**
-         * Specifies the vertical size of the blur.
-         */
-        public set blurVerticalSize(value: number) {
-            this._verticalBlurPostprocess.kernel = value;
-        }
-
-        /**
-         * Gets the horizontal size of the blur.
-         */
-        public get blurHorizontalSize(): number {
-            return this._horizontalBlurPostprocess.kernel
-        }
-
-        /**
-         * Gets the vertical size of the blur.
-         */
-        public get blurVerticalSize(): number {
-            return this._verticalBlurPostprocess.kernel;
-        }
-
-        /**
-         * Gets the camera attached to the layer.
-         */
-        public get camera(): Nullable<Camera> {
-            return this._options.camera;
-        }
-
-        /**
-         * An event triggered when the highlight layer has been disposed.
-         * @type {BABYLON.Observable}
-         */
-        public onDisposeObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer is about rendering the main texture with the glowy parts.
-         * @type {BABYLON.Observable}
-         */
-        public onBeforeRenderMainTextureObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer is being blurred.
-         * @type {BABYLON.Observable}
-         */
-        public onBeforeBlurObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer has been blurred.
-         * @type {BABYLON.Observable}
-         */
-        public onAfterBlurObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the glowing blurred texture is being merged in the scene.
-         * @type {BABYLON.Observable}
-         */
-        public onBeforeComposeObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the glowing blurred texture has been merged in the scene.
-         * @type {BABYLON.Observable}
-         */
-        public onAfterComposeObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer changes its size.
-         * @type {BABYLON.Observable}
-         */
-        public onSizeChangedObservable = new Observable<HighlightLayer>();
-
-        /**
-         * Instantiates a new highlight Layer and references it to the scene..
-         * @param name The name of the layer
-         * @param scene The scene to use the layer in
-         * @param options Sets of none mandatory options to use with the layer (see IHighlightLayerOptions for more information)
-         */
-        constructor(public name: string, scene: Scene, options?: IHighlightLayerOptions) {
-            this._scene = scene || Engine.LastCreatedScene;
-            var engine = scene.getEngine();
-            this._engine = engine;
-            this._maxSize = this._engine.getCaps().maxTextureSize;
-            this._scene.highlightLayers.push(this);
-
-            // Warn on stencil.
-            if (!this._engine.isStencilEnable) {
-                Tools.Warn("Rendering the Highlight Layer requires the stencil to be active on the canvas. var engine = new BABYLON.Engine(canvas, antialias, { stencil: true }");
-            }
-
-            // Adapt options
-            this._options = options || {
-                mainTextureRatio: 0.5,
-                blurTextureSizeRatio: 0.5,
-                blurHorizontalSize: 1.0,
-                blurVerticalSize: 1.0,
-                alphaBlendingMode: Engine.ALPHA_COMBINE,
-                camera: null
-            };
-            this._options.mainTextureRatio = this._options.mainTextureRatio || 0.5;
-            this._options.blurTextureSizeRatio = this._options.blurTextureSizeRatio || 1.0;
-            this._options.blurHorizontalSize = this._options.blurHorizontalSize || 1;
-            this._options.blurVerticalSize = this._options.blurVerticalSize || 1;
-            this._options.alphaBlendingMode = this._options.alphaBlendingMode || Engine.ALPHA_COMBINE;
-
-            // VBO
-            var vertices = [];
-            vertices.push(1, 1);
-            vertices.push(-1, 1);
-            vertices.push(-1, -1);
-            vertices.push(1, -1);
-
-            var vertexBuffer = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
-            this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
-
-            this._createIndexBuffer();
-
-            // Effect
-            this._glowMapMergeEffect = engine.createEffect("glowMapMerge",
-                [VertexBuffer.PositionKind],
-                ["offset"],
-                ["textureSampler"],
-                this._options.isStroke ? "#define STROKE \n" : undefined);
-
-            // Render target
-            this.setMainTextureSize();
-
-            // Create Textures and post processes
-            this.createTextureAndPostProcesses();
-        }
-
-        private _createIndexBuffer(): void {
-            var engine = this._scene.getEngine();
-
-            // Indices
-            var indices = [];
-            indices.push(0);
-            indices.push(1);
-            indices.push(2);
-
-            indices.push(0);
-            indices.push(2);
-            indices.push(3);
-
-            this._indexBuffer = engine.createIndexBuffer(indices);
-        }
-
-        public _rebuild(): void {
-            let vb = this._vertexBuffers[VertexBuffer.PositionKind];
-
-            if (vb) {
-                vb._rebuild();
-            }
-
-            this._createIndexBuffer();
-        }
-
-        /**
-         * Creates the render target textures and post processes used in the highlight layer.
-         */
-        private createTextureAndPostProcesses(): void {
-            var blurTextureWidth = this._mainTextureDesiredSize.width * this._options.blurTextureSizeRatio;
-            var blurTextureHeight = this._mainTextureDesiredSize.height * this._options.blurTextureSizeRatio;
-            blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
-            blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
-
-            this._mainTexture = new RenderTargetTexture("HighlightLayerMainRTT",
-                {
-                    width: this._mainTextureDesiredSize.width,
-                    height: this._mainTextureDesiredSize.height
-                },
-                this._scene,
-                false,
-                true,
-                Engine.TEXTURETYPE_UNSIGNED_INT);
-            this._mainTexture.activeCamera = this._options.camera;
-            this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
-            this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this._mainTexture.anisotropicFilteringLevel = 1;
-            this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
-            this._mainTexture.renderParticles = false;
-            this._mainTexture.renderList = null;
-            this._mainTexture.ignoreCameraViewport = true;
-
-            this._blurTexture = new RenderTargetTexture("HighlightLayerBlurRTT",
-                {
-                    width: blurTextureWidth,
-                    height: blurTextureHeight
-                },
-                this._scene,
-                false,
-                true,
-                Engine.TEXTURETYPE_UNSIGNED_INT);
-            this._blurTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
-            this._blurTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this._blurTexture.anisotropicFilteringLevel = 16;
-            this._blurTexture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
-            this._blurTexture.renderParticles = false;
-            this._blurTexture.ignoreCameraViewport = true;
-
-            this._downSamplePostprocess = new PassPostProcess("HighlightLayerPPP", this._options.blurTextureSizeRatio,
-                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-            this._downSamplePostprocess.onApplyObservable.add(effect => {
-                effect.setTexture("textureSampler", this._mainTexture);
-            });
-
-            if (this._options.alphaBlendingMode === Engine.ALPHA_COMBINE) {
-                this._horizontalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-
-                this._verticalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._verticalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-            }
-            else {
-                this._horizontalBlurPostprocess = new BlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-
-                this._verticalBlurPostprocess = new BlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._verticalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-            }
-
-            this._mainTexture.onAfterUnbindObservable.add(() => {
-                this.onBeforeBlurObservable.notifyObservers(this);
-
-                let internalTexture = this._blurTexture.getInternalTexture();
-
-                if (internalTexture) {
-                    this._scene.postProcessManager.directRender(
-                        [this._downSamplePostprocess, this._horizontalBlurPostprocess, this._verticalBlurPostprocess],
-                        internalTexture, true);
-                }
-
-                this.onAfterBlurObservable.notifyObservers(this);
-            });
-
-            // Custom render function
-            var renderSubMesh = (subMesh: SubMesh): void => {
-                if (!this._meshes) {
-                    return;
-                }
-
-                var material = subMesh.getMaterial();
-                var mesh = subMesh.getRenderingMesh();
-                var scene = this._scene;
-                var engine = scene.getEngine();
-
-                if (!material) {
-                    return;
-                }
-
-                // Do not block in blend mode.
-                if (material.needAlphaBlendingForMesh(mesh)) {
-                    return;
-                }
-
-                // Culling
-                engine.setState(material.backFaceCulling);
-
-                // Managing instances
-                var batch = mesh._getInstancesRenderList(subMesh._id);
-                if (batch.mustReturn) {
-                    return;
-                }
-
-                // Excluded Mesh
-                if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
-                    return;
-                };
-
-                var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
-
-                var highlightLayerMesh = this._meshes[mesh.uniqueId];
-                var emissiveTexture: Nullable<Texture> = null;
-                if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
-                    emissiveTexture = (<any>material).emissiveTexture;
-                }
-
-                if (this._isReady(subMesh, hardwareInstancedRendering, emissiveTexture)) {
-                    engine.enableEffect(this._glowMapGenerationEffect);
-                    mesh._bind(subMesh, this._glowMapGenerationEffect, Material.TriangleFillMode);
-
-                    this._glowMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
-                    if (highlightLayerMesh) {
-                        this._glowMapGenerationEffect.setFloat4("color",
-                            highlightLayerMesh.color.r,
-                            highlightLayerMesh.color.g,
-                            highlightLayerMesh.color.b,
-                            1.0);
-                    }
-                    else {
-                        this._glowMapGenerationEffect.setFloat4("color",
-                            HighlightLayer.neutralColor.r,
-                            HighlightLayer.neutralColor.g,
-                            HighlightLayer.neutralColor.b,
-                            HighlightLayer.neutralColor.a);
-                    }
-
-                    // Alpha test
-                    if (material && material.needAlphaTesting()) {
-                        var alphaTexture = material.getAlphaTestTexture();
-                        if (alphaTexture) {
-                            this._glowMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
-                            let textureMatrix = alphaTexture.getTextureMatrix();
-
-                            if (textureMatrix) {
-                                this._glowMapGenerationEffect.setMatrix("diffuseMatrix", textureMatrix);
-                            }
-                        }
-                    }
-
-                    // Glow emissive only
-                    if (emissiveTexture) {
-                        this._glowMapGenerationEffect.setTexture("emissiveSampler", emissiveTexture);
-                        this._glowMapGenerationEffect.setMatrix("emissiveMatrix", emissiveTexture.getTextureMatrix());
-                    }
-
-                    // Bones
-                    if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                        this._glowMapGenerationEffect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
-                    }
-
-                    // Draw
-                    mesh._processRendering(subMesh, this._glowMapGenerationEffect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
-                        (isInstance, world) => this._glowMapGenerationEffect.setMatrix("world", world));
-                } else {
-                    // Need to reset refresh rate of the shadowMap
-                    this._mainTexture.resetRefreshCounter();
-                }
-            };
-
-            this._mainTexture.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
-                this.onBeforeRenderMainTextureObservable.notifyObservers(this);
-
-                var index: number;
-
-                let engine = this._scene.getEngine();
-
-                if (depthOnlySubMeshes.length) {
-                    engine.setColorWrite(false);
-                    for (index = 0; index < depthOnlySubMeshes.length; index++) {
-                        renderSubMesh(depthOnlySubMeshes.data[index]);
-                    }
-                    engine.setColorWrite(true);
-                }
-
-                for (index = 0; index < opaqueSubMeshes.length; index++) {
-                    renderSubMesh(opaqueSubMeshes.data[index]);
-                }
-
-                for (index = 0; index < alphaTestSubMeshes.length; index++) {
-                    renderSubMesh(alphaTestSubMeshes.data[index]);
-                }
-
-                for (index = 0; index < transparentSubMeshes.length; index++) {
-                    renderSubMesh(transparentSubMeshes.data[index]);
-                }
-            };
-
-            this._mainTexture.onClearObservable.add((engine: Engine) => {
-                engine.clear(HighlightLayer.neutralColor, true, true, true);
-            });
-        }
-
-        /**
-         * Checks for the readiness of the element composing the layer.
-         * @param subMesh the mesh to check for
-         * @param useInstances specify wether or not to use instances to render the mesh
-         * @param emissiveTexture the associated emissive texture used to generate the glow
-         * @return true if ready otherwise, false
-         */
-        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
-            let material = subMesh.getMaterial();
-            let mesh = subMesh.getRenderingMesh();
-
-            if (!material || !mesh || !this._meshes) {
-                return false;
-            }
-
-            let emissiveTexture: Nullable<Texture> = null;
-            let highlightLayerMesh = this._meshes[mesh.uniqueId];
-
-            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
-                emissiveTexture = (<any>material).emissiveTexture;
-            }
-            return this._isReady(subMesh, useInstances, emissiveTexture);
-        }
-
-        /**
-         * Checks for the readiness of the element composing the layer.
-         * @param subMesh the mesh to check for
-         * @param useInstances specify wether or not to use instances to render the mesh
-         * @param emissiveTexture the associated emissive texture used to generate the glow
-         * @return true if ready otherwise, false
-         */
-        private _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<Texture>): boolean {
-            let material = subMesh.getMaterial();
-
-            if (!material) {
-                return false;
-            }
-
-            if (!material.isReady(subMesh.getMesh(), useInstances)) {
-                return false;
-            }
-
-            var defines = [];
-
-            var attribs = [VertexBuffer.PositionKind];
-
-            var mesh = subMesh.getMesh();
-            var uv1 = false;
-            var uv2 = false;
-
-            // Alpha test
-            if (material && material.needAlphaTesting()) {
-                var alphaTexture = material.getAlphaTestTexture();
-                if (alphaTexture) {
-                    defines.push("#define ALPHATEST");
-                    if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
-                        alphaTexture.coordinatesIndex === 1) {
-                        defines.push("#define DIFFUSEUV2");
-                        uv2 = true;
-                    }
-                    else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                        defines.push("#define DIFFUSEUV1");
-                        uv1 = true;
-                    }
-                }
-            }
-
-            // Emissive
-            if (emissiveTexture) {
-                defines.push("#define EMISSIVE");
-                if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
-                    emissiveTexture.coordinatesIndex === 1) {
-                    defines.push("#define EMISSIVEUV2");
-                    uv2 = true;
-                }
-                else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                    defines.push("#define EMISSIVEUV1");
-                    uv1 = true;
-                }
-            }
-
-            if (uv1) {
-                attribs.push(VertexBuffer.UVKind);
-                defines.push("#define UV1");
-            }
-            if (uv2) {
-                attribs.push(VertexBuffer.UV2Kind);
-                defines.push("#define UV2");
-            }
-
-            // Bones
-            if (mesh.useBones && mesh.computeBonesUsingShaders) {
-                attribs.push(VertexBuffer.MatricesIndicesKind);
-                attribs.push(VertexBuffer.MatricesWeightsKind);
-                if (mesh.numBoneInfluencers > 4) {
-                    attribs.push(VertexBuffer.MatricesIndicesExtraKind);
-                    attribs.push(VertexBuffer.MatricesWeightsExtraKind);
-                }
-                defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
-                defines.push("#define BonesPerMesh " + (mesh.skeleton ? (mesh.skeleton.bones.length + 1) : 0));
-            } else {
-                defines.push("#define NUM_BONE_INFLUENCERS 0");
-            }
-
-            // Instances
-            if (useInstances) {
-                defines.push("#define INSTANCES");
-                attribs.push("world0");
-                attribs.push("world1");
-                attribs.push("world2");
-                attribs.push("world3");
-            }
-
-            // Get correct effect      
-            var join = defines.join("\n");
-            if (this._cachedDefines !== join) {
-                this._cachedDefines = join;
-                this._glowMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
-                    attribs,
-                    ["world", "mBones", "viewProjection", "diffuseMatrix", "color", "emissiveMatrix"],
-                    ["diffuseSampler", "emissiveSampler"], join);
-            }
-
-            return this._glowMapGenerationEffect.isReady();
-        }
-
-        /**
-         * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene.
-         */
-        public render(): void {
-            var currentEffect = this._glowMapMergeEffect;
-
-            // Check
-            if (!currentEffect.isReady() || !this._blurTexture.isReady())
-                return;
-
-            var engine = this._scene.getEngine();
-
-            this.onBeforeComposeObservable.notifyObservers(this);
-
-            // Render
-            engine.enableEffect(currentEffect);
-            engine.setState(false);
-
-            // Cache
-            var previousStencilBuffer = engine.getStencilBuffer();
-            var previousStencilFunction = engine.getStencilFunction();
-            var previousStencilMask = engine.getStencilMask();
-            var previousStencilOperationPass = engine.getStencilOperationPass();
-            var previousStencilOperationFail = engine.getStencilOperationFail();
-            var previousStencilOperationDepthFail = engine.getStencilOperationDepthFail();
-            var previousAlphaMode = engine.getAlphaMode();
-
-            // Texture
-            currentEffect.setTexture("textureSampler", this._blurTexture);
-
-            // VBOs
-            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
-
-            // Stencil operations
-            engine.setStencilOperationPass(Engine.REPLACE);
-            engine.setStencilOperationFail(Engine.KEEP);
-            engine.setStencilOperationDepthFail(Engine.KEEP);
-
-            // Draw order
-            engine.setAlphaMode(this._options.alphaBlendingMode);
-            engine.setStencilMask(0x00);
-            engine.setStencilBuffer(true);
-            engine.setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
-
-            if (this.outerGlow) {
-                currentEffect.setFloat("offset", 0);
-                engine.setStencilFunction(Engine.NOTEQUAL);
-                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
-            }
-            if (this.innerGlow) {
-                currentEffect.setFloat("offset", 1);
-                engine.setStencilFunction(Engine.EQUAL);
-                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
-            }
-
-            // Restore Cache
-            engine.setStencilFunction(previousStencilFunction);
-            engine.setStencilMask(previousStencilMask);
-            engine.setAlphaMode(previousAlphaMode);
-            engine.setStencilBuffer(previousStencilBuffer);
-            engine.setStencilOperationPass(previousStencilOperationPass);
-            engine.setStencilOperationFail(previousStencilOperationFail);
-            engine.setStencilOperationDepthFail(previousStencilOperationDepthFail);
-
-            (<any>engine)._stencilState.reset();
-
-            this.onAfterComposeObservable.notifyObservers(this);
-
-            // Handle size changes.
-            var size = this._mainTexture.getSize();
-            this.setMainTextureSize();
-            if (size.width !== this._mainTextureDesiredSize.width || size.height !== this._mainTextureDesiredSize.height) {
-                // Recreate RTT and post processes on size change.
-                this.onSizeChangedObservable.notifyObservers(this);
-                this.disposeTextureAndPostProcesses();
-                this.createTextureAndPostProcesses();
-            }
-        }
-
-        /**
-         * Add a mesh in the exclusion list to prevent it to impact or being impacted by the highlight layer.
-         * @param mesh The mesh to exclude from the highlight layer
-         */
-        public addExcludedMesh(mesh: Mesh) {
-            if (!this._excludedMeshes) {
-                return;
-            }
-
-            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
-            if (!meshExcluded) {
-                this._excludedMeshes[mesh.uniqueId] = {
-                    mesh: mesh,
-                    beforeRender: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
-                        mesh.getEngine().setStencilBuffer(false);
-                    }),
-                    afterRender: mesh.onAfterRenderObservable.add((mesh: Mesh) => {
-                        mesh.getEngine().setStencilBuffer(true);
-                    }),
-                }
-            }
-        }
-
-        /**
-          * Remove a mesh from the exclusion list to let it impact or being impacted by the highlight layer.
-          * @param mesh The mesh to highlight
-          */
-        public removeExcludedMesh(mesh: Mesh) {
-            if (!this._excludedMeshes) {
-                return;
-            }
-
-            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
-            if (meshExcluded) {
-                if (meshExcluded.beforeRender) {
-                    mesh.onBeforeRenderObservable.remove(meshExcluded.beforeRender);
-                }
-
-                if (meshExcluded.afterRender) {
-                    mesh.onAfterRenderObservable.remove(meshExcluded.afterRender);
-                }
-            }
-
-            this._excludedMeshes[mesh.uniqueId] = null;
-        }
-
-        /**
-         * Determine if a given mesh will be highlighted by the current HighlightLayer
-         * @param mesh mesh to test
-         * @returns true if the mesh will be highlighted by the current HighlightLayer
-         */
-        public hasMesh(mesh: AbstractMesh): boolean {
-            if (!this._meshes) {
-                return false;
-            }
-
-            return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
-        }
-
-        /**
-         * Add a mesh in the highlight layer in order to make it glow with the chosen color.
-         * @param mesh The mesh to highlight
-         * @param color The color of the highlight
-         * @param glowEmissiveOnly Extract the glow from the emissive texture
-         */
-        public addMesh(mesh: Mesh, color: Color3, glowEmissiveOnly = false) {
-            if (!this._meshes) {
-                return;
-            }
-
-            var meshHighlight = this._meshes[mesh.uniqueId];
-            if (meshHighlight) {
-                meshHighlight.color = color;
-            }
-            else {
-                this._meshes[mesh.uniqueId] = {
-                    mesh: mesh,
-                    color: color,
-                    // Lambda required for capture due to Observable this context
-                    observerHighlight: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
-                        if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
-                            this.defaultStencilReference(mesh);
-                        }
-                        else {
-                            mesh.getScene().getEngine().setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
-                        }
-                    }),
-                    observerDefault: mesh.onAfterRenderObservable.add(this.defaultStencilReference),
-                    glowEmissiveOnly: glowEmissiveOnly
-                };
-            }
-
-            this._shouldRender = true;
-        }
-
-        /**
-         * Remove a mesh from the highlight layer in order to make it stop glowing.
-         * @param mesh The mesh to highlight
-         */
-        public removeMesh(mesh: Mesh) {
-            if (!this._meshes) {
-                return;
-            }
-
-            var meshHighlight = this._meshes[mesh.uniqueId];
-            if (meshHighlight) {
-
-                if (meshHighlight.observerHighlight) {
-                    mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
-                }
-
-                if (meshHighlight.observerDefault) {
-                    mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
-                }
-                delete this._meshes[mesh.uniqueId];
-            }
-
-            this._shouldRender = false;
-            for (var meshHighlightToCheck in this._meshes) {
-                if (this._meshes[meshHighlightToCheck]) {
-                    this._shouldRender = true;
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Returns true if the layer contains information to display, otherwise false.
-         */
-        public shouldRender(): boolean {
-            return this.isEnabled && this._shouldRender;
-        }
-
-        /**
-         * Sets the main texture desired size which is the closest power of two
-         * of the engine canvas size.
-         */
-        private setMainTextureSize(): void {
-            if (this._options.mainTextureFixedSize) {
-                this._mainTextureDesiredSize.width = this._options.mainTextureFixedSize;
-                this._mainTextureDesiredSize.height = this._options.mainTextureFixedSize;
-            }
-            else {
-                this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._options.mainTextureRatio;
-                this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._options.mainTextureRatio;
-
-                this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
-                this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;
-            }
-        }
-
-        /**
-         * Force the stencil to the normal expected value for none glowing parts
-         */
-        private defaultStencilReference(mesh: Mesh) {
-            mesh.getScene().getEngine().setStencilFunctionReference(HighlightLayer.normalMeshStencilReference);
-        }
-
-        /**
-         * Dispose only the render target textures and post process.
-         */
-        private disposeTextureAndPostProcesses(): void {
-            this._blurTexture.dispose();
-            this._mainTexture.dispose();
-
-            this._downSamplePostprocess.dispose();
-            this._horizontalBlurPostprocess.dispose();
-            this._verticalBlurPostprocess.dispose();
-        }
-
-        /**
-         * Dispose the highlight layer and free resources.
-         */
-        public dispose(): void {
-            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
-            if (vertexBuffer) {
-                vertexBuffer.dispose();
-                this._vertexBuffers[VertexBuffer.PositionKind] = null;
-            }
-
-            if (this._indexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._indexBuffer);
-                this._indexBuffer = null;
-            }
-
-            // Clean textures and post processes
-            this.disposeTextureAndPostProcesses();
-
-            if (this._meshes) {
-                // Clean mesh references 
-                for (let id in this._meshes) {
-                    let meshHighlight = this._meshes[id];
-                    if (meshHighlight && meshHighlight.mesh) {
-
-                        if (meshHighlight.observerHighlight) {
-                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
-                        }
-
-                        if (meshHighlight.observerDefault) {
-                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
-                        }
-                    }
-                }
-                this._meshes = null;
-            }
-
-            if (this._excludedMeshes) {
-                for (let id in this._excludedMeshes) {
-                    let meshHighlight = this._excludedMeshes[id];
-                    if (meshHighlight) {
-
-                        if (meshHighlight.beforeRender) {
-                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.beforeRender);
-                        }
-
-                        if (meshHighlight.afterRender) {
-                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.afterRender);
-                        }
-                    }
-                }
-                this._excludedMeshes = null;
-            }
-
-            // Remove from scene
-            var index = this._scene.highlightLayers.indexOf(this, 0);
-            if (index > -1) {
-                this._scene.highlightLayers.splice(index, 1);
-            }
-
-            // Callback
-            this.onDisposeObservable.notifyObservers(this);
-
-            this.onDisposeObservable.clear();
-            this.onBeforeRenderMainTextureObservable.clear();
-            this.onBeforeBlurObservable.clear();
-            this.onBeforeComposeObservable.clear();
-            this.onAfterComposeObservable.clear();
-            this.onSizeChangedObservable.clear();
-        }
-    }
-} 

+ 6 - 7
src/Mesh/babylon.mesh.ts

@@ -1740,13 +1740,12 @@
                 this.instances[0].dispose();
             }
 
-            // Highlight layers.
-            let highlightLayers = this.getScene().highlightLayers;
-            for (let i = 0; i < highlightLayers.length; i++) {
-                let highlightLayer = highlightLayers[i];
-                if (highlightLayer) {
-                    highlightLayer.removeMesh(this);
-                    highlightLayer.removeExcludedMesh(this);
+            // Effect layers.
+            let effectLayers = this.getScene().effectLayers;
+            for (let i = 0; i < effectLayers.length; i++) {
+                let effectLayer = effectLayers[i];
+                if (effectLayer) {
+                    effectLayer._disposeMesh(this);
                 }
             }
             super.dispose(doNotRecurse, disposeMaterialAndTextures);

+ 1 - 1
src/Shaders/glowMapGeneration.fragment.fx

@@ -18,7 +18,7 @@ void main(void)
 #endif
 
 #ifdef EMISSIVE
-	gl_FragColor = texture2D(emissiveSampler, vUVEmissive);
+	gl_FragColor = texture2D(emissiveSampler, vUVEmissive) * color;
 #else
 	gl_FragColor = color;
 #endif

+ 15 - 7
src/Shaders/glowMapMerge.fragment.fx

@@ -1,20 +1,28 @@
 // Samplers
 varying vec2 vUV;
 uniform sampler2D textureSampler;
+#ifdef EMISSIVE
+    uniform sampler2D textureSampler2;
+#endif
 
 // Offset
 uniform float offset;
 
 void main(void) {
-	vec4 baseColor = texture2D(textureSampler, vUV);
+    vec4 baseColor = texture2D(textureSampler, vUV);
 
-	baseColor.a = abs(offset - baseColor.a);
+    #ifdef EMISSIVE
+        baseColor += texture2D(textureSampler2, vUV);
+        baseColor *= offset;
+    #else
+        baseColor.a = abs(offset - baseColor.a);
 
-	#ifdef STROKE
-        float alpha = smoothstep(.0, .1, baseColor.a);
-        baseColor.a = alpha;
-        baseColor.rgb = baseColor.rgb * alpha;
+        #ifdef STROKE
+            float alpha = smoothstep(.0, .1, baseColor.a);
+            baseColor.a = alpha;
+            baseColor.rgb = baseColor.rgb * alpha;
+        #endif
     #endif
 
-	gl_FragColor = baseColor;
+    gl_FragColor = baseColor;
 }

+ 54 - 31
src/babylon.scene.ts

@@ -748,9 +748,15 @@
         public spritesEnabled = true;
         public spriteManagers = new Array<SpriteManager>();
 
-        // Layers
+        /**
+         * The list of layers (background and foreground) of the scene.
+         */
         public layers = new Array<Layer>();
-        public highlightLayers = new Array<HighlightLayer>();
+
+        /**
+         * The list of effect layers (highlights/glow) contained in the scene.
+         */
+        public effectLayers = new Array<EffectLayer>();
 
         // Skeletons
         private _skeletonsEnabled = true;
@@ -1911,9 +1917,9 @@
                     return false;
                 }
 
-                // Highlight layers
+                // Effect layers
                 let hardwareInstancedRendering = mesh.getClassName() === "InstancedMesh" || engine.getCaps().instancedArrays && (<Mesh>mesh).instances.length > 0;
-                for (var layer of this.highlightLayers) {
+                for (var layer of this.effectLayers) {
                     if (!layer.hasMesh(mesh)) {
                         continue;
                     }
@@ -3049,9 +3055,24 @@
          * @return The highlight layer if found otherwise null.
          */
         public getHighlightLayerByName(name: string): Nullable<HighlightLayer> {
-            for (var index = 0; index < this.highlightLayers.length; index++) {
-                if (this.highlightLayers[index].name === name) {
-                    return this.highlightLayers[index];
+            for (var index = 0; index < this.effectLayers.length; index++) {
+                if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === HighlightLayer.EffectName) {
+                    return (<any>this.effectLayers[index]) as HighlightLayer;
+                }
+            }
+
+            return null;
+        }
+
+        /**
+         * Return a the first highlight layer of the scene with a given name.
+         * @param name The name of the highlight layer to look for.
+         * @return The highlight layer if found otherwise null.
+         */
+        public getGlowLayerByName(name: string): Nullable<GlowLayer> {
+            for (var index = 0; index < this.effectLayers.length; index++) {
+                if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === GlowLayer.EffectName) {
+                    return (<any>this.effectLayers[index]) as GlowLayer;
                 }
             }
 
@@ -3410,22 +3431,24 @@
                 needsRestoreFrameBuffer = true; // Restore back buffer
             }
 
-            // Render HighlightLayer Texture
+            // Render EffecttLayer Texture
             var stencilState = this._engine.getStencilBuffer();
-            var renderhighlights = false;
-            if (this.renderTargetsEnabled && this.highlightLayers && this.highlightLayers.length > 0) {
+            var renderEffects = false;
+            var needStencil = false;
+            if (this.renderTargetsEnabled && this.effectLayers && this.effectLayers.length > 0) {
                 this._intermediateRendering = true;
-                for (let i = 0; i < this.highlightLayers.length; i++) {
-                    let highlightLayer = this.highlightLayers[i];
+                for (let i = 0; i < this.effectLayers.length; i++) {
+                    let effectLayer = this.effectLayers[i];
 
-                    if (highlightLayer.shouldRender() &&
-                        (!highlightLayer.camera ||
-                            (highlightLayer.camera.cameraRigMode === Camera.RIG_MODE_NONE && camera === highlightLayer.camera) ||
-                            (highlightLayer.camera.cameraRigMode !== Camera.RIG_MODE_NONE && highlightLayer.camera._rigCameras.indexOf(camera) > -1))) {
+                    if (effectLayer.shouldRender() &&
+                        (!effectLayer.camera ||
+                            (effectLayer.camera.cameraRigMode === Camera.RIG_MODE_NONE && camera === effectLayer.camera) ||
+                            (effectLayer.camera.cameraRigMode !== Camera.RIG_MODE_NONE && effectLayer.camera._rigCameras.indexOf(camera) > -1))) {
 
-                        renderhighlights = true;
+                        renderEffects = true;
+                        needStencil = needStencil || effectLayer.needStencil();
 
-                        let renderTarget = (<RenderTargetTexture>(<any>highlightLayer)._mainTexture);
+                        let renderTarget = (<RenderTargetTexture>(<any>effectLayer)._mainTexture);
                         if (renderTarget._shouldRender()) {
                             this._renderId++;
                             renderTarget.render(false, false);
@@ -3461,8 +3484,8 @@
                 engine.setDepthBuffer(true);
             }
 
-            // Activate HighlightLayer stencil
-            if (renderhighlights) {
+            // Activate effect Layer stencil
+            if (needStencil) {
                 this._engine.setStencilBuffer(true);
             }
 
@@ -3471,8 +3494,8 @@
             this._renderingManager.render(null, null, true, true);
             this.onAfterDrawPhaseObservable.notifyObservers(this);
 
-            // Restore HighlightLayer stencil
-            if (renderhighlights) {
+            // Restore effect Layer stencil
+            if (needStencil) {
                 this._engine.setStencilBuffer(stencilState);
             }
 
@@ -3506,12 +3529,12 @@
                 engine.setDepthBuffer(true);
             }
 
-            // Highlight Layer
-            if (renderhighlights) {
+            // Effect Layer
+            if (renderEffects) {
                 engine.setDepthBuffer(false);
-                for (let i = 0; i < this.highlightLayers.length; i++) {
-                    if (this.highlightLayers[i].shouldRender()) {
-                        this.highlightLayers[i].render();
+                for (let i = 0; i < this.effectLayers.length; i++) {
+                    if (this.effectLayers[i].shouldRender()) {
+                        this.effectLayers[i].render();
                     }
                 }
                 engine.setDepthBuffer(true);
@@ -4146,8 +4169,8 @@
             while (this.layers.length) {
                 this.layers[0].dispose();
             }
-            while (this.highlightLayers.length) {
-                this.highlightLayers[0].dispose();
+            while (this.effectLayers.length) {
+                this.effectLayers[0].dispose();
             }
 
             // Release textures
@@ -4606,8 +4629,8 @@
                 layer._rebuild();
             }
 
-            for (var highlightLayer of this.highlightLayers) {
-                highlightLayer._rebuild();
+            for (var effectLayer of this.effectLayers) {
+                effectLayer._rebuild();
             }
 
             if (this._boundingBoxRenderer) {