David Catuhe 9 лет назад
Родитель
Сommit
43ebd93d46

+ 1 - 0
Tools/Gulp/config.json

@@ -17,6 +17,7 @@
     "files": [
       "../../src/Math/babylon.math.js",
       "../../src/Tools/babylon.decorators.js",
+      "../../src/Tools/babylon.observable.js",
       "../../src/Tools/babylon.database.js",
       "../../src/Tools/babylon.tools.tga.js",
       "../../src/Tools/babylon.smartArray.js",

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


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


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


+ 242 - 81
dist/preview release/babylon.max.js

@@ -3076,6 +3076,73 @@ var BABYLON;
 
 var BABYLON;
 (function (BABYLON) {
+    var Observer = (function () {
+        function Observer(callback) {
+            this.callback = callback;
+        }
+        return Observer;
+    })();
+    BABYLON.Observer = Observer;
+    var Observable = (function () {
+        function Observable() {
+            this._observers = new Array();
+        }
+        /**
+         * Create a new Observer with the specified callback
+         * @param callback the callback that will be executed for that Observer
+         */
+        Observable.prototype.add = function (callback) {
+            var observer = new Observer(callback);
+            this._observers.push(observer);
+            return observer;
+        };
+        /**
+         * Remove an Observer from the Observable object
+         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         */
+        Observable.prototype.remove = function (observer) {
+            var index = this._observers.indexOf(observer);
+            if (index !== -1) {
+                this._observers.splice(index, 1);
+                return true;
+            }
+            return false;
+        };
+        /**
+         * Remove a callback from the Observable object
+         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
+        */
+        Observable.prototype.removeCallback = function (callback) {
+            for (var index = 0; index < this._observers.length; index++) {
+                if (this._observers[index].callback === callback) {
+                    this._observers.splice(index, 1);
+                    return true;
+                }
+            }
+            return false;
+        };
+        /**
+         * Notify all Observers by calling their respective callback with the given data
+         * @param eventData
+         */
+        Observable.prototype.notifyObservers = function (eventData) {
+            this._observers.forEach(function (observer) {
+                observer.callback(eventData);
+            });
+        };
+        /**
+        * Clear the list of observers
+        */
+        Observable.prototype.clear = function () {
+            this._observers = new Array();
+        };
+        return Observable;
+    })();
+    BABYLON.Observable = Observable;
+})(BABYLON || (BABYLON = {}));
+
+var BABYLON;
+(function (BABYLON) {
     var Database = (function () {
         function Database(urlToScene, callbackManifestChecked) {
             // Handling various flavors of prefixed version of IndexedDB
@@ -13798,6 +13865,77 @@ var BABYLON;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
+            // Events
+            /**
+            * An event triggered when the scene is disposed.
+            * @type {BABYLON.Observable}
+            */
+            this.onDisposeObservable = new BABYLON.Observable();
+            /**
+            * An event triggered before rendering the scene
+            * @type {BABYLON.Observable}
+            */
+            this.onBeforeRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered after rendering the scene
+            * @type {BABYLON.Observable}
+            */
+            this.onAfterRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when the scene is ready
+            * @type {BABYLON.Observable}
+            */
+            this.onReadyObservable = new BABYLON.Observable();
+            /**
+            * An event triggered before rendering a camera
+            * @type {BABYLON.Observable}
+            */
+            this.onBeforeCameraRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered after rendering a camera
+            * @type {BABYLON.Observable}
+            */
+            this.onAfterCameraRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a camera is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewCameraAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a camera is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onCameraRemovedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a light is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewLightAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a light is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onLightRemovedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a geometry is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewGeometryAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a geometry is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onGeometryRemovedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a mesh is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewMeshAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a mesh is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onMeshRemovedObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
             this.cameraToUseForPointers = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
@@ -13904,10 +14042,7 @@ var BABYLON;
             this._executeWhenReadyTimeoutId = -1;
             this._intermediateRendering = false;
             this._toBeDisposed = new BABYLON.SmartArray(256);
-            this._onReadyCallbacks = new Array();
             this._pendingData = []; //ANY
-            this._onBeforeRenderCallbacks = new Array();
-            this._onAfterRenderCallbacks = new Array();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._processedMaterials = new BABYLON.SmartArray(256);
             this._renderTargets = new BABYLON.SmartArray(256);
@@ -13968,6 +14103,56 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Scene.prototype, "onDispose", {
+            set: function (callback) {
+                if (this._onDisposeObserver) {
+                    this.onDisposeObservable.remove(this._onDisposeObserver);
+                }
+                this._onDisposeObserver = this.onDisposeObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "beforeRender", {
+            set: function (callback) {
+                if (this._onBeforeRenderObserver) {
+                    this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
+                }
+                this._onBeforeRenderObserver = this.onBeforeRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "afterRender", {
+            set: function (callback) {
+                if (this._onAfterRenderObserver) {
+                    this.onAfterRenderObservable.remove(this._onAfterRenderObserver);
+                }
+                this._onAfterRenderObserver = this.onAfterRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "beforeCameraRender", {
+            set: function (callback) {
+                if (this._onBeforeCameraRenderObserver) {
+                    this.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
+                }
+                this._onBeforeCameraRenderObserver = this.onBeforeCameraRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "afterCameraRender", {
+            set: function (callback) {
+                if (this._onAfterCameraRenderObserver) {
+                    this.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver);
+                }
+                this._onAfterCameraRenderObserver = this.onAfterCameraRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Scene.prototype, "debugLayer", {
             // Properties
             get: function () {
@@ -14329,22 +14514,16 @@ var BABYLON;
             this._cachedMaterial = null;
         };
         Scene.prototype.registerBeforeRender = function (func) {
-            this._onBeforeRenderCallbacks.push(func);
+            this.onBeforeRenderObservable.add(func);
         };
         Scene.prototype.unregisterBeforeRender = function (func) {
-            var index = this._onBeforeRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onBeforeRenderCallbacks.splice(index, 1);
-            }
+            this.onBeforeRenderObservable.removeCallback(func);
         };
         Scene.prototype.registerAfterRender = function (func) {
-            this._onAfterRenderCallbacks.push(func);
+            this.onAfterRenderObservable.add(func);
         };
         Scene.prototype.unregisterAfterRender = function (func) {
-            var index = this._onAfterRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onAfterRenderCallbacks.splice(index, 1);
-            }
+            this.onAfterRenderObservable.removeCallback(func);
         };
         Scene.prototype._addPendingData = function (data) {
             this._pendingData.push(data);
@@ -14364,7 +14543,7 @@ var BABYLON;
          */
         Scene.prototype.executeWhenReady = function (func) {
             var _this = this;
-            this._onReadyCallbacks.push(func);
+            this.onReadyObservable.add(func);
             if (this._executeWhenReadyTimeoutId !== -1) {
                 return;
             }
@@ -14375,10 +14554,8 @@ var BABYLON;
         Scene.prototype._checkIsReady = function () {
             var _this = this;
             if (this.isReady()) {
-                this._onReadyCallbacks.forEach(function (func) {
-                    func();
-                });
-                this._onReadyCallbacks = [];
+                this.onReadyObservable.notifyObservers(this);
+                this.onReadyObservable.clear();
                 this._executeWhenReadyTimeoutId = -1;
                 return;
             }
@@ -14491,9 +14668,7 @@ var BABYLON;
             var position = this.meshes.push(newMesh);
             //notify the collision coordinator
             this.collisionCoordinator.onMeshAdded(newMesh);
-            if (this.onNewMeshAdded) {
-                this.onNewMeshAdded(newMesh, position, this);
-            }
+            this.onNewMeshAddedObservable.notifyObservers(newMesh);
         };
         Scene.prototype.removeMesh = function (toRemove) {
             var index = this.meshes.indexOf(toRemove);
@@ -14503,9 +14678,7 @@ var BABYLON;
             }
             //notify the collision coordinator
             this.collisionCoordinator.onMeshRemoved(toRemove);
-            if (this.onMeshRemoved) {
-                this.onMeshRemoved(toRemove);
-            }
+            this.onMeshRemovedObservable.notifyObservers(toRemove);
             return index;
         };
         Scene.prototype.removeSkeleton = function (toRemove) {
@@ -14522,9 +14695,7 @@ var BABYLON;
                 // Remove from the scene if mesh found 
                 this.lights.splice(index, 1);
             }
-            if (this.onLightRemoved) {
-                this.onLightRemoved(toRemove);
-            }
+            this.onLightRemovedObservable.notifyObservers(toRemove);
             return index;
         };
         Scene.prototype.removeCamera = function (toRemove) {
@@ -14548,24 +14719,18 @@ var BABYLON;
                     this.activeCamera = null;
                 }
             }
-            if (this.onCameraRemoved) {
-                this.onCameraRemoved(toRemove);
-            }
+            this.onCameraRemovedObservable.notifyObservers(toRemove);
             return index;
         };
         Scene.prototype.addLight = function (newLight) {
             newLight.uniqueId = this._uniqueIdCounter++;
             var position = this.lights.push(newLight);
-            if (this.onNewLightAdded) {
-                this.onNewLightAdded(newLight, position, this);
-            }
+            this.onNewLightAddedObservable.notifyObservers(newLight);
         };
         Scene.prototype.addCamera = function (newCamera) {
             newCamera.uniqueId = this._uniqueIdCounter++;
             var position = this.cameras.push(newCamera);
-            if (this.onNewCameraAdded) {
-                this.onNewCameraAdded(newCamera, position, this);
-            }
+            this.onNewCameraAddedObservable.notifyObservers(newCamera);
         };
         /**
          * Switch active camera
@@ -14782,9 +14947,7 @@ var BABYLON;
             this._geometries.push(geometry);
             //notify the collision coordinator
             this.collisionCoordinator.onGeometryAdded(geometry);
-            if (this.onGeometryAdded) {
-                this.onGeometryAdded(geometry);
-            }
+            this.onNewGeometryAddedObservable.notifyObservers(geometry);
             return true;
         };
         /**
@@ -14798,9 +14961,7 @@ var BABYLON;
                 this._geometries.splice(index, 1);
                 //notify the collision coordinator
                 this.collisionCoordinator.onGeometryDeleted(geometry);
-                if (this.onGeometryRemoved) {
-                    this.onGeometryRemoved(geometry);
-                }
+                this.onGeometryRemovedObservable.notifyObservers(geometry);
                 return true;
             }
             return false;
@@ -15099,9 +15260,7 @@ var BABYLON;
             this.resetCachedMaterial();
             this._renderId++;
             this.updateTransformMatrix();
-            if (this.beforeCameraRender) {
-                this.beforeCameraRender(this.activeCamera);
-            }
+            this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             // Meshes
             var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
@@ -15192,9 +15351,7 @@ var BABYLON;
             this.activeCamera._updateFromScene();
             // Reset some special arrays
             this._renderTargets.reset();
-            if (this.afterCameraRender) {
-                this.afterCameraRender(this.activeCamera);
-            }
+            this.onAfterCameraRenderObservable.notifyObservers(this.activeCamera);
             BABYLON.Tools.EndPerformanceCounter("Rendering camera " + this.activeCamera.name);
         };
         Scene.prototype._processSubCameras = function (camera) {
@@ -15279,13 +15436,7 @@ var BABYLON;
                 BABYLON.Tools.EndPerformanceCounter("Physics");
             }
             // Before render
-            if (this.beforeRender) {
-                this.beforeRender();
-            }
-            var callbackIndex;
-            for (callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
-                this._onBeforeRenderCallbacks[callbackIndex]();
-            }
+            this.onBeforeRenderObservable.notifyObservers(this);
             // Customs render targets
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
@@ -15370,9 +15521,7 @@ var BABYLON;
             if (this.afterRender) {
                 this.afterRender();
             }
-            for (callbackIndex = 0; callbackIndex < this._onAfterRenderCallbacks.length; callbackIndex++) {
-                this._onAfterRenderCallbacks[callbackIndex]();
-            }
+            this.onAfterRenderObservable.notifyObservers(this);
             // Cleaning
             for (var index = 0; index < this._toBeDisposed.length; index++) {
                 this._toBeDisposed.data[index].dispose();
@@ -15533,8 +15682,8 @@ var BABYLON;
             if (this.onDispose) {
                 this.onDispose();
             }
-            this._onBeforeRenderCallbacks = [];
-            this._onAfterRenderCallbacks = [];
+            this.onBeforeRenderObservable.clear();
+            this.onAfterRenderObservable.clear();
             this.detachControl();
             // Release sounds & sounds tracks
             if (BABYLON.AudioEngine) {
@@ -16296,12 +16445,26 @@ var BABYLON;
         function Mesh(name, scene, parent, source, doNotCloneChildren) {
             if (parent === void 0) { parent = null; }
             _super.call(this, name, scene);
+            // Events 
+            /**
+             * An event triggered before rendering the mesh
+             * @type {BABYLON.Observable}
+             */
+            this.onBeforeRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered after rendering the mesh
+            * @type {BABYLON.Observable}
+            */
+            this.onAfterRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered before drawing the mesh
+            * @type {BABYLON.Observable}
+            */
+            this.onBeforeDrawObservable = new BABYLON.Observable();
             // Members
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NONE;
             this.instances = new Array();
             this._LODLevels = new Array();
-            this._onBeforeRenderCallbacks = new Array();
-            this._onAfterRenderCallbacks = new Array();
             this._visibleInstances = {};
             this._renderIdForInstances = new Array();
             this._batchCache = new _InstancesBatch();
@@ -16401,6 +16564,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Mesh.prototype, "onBeforeDraw", {
+            set: function (callback) {
+                if (this._onBeforeDrawObserver) {
+                    this.onBeforeDrawObservable.remove(this._onBeforeDrawObserver);
+                }
+                this._onBeforeDrawObserver = this.onBeforeDrawObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Methods
         /**
          * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
@@ -16778,9 +16951,7 @@ var BABYLON;
             if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
                 return;
             }
-            if (this.onBeforeDraw) {
-                this.onBeforeDraw();
-            }
+            this.onBeforeDrawObservable.notifyObservers(this);
             var engine = this.getScene().getEngine();
             // Draw order
             switch (fillMode) {
@@ -16805,22 +16976,16 @@ var BABYLON;
             }
         };
         Mesh.prototype.registerBeforeRender = function (func) {
-            this._onBeforeRenderCallbacks.push(func);
+            this.onBeforeRenderObservable.add(func);
         };
         Mesh.prototype.unregisterBeforeRender = function (func) {
-            var index = this._onBeforeRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onBeforeRenderCallbacks.splice(index, 1);
-            }
+            this.onBeforeRenderObservable.removeCallback(func);
         };
         Mesh.prototype.registerAfterRender = function (func) {
-            this._onAfterRenderCallbacks.push(func);
+            this.onAfterRenderObservable.add(func);
         };
         Mesh.prototype.unregisterAfterRender = function (func) {
-            var index = this._onAfterRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onAfterRenderCallbacks.splice(index, 1);
-            }
+            this.onAfterRenderObservable.removeCallback(func);
         };
         Mesh.prototype._getInstancesRenderList = function (subMeshId) {
             var scene = this.getScene();
@@ -16929,9 +17094,7 @@ var BABYLON;
                 return;
             }
             var callbackIndex;
-            for (callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
-                this._onBeforeRenderCallbacks[callbackIndex](this);
-            }
+            this.onBeforeRenderObservable.notifyObservers(this);
             var engine = scene.getEngine();
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
             // Material
@@ -16979,9 +17142,7 @@ var BABYLON;
                 scene.getOutlineRenderer().render(subMesh, batch, true);
                 engine.setAlphaMode(currentMode);
             }
-            for (callbackIndex = 0; callbackIndex < this._onAfterRenderCallbacks.length; callbackIndex++) {
-                this._onAfterRenderCallbacks[callbackIndex](this);
-            }
+            this.onAfterRenderObservable.notifyObservers(this);
         };
         Mesh.prototype.getEmittedParticleSystems = function () {
             var results = new Array();

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


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

@@ -9,6 +9,7 @@
     - Animations blending. See [demo here](http://www.babylonjs-playground.com/#2BLI9T#3). More [info here](NEED DOC!) ([deltakosh](https://github.com/deltakosh))
     - New debuger tool: SkeletonViewer. See [demo here](Demo available here: http://www.babylonjs-playground.com/#1BZJVJ#8) (Adam & [deltakosh](https://github.com/deltakosh))
     - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance [gleborgne](https://github.com/gleborgne)
+    - Introduced new observable system to handle events ([nockawa](https://github.com/nockawa), [deltakosh](https://github.com/deltakosh))
   - **Updates**
     - Added skeleton.getBoneIndexByName(boneName: string) [dad72](https://github.com/dad72)
     - Added node._children to track children hierarchy ([deltakosh](https://github.com/deltakosh))
@@ -27,3 +28,4 @@
     - Fixed bug with ArcRotateCamera.setTarget ([deltakosh](https://github.com/deltakosh))
   - **Breaking changes**
     - `VertexData.CreateLines()` removed as `MeshBuilder.CreateLines()` now calls `MeshBuilder.CreateLineSystem()`
+    - `scene.onNewXXXAdded` and `scene.onXXXRemoved' callbacks were removed and replaced by `scene.onNewXXXAddedObservable` and `scene.onXXXRemovedObservable`

+ 33 - 21
src/Mesh/babylon.mesh.js

@@ -29,12 +29,26 @@ var BABYLON;
         function Mesh(name, scene, parent, source, doNotCloneChildren) {
             if (parent === void 0) { parent = null; }
             _super.call(this, name, scene);
+            // Events 
+            /**
+             * An event triggered before rendering the mesh
+             * @type {BABYLON.Observable}
+             */
+            this.onBeforeRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered after rendering the mesh
+            * @type {BABYLON.Observable}
+            */
+            this.onAfterRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered before drawing the mesh
+            * @type {BABYLON.Observable}
+            */
+            this.onBeforeDrawObservable = new BABYLON.Observable();
             // Members
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NONE;
             this.instances = new Array();
             this._LODLevels = new Array();
-            this._onBeforeRenderCallbacks = new Array();
-            this._onAfterRenderCallbacks = new Array();
             this._visibleInstances = {};
             this._renderIdForInstances = new Array();
             this._batchCache = new _InstancesBatch();
@@ -134,6 +148,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Mesh.prototype, "onBeforeDraw", {
+            set: function (callback) {
+                if (this._onBeforeDrawObserver) {
+                    this.onBeforeDrawObservable.remove(this._onBeforeDrawObserver);
+                }
+                this._onBeforeDrawObserver = this.onBeforeDrawObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Methods
         /**
          * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
@@ -511,9 +535,7 @@ var BABYLON;
             if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
                 return;
             }
-            if (this.onBeforeDraw) {
-                this.onBeforeDraw();
-            }
+            this.onBeforeDrawObservable.notifyObservers(this);
             var engine = this.getScene().getEngine();
             // Draw order
             switch (fillMode) {
@@ -538,22 +560,16 @@ var BABYLON;
             }
         };
         Mesh.prototype.registerBeforeRender = function (func) {
-            this._onBeforeRenderCallbacks.push(func);
+            this.onBeforeRenderObservable.add(func);
         };
         Mesh.prototype.unregisterBeforeRender = function (func) {
-            var index = this._onBeforeRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onBeforeRenderCallbacks.splice(index, 1);
-            }
+            this.onBeforeRenderObservable.removeCallback(func);
         };
         Mesh.prototype.registerAfterRender = function (func) {
-            this._onAfterRenderCallbacks.push(func);
+            this.onAfterRenderObservable.add(func);
         };
         Mesh.prototype.unregisterAfterRender = function (func) {
-            var index = this._onAfterRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onAfterRenderCallbacks.splice(index, 1);
-            }
+            this.onAfterRenderObservable.removeCallback(func);
         };
         Mesh.prototype._getInstancesRenderList = function (subMeshId) {
             var scene = this.getScene();
@@ -662,9 +678,7 @@ var BABYLON;
                 return;
             }
             var callbackIndex;
-            for (callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
-                this._onBeforeRenderCallbacks[callbackIndex](this);
-            }
+            this.onBeforeRenderObservable.notifyObservers(this);
             var engine = scene.getEngine();
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
             // Material
@@ -712,9 +726,7 @@ var BABYLON;
                 scene.getOutlineRenderer().render(subMesh, batch, true);
                 engine.setAlphaMode(currentMode);
             }
-            for (callbackIndex = 0; callbackIndex < this._onAfterRenderCallbacks.length; callbackIndex++) {
-                this._onAfterRenderCallbacks[callbackIndex](this);
-            }
+            this.onAfterRenderObservable.notifyObservers(this);
         };
         Mesh.prototype.getEmittedParticleSystems = function () {
             var results = new Array();

+ 35 - 24
src/Mesh/babylon.mesh.ts

@@ -44,6 +44,34 @@
             return Mesh._CAP_ALL;
         }
 
+        // Events 
+
+        /**
+         * An event triggered before rendering the mesh
+         * @type {BABYLON.Observable}
+         */
+        public onBeforeRenderObservable = new Observable<Mesh>();
+
+        /**
+        * An event triggered after rendering the mesh
+        * @type {BABYLON.Observable}
+        */
+        public onAfterRenderObservable = new Observable<Mesh>();
+
+        /**
+        * An event triggered before drawing the mesh
+        * @type {BABYLON.Observable}
+        */
+        public onBeforeDrawObservable = new Observable<Mesh>();
+
+        private _onBeforeDrawObserver: Observer<Mesh>;
+        public set onBeforeDraw(callback: () => void) {
+            if (this._onBeforeDrawObserver) {
+                this.onBeforeDrawObservable.remove(this._onBeforeDrawObserver);
+            }
+            this._onBeforeDrawObserver = this.onBeforeDrawObservable.add(callback);
+        }
+
         // Members
         public delayLoadState = Engine.DELAYLOADSTATE_NONE;
         public instances = new Array<InstancedMesh>();
@@ -51,12 +79,9 @@
         public _binaryInfo: any;
         private _LODLevels = new Array<Internals.MeshLODLevel>();
         public onLODLevelSelection: (distance: number, mesh: Mesh, selectedLevel: Mesh) => void;
-        public onBeforeDraw: () => void;
 
         // Private
         public _geometry: Geometry;
-        private _onBeforeRenderCallbacks = new Array<(mesh: AbstractMesh) => void>();
-        private _onAfterRenderCallbacks = new Array<(mesh: AbstractMesh) => void>();
         public _delayInfo; //ANY
         public _delayLoadingFunction: (any: any, mesh: Mesh) => void;
         public _visibleInstances: any = {};
@@ -564,9 +589,7 @@
                 return;
             }
 
-            if (this.onBeforeDraw) {
-                this.onBeforeDraw();
-            }
+            this.onBeforeDrawObservable.notifyObservers(this);
 
             var engine = this.getScene().getEngine();
 
@@ -593,27 +616,19 @@
         }
 
         public registerBeforeRender(func: (mesh: AbstractMesh) => void): void {
-            this._onBeforeRenderCallbacks.push(func);
+            this.onBeforeRenderObservable.add(func);
         }
 
         public unregisterBeforeRender(func: (mesh: AbstractMesh) => void): void {
-            var index = this._onBeforeRenderCallbacks.indexOf(func);
-
-            if (index > -1) {
-                this._onBeforeRenderCallbacks.splice(index, 1);
-            }
+            this.onBeforeRenderObservable.removeCallback(func);
         }
 
         public registerAfterRender(func: (mesh: AbstractMesh) => void): void {
-            this._onAfterRenderCallbacks.push(func);
+            this.onAfterRenderObservable.add(func);
         }
 
         public unregisterAfterRender(func: (mesh: AbstractMesh) => void): void {
-            var index = this._onAfterRenderCallbacks.indexOf(func);
-
-            if (index > -1) {
-                this._onAfterRenderCallbacks.splice(index, 1);
-            }
+            this.onAfterRenderObservable.removeCallback(func);
         }
 
         public _getInstancesRenderList(subMeshId: number): _InstancesBatch {
@@ -753,9 +768,7 @@
             }
 
             var callbackIndex: number;
-            for (callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
-                this._onBeforeRenderCallbacks[callbackIndex](this);
-            }
+            this.onBeforeRenderObservable.notifyObservers(this);
 
             var engine = scene.getEngine();
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
@@ -818,9 +831,7 @@
                 engine.setAlphaMode(currentMode);
             }
 
-            for (callbackIndex = 0; callbackIndex < this._onAfterRenderCallbacks.length; callbackIndex++) {
-                this._onAfterRenderCallbacks[callbackIndex](this);
-            }
+            this.onAfterRenderObservable.notifyObservers(this);
         }
 
         public getEmittedParticleSystems(): ParticleSystem[] {

+ 66 - 0
src/Tools/babylon.observable.js

@@ -0,0 +1,66 @@
+var BABYLON;
+(function (BABYLON) {
+    var Observer = (function () {
+        function Observer(callback) {
+            this.callback = callback;
+        }
+        return Observer;
+    })();
+    BABYLON.Observer = Observer;
+    var Observable = (function () {
+        function Observable() {
+            this._observers = new Array();
+        }
+        /**
+         * Create a new Observer with the specified callback
+         * @param callback the callback that will be executed for that Observer
+         */
+        Observable.prototype.add = function (callback) {
+            var observer = new Observer(callback);
+            this._observers.push(observer);
+            return observer;
+        };
+        /**
+         * Remove an Observer from the Observable object
+         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         */
+        Observable.prototype.remove = function (observer) {
+            var index = this._observers.indexOf(observer);
+            if (index !== -1) {
+                this._observers.splice(index, 1);
+                return true;
+            }
+            return false;
+        };
+        /**
+         * Remove a callback from the Observable object
+         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
+        */
+        Observable.prototype.removeCallback = function (callback) {
+            for (var index = 0; index < this._observers.length; index++) {
+                if (this._observers[index].callback === callback) {
+                    this._observers.splice(index, 1);
+                    return true;
+                }
+            }
+            return false;
+        };
+        /**
+         * Notify all Observers by calling their respective callback with the given data
+         * @param eventData
+         */
+        Observable.prototype.notifyObservers = function (eventData) {
+            this._observers.forEach(function (observer) {
+                observer.callback(eventData);
+            });
+        };
+        /**
+        * Clear the list of observers
+        */
+        Observable.prototype.clear = function () {
+            this._observers = new Array();
+        };
+        return Observable;
+    })();
+    BABYLON.Observable = Observable;
+})(BABYLON || (BABYLON = {}));

+ 72 - 0
src/Tools/babylon.observable.ts

@@ -0,0 +1,72 @@
+module BABYLON {
+    export class Observer<T> {
+        constructor(public callback: (eventData: T) => void) {
+        }
+    }
+
+    export class Observable<T> {
+        _observers = new Array<Observer<T>>();
+
+        /**
+         * Create a new Observer with the specified callback
+         * @param callback the callback that will be executed for that Observer
+         */
+        public add(callback: (eventData: T) => void): Observer<T> {
+            var observer = new Observer(callback);
+
+            this._observers.push(observer);
+
+            return observer;
+        }
+
+        /**
+         * Remove an Observer from the Observable object
+         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         */
+        public remove(observer: Observer<T>): boolean {
+            var index = this._observers.indexOf(observer);
+
+            if (index !== -1) {
+
+                this._observers.splice(index, 1);
+                return true;
+            }
+
+            return false;
+        }
+
+
+        /**
+         * Remove a callback from the Observable object
+         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
+        */
+        public removeCallback(callback: (eventData: T) => void): boolean {
+
+            for (var index = 0; index < this._observers.length; index++) {
+                if (this._observers[index].callback === callback) {
+                    this._observers.splice(index, 1);
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         * Notify all Observers by calling their respective callback with the given data
+         * @param eventData
+         */
+        public notifyObservers(eventData: T) {
+            this._observers.forEach((observer: Observer<T>) => {
+                observer.callback(eventData);
+            });
+        }
+
+        /**
+        * Clear the list of observers
+        */
+        public clear() {
+            this._observers = new Array<Observer<T>>();
+        }
+    }
+}

+ 142 - 60
src/babylon.scene.js

@@ -19,6 +19,77 @@ var BABYLON;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
+            // Events
+            /**
+            * An event triggered when the scene is disposed.
+            * @type {BABYLON.Observable}
+            */
+            this.onDisposeObservable = new BABYLON.Observable();
+            /**
+            * An event triggered before rendering the scene
+            * @type {BABYLON.Observable}
+            */
+            this.onBeforeRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered after rendering the scene
+            * @type {BABYLON.Observable}
+            */
+            this.onAfterRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when the scene is ready
+            * @type {BABYLON.Observable}
+            */
+            this.onReadyObservable = new BABYLON.Observable();
+            /**
+            * An event triggered before rendering a camera
+            * @type {BABYLON.Observable}
+            */
+            this.onBeforeCameraRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered after rendering a camera
+            * @type {BABYLON.Observable}
+            */
+            this.onAfterCameraRenderObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a camera is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewCameraAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a camera is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onCameraRemovedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a light is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewLightAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a light is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onLightRemovedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a geometry is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewGeometryAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a geometry is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onGeometryRemovedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a mesh is created
+            * @type {BABYLON.Observable}
+            */
+            this.onNewMeshAddedObservable = new BABYLON.Observable();
+            /**
+            * An event triggered when a mesh is removed
+            * @type {BABYLON.Observable}
+            */
+            this.onMeshRemovedObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
             this.cameraToUseForPointers = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
@@ -125,10 +196,7 @@ var BABYLON;
             this._executeWhenReadyTimeoutId = -1;
             this._intermediateRendering = false;
             this._toBeDisposed = new BABYLON.SmartArray(256);
-            this._onReadyCallbacks = new Array();
             this._pendingData = []; //ANY
-            this._onBeforeRenderCallbacks = new Array();
-            this._onAfterRenderCallbacks = new Array();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._processedMaterials = new BABYLON.SmartArray(256);
             this._renderTargets = new BABYLON.SmartArray(256);
@@ -189,6 +257,56 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Scene.prototype, "onDispose", {
+            set: function (callback) {
+                if (this._onDisposeObserver) {
+                    this.onDisposeObservable.remove(this._onDisposeObserver);
+                }
+                this._onDisposeObserver = this.onDisposeObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "beforeRender", {
+            set: function (callback) {
+                if (this._onBeforeRenderObserver) {
+                    this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
+                }
+                this._onBeforeRenderObserver = this.onBeforeRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "afterRender", {
+            set: function (callback) {
+                if (this._onAfterRenderObserver) {
+                    this.onAfterRenderObservable.remove(this._onAfterRenderObserver);
+                }
+                this._onAfterRenderObserver = this.onAfterRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "beforeCameraRender", {
+            set: function (callback) {
+                if (this._onBeforeCameraRenderObserver) {
+                    this.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
+                }
+                this._onBeforeCameraRenderObserver = this.onBeforeCameraRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Scene.prototype, "afterCameraRender", {
+            set: function (callback) {
+                if (this._onAfterCameraRenderObserver) {
+                    this.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver);
+                }
+                this._onAfterCameraRenderObserver = this.onAfterCameraRenderObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Scene.prototype, "debugLayer", {
             // Properties
             get: function () {
@@ -550,22 +668,16 @@ var BABYLON;
             this._cachedMaterial = null;
         };
         Scene.prototype.registerBeforeRender = function (func) {
-            this._onBeforeRenderCallbacks.push(func);
+            this.onBeforeRenderObservable.add(func);
         };
         Scene.prototype.unregisterBeforeRender = function (func) {
-            var index = this._onBeforeRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onBeforeRenderCallbacks.splice(index, 1);
-            }
+            this.onBeforeRenderObservable.removeCallback(func);
         };
         Scene.prototype.registerAfterRender = function (func) {
-            this._onAfterRenderCallbacks.push(func);
+            this.onAfterRenderObservable.add(func);
         };
         Scene.prototype.unregisterAfterRender = function (func) {
-            var index = this._onAfterRenderCallbacks.indexOf(func);
-            if (index > -1) {
-                this._onAfterRenderCallbacks.splice(index, 1);
-            }
+            this.onAfterRenderObservable.removeCallback(func);
         };
         Scene.prototype._addPendingData = function (data) {
             this._pendingData.push(data);
@@ -585,7 +697,7 @@ var BABYLON;
          */
         Scene.prototype.executeWhenReady = function (func) {
             var _this = this;
-            this._onReadyCallbacks.push(func);
+            this.onReadyObservable.add(func);
             if (this._executeWhenReadyTimeoutId !== -1) {
                 return;
             }
@@ -596,10 +708,8 @@ var BABYLON;
         Scene.prototype._checkIsReady = function () {
             var _this = this;
             if (this.isReady()) {
-                this._onReadyCallbacks.forEach(function (func) {
-                    func();
-                });
-                this._onReadyCallbacks = [];
+                this.onReadyObservable.notifyObservers(this);
+                this.onReadyObservable.clear();
                 this._executeWhenReadyTimeoutId = -1;
                 return;
             }
@@ -712,9 +822,7 @@ var BABYLON;
             var position = this.meshes.push(newMesh);
             //notify the collision coordinator
             this.collisionCoordinator.onMeshAdded(newMesh);
-            if (this.onNewMeshAdded) {
-                this.onNewMeshAdded(newMesh, position, this);
-            }
+            this.onNewMeshAddedObservable.notifyObservers(newMesh);
         };
         Scene.prototype.removeMesh = function (toRemove) {
             var index = this.meshes.indexOf(toRemove);
@@ -724,9 +832,7 @@ var BABYLON;
             }
             //notify the collision coordinator
             this.collisionCoordinator.onMeshRemoved(toRemove);
-            if (this.onMeshRemoved) {
-                this.onMeshRemoved(toRemove);
-            }
+            this.onMeshRemovedObservable.notifyObservers(toRemove);
             return index;
         };
         Scene.prototype.removeSkeleton = function (toRemove) {
@@ -743,9 +849,7 @@ var BABYLON;
                 // Remove from the scene if mesh found 
                 this.lights.splice(index, 1);
             }
-            if (this.onLightRemoved) {
-                this.onLightRemoved(toRemove);
-            }
+            this.onLightRemovedObservable.notifyObservers(toRemove);
             return index;
         };
         Scene.prototype.removeCamera = function (toRemove) {
@@ -769,24 +873,18 @@ var BABYLON;
                     this.activeCamera = null;
                 }
             }
-            if (this.onCameraRemoved) {
-                this.onCameraRemoved(toRemove);
-            }
+            this.onCameraRemovedObservable.notifyObservers(toRemove);
             return index;
         };
         Scene.prototype.addLight = function (newLight) {
             newLight.uniqueId = this._uniqueIdCounter++;
             var position = this.lights.push(newLight);
-            if (this.onNewLightAdded) {
-                this.onNewLightAdded(newLight, position, this);
-            }
+            this.onNewLightAddedObservable.notifyObservers(newLight);
         };
         Scene.prototype.addCamera = function (newCamera) {
             newCamera.uniqueId = this._uniqueIdCounter++;
             var position = this.cameras.push(newCamera);
-            if (this.onNewCameraAdded) {
-                this.onNewCameraAdded(newCamera, position, this);
-            }
+            this.onNewCameraAddedObservable.notifyObservers(newCamera);
         };
         /**
          * Switch active camera
@@ -1003,9 +1101,7 @@ var BABYLON;
             this._geometries.push(geometry);
             //notify the collision coordinator
             this.collisionCoordinator.onGeometryAdded(geometry);
-            if (this.onGeometryAdded) {
-                this.onGeometryAdded(geometry);
-            }
+            this.onNewGeometryAddedObservable.notifyObservers(geometry);
             return true;
         };
         /**
@@ -1019,9 +1115,7 @@ var BABYLON;
                 this._geometries.splice(index, 1);
                 //notify the collision coordinator
                 this.collisionCoordinator.onGeometryDeleted(geometry);
-                if (this.onGeometryRemoved) {
-                    this.onGeometryRemoved(geometry);
-                }
+                this.onGeometryRemovedObservable.notifyObservers(geometry);
                 return true;
             }
             return false;
@@ -1320,9 +1414,7 @@ var BABYLON;
             this.resetCachedMaterial();
             this._renderId++;
             this.updateTransformMatrix();
-            if (this.beforeCameraRender) {
-                this.beforeCameraRender(this.activeCamera);
-            }
+            this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             // Meshes
             var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
@@ -1413,9 +1505,7 @@ var BABYLON;
             this.activeCamera._updateFromScene();
             // Reset some special arrays
             this._renderTargets.reset();
-            if (this.afterCameraRender) {
-                this.afterCameraRender(this.activeCamera);
-            }
+            this.onAfterCameraRenderObservable.notifyObservers(this.activeCamera);
             BABYLON.Tools.EndPerformanceCounter("Rendering camera " + this.activeCamera.name);
         };
         Scene.prototype._processSubCameras = function (camera) {
@@ -1500,13 +1590,7 @@ var BABYLON;
                 BABYLON.Tools.EndPerformanceCounter("Physics");
             }
             // Before render
-            if (this.beforeRender) {
-                this.beforeRender();
-            }
-            var callbackIndex;
-            for (callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
-                this._onBeforeRenderCallbacks[callbackIndex]();
-            }
+            this.onBeforeRenderObservable.notifyObservers(this);
             // Customs render targets
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
@@ -1591,9 +1675,7 @@ var BABYLON;
             if (this.afterRender) {
                 this.afterRender();
             }
-            for (callbackIndex = 0; callbackIndex < this._onAfterRenderCallbacks.length; callbackIndex++) {
-                this._onAfterRenderCallbacks[callbackIndex]();
-            }
+            this.onAfterRenderObservable.notifyObservers(this);
             // Cleaning
             for (var index = 0; index < this._toBeDisposed.length; index++) {
                 this._toBeDisposed.data[index].dispose();
@@ -1754,8 +1836,8 @@ var BABYLON;
             if (this.onDispose) {
                 this.onDispose();
             }
-            this._onBeforeRenderCallbacks = [];
-            this._onAfterRenderCallbacks = [];
+            this.onBeforeRenderObservable.clear();
+            this.onAfterRenderObservable.clear();
             this.detachControl();
             // Release sounds & sounds tracks
             if (BABYLON.AudioEngine) {

+ 151 - 90
src/babylon.scene.ts

@@ -37,23 +37,7 @@
         public autoClear = true;
         public clearColor: any = new Color3(0.2, 0.2, 0.3);
         public ambientColor = new Color3(0, 0, 0);
-        /**
-        * A function to be executed before rendering this scene
-        * @type {Function}
-        */
-        public beforeRender: () => void;
-        /**
-        * A function to be executed after rendering this scene
-        * @type {Function}
-        */
-        public afterRender: () => void;
-        /**
-        * A function to be executed when this scene is disposed.
-        * @type {Function}
-        */
-        public onDispose: () => void;
-        public beforeCameraRender: (camera: Camera) => void;
-        public afterCameraRender: (camera: Camera) => void;
+
         public forceWireframe = false;
         public forcePointsCloud = false;
         public forceShowBoundingBoxes = false;
@@ -61,6 +45,133 @@
         public animationsEnabled = true;
         public constantlyUpdateMeshUnderPointer = false;
 
+        // Events
+
+        /**
+        * An event triggered when the scene is disposed.
+        * @type {BABYLON.Observable}
+        */
+        public onDisposeObservable = new Observable<Scene>();
+
+        private _onDisposeObserver: Observer<Scene>;
+        public set onDispose(callback: () => void) {
+            if (this._onDisposeObserver) {
+                this.onDisposeObservable.remove(this._onDisposeObserver);
+            }
+            this._onDisposeObserver = this.onDisposeObservable.add(callback);
+        }
+
+        /**
+        * An event triggered before rendering the scene
+        * @type {BABYLON.Observable}
+        */
+        public onBeforeRenderObservable = new Observable<Scene>();
+
+        private _onBeforeRenderObserver: Observer<Scene>;
+        public set beforeRender(callback: () => void) {
+            if (this._onBeforeRenderObserver) {
+                this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
+            }
+            this._onBeforeRenderObserver = this.onBeforeRenderObservable.add(callback);
+        }
+
+        /**
+        * An event triggered after rendering the scene
+        * @type {BABYLON.Observable}
+        */
+        public onAfterRenderObservable = new Observable<Scene>();
+
+        private _onAfterRenderObserver: Observer<Scene>;
+        public set afterRender(callback: () => void) {
+            if (this._onAfterRenderObserver) {
+                this.onAfterRenderObservable.remove(this._onAfterRenderObserver);
+            }
+            this._onAfterRenderObserver = this.onAfterRenderObservable.add(callback);
+        }
+
+        /**
+        * An event triggered when the scene is ready
+        * @type {BABYLON.Observable}
+        */
+        public onReadyObservable = new Observable<Scene>();
+
+        /**
+        * An event triggered before rendering a camera
+        * @type {BABYLON.Observable}
+        */
+        public onBeforeCameraRenderObservable = new Observable<Camera>();
+
+        private _onBeforeCameraRenderObserver: Observer<Camera>;
+        public set beforeCameraRender(callback: () => void) {
+            if (this._onBeforeCameraRenderObserver) {
+                this.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
+            }
+
+            this._onBeforeCameraRenderObserver = this.onBeforeCameraRenderObservable.add(callback);
+        }
+
+        /**
+        * An event triggered after rendering a camera
+        * @type {BABYLON.Observable}
+        */
+        public onAfterCameraRenderObservable = new Observable<Camera>();
+
+        private _onAfterCameraRenderObserver: Observer<Camera>;
+        public set afterCameraRender(callback: () => void) {
+            if (this._onAfterCameraRenderObserver) {
+                this.onAfterCameraRenderObservable.remove(this._onAfterCameraRenderObserver);
+            }
+            this._onAfterCameraRenderObserver = this.onAfterCameraRenderObservable.add(callback);
+        }
+
+        /**
+        * An event triggered when a camera is created
+        * @type {BABYLON.Observable}
+        */
+        public onNewCameraAddedObservable = new Observable<Camera>();
+
+        /**
+        * An event triggered when a camera is removed
+        * @type {BABYLON.Observable}
+        */
+        public onCameraRemovedObservable = new Observable<Camera>();
+
+        /**
+        * An event triggered when a light is created
+        * @type {BABYLON.Observable}
+        */
+        public onNewLightAddedObservable = new Observable<Light>();
+
+        /**
+        * An event triggered when a light is removed
+        * @type {BABYLON.Observable}
+        */
+        public onLightRemovedObservable = new Observable<Light>();
+
+        /**
+        * An event triggered when a geometry is created
+        * @type {BABYLON.Observable}
+        */
+        public onNewGeometryAddedObservable = new Observable<Geometry>();
+
+        /**
+        * An event triggered when a geometry is removed
+        * @type {BABYLON.Observable}
+        */
+        public onGeometryRemovedObservable = new Observable<Geometry>();
+
+        /**
+        * An event triggered when a mesh is created
+        * @type {BABYLON.Observable}
+        */
+        public onNewMeshAddedObservable = new Observable<AbstractMesh>();
+
+        /**
+        * An event triggered when a mesh is removed
+        * @type {BABYLON.Observable}
+        */
+        public onMeshRemovedObservable = new Observable<AbstractMesh>();
+
         // Animations
         public animations: Animation[] = [];
 
@@ -118,8 +229,6 @@
         * @type {BABYLON.Light[]}
         */
         public lights = new Array<Light>();
-        public onNewLightAdded: (newLight?: Light, positionInArray?: number, scene?: Scene) => void;
-        public onLightRemoved: (removedLight?: Light) => void;
 
         // Cameras
         /**
@@ -128,8 +237,6 @@
         * @type {BABYLON.Camera[]}
         */
         public cameras = new Array<Camera>();
-        public onNewCameraAdded: (newCamera?: Camera, positionInArray?: number, scene?: Scene) => void;
-        public onCameraRemoved: (removedCamera?: Camera) => void;
         public activeCameras = new Array<Camera>();
         public activeCamera: Camera;
 
@@ -140,13 +247,9 @@
         * @type {BABYLON.AbstractMesh[]}
         */
         public meshes = new Array<AbstractMesh>();
-        public onNewMeshAdded: (newMesh?: AbstractMesh, positionInArray?: number, scene?: Scene) => void;
-        public onMeshRemoved: (removedMesh?: AbstractMesh) => void;
 
         // Geometries
         private _geometries = new Array<Geometry>();
-        public onGeometryAdded: (newGeometry?: Geometry) => void;
-        public onGeometryRemoved: (removedGeometry?: Geometry) => void;
 
         public materials = new Array<Material>();
         public multiMaterials = new Array<MultiMaterial>();
@@ -246,13 +349,8 @@
         private _intermediateRendering = false;
 
         public _toBeDisposed = new SmartArray<IDisposable>(256);
-
-        private _onReadyCallbacks = new Array<() => void>();
         private _pendingData = [];//ANY
 
-        private _onBeforeRenderCallbacks = new Array<() => void>();
-        private _onAfterRenderCallbacks = new Array<() => void>();
-
         private _activeMeshes = new SmartArray<Mesh>(256);
         private _processedMaterials = new SmartArray<Material>(256);
         private _renderTargets = new SmartArray<RenderTargetTexture>(256);
@@ -746,27 +844,19 @@
         }
 
         public registerBeforeRender(func: () => void): void {
-            this._onBeforeRenderCallbacks.push(func);
+            this.onBeforeRenderObservable.add(func);
         }
 
         public unregisterBeforeRender(func: () => void): void {
-            var index = this._onBeforeRenderCallbacks.indexOf(func);
-
-            if (index > -1) {
-                this._onBeforeRenderCallbacks.splice(index, 1);
-            }
+            this.onBeforeRenderObservable.removeCallback(func);
         }
 
         public registerAfterRender(func: () => void): void {
-            this._onAfterRenderCallbacks.push(func);
+            this.onAfterRenderObservable.add(func);
         }
 
         public unregisterAfterRender(func: () => void): void {
-            var index = this._onAfterRenderCallbacks.indexOf(func);
-
-            if (index > -1) {
-                this._onAfterRenderCallbacks.splice(index, 1);
-            }
+            this.onAfterRenderObservable.removeCallback(func);
         }
 
         public _addPendingData(data): void {
@@ -790,7 +880,7 @@
          * @param {Function} func - the function to be executed.
          */
         public executeWhenReady(func: () => void): void {
-            this._onReadyCallbacks.push(func);
+            this.onReadyObservable.add(func);
 
             if (this._executeWhenReadyTimeoutId !== -1) {
                 return;
@@ -803,11 +893,9 @@
 
         public _checkIsReady() {
             if (this.isReady()) {
-                this._onReadyCallbacks.forEach(func => {
-                    func();
-                });
+                this.onReadyObservable.notifyObservers(this);
 
-                this._onReadyCallbacks = [];
+                this.onReadyObservable.clear();
                 this._executeWhenReadyTimeoutId = -1;
                 return;
             }
@@ -944,9 +1032,7 @@
             //notify the collision coordinator
             this.collisionCoordinator.onMeshAdded(newMesh);
 
-            if (this.onNewMeshAdded) {
-                this.onNewMeshAdded(newMesh, position, this);
-            }
+            this.onNewMeshAddedObservable.notifyObservers(newMesh);
         }
 
         public removeMesh(toRemove: AbstractMesh): number {
@@ -958,9 +1044,8 @@
             //notify the collision coordinator
             this.collisionCoordinator.onMeshRemoved(toRemove);
 
-            if (this.onMeshRemoved) {
-                this.onMeshRemoved(toRemove);
-            }
+            this.onMeshRemovedObservable.notifyObservers(toRemove);
+
             return index;
         }
 
@@ -980,9 +1065,7 @@
                 // Remove from the scene if mesh found 
                 this.lights.splice(index, 1);
             }
-            if (this.onLightRemoved) {
-                this.onLightRemoved(toRemove);
-            }
+            this.onLightRemovedObservable.notifyObservers(toRemove);
             return index;
         }
 
@@ -1006,26 +1089,20 @@
                     this.activeCamera = null;
                 }
             }
-            if (this.onCameraRemoved) {
-                this.onCameraRemoved(toRemove);
-            }
+            this.onCameraRemovedObservable.notifyObservers(toRemove);
             return index;
         }
 
         public addLight(newLight: Light) {
             newLight.uniqueId = this._uniqueIdCounter++;
             var position = this.lights.push(newLight);
-            if (this.onNewLightAdded) {
-                this.onNewLightAdded(newLight, position, this);
-            }
+            this.onNewLightAddedObservable.notifyObservers(newLight);
         }
 
         public addCamera(newCamera: Camera) {
             newCamera.uniqueId = this._uniqueIdCounter++;
             var position = this.cameras.push(newCamera);
-            if (this.onNewCameraAdded) {
-                this.onNewCameraAdded(newCamera, position, this);
-            }
+            this.onNewCameraAddedObservable.notifyObservers(newCamera);
         }
         
         /**
@@ -1279,9 +1356,7 @@
             //notify the collision coordinator
             this.collisionCoordinator.onGeometryAdded(geometry);
 
-            if (this.onGeometryAdded) {
-                this.onGeometryAdded(geometry);
-            }
+            this.onNewGeometryAddedObservable.notifyObservers(geometry);
 
             return true;
         }
@@ -1300,9 +1375,7 @@
                 //notify the collision coordinator
                 this.collisionCoordinator.onGeometryDeleted(geometry);
 
-                if (this.onGeometryRemoved) {
-                    this.onGeometryRemoved(geometry);
-                }
+                this.onGeometryRemovedObservable.notifyObservers(geometry);
                 return true;
             }
             return false;
@@ -1678,10 +1751,8 @@
             this._renderId++;
             this.updateTransformMatrix();
 
-            if (this.beforeCameraRender) {
-                this.beforeCameraRender(this.activeCamera);
-            }
-
+            this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
+            
             // Meshes
             var beforeEvaluateActiveMeshesDate = Tools.Now;
             Tools.StartPerformanceCounter("Active meshes evaluation");
@@ -1791,9 +1862,7 @@
             // Reset some special arrays
             this._renderTargets.reset();
 
-            if (this.afterCameraRender) {
-                this.afterCameraRender(this.activeCamera);
-            }
+            this.onAfterCameraRenderObservable.notifyObservers(this.activeCamera);
 
             Tools.EndPerformanceCounter("Rendering camera " + this.activeCamera.name);
         }
@@ -1895,13 +1964,7 @@
             }
 
             // Before render
-            if (this.beforeRender) {
-                this.beforeRender();
-            }
-            var callbackIndex: number;
-            for (callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
-                this._onBeforeRenderCallbacks[callbackIndex]();
-            }
+            this.onBeforeRenderObservable.notifyObservers(this);
 
             // Customs render targets
             var beforeRenderTargetDate = Tools.Now;
@@ -2006,9 +2069,7 @@
                 this.afterRender();
             }
 
-            for (callbackIndex = 0; callbackIndex < this._onAfterRenderCallbacks.length; callbackIndex++) {
-                this._onAfterRenderCallbacks[callbackIndex]();
-            }
+            this.onAfterRenderObservable.notifyObservers(this);
 
             // Cleaning
             for (var index = 0; index < this._toBeDisposed.length; index++) {
@@ -2192,8 +2253,8 @@
                 this.onDispose();
             }
 
-            this._onBeforeRenderCallbacks = [];
-            this._onAfterRenderCallbacks = [];
+            this.onBeforeRenderObservable.clear();
+            this.onAfterRenderObservable.clear();
 
             this.detachControl();