Browse Source

Merge pull request #4611 from sebavan/master

Scene Components Step 1
sebavan 7 years ago
parent
commit
0a015538e4

File diff suppressed because it is too large
+ 0 - 42065
Playground/babylon.d.txt


+ 5 - 0
Tools/Gulp/config.json

@@ -165,6 +165,8 @@
                 "../../src/Cameras/babylon.camera.js",
                 "../../src/Rendering/babylon.renderingManager.js",
                 "../../src/Rendering/babylon.renderingGroup.js",
+                "../../src/babylon.sceneComponent.js",
+                "../../src/babylon.abstractScene.js",
                 "../../src/babylon.scene.js",
                 "../../src/babylon.assetContainer.js",
                 "../../src/Mesh/babylon.buffer.js",
@@ -1007,6 +1009,7 @@
         "lensFlares": {
             "files": [
                 "../../src/LensFlare/babylon.lensFlare.js",
+                "../../src/LensFlare/babylon.lensFlareSystemSceneComponent.js",
                 "../../src/LensFlare/babylon.lensFlareSystem.js"
             ],
             "dependUpon": [
@@ -1153,6 +1156,7 @@
             "files": [
                 "../../src/Rendering/babylon.outlineRenderer.js",
                 "../../src/Rendering/babylon.edgesRenderer.js",
+                "../../src/Layer/babylon.effectLayerSceneComponent.js",
                 "../../src/Layer/babylon.effectLayer.js",
                 "../../src/Layer/babylon.highlightLayer.js",
                 "../../src/Layer/babylon.glowLayer.js"
@@ -1198,6 +1202,7 @@
         },
         "layer": {
             "files": [
+                "../../src/Layer/babylon.layerSceneComponent.js",
                 "../../src/Layer/babylon.layer.js"
             ],
             "dependUpon": [

File diff suppressed because it is too large
+ 13199 - 12977
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 48 - 47
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 972 - 685
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 972 - 685
dist/preview release/babylon.no-module.max.js


File diff suppressed because it is too large
+ 49 - 48
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 974 - 687
dist/preview release/es6.js


+ 63 - 2
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,68 @@
 {
-  "errors": 4318,
+  "errors": 4329,
   "babylon.typedoc.json": {
-    "errors": 4318,
+    "errors": 4329,
+    "AbstractScene": {
+      "Property": {
+        "effectLayers": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "layers": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "lensFlareSystems": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        }
+      },
+      "Method": {
+        "addEffectLayer": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "addLensFlareSystem": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "getGlowLayerByName": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "getHighlightLayerByName": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "getLensFlareSystemByID": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "getLensFlareSystemByName": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "removeEffectLayer": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        },
+        "removeLensFlareSystem": {
+          "Naming": {
+            "NotPascalCase": true
+          }
+        }
+      }
+    },
     "Animatable": {
       "Class": {
         "Comments": {

File diff suppressed because it is too large
+ 65 - 64
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 972 - 685
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -23,6 +23,7 @@
   - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
   - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
   - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
+- Added SceneComponent to help decoupling Scene from its components ([sebavan](http://www.github.com/sebavan))
 
 ## Updates
 

+ 3 - 3
src/Actions/babylon.actionManager.ts

@@ -257,7 +257,7 @@
         constructor(scene: Scene) {
             this._scene = scene || Engine.LastCreatedScene;
 
-            scene._actionManagers.push(this);
+            scene.actionManagers.push(this);
         }
 
         // Methods
@@ -266,7 +266,7 @@
          * Releases all associated resources
          */
         public dispose(): void {
-            var index = this._scene._actionManagers.indexOf(this);
+            var index = this._scene.actionManagers.indexOf(this);
 
             for (var i = 0; i < this.actions.length; i++) {
                 var action = this.actions[i];
@@ -277,7 +277,7 @@
             }
 
             if (index > -1) {
-                this._scene._actionManagers.splice(index, 1);
+                this._scene.actionManagers.splice(index, 1);
             }
         }
 

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -1037,7 +1037,7 @@
         private _uintIndicesCurrentlySet = false;
         private _currentBoundBuffer = new Array<Nullable<WebGLBuffer>>();
         /** @hidden */
-        protected _currentFramebuffer: Nullable<WebGLFramebuffer>;
+        protected _currentFramebuffer: Nullable<WebGLFramebuffer> = null;
         private _currentBufferPointers = new Array<BufferPointer>();
         private _currentInstanceLocations = new Array<number>();
         private _currentInstanceBuffers = new Array<WebGLBuffer>();

+ 8 - 2
src/Layer/babylon.effectLayer.ts

@@ -116,7 +116,13 @@
             this.name = name;
 
             this._scene = scene || Engine.LastCreatedScene;
-            this._engine = scene.getEngine();
+            let component = this._scene._getComponent(SceneComponentConstants.NAME_EFFECTLAYER) as EffectLayerSceneComponent;
+            if (!component) {
+                component = new EffectLayerSceneComponent(this._scene);
+                this._scene._addComponent(component);
+            }
+
+            this._engine = this._scene.getEngine();
             this._maxSize = this._engine.getCaps().maxTextureSize;
             this._scene.effectLayers.push(this);
 
@@ -595,7 +601,7 @@
                 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
+                // Need to reset refresh rate of the main map
                 this._mainTexture.resetRefreshCounter();
             }
         }

+ 220 - 0
src/Layer/babylon.effectLayerSceneComponent.ts

@@ -0,0 +1,220 @@
+module BABYLON {
+    // Adds the parser to the scene parsers.
+    AbstractScene.AddParser(SceneComponentConstants.NAME_EFFECTLAYER, (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => {
+        if (parsedData.effectLayers) {
+            for (let index = 0; index < parsedData.effectLayers.length; index++) {
+                var effectLayer = EffectLayer.Parse(parsedData.effectLayers[index], scene, rootUrl);
+                container.effectLayers.push(effectLayer);
+            }
+        }
+    });
+
+    export interface AbstractScene {
+        /**
+         * The list of effect layers (highlights/glow) added to the scene
+         * @see http://doc.babylonjs.com/how_to/highlight_layer
+         * @see http://doc.babylonjs.com/how_to/glow_layer
+         */
+        effectLayers: Array<EffectLayer>;
+
+        /**
+         * Removes the given effect layer from this scene.
+         * @param toRemove defines the effect layer to remove
+         * @returns the index of the removed effect layer
+         */
+        removeEffectLayer(toRemove: EffectLayer): number;
+
+        /**
+         * Adds the given effect layer to this scene
+         * @param newEffectLayer defines the effect layer to add
+         */     
+        addEffectLayer(newEffectLayer: EffectLayer): void;
+    }
+
+    AbstractScene.prototype.removeEffectLayer = function(toRemove: EffectLayer): number {
+        var index = this.effectLayers.indexOf(toRemove);
+        if (index !== -1) {
+            this.effectLayers.splice(index, 1);
+        }
+
+        return index;
+    }
+
+    AbstractScene.prototype.addEffectLayer = function(newEffectLayer: EffectLayer): void {
+        this.effectLayers.push(newEffectLayer);
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage any effect layers
+     * in a given scene.
+     */
+    export class EffectLayerSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_EFFECTLAYER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene
+
+        private _engine: Engine;
+        private _effectLayers: Array<EffectLayer>;
+
+        private _renderEffects = false;
+        private _needStencil = false;
+        private _previousStencilState = false;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this._effectLayers = scene.effectLayers = new Array<EffectLayer>();
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._isReadyForMeshStage.registerStep(SceneComponentConstants.STEP_ISREADYFORMESH_EFFECTLAYER, this, this._isReadyForMesh);
+
+            this.scene._cameraDrawRenderTargetStage.registerStep(SceneComponentConstants.STEP_CAMERADRAWRENDERTARGET_EFFECTLAYER, this, this._renderMainTexture);
+
+            this.scene._beforeCameraDrawStage.registerStep(SceneComponentConstants.STEP_BEFORECAMERADRAW_EFFECTLAYER, this, this._setStencil);
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER, this, this._setStencilBack);
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW, this, this._draw);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            for (let effectLayer of this._effectLayers) {
+                effectLayer._rebuild();
+            }
+        }
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        public serialize(serializationObject: any): void {
+            // Effect layers
+            serializationObject.effectLayers = [];
+
+            for (let effectLayer of this._effectLayers) {
+                if (effectLayer.serialize) {
+                    serializationObject.effectLayers.push(effectLayer.serialize());
+                }
+            }
+        }
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        public addFromContainer(container: AbstractScene): void {
+            if (!container.effectLayers) {
+                return;
+            }
+            container.effectLayers.forEach((o) => {
+                this.scene.addEffectLayer(o);
+            });
+        }
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        public removeFromContainer(container: AbstractScene): void {
+            if (!container.effectLayers) {
+                return;
+            }
+            container.effectLayers.forEach((o) => {
+                this.scene.removeEffectLayer(o);
+            });
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            while (this._effectLayers.length) {
+                this._effectLayers[0].dispose();
+            }
+        }
+
+        private _isReadyForMesh(mesh: AbstractMesh, hardwareInstancedRendering: boolean): boolean {
+            for (let layer of this._effectLayers) {
+                if (!layer.hasMesh(mesh)) {
+                    continue;
+                }
+
+                for (var subMesh of mesh.subMeshes) {
+                    if (!layer.isReady(subMesh, hardwareInstancedRendering)) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        private _renderMainTexture(camera: Camera): void {
+            this._renderEffects = false;
+            this._needStencil = false;
+
+            if (this._effectLayers && this._effectLayers.length > 0) {
+                this._previousStencilState = this._engine.getStencilBuffer();
+                for (let effectLayer of this._effectLayers) {
+                    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))) {
+
+                        this._renderEffects = true;
+                        this._needStencil = this._needStencil || effectLayer.needStencil();
+
+                        let renderTarget = (<RenderTargetTexture>(<any>effectLayer)._mainTexture);
+                        if (renderTarget._shouldRender()) {
+                            this.scene.incrementRenderId();
+                            renderTarget.render(false, false);
+                        }
+                    }
+                }
+
+                this.scene.incrementRenderId();
+            }
+        }
+
+        private _setStencil(camera: Camera) {
+            // Activate effect Layer stencil
+            if (this._needStencil) {
+                this._engine.setStencilBuffer(true);
+            }
+        }
+
+        private _setStencilBack(camera: Camera) {
+            // Restore effect Layer stencil
+            if (this._needStencil) {
+                this._engine.setStencilBuffer(this._previousStencilState);
+            }
+        }
+
+        private _draw(camera: Camera): void {
+            if (this._renderEffects) {
+                this._engine.setDepthBuffer(false);
+                for (let i = 0; i < this._effectLayers.length; i++) {
+                    if (this._effectLayers[i].shouldRender()) {
+                        this._effectLayers[i].render();
+                    }
+                }
+                this._engine.setDepthBuffer(true);
+            }
+        }
+    }
+} 

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

@@ -1,4 +1,24 @@
 module BABYLON {
+
+    export interface AbstractScene {
+        /**
+         * 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.
+         */
+        getGlowLayerByName(name: string): Nullable<GlowLayer>;
+    }
+
+    AbstractScene.prototype.getGlowLayerByName = function(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;
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Glow layer options. This helps customizing the behaviour
      * of the glow layer.

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

@@ -1,4 +1,23 @@
 module BABYLON {
+    export interface AbstractScene {
+        /**
+         * 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.
+         */
+        getHighlightLayerByName(name: string): Nullable<HighlightLayer>;
+    }
+
+    AbstractScene.prototype.getHighlightLayerByName = function(name: string): Nullable<HighlightLayer> {
+        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;
+    }
+
     /**
      * Special Glow Blur post process only blurring the alpha channel
      * It enforces keeping the most luminous color in the color channel.
@@ -587,6 +606,10 @@
                     observerDefault: mesh.onAfterRenderObservable.add(this._defaultStencilReference),
                     glowEmissiveOnly: glowEmissiveOnly
                 };
+
+                mesh.onDisposeObservable.add(() => {
+                    this._disposeMesh(mesh);
+                });
             }
 
             this._shouldRender = true;

+ 5 - 0
src/Layer/babylon.layer.ts

@@ -63,6 +63,11 @@
             this.color = color === undefined ? new Color4(1, 1, 1, 1) : color;
             
             this._scene = <Scene>(scene || Engine.LastCreatedScene);
+            let layerComponent = this._scene._getComponent(SceneComponentConstants.NAME_LAYER) as LayerSceneComponent;
+            if (!layerComponent) {
+                layerComponent = new LayerSceneComponent(this._scene);
+                this._scene._addComponent(layerComponent);
+            }
             this._scene.layers.push(this);
 
             var engine = this._scene.getEngine();

+ 126 - 0
src/Layer/babylon.layerSceneComponent.ts

@@ -0,0 +1,126 @@
+module BABYLON {
+    export interface AbstractScene {
+        /**
+         * The list of layers (background and foreground) of the scene
+         */
+        layers: Array<Layer>;
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage any layers
+     * in a given scene.
+     */
+    export class LayerSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_LAYER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        private _engine: Engine;
+        private _layers: Array<Layer>;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this._layers = scene.layers = new Array<Layer>();
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._beforeCameraDrawStage.registerStep(SceneComponentConstants.STEP_BEFORECAMERADRAW_LAYER, this, this._drawBackground);
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_LAYER, this, this._drawForeground);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            for (let layer of this._layers) {
+                layer._rebuild();
+            }
+        }
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        public serialize(serializationObject: any): void {
+            // TODO. Implement layer serialization
+            // serializationObject.layers = [];
+
+            // for (let layer of this._layers) {
+            //     if (layer.serialize) {
+            //         serializationObject.layers.push(layer.serialize());
+            //     }
+            // }
+        }
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        public addFromContainer(container: AbstractScene): void {
+            if (!container.layers) {
+                return;
+            }
+            // container.layers.forEach((o) => {
+            //     this.scene.addLayer(o);
+            // });
+        }
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        public removeFromContainer(container: AbstractScene): void {
+            if (!container.layers) {
+                return;
+            }
+            // container.layers.forEach((o) => {
+            //     this.scene.removeLayer(o);
+            // });
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            while (this._layers.length) {
+                this._layers[0].dispose();
+            }
+        }
+
+        private _draw(camera: Camera, isBackground: boolean): void {
+            if (this._layers.length) {
+                this._engine.setDepthBuffer(false);
+                const cameraLayerMask = camera.layerMask;
+                for (let layer of this._layers) {
+                    if (layer.isBackground === isBackground && ((layer.layerMask & cameraLayerMask) !== 0)) {
+                        layer.render();
+                    }
+                }
+                this._engine.setDepthBuffer(true);
+            }
+        }
+
+        private _drawBackground(camera: Camera): void {
+            this._draw(camera, true);
+        }
+
+        private _drawForeground(camera: Camera): void {
+            this._draw(camera, false);
+        }
+    }
+} 

+ 7 - 1
src/LensFlare/babylon.lensFlareSystem.ts

@@ -19,6 +19,12 @@
         constructor(public name: string, emitter: any, scene: Scene) {
 
             this._scene = scene || Engine.LastCreatedScene;
+            let component = this._scene._getComponent(SceneComponentConstants.NAME_LENSFLARESYSTEM) as LensFlareSystemSceneComponent;
+            if (!component) {
+                component = new LensFlareSystemSceneComponent(this._scene);
+                scene._addComponent(component);
+            }
+
             this._emitter = emitter;
             this.id = name;
             scene.lensFlareSystems.push(this);
@@ -305,4 +311,4 @@
             return serializationObject;
         }
     }
-} 
+}

+ 182 - 0
src/LensFlare/babylon.lensFlareSystemSceneComponent.ts

@@ -0,0 +1,182 @@
+module BABYLON {
+    // Adds the parser to the scene parsers.
+    AbstractScene.AddParser(SceneComponentConstants.NAME_LENSFLARESYSTEM, (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => {
+        // Lens flares
+        if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
+            for (let index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
+                var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
+                var lf = LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
+                container.lensFlareSystems.push(lf);
+            }
+        }
+    });
+
+    export interface AbstractScene {
+        /**
+         * The list of lens flare system added to the scene
+         * @see http://doc.babylonjs.com/how_to/how_to_use_lens_flares
+         */         
+        lensFlareSystems: Array<LensFlareSystem>;
+        /**
+         * Removes the given lens flare system from this scene.
+         * @param toRemove The lens flare system to remove
+         * @returns The index of the removed lens flare system
+         */          
+        removeLensFlareSystem(toRemove: LensFlareSystem): number;
+
+        /**
+         * Adds the given lens flare system to this scene
+         * @param newLensFlareSystem The lens flare system to add
+         */          
+        addLensFlareSystem(newLensFlareSystem: LensFlareSystem): void;
+
+        /**
+         * Gets a lens flare system using its name
+         * @param name defines the name to look for
+         * @returns the lens flare system or null if not found
+         */
+        getLensFlareSystemByName(name: string): Nullable<LensFlareSystem>;
+
+        /**
+         * Gets a lens flare system using its id
+         * @param id defines the id to look for
+         * @returns the lens flare system or null if not found
+         */
+        getLensFlareSystemByID(id: string): Nullable<LensFlareSystem>;
+    }
+
+    AbstractScene.prototype.getLensFlareSystemByName = function(name: string): Nullable<LensFlareSystem> {
+        for (var index = 0; index < this.lensFlareSystems.length; index++) {
+            if (this.lensFlareSystems[index].name === name) {
+                return this.lensFlareSystems[index];
+            }
+        }
+
+        return null;
+    }
+
+    AbstractScene.prototype.getLensFlareSystemByID = function(id: string): Nullable<LensFlareSystem> {
+        for (var index = 0; index < this.lensFlareSystems.length; index++) {
+            if (this.lensFlareSystems[index].id === id) {
+                return this.lensFlareSystems[index];
+            }
+        }
+
+        return null;
+    }
+
+    AbstractScene.prototype.removeLensFlareSystem = function(toRemove: LensFlareSystem): number {
+        var index = this.lensFlareSystems.indexOf(toRemove);
+        if (index !== -1) {
+            this.lensFlareSystems.splice(index, 1);
+        }
+        return index;
+    }
+
+    AbstractScene.prototype.addLensFlareSystem = function(newLensFlareSystem: LensFlareSystem): void {
+        this.lensFlareSystems.push(newLensFlareSystem);
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage any layers
+     * in a given scene.
+     */
+    export class LensFlareSystemSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_LENSFLARESYSTEM;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        private _lensFlareSystems: Array<LensFlareSystem>;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+
+            this._lensFlareSystems = scene.lensFlareSystems = new Array<LensFlareSystem>();
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_LENSFLARESYSTEM, this, this._draw);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            // Nothing to do for lens flare
+        }
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        public addFromContainer(container: AbstractScene): void {
+            if (!container.lensFlareSystems) {
+                return;
+            }
+            container.lensFlareSystems.forEach((o) => {
+                this.scene.addLensFlareSystem(o);
+            });
+        }
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        public removeFromContainer(container: AbstractScene): void {
+            if (!container.lensFlareSystems) {
+                return;
+            }
+            container.lensFlareSystems.forEach((o) => {
+                this.scene.removeLensFlareSystem(o);
+            });
+        }
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        public serialize(serializationObject: any): void {
+            // Lens flares
+            serializationObject.lensFlareSystems = [];
+            for (let lensFlareSystem of this._lensFlareSystems) {
+                serializationObject.lensFlareSystems.push(lensFlareSystem.serialize());
+            }
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            while (this._lensFlareSystems.length) {
+                this._lensFlareSystems[0].dispose();
+            }
+        }
+
+        private _draw(camera: Camera): void {
+            // Lens flares
+            if (this.scene.lensFlaresEnabled) {
+                Tools.StartPerformanceCounter("Lens flares", this._lensFlareSystems.length > 0);
+                for (let lensFlareSystem of this._lensFlareSystems) {
+                    if ((camera.layerMask & lensFlareSystem.layerMask) !== 0) {
+                        lensFlareSystem.render();
+                    }
+                }
+                Tools.EndPerformanceCounter("Lens flares", this._lensFlareSystems.length > 0);
+            }
+        }
+    }
+} 

+ 3 - 19
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -1,5 +1,4 @@
 module BABYLON {
-
     var parseMaterialById = (id: string, parsedData: any, scene: Scene, rootUrl: string) => {
         for (var index = 0, cache = parsedData.materials.length; index < cache; index++) {
             var parsedMaterial = parsedData.materials[index];
@@ -309,21 +308,12 @@
                 }
             }
 
-            // Lens flares
-            if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
-                for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
-                    var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
-                    var lf = LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
-                    container.lensFlareSystems.push(lf);
-                }
-            }
-
             // Shadows
             if (parsedData.shadowGenerators !== undefined && parsedData.shadowGenerators !== null) {
                 for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
                     var parsedShadowGenerator = parsedData.shadowGenerators[index];
-                    var sg = ShadowGenerator.Parse(parsedShadowGenerator, scene);
-                    container.shadowGenerators.push(sg);
+                    ShadowGenerator.Parse(parsedShadowGenerator, scene);
+                    // SG would be available on their associated lights
                 }
             }
 
@@ -357,13 +347,7 @@
                 }
             }
 
-            // Effect layers
-            if (parsedData.effectLayers) {
-                for (index = 0; index < parsedData.effectLayers.length; index++) {
-                    var effectLayer = EffectLayer.Parse(parsedData.effectLayers[index], scene, rootUrl);
-                    container.effectLayers.push(effectLayer);
-                }
-            }
+            AbstractScene.Parse(parsedData, scene, container, rootUrl);
 
             // Actions (scene)
             if (parsedData.actions !== undefined && parsedData.actions !== null) {

+ 0 - 8
src/Mesh/babylon.mesh.ts

@@ -1766,14 +1766,6 @@
                 this.instances[0].dispose();
             }
 
-            // 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);
         }
 

+ 3 - 14
src/Tools/babylon.sceneSerializer.ts

@@ -272,12 +272,6 @@
                 serializationObject.particleSystems.push(scene.particleSystems[index].serialize());
             }
 
-            // Lens flares
-            serializationObject.lensFlareSystems = [];
-            for (index = 0; index < scene.lensFlareSystems.length; index++) {
-                serializationObject.lensFlareSystems.push(scene.lensFlareSystems[index].serialize());
-            }
-
             // Shadows
             serializationObject.shadowGenerators = [];
             for (index = 0; index < scene.lights.length; index++) {
@@ -305,14 +299,9 @@
                 }
             }
 
-            // Effect layers
-            serializationObject.effectLayers = [];
-
-            for (index = 0; index < scene.effectLayers.length; index++) {
-                var layer = scene.effectLayers[index];
-                if (layer.serialize) {
-                    serializationObject.effectLayers.push(layer.serialize());
-                }
+            // Components
+            for (let component of scene._components) {
+                component.serialize(serializationObject);
             }
 
             return serializationObject;

+ 3 - 3
src/Tools/babylon.tools.ts

@@ -1050,9 +1050,9 @@
          */
         static ToBlob(canvas: HTMLCanvasElement, successCallback: (blob: Nullable<Blob>) => void, mimeType: string = "image/png"): void {
             // We need HTMLCanvasElement.toBlob for HD screenshots
-            if (!screenshotCanvas.toBlob) {
+            if (!canvas.toBlob) {
                 //  low performance polyfill based on toDataURL (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob)
-                screenshotCanvas.toBlob = function (callback, type, quality) {
+                canvas.toBlob = function (callback, type, quality) {
                     setTimeout(() => {
                         var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
                             len = binStr.length,
@@ -1065,7 +1065,7 @@
                     });
                 }
             }
-            screenshotCanvas.toBlob(function (blob) {
+            canvas.toBlob(function (blob) {
                 successCallback(blob);
             }, mimeType);
         }

+ 125 - 0
src/babylon.abstractScene.ts

@@ -0,0 +1,125 @@
+module BABYLON {
+    /**
+     * Defines how the parser contract is defined.
+     */
+    export type BabylonFileParser = (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => void;
+
+    /**
+     * Base class of the scene acting as a container for the different elements composing a scene.
+     * This class is dynamically extended by the different components of the scene increasing 
+     * flexibility and reducing coupling.
+     */
+    export abstract class AbstractScene {
+        /**
+         * Stores the list of available parsers in the application.
+         */
+        private static _BabylonFileParsers: { [key: string]: BabylonFileParser } = { };
+
+        /**
+         * Adds a parser in the list of availables ones.
+         * @param name Defines the name of the parser
+         * @param parser Defines the parser to add
+         */
+        public static AddParser(name: string, parser: BabylonFileParser): void {
+            this._BabylonFileParsers[name] = parser;
+        }
+
+        /**
+         * Parser json data and populate both a scene and its associated container object
+         * @param jsonData Defines the data to parse
+         * @param scene Defines the scene to parse the data for
+         * @param container Defines the container attached to the parsing sequence
+         * @param rootUrl Defines the root url of the data
+         */
+        public static Parse(jsonData: any, scene: Scene, container: AssetContainer, rootUrl: string): void {
+            for (let parserName in this._BabylonFileParsers) {
+                if (this._BabylonFileParsers.hasOwnProperty(parserName)) {
+                    this._BabylonFileParsers[parserName](jsonData, scene, container, rootUrl);
+                }
+            }
+        }
+
+        /** All of the cameras added to this scene
+         * @see http://doc.babylonjs.com/babylon101/cameras
+         */
+        public cameras = new Array<Camera>();
+
+        /**
+        * All of the lights added to this scene
+        * @see http://doc.babylonjs.com/babylon101/lights
+        */
+        public lights = new Array<Light>();
+
+        /**
+        * All of the (abstract) meshes added to this scene
+        */
+        public meshes = new Array<AbstractMesh>();
+
+        /**
+         * The list of skeletons added to the scene
+         * @see http://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons
+         */
+        public skeletons = new Array<Skeleton>();
+
+        /**
+        * All of the particle systems added to this scene
+        * @see http://doc.babylonjs.com/babylon101/particles
+        */
+        public particleSystems = new Array<IParticleSystem>();
+
+        /**
+         * Gets a list of Animations associated with the scene
+         */        
+        public animations: Animation[] = [];
+
+        /**
+        * All of the animation groups added to this scene
+        * @see http://doc.babylonjs.com/how_to/group
+        */
+        public animationGroups = new Array<AnimationGroup>();
+
+        /**
+        * All of the multi-materials added to this scene
+        * @see http://doc.babylonjs.com/how_to/multi_materials
+        */
+        public multiMaterials = new Array<MultiMaterial>();
+
+        /**
+        * All of the materials added to this scene
+        * @see http://doc.babylonjs.com/babylon101/materials
+        */
+        public materials = new Array<Material>();
+
+        /**
+         * The list of morph target managers added to the scene
+         * @see http://doc.babylonjs.com/how_to/how_to_dynamically_morph_a_mesh
+         */
+        public morphTargetManagers = new Array<MorphTargetManager>();
+        
+        /**
+         * The list of geometries used in the scene.
+         */
+        public geometries = new Array<Geometry>();
+
+        /**
+        * All of the tranform nodes added to this scene
+        * @see http://doc.babylonjs.com/how_to/transformnode
+        */
+        public transformNodes = new Array<TransformNode>();
+
+        /**
+         * ActionManagers available on the scene.
+         */
+        public actionManagers = new Array<ActionManager>();
+
+        /**
+         * Sounds to keep.
+         */
+        public sounds = new Array<Sound>();
+
+        /**
+         * Textures to keep.
+         */
+        public textures = new Array<BaseTexture>();
+    }
+}

+ 27 - 182
src/babylon.assetContainer.ts

@@ -2,169 +2,23 @@ module BABYLON {
     /**
      * Set of assets to keep when moving a scene into an asset container.
      */
-    export class KeepAssets {
-        /**
-         * Cameras to keep.
-         */
-        public cameras = new Array<Camera>();
-        /**
-         * Lights to keep.
-         */
-        public lights = new Array<Light>();
-        /**
-         * Meshes to keep.
-         */
-        public meshes = new Array<AbstractMesh>();
-        /**
-         * Skeletons to keep.
-         */
-        public skeletons = new Array<Skeleton>();
-        /**
-         * ParticleSystems to keep.
-         */
-        public particleSystems = new Array<IParticleSystem>();
-        /**
-         * Animations to keep.
-         */
-        public animations = new Array<Animation>();
-        /**
-         * AnimationGroups to keep.
-         */
-        public animationGroups = new Array<AnimationGroup>();
-        /**
-         * MultiMaterials to keep.
-         */
-        public multiMaterials = new Array<MultiMaterial>();
-        /**
-         * Materials to keep.
-         */
-        public materials = new Array<Material>();
-        /**
-         * MorphTargetManagers to keep.
-         */
-        public morphTargetManagers = new Array<MorphTargetManager>();
-        /**
-         * Geometries to keep.
-         */
-        public geometries = new Array<Geometry>();
-        /**
-         * TransformNodes to keep.
-         */
-        public transformNodes = new Array<TransformNode>();
-        /**
-         * LensFlareSystems to keep.
-         */
-        public lensFlareSystems = new Array<LensFlareSystem>();
-        /**
-         * ShadowGenerators to keep.
-         */
-        public shadowGenerators = new Array<ShadowGenerator>();
-        /**
-         * ActionManagers to keep.
-         */
-        public actionManagers = new Array<ActionManager>();
-        /**
-         * Sounds to keep.
-         */
-        public sounds = new Array<Sound>();
-        /**
-         * Textures to keep.
-         */
-        public textures = new Array<Texture>();
-        /**
-         * Effect layers to keep.
-         */
-        public effectLayers = new Array<EffectLayer>();
-    }
+    export class KeepAssets extends AbstractScene { }
 
     /**
      * Container with a set of assets that can be added or removed from a scene.
      */
-    export class AssetContainer {
+    export class AssetContainer extends AbstractScene {
         /**
          * The scene the AssetContainer belongs to.
          */
         public scene: Scene;
 
-        // Objects
-        /**
-         * Cameras populated in the container.
-         */
-        public cameras = new Array<Camera>();
-        /**
-         * Lights populated in the container.
-         */
-        public lights = new Array<Light>();
-        /**
-         * Meshes populated in the container.
-         */
-        public meshes = new Array<AbstractMesh>();
-        /**
-         * Skeletons populated in the container.
-         */
-        public skeletons = new Array<Skeleton>();
-        /**
-         * ParticleSystems populated in the container.
-         */
-        public particleSystems = new Array<IParticleSystem>();
-        /**
-         * Animations populated in the container.
-         */
-        public animations = new Array<Animation>();
-        /**
-         * AnimationGroups populated in the container.
-         */
-        public animationGroups = new Array<AnimationGroup>();
-        /**
-         * MultiMaterials populated in the container.
-         */
-        public multiMaterials = new Array<MultiMaterial>();
-        /**
-         * Materials populated in the container.
-         */
-        public materials = new Array<Material>();
-        /**
-         * MorphTargetManagers populated in the container.
-         */
-        public morphTargetManagers = new Array<MorphTargetManager>();
-        /**
-         * Geometries populated in the container.
-         */
-        public geometries = new Array<Geometry>();
-        /**
-         * TransformNodes populated in the container.
-         */
-        public transformNodes = new Array<TransformNode>();
-        /**
-         * LensFlareSystems populated in the container.
-         */
-        public lensFlareSystems = new Array<LensFlareSystem>();
-        /**
-         * ShadowGenerators populated in the container.
-         */
-        public shadowGenerators = new Array<ShadowGenerator>();
-        /**
-         * ActionManagers populated in the container.
-         */
-        public actionManagers = new Array<ActionManager>();
-        /**
-         * Sounds populated in the container.
-         */
-        public sounds = new Array<Sound>();
-        /**
-         * Textures populated in the container.
-         */
-        public textures = new Array<Texture>();
-        /**
-         * Effect layers populated in the container.
-         */
-        public effectLayers = new Array<EffectLayer>();
-
         /**
          * Instantiates an AssetContainer.
          * @param scene The scene the AssetContainer belongs to.
          */
         constructor(scene: Scene) {
+            super();
             this.scene = scene;
         }
 
@@ -208,9 +62,6 @@ module BABYLON {
             this.transformNodes.forEach((o) => {
                 this.scene.addTransformNode(o);
             });
-            this.lensFlareSystems.forEach((o) => {
-                this.scene.addLensFlareSystem(o);
-            });
             this.actionManagers.forEach((o) => {
                 this.scene.addActionManager(o);
             });
@@ -222,9 +73,10 @@ module BABYLON {
             this.textures.forEach((o) => {
                 this.scene.addTexture(o);
             });
-            this.effectLayers.forEach((o) => {
-                this.scene.addEffectLayer(o);
-            });
+
+            for (let component of this.scene._components) {
+                component.addFromContainer(this.scene);
+            }
         }
 
         /**
@@ -267,9 +119,6 @@ module BABYLON {
             this.transformNodes.forEach((o) => {
                 this.scene.removeTransformNode(o);
             });
-            this.lensFlareSystems.forEach((o) => {
-                this.scene.removeLensFlareSystem(o);
-            });
             this.actionManagers.forEach((o) => {
                 this.scene.removeActionManager(o);
             });
@@ -281,18 +130,25 @@ module BABYLON {
             this.textures.forEach((o) => {
                 this.scene.removeTexture(o);
             });
-            this.effectLayers.forEach((o) => {
-                this.scene.removeEffectLayer(o);
-            });
+
+            for (let component of this.scene._components) {
+                component.removeFromContainer(this.scene);
+            }
         }
 
         private _moveAssets<T>(sourceAssets: T[], targetAssets: T[], keepAssets: T[]): void {
+            if (!sourceAssets) {
+                return;
+            }
+
             for (let asset of sourceAssets) {
                 let move = true;
-                for (let keepAsset of keepAssets) {
-                    if (asset === keepAsset) {
-                        move = false;
-                        break;
+                if (keepAssets) {
+                    for (let keepAsset of keepAssets) {
+                        if (asset === keepAsset) {
+                            move = false;
+                            break;
+                        }
                     }
                 }
 
@@ -312,23 +168,12 @@ module BABYLON {
                 keepAssets = new KeepAssets();
             }
 
-            this._moveAssets(this.scene.cameras, this.cameras, keepAssets.cameras);
-            this._moveAssets(this.scene.meshes, this.meshes, keepAssets.meshes);
-            this._moveAssets(this.scene.getGeometries(), this.geometries, keepAssets.geometries);
-            this._moveAssets(this.scene.materials, this.materials, keepAssets.materials);
-            this._moveAssets(this.scene._actionManagers, this.actionManagers, keepAssets.actionManagers);
-            this._moveAssets(this.scene.animations, this.animations, keepAssets.animations);
-            this._moveAssets(this.scene.animationGroups, this.animationGroups, keepAssets.animationGroups);
-            this._moveAssets(this.scene.lensFlareSystems, this.lensFlareSystems, keepAssets.lensFlareSystems);
-            this._moveAssets(this.scene.lights, this.lights, keepAssets.lights);
-            this._moveAssets(this.scene.morphTargetManagers, this.morphTargetManagers, keepAssets.morphTargetManagers);
-            this._moveAssets(this.scene.multiMaterials, this.multiMaterials, keepAssets.multiMaterials);
-            this._moveAssets(this.scene.skeletons, this.skeletons, keepAssets.skeletons);
-            this._moveAssets(this.scene.particleSystems, this.particleSystems, keepAssets.particleSystems);
-            this._moveAssets(this.scene.mainSoundTrack.soundCollection, this.sounds, keepAssets.sounds);
-            this._moveAssets(this.scene.transformNodes, this.transformNodes, keepAssets.transformNodes);
-            this._moveAssets(this.scene.textures, this.textures, keepAssets.textures);
-            this._moveAssets(this.scene.effectLayers, this.effectLayers, keepAssets.effectLayers);
+            for (let key in this) {
+                if (this.hasOwnProperty(key)) {
+                    (<any>this)[key] = (<any>this)[key] || [];
+                    this._moveAssets((<any>this.scene)[key], (<any>this)[key], (<any>keepAssets)[key]);
+                }
+            }
 
             this.removeAllFromScene();
         }

+ 122 - 331
src/babylon.scene.ts

@@ -112,7 +112,7 @@
      * Represents a scene to be rendered by the engine.
      * @see http://doc.babylonjs.com/features/scene
      */
-    export class Scene implements IAnimatable {
+    export class Scene extends AbstractScene implements IAnimatable {
         // Statics
         private static _FOGMODE_NONE = 0;
         private static _FOGMODE_EXP = 1;
@@ -544,10 +544,6 @@
         public onRenderingGroupObservable = new Observable<RenderingGroupInfo>();
 
         // Animations
-        /**
-         * Gets a list of Animations associated with the scene
-         */        
-        public animations: Animation[] = [];
         private _registeredForLateAnimationBindings = new SmartArrayNoDuplicate<any>(256);
 
         // Pointers
@@ -800,53 +796,11 @@
             return this._lightsEnabled;
         }
 
-        /**
-        * All of the lights added to this scene
-        * @see http://doc.babylonjs.com/babylon101/lights
-        */
-        public lights = new Array<Light>();
-
-        // Cameras
-        /** All of the cameras added to this scene. 
-         * @see http://doc.babylonjs.com/babylon101/cameras
-         */
-        public cameras = new Array<Camera>();
         /** All of the active cameras added to this scene. */
         public activeCameras = new Array<Camera>();
         /** The current active camera */
         public activeCamera: Nullable<Camera>;
 
-        // Meshes
-        /**
-        * All of the tranform nodes added to this scene
-        * @see http://doc.babylonjs.com/how_to/transformnode
-        */
-        public transformNodes = new Array<TransformNode>();
-
-        /**
-        * All of the (abstract) meshes added to this scene
-        */
-        public meshes = new Array<AbstractMesh>();
-
-        /**
-        * All of the animation groups added to this scene
-        * @see http://doc.babylonjs.com/how_to/group
-        */
-        public animationGroups = new Array<AnimationGroup>();
-
-        // Geometries
-        private _geometries = new Array<Geometry>();
-
-        /**
-        * All of the materials added to this scene
-        * @see http://doc.babylonjs.com/babylon101/materials
-        */        
-        public materials = new Array<Material>();
-        /**
-        * All of the multi-materials added to this scene
-        * @see http://doc.babylonjs.com/how_to/multi_materials
-        */        
-        public multiMaterials = new Array<MultiMaterial>();
         private _defaultMaterial: Material;
 
         /** The default material used on meshes when no material is affected */
@@ -880,23 +834,12 @@
             return this._texturesEnabled;
         }
 
-        /**
-        * All of the textures added to this scene
-        */       
-        public textures = new Array<BaseTexture>();
-
         // Particles
         /**
         * Gets or sets a boolean indicating if particles are enabled on this scene
         */           
         public particlesEnabled = true;
 
-        /**
-        * All of the particle systems added to this scene
-        * @see http://doc.babylonjs.com/babylon101/particles
-        */            
-        public particleSystems = new Array<IParticleSystem>();
-
         // Sprites
         /**
         * Gets or sets a boolean indicating if sprites are enabled on this scene
@@ -908,18 +851,6 @@
         */          
         public spriteManagers = new Array<SpriteManager>();
 
-        /**
-         * The list of layers (background and foreground) of the scene
-         */
-        public layers = new Array<Layer>();
-
-        /**
-         * The list of effect layers (highlights/glow) added to the scene
-         * @see http://doc.babylonjs.com/how_to/highlight_layer
-         * @see http://doc.babylonjs.com/how_to/glow_layer
-         */
-        public effectLayers = new Array<EffectLayer>();
-
         // Skeletons
         private _skeletonsEnabled = true;
         /**
@@ -937,29 +868,11 @@
             return this._skeletonsEnabled;
         }
 
-        /**
-         * The list of skeletons added to the scene
-         * @see http://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons
-         */        
-        public skeletons = new Array<Skeleton>();
-
-        // Morph targets
-        /**
-         * The list of morph target managers added to the scene
-         * @see http://doc.babylonjs.com/how_to/how_to_dynamically_morph_a_mesh
-         */            
-        public morphTargetManagers = new Array<MorphTargetManager>();
-
         // Lens flares
         /**
         * Gets or sets a boolean indicating if lens flares are enabled on this scene
         */          
         public lensFlaresEnabled = true;
-        /**
-         * The list of lens flare system added to the scene
-         * @see http://doc.babylonjs.com/how_to/how_to_use_lens_flares
-         */         
-        public lensFlareSystems = new Array<LensFlareSystem>();
 
         // Collisions
         /**
@@ -1052,8 +965,6 @@
         */
         public actionManager: ActionManager;
 
-        /** @hidden */
-        public _actionManagers = new Array<ActionManager>();
         private _meshesForIntersections = new SmartArrayNoDuplicate<AbstractMesh>(256);
 
         // Procedural textures
@@ -1234,10 +1145,76 @@
         private _uid: Nullable<string>;
 
         /**
+         * Backing store of defined scene components.
+         */
+        public _components: ISceneComponent[] = [];
+
+        /**
+         * List of components to register on the next registration step.
+         */
+        private _transientComponents: ISceneComponent[] = [];
+
+        /**
+         * Registers the transient components if needed.
+         */
+        private _registerTransientComponents(): void {
+            // Register components that have been associated lately to the scene.
+            if (this._transientComponents.length > 0) {
+                for (let component of this._transientComponents) {
+                    component.register();
+                }
+                this._transientComponents = [];
+            }
+        }
+
+        /**
+         * Add a component to the scene.
+         * Note that the ccomponent could be registered on th next frame if this is called after 
+         * the register component stage.
+         * @param component Defines the component to add to the scene
+         */
+        public _addComponent(component: ISceneComponent) {
+            this._components.push(component);
+            this._transientComponents.push(component);
+        }
+
+        /**
+         * Gets a component from the scene.
+         * @param name defines the name of the component to retrieve
+         * @returns the component or null if not present
+         */
+        public _getComponent(name: string): Nullable<ISceneComponent> {
+            for (let component of this._components) {
+                if (component.name === name) {
+                    return component;
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Defines the actions happening during the per mesh ready checks.
+         */
+        public _isReadyForMeshStage = Stage.Create<MeshStageAction>();
+        /**
+         * Defines the actions happening during the per camera render target step.
+         */
+        public _cameraDrawRenderTargetStage = Stage.Create<CameraStageAction>();
+        /**
+         * Defines the actions happening just before the active camera is drawing.
+         */
+        public _beforeCameraDrawStage = Stage.Create<CameraStageAction>();
+        /**
+         * Defines the actions happening just after the active camera is drawing.
+         */
+        public _afterCameraDrawStage = Stage.Create<CameraStageAction>();
+       
+        /**
          * Creates a new Scene
          * @param engine defines the engine to use to render this scene
          */
         constructor(engine: Engine) {
+            super();
             this._engine = engine || Engine.LastCreatedEngine;
 
             this._engine.scenes.push(this);
@@ -2322,8 +2299,8 @@
             let engine = this.getEngine();
 
             // Geometries
-            for (index = 0; index < this._geometries.length; index++) {
-                var geometry = this._geometries[index];
+            for (index = 0; index < this.geometries.length; index++) {
+                var geometry = this.geometries[index];
 
                 if (geometry.delayLoadState === Engine.DELAYLOADSTATE_LOADING) {
                     return false;
@@ -2346,17 +2323,11 @@
                     return false;
                 }
 
-                // Effect layers
                 let hardwareInstancedRendering = mesh.getClassName() === "InstancedMesh" || engine.getCaps().instancedArrays && (<Mesh>mesh).instances.length > 0;
-                for (var layer of this.effectLayers) {
-                    if (!layer.hasMesh(mesh)) {
-                        continue;
-                    }
-
-                    for (var subMesh of mesh.subMeshes) {
-                        if (!layer.isReady(subMesh, hardwareInstancedRendering)) {
-                            return false;
-                        }
+                // Is Ready For Mesh
+                for (let step of this._isReadyForMeshStage) {
+                    if (!step.action(mesh, hardwareInstancedRendering)) {
+                        return false;
                     }
                 }
             }
@@ -2514,6 +2485,8 @@
 
         /** @hidden */
         public _checkIsReady() {
+            this._registerTransientComponents();
+
             if (this.isReady()) {
                 this.onReadyObservable.notifyObservers(this);
 
@@ -3258,46 +3231,18 @@
         }
 
         /**
-         * Removes the given lens flare system from this scene.
-         * @param toRemove The lens flare system to remove
-         * @returns The index of the removed lens flare system
-         */          
-        public removeLensFlareSystem(toRemove: LensFlareSystem): number {
-            var index = this.lensFlareSystems.indexOf(toRemove);
-            if (index !== -1) {
-                this.lensFlareSystems.splice(index, 1);
-            }
-            return index;
-        }
-
-        /**
          * Removes the given action manager from this scene.
          * @param toRemove The action manager to remove
          * @returns The index of the removed action manager
          */           
         public removeActionManager(toRemove: ActionManager): number {
-            var index = this._actionManagers.indexOf(toRemove);
+            var index = this.actionManagers.indexOf(toRemove);
             if (index !== -1) {
-                this._actionManagers.splice(index, 1);
+                this.actionManagers.splice(index, 1);
             }
             return index;
         }
 
-        
-        /**
-         * Removes the given effect layer from this scene.
-         * @param toRemove defines the effect layer to remove
-         * @returns the index of the removed effect layer
-         */    
-        public removeEffectLayer(toRemove: EffectLayer): number {
-            var index = this.effectLayers.indexOf(toRemove);
-            if (index !== -1) {
-                this.effectLayers.splice(index, 1);
-            }
-
-            return index;
-        }
-
         /**
          * Removes the given texture from this scene.
          * @param toRemove The texture to remove
@@ -3409,23 +3354,7 @@
          * @param newGeometry The geometry to add
          */           
         public addGeometry(newGeometry: Geometry): void {
-            this._geometries.push(newGeometry);
-        }
-
-        /**
-         * Adds the given lens flare system to this scene
-         * @param newLensFlareSystem The lens flare system to add
-         */          
-        public addLensFlareSystem(newLensFlareSystem: LensFlareSystem): void {
-            this.lensFlareSystems.push(newLensFlareSystem);
-        }
-
-        /**
-         * Adds the given effect layer to this scene
-         * @param newEffectLayer defines the effect layer to add
-         */     
-        public addEffectLayer(newEffectLayer: EffectLayer): void {
-            this.effectLayers.push(newEffectLayer);
+            this.geometries.push(newGeometry);
         }
 
         /**
@@ -3433,7 +3362,7 @@
          * @param newActionManager The action manager to add
          */   
         public addActionManager(newActionManager: ActionManager): void {
-            this._actionManagers.push(newActionManager);
+            this.actionManagers.push(newActionManager);
         }
 
         /**
@@ -3543,36 +3472,6 @@
         }
 
         /**
-         * Gets a lens flare system using its name
-         * @param name defines the name to look for
-         * @returns the lens flare system or null if not found
-         */
-        public getLensFlareSystemByName(name: string): Nullable<LensFlareSystem> {
-            for (var index = 0; index < this.lensFlareSystems.length; index++) {
-                if (this.lensFlareSystems[index].name === name) {
-                    return this.lensFlareSystems[index];
-                }
-            }
-
-            return null;
-        }
-
-        /**
-         * Gets a lens flare system using its id
-         * @param id defines the id to look for
-         * @returns the lens flare system or null if not found
-         */        
-        public getLensFlareSystemByID(id: string): Nullable<LensFlareSystem> {
-            for (var index = 0; index < this.lensFlareSystems.length; index++) {
-                if (this.lensFlareSystems[index].id === id) {
-                    return this.lensFlareSystems[index];
-                }
-            }
-
-            return null;
-        }
-
-        /**
          * Gets a camera using its id
          * @param id defines the id to look for
          * @returns the camera or null if not found
@@ -3720,9 +3619,9 @@
          * @return the geometry or null if none found.
          */
         public getGeometryByID(id: string): Nullable<Geometry> {
-            for (var index = 0; index < this._geometries.length; index++) {
-                if (this._geometries[index].id === id) {
-                    return this._geometries[index];
+            for (var index = 0; index < this.geometries.length; index++) {
+                if (this.geometries[index].id === id) {
+                    return this.geometries[index];
                 }
             }
 
@@ -3740,7 +3639,7 @@
                 return false;
             }
 
-            this._geometries.push(geometry);
+            this.geometries.push(geometry);
 
             //notify the collision coordinator
             if (this.collisionCoordinator) {
@@ -3758,10 +3657,10 @@
          * @return a boolean defining if the geometry was removed or not
          */
         public removeGeometry(geometry: Geometry): boolean {
-            var index = this._geometries.indexOf(geometry);
+            var index = this.geometries.indexOf(geometry);
 
             if (index > -1) {
-                this._geometries.splice(index, 1);
+                this.geometries.splice(index, 1);
 
                 //notify the collision coordinator
                 if (this.collisionCoordinator) {
@@ -3779,7 +3678,7 @@
          * @returns an array of Geometry
          */
         public getGeometries(): Geometry[] {
-            return this._geometries;
+            return this.geometries;
         }
 
         /**
@@ -4082,36 +3981,6 @@
         }
 
         /**
-         * 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 getHighlightLayerByName(name: string): Nullable<HighlightLayer> {
-            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;
-                }
-            }
-
-            return null;
-        }
-
-        /**
          * Return a unique id as a string which can serve as an identifier for the scene
          */
         public get uid(): string {
@@ -4504,7 +4373,6 @@
 
             // Render targets
             this.onBeforeRenderTargetsRenderObservable.notifyObservers(this);
-            var needsRestoreFrameBuffer = false;
 
             if (camera.customRenderTargets && camera.customRenderTargets.length > 0) {
                 this._renderTargets.concatWithNoDuplicate(camera.customRenderTargets);
@@ -4514,57 +4382,31 @@
                 this._renderTargets.concatWithNoDuplicate(rigParent.customRenderTargets);
             }
 
-            if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
-                this._intermediateRendering = true;
-                Tools.StartPerformanceCounter("Render targets", this._renderTargets.length > 0);
-                for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
-                    let renderTarget = this._renderTargets.data[renderIndex];
-                    if (renderTarget._shouldRender()) {
-                        this._renderId++;
-                        var hasSpecialRenderTargetCamera = renderTarget.activeCamera && renderTarget.activeCamera !== this.activeCamera;
-                        renderTarget.render((<boolean>hasSpecialRenderTargetCamera), this.dumpNextRenderTargets);
-                    }
-                }
-                Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
-
-                this._intermediateRendering = false;
-                this._renderId++;
-
-                needsRestoreFrameBuffer = true; // Restore back buffer
-            }
-
-            // Render EffectLayer Texture
-            var stencilState = this._engine.getStencilBuffer();
-            var renderEffects = false;
-            var needStencil = false;
-            if (this.renderTargetsEnabled && this.effectLayers && this.effectLayers.length > 0) {
+            if (this.renderTargetsEnabled) {
                 this._intermediateRendering = true;
-                for (let i = 0; i < this.effectLayers.length; i++) {
-                    let effectLayer = this.effectLayers[i];
-
-                    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))) {
-
-                        renderEffects = true;
-                        needStencil = needStencil || effectLayer.needStencil();
-
-                        let renderTarget = (<RenderTargetTexture>(<any>effectLayer)._mainTexture);
+                
+                if (this._renderTargets.length > 0) {
+                    Tools.StartPerformanceCounter("Render targets", this._renderTargets.length > 0);
+                    for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
+                        let renderTarget = this._renderTargets.data[renderIndex];
                         if (renderTarget._shouldRender()) {
                             this._renderId++;
-                            renderTarget.render(false, false);
-                            needsRestoreFrameBuffer = true;
+                            var hasSpecialRenderTargetCamera = renderTarget.activeCamera && renderTarget.activeCamera !== this.activeCamera;
+                            renderTarget.render((<boolean>hasSpecialRenderTargetCamera), this.dumpNextRenderTargets);
                         }
                     }
+                    Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
+
+                    this._renderId++;
+                }
+
+                for (let step of this._cameraDrawRenderTargetStage) {
+                    step.action(this.activeCamera);
                 }
 
                 this._intermediateRendering = false;
-                this._renderId++;
-            }
 
-            if (needsRestoreFrameBuffer) {
-                engine.restoreDefaultFramebuffer(); // Restore back buffer
+                engine.restoreDefaultFramebuffer(); // Restore back buffer if needed
             }
 
             this.onAfterRenderTargetsRenderObservable.notifyObservers(this);
@@ -4574,23 +4416,9 @@
                 this.postProcessManager._prepareFrame();
             }
 
-            // Backgrounds
-            var layerIndex;
-            var layer;
-            if (this.layers.length) {
-                engine.setDepthBuffer(false);
-                for (layerIndex = 0; layerIndex < this.layers.length; layerIndex++) {
-                    layer = this.layers[layerIndex];
-                    if (layer.isBackground && ((layer.layerMask & this.activeCamera.layerMask) !== 0)) {
-                        layer.render();
-                    }
-                }
-                engine.setDepthBuffer(true);
-            }
-
-            // Activate effect Layer stencil
-            if (needStencil) {
-                this._engine.setStencilBuffer(true);
+            // Before Camera Draw
+            for (let step of this._beforeCameraDrawStage) {
+                step.action(this.activeCamera);
             }
 
             // Render
@@ -4598,51 +4426,15 @@
             this._renderingManager.render(null, null, true, true);
             this.onAfterDrawPhaseObservable.notifyObservers(this);
 
-            // Restore effect Layer stencil
-            if (needStencil) {
-                this._engine.setStencilBuffer(stencilState);
+            // After Camera Draw
+            for (let step of this._afterCameraDrawStage) {
+                step.action(this.activeCamera);
             }
 
             // Bounding boxes
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.render();
             }
-
-            // Lens flares
-            if (this.lensFlaresEnabled) {
-                Tools.StartPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
-                for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
-
-                    var lensFlareSystem = this.lensFlareSystems[lensFlareSystemIndex];
-                    if ((camera.layerMask & lensFlareSystem.layerMask) !== 0) {
-                        lensFlareSystem.render();
-                    }
-                }
-                Tools.EndPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
-            }
-
-            // Effect Layer
-            if (renderEffects) {
-                engine.setDepthBuffer(false);
-                for (let i = 0; i < this.effectLayers.length; i++) {
-                    if (this.effectLayers[i].shouldRender()) {
-                        this.effectLayers[i].render();
-                    }
-                }
-                engine.setDepthBuffer(true);
-            }
-
-            // Foregrounds
-            if (this.layers.length) {
-                engine.setDepthBuffer(false);
-                for (layerIndex = 0; layerIndex < this.layers.length; layerIndex++) {
-                    layer = this.layers[layerIndex];
-                    if (!layer.isBackground && ((layer.layerMask & this.activeCamera.layerMask) !== 0)) {
-                        layer.render();
-                    }
-                }
-                engine.setDepthBuffer(true);
-            }
             
             // Finalize frame
             if (this.postProcessManager) {
@@ -4727,6 +4519,9 @@
                 return;
             }
 
+            // Register components that have been associated lately to the scene.
+            this._registerTransientComponents();
+
             this._activeParticles.fetchNewFrame();
             this._totalVertices.fetchNewFrame();
             this._activeIndices.fetchNewFrame();
@@ -5203,6 +4998,14 @@
 
             this.skeletons = [];
             this.morphTargetManagers = [];
+            this._transientComponents = [];
+            this._isReadyForMeshStage.clear();
+            this._cameraDrawRenderTargetStage.clear();
+            this._beforeCameraDrawStage.clear();
+            this._afterCameraDrawStage.clear();
+            for (let component of this._components) {
+                component.dispose();
+            }
 
             this.importedMeshesFiles = new Array<string>();
 
@@ -5344,14 +5147,6 @@
                 this.postProcesses[0].dispose();
             }
 
-            // Release layers
-            while (this.layers.length) {
-                this.layers[0].dispose();
-            }
-            while (this.effectLayers.length) {
-                this.effectLayers[0].dispose();
-            }
-
             // Release textures
             while (this.textures.length) {
                 this.textures[0].dispose();
@@ -5927,7 +5722,7 @@
         // Misc.
         /** @hidden */
         public _rebuildGeometries(): void {
-            for (var geometry of this._geometries) {
+            for (var geometry of this.geometries) {
                 geometry._rebuild();
             }
 
@@ -5939,12 +5734,8 @@
                 this.postProcessManager._rebuild();
             }
 
-            for (var layer of this.layers) {
-                layer._rebuild();
-            }
-
-            for (var effectLayer of this.effectLayers) {
-                effectLayer._rebuild();
+            for (let component of this._components) {
+                component.rebuild();
             }
 
             if (this._boundingBoxRenderer) {

+ 136 - 0
src/babylon.sceneComponent.ts

@@ -0,0 +1,136 @@
+module BABYLON {
+    /**
+     * Groups all the scene component constants in one place to ease maintenance.
+     * @hidden
+     */
+    export class SceneComponentConstants {
+        public static readonly NAME_EFFECTLAYER = "EffectLayer";
+        public static readonly NAME_LAYER = "Layer";
+        public static readonly NAME_LENSFLARESYSTEM = "LensFlareSystem";
+
+        public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;
+
+        public static readonly STEP_CAMERADRAWRENDERTARGET_EFFECTLAYER = 1;
+        
+        public static readonly STEP_BEFORECAMERADRAW_EFFECTLAYER = 0;
+        public static readonly STEP_BEFORECAMERADRAW_LAYER = 1;
+
+        public static readonly STEP_AFTERCAMERADRAW_EFFECTLAYER = 0;
+        public static readonly STEP_AFTERCAMERADRAW_LENSFLARESYSTEM = 1;
+        public static readonly STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW = 2;
+        public static readonly STEP_AFTERCAMERADRAW_LAYER = 3;
+
+    }
+
+    /**
+     * This represents a scene component.
+     * 
+     * This is used to decouple the dependency the scene is having on the different workloads like
+     * layers, post processes...
+     */
+    export interface ISceneComponent {
+        /**
+         * The name of the component. Each component must have a unique name.
+         */
+        name: string;
+
+        /**
+         * The scene the component belongs to.
+         */
+        scene: Scene;
+
+        /**
+         * Register the component to one instance of a scene.
+         */
+        register(): void;
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        addFromContainer(container: AbstractScene): void;
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        removeFromContainer(container: AbstractScene): void;
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        rebuild(): void;
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        serialize(serializationObject: any): void;
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        dispose(): void;
+    }
+
+    /** 
+     * Strong typing of a Mesh related stage step action 
+     */
+    export type MeshStageAction = (mesh: AbstractMesh, hardwareInstancedRendering: boolean) => boolean;
+
+    /** 
+     * Strong typing of a Camera related stage step action 
+     */
+    export type CameraStageAction = (camera: Camera) => void;
+
+    /** 
+     * Strong typing of a simple stage step action 
+     */
+    export type SimpleStageAction = () => void;
+
+    /** 
+     * Repressentation of a stage in the scene (Basically a list of ordered steps) 
+     * @hidden
+     */
+    export class Stage<T extends Function> extends Array<{ index: number, component: ISceneComponent, action: T }> {
+        /**
+         * Hide ctor from the rest of the world.
+         * @param items The items to add.
+         */
+        private constructor(items?: { index: number, component: ISceneComponent, action: T }[]) {
+            super(...<any>items)
+        }
+
+        /**
+         * Creates a new Stage.
+         * @returns A new instance of a Stage
+         */
+        static Create<T extends Function>(): Stage<T> {
+            return Object.create(Stage.prototype);
+        }
+
+        /**
+         * Registers a step in an ordered way in the targeted stage.
+         * @param index Defines the position to register the step in
+         * @param component Defines the component attached to the step
+         * @param action Defines the action to launch during the step
+         */
+        public registerStep(index: number, component: ISceneComponent, action: T): void {
+            let i = 0;
+            let maxIndex = Number.MAX_VALUE;
+            for (; i < this.length && i < maxIndex; i++) {
+                let step = this[i];
+                maxIndex = step.index;
+            }
+            this.splice(i, 0, { index, component, action: action.bind(component) });
+        }
+
+        /**
+         * Clears all the steps from the stage.
+         */
+        public clear(): void {
+            this.length = 0;
+        }
+    }
+}

+ 1 - 2
tslint.json

@@ -1,6 +1,5 @@
 {
-    "rules": {  
-        "no-invalid-this": true,
+    "rules": {
         "no-this-assignment": true,
         "completed-docs": true
     }