瀏覽代碼

Async Native JS Events

Added the ability to fetch native javascript events directly from
babylon's scene.
Events supported are "pointerOut", "pointerOver", "pick" over meshes,
"nodeAdded" for cameras, lights and meshes (over the canvas).
The scene has a flag to disable it (asyncEventTriggers), default is
true.
More will be details in a short documentation that will follow.
Raanan Weber 10 年之前
父節點
當前提交
33f9bd415a

+ 2 - 2
Babylon/Actions/babylon.actionManager.js

@@ -8,9 +8,9 @@
             this.meshUnderPointer = meshUnderPointer;
             this.sourceEvent = sourceEvent;
         }
-        ActionEvent.CreateNew = function (source) {
+        ActionEvent.CreateNew = function (source, sourceEvent) {
             var scene = source.getScene();
-            return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer);
+            return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, sourceEvent);
         };
 
         ActionEvent.CreateNewFromScene = function (scene, evt) {

+ 2 - 2
Babylon/Actions/babylon.actionManager.ts

@@ -5,9 +5,9 @@
             
         }
 
-        public static CreateNew(source: AbstractMesh): ActionEvent {
+        public static CreateNew(source: AbstractMesh, sourceEvent?: Event): ActionEvent {
             var scene = source.getScene();
-            return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer);
+            return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, sourceEvent);
         }
 
         public static CreateNewFromScene(scene: Scene, evt:Event): ActionEvent {

+ 2 - 3
Babylon/Cameras/babylon.camera.js

@@ -31,7 +31,7 @@ var BABYLON;
             this._postProcesses = new Array();
             this._postProcessesTakenIndices = [];
 
-            scene.cameras.push(this);
+            scene.addCamera(this);
 
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
@@ -287,8 +287,7 @@ var BABYLON;
 
         Camera.prototype.dispose = function () {
             // Remove from scene
-            var index = this.getScene().cameras.indexOf(this);
-            this.getScene().cameras.splice(index, 1);
+            this.getScene().removeCamera(this);
 
             for (var i = 0; i < this._postProcessesTakenIndices.length; ++i) {
                 this._postProcesses[this._postProcessesTakenIndices[i]].dispose(this);

+ 2 - 3
Babylon/Cameras/babylon.camera.ts

@@ -29,7 +29,7 @@
         constructor(name: string, public position: Vector3, scene: Scene) {
             super(name, scene);
 
-            scene.cameras.push(this);
+            scene.addCamera(this);
 
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
@@ -303,8 +303,7 @@
 
         public dispose(): void {
             // Remove from scene
-            var index = this.getScene().cameras.indexOf(this);
-            this.getScene().cameras.splice(index, 1);
+            this.getScene().removeCamera(this);
 
             // Postprocesses
             for (var i = 0; i < this._postProcessesTakenIndices.length; ++i) {

+ 1 - 1
Babylon/Culling/babylon.boundingBox.js

@@ -171,4 +171,4 @@
     })();
     BABYLON.BoundingBox = BoundingBox;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.boundingBox.js.map
+//# sourceMappingURL=babylon.BoundingBox.js.map

+ 2 - 3
Babylon/Lights/babylon.light.js

@@ -19,7 +19,7 @@ var BABYLON;
             this._excludedMeshesIds = new Array();
             this._includedOnlyMeshesIds = new Array();
 
-            scene.lights.push(this);
+            scene.addLight(this);
         }
         Light.prototype.getShadowGenerator = function () {
             return this._shadowGenerator;
@@ -73,8 +73,7 @@ var BABYLON;
             }
 
             // Remove from scene
-            var index = this.getScene().lights.indexOf(this);
-            this.getScene().lights.splice(index, 1);
+            this.getScene().removeLight(this);
         };
         return Light;
     })(BABYLON.Node);

+ 2 - 3
Babylon/Lights/babylon.light.ts

@@ -15,7 +15,7 @@
         constructor(name: string, scene: Scene) {
             super(name, scene);
 
-            scene.lights.push(this);
+            scene.addLight(this);
         }
 
         public getShadowGenerator(): ShadowGenerator {
@@ -70,8 +70,7 @@
             }
 
             // Remove from scene
-            var index = this.getScene().lights.indexOf(this);
-            this.getScene().lights.splice(index, 1);
+            this.getScene().removeLight(this);
         }
     }
 } 

+ 3 - 7
Babylon/Mesh/babylon.abstractMesh.js

@@ -62,7 +62,7 @@ var BABYLON;
             this._renderId = 0;
             this._intersectionsInProgress = new Array();
 
-            scene.meshes.push(this);
+            scene.addMesh(this);
         }
         Object.defineProperty(AbstractMesh, "BILLBOARDMODE_NONE", {
             get: function () {
@@ -794,14 +794,10 @@ var BABYLON;
             this.releaseSubMeshes();
 
             // Remove from scene
-            var index = this.getScene().meshes.indexOf(this);
-            if (index != -1) {
-                // Remove from the scene if mesh found
-                this.getScene().meshes.splice(index, 1);
-            }
+            this.getScene().removeMesh(this);
 
             if (!doNotRecurse) {
-                for (index = 0; index < this.getScene().particleSystems.length; index++) {
+                for (var index = 0; index < this.getScene().particleSystems.length; index++) {
                     if (this.getScene().particleSystems[index].emitter == this) {
                         this.getScene().particleSystems[index].dispose();
                         index--;

+ 3 - 7
Babylon/Mesh/babylon.abstractMesh.ts

@@ -100,7 +100,7 @@
         constructor(name: string, scene: Scene) {
             super(name, scene);
 
-            scene.meshes.push(this);
+            scene.addMesh(this);
         }
 
         // Methods
@@ -786,15 +786,11 @@
             this.releaseSubMeshes();
 
             // Remove from scene
-            var index = this.getScene().meshes.indexOf(this);
-            if (index != -1) {
-                // Remove from the scene if mesh found 
-                this.getScene().meshes.splice(index, 1);
-            }
+            this.getScene().removeMesh(this);
 
             if (!doNotRecurse) {
                 // Particles
-                for (index = 0; index < this.getScene().particleSystems.length; index++) {
+                for (var index = 0; index < this.getScene().particleSystems.length; index++) {
                     if (this.getScene().particleSystems[index].emitter == this) {
                         this.getScene().particleSystems[index].dispose();
                         index--;

+ 1 - 0
Babylon/babylon.node.ts

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

+ 109 - 16
Babylon/babylon.scene.js

@@ -56,6 +56,7 @@
             this.importedMeshesFiles = new Array();
             this._actionManagers = new Array();
             this._meshesForIntersections = new BABYLON.SmartArray(256);
+            this.asyncEventTriggers = true;
             // Procedural textures
             this.proceduralTexturesEnabled = true;
             this._proceduralTextures = new Array();
@@ -206,8 +207,8 @@
                 _this._updatePointerPosition(evt);
 
                 var pickResult = _this.pick(_this._pointerX, _this._pointerY, function (mesh) {
-                    return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasPointerTriggers;
-                }, false, _this.cameraToUseForPointers);
+                    return mesh.isPickable && mesh.isVisible && mesh.isReady();
+                } /* && mesh.actionManager && mesh.actionManager.hasPointerTriggers*/ , false, _this.cameraToUseForPointers);
 
                 if (pickResult.hit) {
                     _this._meshUnderPointer = pickResult.pickedMesh;
@@ -226,7 +227,7 @@
 
                 if (!_this.onPointerDown) {
                     predicate = function (mesh) {
-                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasPickTriggers;
+                        return mesh.isPickable && mesh.isVisible && mesh.isReady();
                     };
                 }
 
@@ -235,19 +236,21 @@
                 var pickResult = _this.pick(_this._pointerX, _this._pointerY, predicate, false, _this.cameraToUseForPointers);
 
                 if (pickResult.hit) {
-                    if (pickResult.pickedMesh.actionManager) {
+                    var actionEvent = BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt);
+                    _this._triggerJsEvent("pick", actionEvent, pickResult.pickedMesh.htmlId);
+                    if (pickResult.pickedMesh.actionManager && pickResult.pickedMesh.actionManager.hasPickTriggers) {
                         switch (evt.button) {
                             case 0:
-                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh));
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, actionEvent);
                                 break;
                             case 1:
-                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh));
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, actionEvent);
                                 break;
                             case 2:
-                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh));
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, actionEvent);
                                 break;
                         }
-                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh));
+                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, actionEvent);
                     }
                 }
 
@@ -257,14 +260,18 @@
             };
 
             this._onKeyDown = function (evt) {
+                var actionEvent = BABYLON.ActionEvent.CreateNewFromScene(_this, evt);
+                _this._triggerJsEvent("keyDown", actionEvent);
                 if (_this.actionManager) {
-                    _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyDownTrigger, BABYLON.ActionEvent.CreateNewFromScene(_this, evt));
+                    _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyDownTrigger, actionEvent);
                 }
             };
 
             this._onKeyUp = function (evt) {
+                var actionEvent = BABYLON.ActionEvent.CreateNewFromScene(_this, evt);
+                _this._triggerJsEvent("keyUp", actionEvent);
                 if (_this.actionManager) {
-                    _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyUpTrigger, BABYLON.ActionEvent.CreateNewFromScene(_this, evt));
+                    _this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyUpTrigger, actionEvent);
                 }
             };
 
@@ -276,6 +283,16 @@
             window.addEventListener("keyup", this._onKeyUp, false);
         };
 
+        Scene.prototype._triggerJsEvent = function (evt, eventData, htmlId) {
+            if (!this.asyncEventTriggers) {
+                return;
+            }
+            var newEvent = document.createEvent('CustomEvent');
+            newEvent.initCustomEvent(evt, true, true, eventData);
+            var htmlElement = htmlId ? document.querySelector("#" + htmlId) : this._engine.getRenderingCanvas();
+            htmlElement.dispatchEvent(newEvent);
+        };
+
         Scene.prototype.detachControl = function () {
             var eventPrefix = BABYLON.Tools.GetPointerPrefix();
             this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "move", this._onPointerMove);
@@ -468,6 +485,70 @@
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
         };
 
+        //Node adding
+        Scene.prototype.addMesh = function (mesh) {
+            this.meshes.push(mesh);
+            this._generateHtmlElement("mesh", mesh);
+        };
+
+        Scene.prototype.removeMesh = function (mesh) {
+            var index = this.meshes.indexOf(mesh);
+            if (index != -1) {
+                this.meshes.splice(index, 1);
+            }
+            this._removeHtmlElement("mesh", mesh.htmlId);
+        };
+
+        Scene.prototype.addLight = function (light) {
+            this.lights.push(light);
+            this._generateHtmlElement("light", light);
+        };
+
+        Scene.prototype.removeLight = function (light) {
+            var index = this.lights.indexOf(light);
+            if (index != -1) {
+                this.lights.splice(index, 1);
+            }
+            this._removeHtmlElement("light", light.htmlId);
+        };
+
+        Scene.prototype.addCamera = function (camera) {
+            this.cameras.push(camera);
+            this._generateHtmlElement("camera", camera);
+        };
+
+        Scene.prototype.removeCamera = function (camera) {
+            var index = this.cameras.indexOf(camera);
+            if (index != -1) {
+                this.cameras.splice(index, 1);
+            }
+            this._removeHtmlElement("camera", camera.htmlId);
+        };
+
+        Scene.prototype._generateHtmlElement = function (kind, node) {
+            var element = document.createElement(kind);
+            var htmlId = kind + "-" + node.id;
+            var idNumeration = 0;
+            if (document.getElementById(htmlId)) {
+                while (document.getElementById(htmlId + "_" + idNumeration++)) {
+                }
+
+                htmlId = htmlId + "_" + idNumeration;
+                BABYLON.Tools.Warn("Extra " + kind + " with the same id was added with id " + htmlId);
+            }
+            node.htmlId = htmlId;
+            element.id = htmlId;
+            var canvas = this.getEngine().getRenderingCanvas();
+            canvas.insertBefore(element, null);
+            this._triggerJsEvent("nodeAdded", { kind: kind, originalId: node.id, htmlId: htmlId });
+        };
+
+        Scene.prototype._removeHtmlElement = function (kind, id) {
+            var element = document.getElementById(id);
+            element.parentElement.removeChild(element);
+            this._triggerJsEvent("nodeRemoved", { kind: kind, id: id });
+        };
+
         // Methods
         Scene.prototype.setActiveCameraByID = function (id) {
             var camera = this.getCameraByID(id);
@@ -952,10 +1033,14 @@
                         var currentIntersectionInProgress = sourceMesh._intersectionsInProgress.indexOf(otherMesh);
 
                         if (areIntersecting && currentIntersectionInProgress === -1 && action.trigger == BABYLON.ActionManager.OnIntersectionEnterTrigger) {
-                            sourceMesh.actionManager.processTrigger(BABYLON.ActionManager.OnIntersectionEnterTrigger, BABYLON.ActionEvent.CreateNew(sourceMesh));
+                            var actionEvent = BABYLON.ActionEvent.CreateNew(sourceMesh);
+                            this._triggerJsEvent("intersectionEnter", actionEvent, sourceMesh.htmlId);
+                            sourceMesh.actionManager.processTrigger(BABYLON.ActionManager.OnIntersectionEnterTrigger, actionEvent);
                             sourceMesh._intersectionsInProgress.push(otherMesh);
                         } else if (!areIntersecting && currentIntersectionInProgress > -1 && action.trigger == BABYLON.ActionManager.OnIntersectionExitTrigger) {
-                            sourceMesh.actionManager.processTrigger(BABYLON.ActionManager.OnIntersectionExitTrigger, BABYLON.ActionEvent.CreateNew(sourceMesh));
+                            var actionEvent = BABYLON.ActionEvent.CreateNew(sourceMesh);
+                            this._triggerJsEvent("intersectionExit", actionEvent, sourceMesh.htmlId);
+                            sourceMesh.actionManager.processTrigger(BABYLON.ActionManager.OnIntersectionExitTrigger, actionEvent);
 
                             var indexOfOther = sourceMesh._intersectionsInProgress.indexOf(otherMesh);
 
@@ -1338,13 +1423,21 @@
                 return;
             }
 
-            if (this._pointerOverMesh && this._pointerOverMesh.actionManager) {
-                this._pointerOverMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOutTrigger, BABYLON.ActionEvent.CreateNew(this._pointerOverMesh));
+            if (this._pointerOverMesh) {
+                var actionEvent = BABYLON.ActionEvent.CreateNew(this._pointerOverMesh);
+                this._triggerJsEvent("pointerOut", actionEvent, this._pointerOverMesh.htmlId);
+                if (this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
+                    this._pointerOverMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOutTrigger, actionEvent);
+                }
             }
 
             this._pointerOverMesh = mesh;
-            if (this._pointerOverMesh && this._pointerOverMesh.actionManager) {
-                this._pointerOverMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOverTrigger, BABYLON.ActionEvent.CreateNew(this._pointerOverMesh));
+            if (this._pointerOverMesh) {
+                actionEvent = BABYLON.ActionEvent.CreateNew(this._pointerOverMesh);
+                this._triggerJsEvent("pointerOver", actionEvent, this._pointerOverMesh.htmlId);
+                if (this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
+                    this._pointerOverMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOverTrigger, actionEvent);
+                }
             }
         };
 

+ 114 - 17
Babylon/babylon.scene.ts

@@ -112,6 +112,7 @@
         public actionManager: ActionManager;
         public _actionManagers = new Array<ActionManager>();
         private _meshesForIntersections = new SmartArray<AbstractMesh>(256);
+        public asyncEventTriggers = true;
 
         // Procedural textures
         public proceduralTexturesEnabled = true;
@@ -281,7 +282,7 @@
                 this._updatePointerPosition(evt);
 
                 var pickResult = this.pick(this._pointerX, this._pointerY,
-                    (mesh: AbstractMesh): boolean => mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasPointerTriggers,
+                    (mesh: AbstractMesh): boolean => mesh.isPickable && mesh.isVisible && mesh.isReady()/* && mesh.actionManager && mesh.actionManager.hasPointerTriggers*/,
                     false,
                     this.cameraToUseForPointers);
 
@@ -303,7 +304,7 @@
 
                 if (!this.onPointerDown) {
                     predicate = (mesh: AbstractMesh): boolean => {
-                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasPickTriggers;
+                        return mesh.isPickable && mesh.isVisible && mesh.isReady()/* && mesh.actionManager && mesh.actionManager.hasPickTriggers*/;
                     };
                 }
 
@@ -312,20 +313,22 @@
                 var pickResult = this.pick(this._pointerX, this._pointerY, predicate, false, this.cameraToUseForPointers);
 
                 if (pickResult.hit) {
-                    if (pickResult.pickedMesh.actionManager) {
+                    var actionEvent = ActionEvent.CreateNew(pickResult.pickedMesh, evt);
+                    this._triggerJsEvent("pick", actionEvent, pickResult.pickedMesh.htmlId);
+                    if (pickResult.pickedMesh.actionManager && pickResult.pickedMesh.actionManager.hasPickTriggers) {
                         switch (evt.button) {
                             case 0:
-                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh));
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, actionEvent);
                                 break;
                             case 1:
-                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh));
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, actionEvent);
                                 break;
                             case 2:
-                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh));
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, actionEvent);
                                 break;
                         }
-                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh));
-                    }
+                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, actionEvent);
+                    }                    
                 }
 
                 if (this.onPointerDown) {
@@ -334,18 +337,21 @@
             };
 
             this._onKeyDown = (evt: Event) => {
+                var actionEvent = ActionEvent.CreateNewFromScene(this, evt);
+                this._triggerJsEvent("keyDown", actionEvent);
                 if (this.actionManager) {
-                    this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyDownTrigger, ActionEvent.CreateNewFromScene(this, evt));
+                    this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyDownTrigger, actionEvent);
                 }
             };
 
             this._onKeyUp = (evt: Event) => {
+                var actionEvent = ActionEvent.CreateNewFromScene(this, evt);
+                this._triggerJsEvent("keyUp", actionEvent);
                 if (this.actionManager) {
-                    this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyUpTrigger, ActionEvent.CreateNewFromScene(this, evt));
+                    this.actionManager.processTrigger(BABYLON.ActionManager.OnKeyUpTrigger, actionEvent);
                 }
             };
 
-
             var eventPrefix = Tools.GetPointerPrefix();
             this._engine.getRenderingCanvas().addEventListener(eventPrefix + "move", this._onPointerMove, false);
             this._engine.getRenderingCanvas().addEventListener(eventPrefix + "down", this._onPointerDown, false);
@@ -354,6 +360,16 @@
             window.addEventListener("keyup", this._onKeyUp, false);
         }
 
+        private _triggerJsEvent(evt: string, eventData: any, htmlId?: string) {
+            if (!this.asyncEventTriggers) {
+                return;
+            }
+            var newEvent: CustomEvent = <CustomEvent> document.createEvent('CustomEvent');
+            newEvent.initCustomEvent(evt /*+ "_babylon"*/, true, true, eventData);
+            var htmlElement = htmlId ? document.querySelector("#" + htmlId) : this._engine.getRenderingCanvas();
+            htmlElement.dispatchEvent(newEvent);
+        }
+
         public detachControl() {
             var eventPrefix = Tools.GetPointerPrefix();
             this._engine.getRenderingCanvas().removeEventListener(eventPrefix + "move", this._onPointerMove);
@@ -544,6 +560,71 @@
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
         }
 
+        //Node adding
+        public addMesh(mesh: AbstractMesh) {
+            this.meshes.push(mesh);
+            this._generateHtmlElement("mesh", mesh);
+        }
+
+        public removeMesh(mesh: AbstractMesh) {
+            var index = this.meshes.indexOf(mesh);
+            if (index != -1) {
+                this.meshes.splice(index, 1);
+            }
+            this._removeHtmlElement("mesh", mesh.htmlId);
+        }
+
+        public addLight(light: Light) {
+            this.lights.push(light);
+            this._generateHtmlElement("light", light);
+        }
+
+        public removeLight(light: Light) {
+            var index = this.lights.indexOf(light);
+            if (index != -1) {
+                this.lights.splice(index, 1);
+            }
+            this._removeHtmlElement("light", light.htmlId);
+        }
+
+        public addCamera(camera: Camera) {
+            this.cameras.push(camera);
+            this._generateHtmlElement("camera", camera);
+        }
+
+        public removeCamera(camera: Camera) {
+            var index = this.cameras.indexOf(camera);
+            if (index != -1) {
+                this.cameras.splice(index, 1);
+            }
+            this._removeHtmlElement("camera", camera.htmlId);
+        }
+
+        private _generateHtmlElement(kind: string, node:Node) {
+            var element: HTMLElement = document.createElement(kind);
+            var htmlId = kind + "-" + node.id;
+            var idNumeration = 0;
+            if (document.getElementById(htmlId)) {
+                while (document.getElementById(htmlId + "_" + idNumeration++)) {
+                    
+                }
+                
+                htmlId = htmlId + "_" + idNumeration;
+                Tools.Warn("Extra " + kind + " with the same id was added with id " + htmlId);
+            }
+            node.htmlId = htmlId;
+            element.id = htmlId;
+            var canvas = this.getEngine().getRenderingCanvas();
+            canvas.insertBefore(element, null);
+            this._triggerJsEvent("nodeAdded", { kind: kind, originalId:node.id, htmlId: htmlId });
+        }
+
+        private _removeHtmlElement(kind:string, id: string) {
+            var element = document.getElementById(id);
+            element.parentElement.removeChild(element);
+            this._triggerJsEvent("nodeRemoved", { kind: kind, id: id });
+        }
+
         // Methods
         public setActiveCameraByID(id: string): Camera {
             var camera = this.getCameraByID(id);
@@ -1029,11 +1110,15 @@
                         var currentIntersectionInProgress = sourceMesh._intersectionsInProgress.indexOf(otherMesh);
 
                         if (areIntersecting && currentIntersectionInProgress === -1 && action.trigger == ActionManager.OnIntersectionEnterTrigger) {
-                            sourceMesh.actionManager.processTrigger(ActionManager.OnIntersectionEnterTrigger, ActionEvent.CreateNew(sourceMesh));
+                            var actionEvent = ActionEvent.CreateNew(sourceMesh);
+                            this._triggerJsEvent("intersectionEnter", actionEvent, sourceMesh.htmlId);
+                            sourceMesh.actionManager.processTrigger(ActionManager.OnIntersectionEnterTrigger, actionEvent);
                             sourceMesh._intersectionsInProgress.push(otherMesh);
 
                         } else if (!areIntersecting && currentIntersectionInProgress > -1 && action.trigger == ActionManager.OnIntersectionExitTrigger) {
-                            sourceMesh.actionManager.processTrigger(ActionManager.OnIntersectionExitTrigger, ActionEvent.CreateNew(sourceMesh));
+                            var actionEvent = ActionEvent.CreateNew(sourceMesh);
+                            this._triggerJsEvent("intersectionExit", actionEvent, sourceMesh.htmlId);
+                            sourceMesh.actionManager.processTrigger(ActionManager.OnIntersectionExitTrigger, actionEvent);
 
                             var indexOfOther = sourceMesh._intersectionsInProgress.indexOf(otherMesh);
 
@@ -1419,14 +1504,26 @@
                 return;
             }
 
-            if (this._pointerOverMesh && this._pointerOverMesh.actionManager) {
-                this._pointerOverMesh.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNew(this._pointerOverMesh));
+            if (this._pointerOverMesh) {
+                var actionEvent = ActionEvent.CreateNew(this._pointerOverMesh);
+                this._triggerJsEvent("pointerOut", actionEvent, this._pointerOverMesh.htmlId);
+                if (this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
+                    this._pointerOverMesh.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, actionEvent);
+                }
             }
 
+            
+
             this._pointerOverMesh = mesh;
-            if (this._pointerOverMesh && this._pointerOverMesh.actionManager) {
-                this._pointerOverMesh.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNew(this._pointerOverMesh));
+            if (this._pointerOverMesh) {
+                actionEvent = ActionEvent.CreateNew(this._pointerOverMesh);
+                this._triggerJsEvent("pointerOver", actionEvent, this._pointerOverMesh.htmlId);
+                if (this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
+                    this._pointerOverMesh.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, actionEvent);
+                }
             }
+
+            
         }
 
         public getPointerOverMesh(): AbstractMesh {