Selaa lähdekoodia

Merge pull request #1072 from nockawa/master

Introduced a Scene.onPointerObservable property
David Catuhe 9 vuotta sitten
vanhempi
commit
bc532af726

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

@@ -25,6 +25,12 @@
     - New SPS property `computeBoundingBox` ([jerome](https://github.com/jbousquie))  
     - Added a new OnPickOut trigger fired when you release the pointer button outside of a mesh or sprite. ([deltakosh](https://github.com/deltakosh))
     - Added support for OnPointerOver and OnPointerOut for sprites. ([deltakosh](https://github.com/deltakosh))
+    - Added an optional predicate on Node.getDescendants, Node.getChildren to filter out Nodes based on a callback execution. ([nockawa](https://github.com/nockawa))
+    - Added Ray.intersectionPlane & intersectionSegment. ([nockawa](https://github.com/nockawa))
+    - LinesMesh class now supports Intersection. Added the intersectionThreshold property to set a tolerance margin during intersection with wire lines. ([nockawa](https://github.com/nockawa))
+    - Geometry.boundingBias property to enlarge the boundingInfo objects ([nockawa](https://github.com/nockawa))
+    - Tools.ExtractMinAndMax & ExtractMinAndMaxIndexed now supports an optional Bias for Extent computation.
+
   - **API doc**
     - class `SolidParticleSystem` documented
     - class `MeshBuilder` documented

+ 10 - 8
src/Cameras/Inputs/babylon.arcrotatecamera.input.mousewheel.ts

@@ -1,14 +1,16 @@
 module BABYLON {
     export class ArcRotateCameraMouseWheelInput implements ICameraInput<ArcRotateCamera> {
         camera: ArcRotateCamera;
-        
-        private _wheel: (e: MouseWheelEvent) => void;
+
+        private _wheel: (p: PointerInfo, s: EventState) => void;
+        private _observer: Observer<PointerInfo>;
 
         @serialize()
         public wheelPrecision = 3.0;
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
-            this._wheel = event => {
+            this._wheel = (p, s) => {
+                var event = <MouseWheelEvent>p.event;
                 var delta = 0;
                 if (event.wheelDelta) {
                     delta = event.wheelDelta / (this.wheelPrecision * 40);
@@ -25,14 +27,14 @@ module BABYLON {
                     }
                 }
             };
-            element.addEventListener('mousewheel', this._wheel, false);
-            element.addEventListener('DOMMouseScroll', this._wheel, false);
+
+            this._observer = this.camera.getScene().onPointerObservable.add(this._wheel);
         }
 
         public detachControl(element: HTMLElement) {
-            if (this._wheel && element){
-                element.removeEventListener('mousewheel', this._wheel);
-                element.removeEventListener('DOMMouseScroll', this._wheel);
+            if (this._observer && element) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
                 this._wheel = null;
             }
         }

+ 86 - 91
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts

@@ -20,11 +20,10 @@ module BABYLON {
         private _isCtrlPushed: boolean = false;
         public pinchInwards = true;
 
+        private _pointerInput: (p: PointerInfo, s: EventState) => void;
+        private _observer: Observer<PointerInfo>;
         private _onKeyDown: (e: KeyboardEvent) => any;
         private _onKeyUp: (e: KeyboardEvent) => any;
-        private _onPointerDown: (e: PointerEvent) => void;
-        private _onPointerUp: (e: PointerEvent) => void;
-        private _onPointerMove: (e: PointerEvent) => void;
         private _onMouseMove: (e: MouseEvent) => any;
         private _onGestureStart: (e: PointerEvent) => void;
         private _onGesture: (e: MSGestureEvent) => void;
@@ -38,6 +37,85 @@ module BABYLON {
             var pointers = new SmartCollection();
             var previousPinchDistance = 0;
 
+            this._pointerInput = (p, s) => {
+                var evt = <PointerEvent>p.event;
+                if (p.type === PointerEventType.PointerDown) {
+                    evt.srcElement.setPointerCapture(evt.pointerId);
+
+                    // Manage panning with right click
+                    this._isRightClick = evt.button === 2;
+
+                    // manage pointers
+                    pointers.add(evt.pointerId, { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
+                    cacheSoloPointer = pointers.item(evt.pointerId);
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                } else if (p.type === PointerEventType.PointerUp) {
+                    evt.srcElement.releasePointerCapture(evt.pointerId);
+
+                    cacheSoloPointer = null;
+                    previousPinchDistance = 0;
+
+                    //would be better to use pointers.remove(evt.pointerId) for multitouch gestures, 
+                    //but emptying completly pointers collection is required to fix a bug on iPhone : 
+                    //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
+                    //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
+                    pointers.empty();
+
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                } else if (p.type === PointerEventType.PointerMove) {
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+
+                    switch (pointers.count) {
+                        case 1: //normal camera rotation
+                            if (this.panningSensibility !== 0 && ((this._isCtrlPushed && this.camera._useCtrlForPanning) || (!this.camera._useCtrlForPanning && this._isRightClick))) {
+                                this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
+                                this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
+                            } else {
+                                var offsetX = evt.clientX - cacheSoloPointer.x;
+                                var offsetY = evt.clientY - cacheSoloPointer.y;
+                                this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
+                                this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
+                            }
+                            cacheSoloPointer.x = evt.clientX;
+                            cacheSoloPointer.y = evt.clientY;
+                            break;
+
+                        case 2: //pinch
+                            //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
+                            pointers.item(evt.pointerId).x = evt.clientX;
+                            pointers.item(evt.pointerId).y = evt.clientY;
+                            var direction = this.pinchInwards ? 1 : -1;
+                            var distX = pointers.getItemByIndex(0).x - pointers.getItemByIndex(1).x;
+                            var distY = pointers.getItemByIndex(0).y - pointers.getItemByIndex(1).y;
+                            var pinchSquaredDistance = (distX * distX) + (distY * distY);
+                            if (previousPinchDistance === 0) {
+                                previousPinchDistance = pinchSquaredDistance;
+                                return;
+                            }
+
+                            if (pinchSquaredDistance !== previousPinchDistance) {
+                                this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (this.pinchPrecision * ((this.angularSensibilityX + this.angularSensibilityY) / 2) * direction);
+                                previousPinchDistance = pinchSquaredDistance;
+                            }
+                            break;
+
+                        default:
+                            if (pointers.item(evt.pointerId)) {
+                                pointers.item(evt.pointerId).x = evt.clientX;
+                                pointers.item(evt.pointerId).y = evt.clientY;
+                            }
+                    }
+                }
+            }
+
+            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput);
+
             this._onContextMenu = evt => {
                 evt.preventDefault();
             };
@@ -61,81 +139,6 @@ module BABYLON {
                 this._isCtrlPushed = evt.ctrlKey;
             };
 
-            this._onPointerDown = evt => {
-                // Manage panning with right click
-                this._isRightClick = evt.button === 2;
-
-                // manage pointers
-                pointers.add(evt.pointerId, { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
-                cacheSoloPointer = pointers.item(evt.pointerId);
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-            };
-
-            this._onPointerUp = evt => {
-                cacheSoloPointer = null;
-                previousPinchDistance = 0;
-
-                //would be better to use pointers.remove(evt.pointerId) for multitouch gestures, 
-                //but emptying completly pointers collection is required to fix a bug on iPhone : 
-                //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
-                //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
-                pointers.empty();
-
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-            };
-
-            this._onPointerMove = evt => {
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-
-                switch (pointers.count) {
-
-                    case 1: //normal camera rotation
-                        if (this.panningSensibility !== 0 && ((this._isCtrlPushed && this.camera._useCtrlForPanning) || (!this.camera._useCtrlForPanning && this._isRightClick))) {
-                            this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
-                            this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
-                        } else {
-                            var offsetX = evt.clientX - cacheSoloPointer.x;
-                            var offsetY = evt.clientY - cacheSoloPointer.y;
-                            this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
-                            this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
-                        }
-                        cacheSoloPointer.x = evt.clientX;
-                        cacheSoloPointer.y = evt.clientY;
-                        break;
-
-                    case 2: //pinch
-                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                        pointers.item(evt.pointerId).x = evt.clientX;
-                        pointers.item(evt.pointerId).y = evt.clientY;
-                        var direction = this.pinchInwards ? 1 : -1;
-                        var distX = pointers.getItemByIndex(0).x - pointers.getItemByIndex(1).x;
-                        var distY = pointers.getItemByIndex(0).y - pointers.getItemByIndex(1).y;
-                        var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                        if (previousPinchDistance === 0) {
-                            previousPinchDistance = pinchSquaredDistance;
-                            return;
-                        }
-
-                        if (pinchSquaredDistance !== previousPinchDistance) {
-                            this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (this.pinchPrecision * ((this.angularSensibilityX + this.angularSensibilityY) / 2) * direction);
-                            previousPinchDistance = pinchSquaredDistance;
-                        }
-                        break;
-
-                    default:
-                        if (pointers.item(evt.pointerId)) {
-                            pointers.item(evt.pointerId).x = evt.clientX;
-                            pointers.item(evt.pointerId).y = evt.clientY;
-                        }
-                }
-            };
-
             this._onMouseMove = evt => {
                 if (!engine.isPointerLock) {
                     return;
@@ -176,11 +179,7 @@ module BABYLON {
                     }
                 }
             };
-        
-            element.addEventListener(eventPrefix + "down", this._onPointerDown, false);
-            element.addEventListener(eventPrefix + "up", this._onPointerUp, false);
-            element.addEventListener(eventPrefix + "out", this._onPointerUp, false);
-            element.addEventListener(eventPrefix + "move", this._onPointerMove, false);
+
             element.addEventListener("mousemove", this._onMouseMove, false);
             element.addEventListener("MSPointerDown", this._onGestureStart, false);
             element.addEventListener("MSGestureChange", this._onGesture, false);
@@ -193,12 +192,11 @@ module BABYLON {
         }
 
         public detachControl(element: HTMLElement) {
-            if (element && this._onPointerDown) {
+            if (element && this._observer) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
+
                 element.removeEventListener("contextmenu", this._onContextMenu);
-                element.removeEventListener(eventPrefix + "down", this._onPointerDown);
-                element.removeEventListener(eventPrefix + "up", this._onPointerUp);
-                element.removeEventListener(eventPrefix + "out", this._onPointerUp);
-                element.removeEventListener(eventPrefix + "move", this._onPointerMove);
                 element.removeEventListener("mousemove", this._onMouseMove);
                 element.removeEventListener("MSPointerDown", this._onGestureStart);
                 element.removeEventListener("MSGestureChange", this._onGesture);
@@ -209,9 +207,6 @@ module BABYLON {
 
                 this._onKeyDown= null;
                 this._onKeyUp= null;
-                this._onPointerDown= null;
-                this._onPointerUp= null;
-                this._onPointerMove= null;
                 this._onMouseMove= null;
                 this._onGestureStart= null;
                 this._onGesture= null;

+ 54 - 67
src/Cameras/Inputs/babylon.freecamera.input.mouse.ts

@@ -5,92 +5,79 @@ module BABYLON {
         @serialize()
         public angularSensibility = 2000.0;
         
-        private _onMouseDown: (e: MouseEvent) => any;
-        private _onMouseUp: (e: MouseEvent) => any;
-        private _onMouseOut: (e: MouseEvent) => any;
-        private _onMouseMove: (e: MouseEvent) => any;
+        private _pointerInput: (p: PointerInfo, s: EventState) => void;
+        private _observer: Observer<PointerInfo>;
+
         private previousPosition: { x: number, y: number };
 
-        attachControl(element: HTMLElement, noPreventDefault?: boolean){                     
-            if (!this._onMouseDown) {
+        attachControl(element: HTMLElement, noPreventDefault?: boolean) {
+
+            if (!this._pointerInput)
+            {
                 var camera = this.camera;
                 var engine = this.camera.getEngine();
-                
-                this._onMouseDown = evt => {
-                    this.previousPosition = {
-                        x: evt.clientX,
-                        y: evt.clientY
-                    };
+                this._pointerInput = (p, s) => {
+                    var evt = <PointerEvent>p.event;
+                    if (p.type === PointerEventType.PointerDown) {
+                        evt.srcElement.setPointerCapture(evt.pointerId);
 
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                };
+                        this.previousPosition = {
+                            x: evt.clientX,
+                            y: evt.clientY
+                        };
 
-                this._onMouseUp = evt => {
-                    this.previousPosition = null;
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
                     }
-                };
-
-                this._onMouseOut = evt => {
-                    this.previousPosition = null;
-                    
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
+                    else if (p.type === PointerEventType.PointerUp) {
+                        evt.srcElement.releasePointerCapture(evt.pointerId);
+                        this.previousPosition = null;
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
                     }
-                };
 
-                this._onMouseMove = evt => {
-                    if (!this.previousPosition && !engine.isPointerLock) {
-                        return;
-                    }
+                    else if (p.type === PointerEventType.PointerMove) {
+                        if (!this.previousPosition && !engine.isPointerLock) {
+                            return;
+                        }
 
-                    var offsetX;
-                    var offsetY;
+                        var offsetX;
+                        var offsetY;
 
-                    if (!engine.isPointerLock) {
-                        offsetX = evt.clientX - this.previousPosition.x;
-                        offsetY = evt.clientY - this.previousPosition.y;
-                    } else {
-                        offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
-                        offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
-                    }
+                        if (!engine.isPointerLock) {
+                            offsetX = evt.clientX - this.previousPosition.x;
+                            offsetY = evt.clientY - this.previousPosition.y;
+                        } else {
+                            offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
+                            offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
+                        }
 
-                    camera.cameraRotation.y += offsetX / this.angularSensibility;
-                    camera.cameraRotation.x += offsetY / this.angularSensibility;
+                        camera.cameraRotation.y += offsetX / this.angularSensibility;
+                        camera.cameraRotation.x += offsetY / this.angularSensibility;
 
-                    this.previousPosition = {
-                        x: evt.clientX,
-                        y: evt.clientY
-                    };
-                    
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
+                        this.previousPosition = {
+                            x: evt.clientX,
+                            y: evt.clientY
+                        };
+
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
                     }
-                };
+                }
             }
 
-            element.addEventListener("mousedown", this._onMouseDown, false);
-            element.addEventListener("mouseup", this._onMouseUp, false);
-            element.addEventListener("mouseout", this._onMouseOut, false);
-            element.addEventListener("mousemove", this._onMouseMove, false);
-   
+            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput); 
         }
         
-        detachControl(element : HTMLElement){   
-            if (this._onMouseDown && element) {
+        detachControl(element: HTMLElement) {
+            if (this._observer && element) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
+
                 this.previousPosition = null;
-                element.removeEventListener("mousedown", this._onMouseDown);
-                element.removeEventListener("mouseup", this._onMouseUp);
-                element.removeEventListener("mouseout", this._onMouseOut);
-                element.removeEventListener("mousemove", this._onMouseMove);
-                
-                this._onMouseDown = null;
-                this._onMouseUp = null;
-                this._onMouseOut = null;
-                this._onMouseMove = null;
             }        
         }
         

+ 67 - 75
src/Cameras/Inputs/babylon.freecamera.input.touch.ts

@@ -6,9 +6,8 @@ module BABYLON {
         private _offsetY: number = null;
         private _pointerCount: number = 0;
         private _pointerPressed = [];
-        private _onPointerDown: (e: PointerEvent) => any;
-        private _onPointerUp: (e: PointerEvent) => any;
-        private _onPointerMove: (e: PointerEvent) => any;
+        private _pointerInput: (p: PointerInfo, s: EventState) => void;
+        private _observer: Observer<PointerInfo>;
         private _onLostFocus: (e: FocusEvent) => any;
 
         @serialize()
@@ -20,103 +19,96 @@ module BABYLON {
         attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             var previousPosition;
 
-            if (this._onPointerDown === undefined) {
+            if (this._pointerInput === undefined) {
                 this._onLostFocus = (evt) => {
                     this._offsetX = null;
                     this._offsetY = null;
                 }
-                
-                this._onPointerDown = (evt) => {
 
-                    if (evt.pointerType === "mouse") {
-                        return;
-                    }
-
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
+                this._pointerInput = (p, s) => {
+                    var evt = <PointerEvent>p.event;
+                    if (p.type === PointerEventType.PointerDown) {
+                        if (evt.pointerType === "mouse") {
+                            return;
+                        }
 
-                    this._pointerPressed.push(evt.pointerId);
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
 
-                    if (this._pointerPressed.length !== 1) {
-                        return;
-                    }
-
-                    previousPosition = {
-                        x: evt.clientX,
-                        y: evt.clientY
-                    };
-                };
+                        evt.srcElement.setPointerCapture(evt.pointerId);
+                        this._pointerPressed.push(evt.pointerId);
 
-                this._onPointerUp = (evt) => {
+                        if (this._pointerPressed.length !== 1) {
+                            return;
+                        }
 
-                    if (evt.pointerType === "mouse") {
-                        return;
+                        previousPosition = {
+                            x: evt.clientX,
+                            y: evt.clientY
+                        };
                     }
 
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
+                    else if (p.type === PointerEventType.PointerUp) {
+                        if (evt.pointerType === "mouse") {
+                            return;
+                        }
+
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
+
+                        evt.srcElement.releasePointerCapture(evt.pointerId);
+                        var index: number = this._pointerPressed.indexOf(evt.pointerId);
+
+                        if (index === -1) {
+                            return;
+                        }
+                        this._pointerPressed.splice(index, 1);
+
+                        if (index != 0) {
+                            return;
+                        }
+                        previousPosition = null;
+                        this._offsetX = null;
+                        this._offsetY = null;
                     }
 
-                    var index: number = this._pointerPressed.indexOf(evt.pointerId);
-
-                    if (index === -1) {
-                        return;
-                    }
-                    this._pointerPressed.splice(index, 1);
-
-                    if (index != 0) {
-                        return;
-                    }
-                    previousPosition = null;
-                    this._offsetX = null;
-                    this._offsetY = null;
-                };
-
-                this._onPointerMove = (evt) => {
+                    else if (p.type === PointerEventType.PointerMove) {
+                        if (evt.pointerType === "mouse") {
+                            return;
+                        }
 
-                    if (evt.pointerType === "mouse") {
-                        return;
-                    }
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
 
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
+                        if (!previousPosition) {
+                            return;
+                        }
 
-                    if (!previousPosition) {
-                        return;
-                    }
+                        var index: number = this._pointerPressed.indexOf(evt.pointerId);
 
-                    var index: number = this._pointerPressed.indexOf(evt.pointerId);
+                        if (index != 0) {
+                            return;
+                        }
 
-                    if (index != 0) {
-                        return;
+                        this._offsetX = evt.clientX - previousPosition.x;
+                        this._offsetY = -(evt.clientY - previousPosition.y);
                     }
-
-                    this._offsetX = evt.clientX - previousPosition.x;
-                    this._offsetY = -(evt.clientY - previousPosition.y);
-                };
-
-
+                }
             }
+
             element.addEventListener("blur", this._onLostFocus);
-            element.addEventListener("pointerdown", this._onPointerDown);
-            element.addEventListener("pointerup", this._onPointerUp);
-            element.addEventListener("pointerout", this._onPointerUp);
-            element.addEventListener("pointermove", this._onPointerMove);
         }
 
         detachControl(element: HTMLElement) {
-            if (this._onPointerDown && element){
+            if (this._pointerInput && element) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
+
                 element.removeEventListener("blur", this._onLostFocus);
-                element.removeEventListener("pointerdown", this._onPointerDown);
-                element.removeEventListener("pointerup", this._onPointerUp);
-                element.removeEventListener("pointerout", this._onPointerUp);
-                element.removeEventListener("pointermove", this._onPointerMove);
-                
-                this._onPointerDown = null;
-                this._onPointerUp = null;
-                this._onPointerMove = null;
+
                 this._onLostFocus = null;
                 this._pointerPressed = [];
                 this._offsetX = null;

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

@@ -90,6 +90,13 @@
         }
 
         /**
+         * return true is the Observable has at least one Observer registered
+         */
+        public hasObservers(): boolean {
+            return this._observers.length > 0;
+        }
+
+        /**
         * Clear the list of observers
         */
         public clear(): void {

+ 58 - 2
src/babylon.scene.ts

@@ -4,6 +4,22 @@
     }
 
     /**
+     * Different type of pointer event, enumerations are self explanatory
+     */
+    export const enum PointerEventType {
+        None, PointerDown, PointerUp, PointerMove, PointerWheel, PointerPick
+    }
+
+    /**
+     * This type contains all the data related to a pointer event in Babylon.js.
+     * The event member is an instnce of PointerEvent for all types except PointerWheel and is of type MouseWheelEvent when type equals PointerWheel
+     */
+    export class PointerInfo {
+        constructor(public type: PointerEventType, public event: PointerEvent | MouseWheelEvent, public pickInfo: PickingInfo) {
+        }
+    }
+
+    /**
      * Represents a scene to be rendered by the engine.
      * @see http://doc.babylonjs.com/page.php?p=21911
      */
@@ -182,10 +198,29 @@
         private _onPointerMove: (evt: PointerEvent) => void;
         private _onPointerDown: (evt: PointerEvent) => void;
         private _onPointerUp: (evt: PointerEvent) => void;
+
+        /**
+         * @deprecated Use onPointerObservable instead
+         */
         public onPointerMove: (evt: PointerEvent, pickInfo: PickingInfo) => void;
+        /**
+         * @deprecated Use onPointerObservable instead
+         */
         public onPointerDown: (evt: PointerEvent, pickInfo: PickingInfo) => void;
+        /**
+         * @deprecated Use onPointerObservable instead
+         */
         public onPointerUp: (evt: PointerEvent, pickInfo: PickingInfo) => void;
+        /**
+         * @deprecated Use onPointerObservable instead
+         */
         public onPointerPick: (evt: PointerEvent, pickInfo: PickingInfo) => void;
+
+        /**
+         * Observable event triggered each time an input event is received from the rendering canvas
+         */
+        public onPointerObservable = new Observable<PointerInfo>();
+
         public cameraToUseForPointers: Camera = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
         private _pointerX: number;
         private _pointerY: number;
@@ -623,6 +658,11 @@
                 if (this.onPointerMove) {
                     this.onPointerMove(evt, pickResult);
                 }
+
+                if (this.onPointerObservable.hasObservers()) {
+                    let pi = new PointerInfo(evt.type === "mousewheel" ? PointerEventType.PointerWheel : PointerEventType.PointerMove, evt, pickResult);
+                    this.onPointerObservable.notifyObservers(pi);
+                }
             };
 
             this._onPointerDown = (evt: PointerEvent) => {
@@ -687,6 +727,11 @@
                     this.onPointerDown(evt, pickResult);
                 }
 
+                if (this.onPointerObservable.hasObservers()) {
+                    let pi = new PointerInfo(PointerEventType.PointerDown, evt, pickResult);
+                    this.onPointerObservable.notifyObservers(pi);
+                }
+
                 // Sprites
                 this._pickedDownSprite = null;
                 if (this.spriteManagers.length > 0) {
@@ -729,8 +774,14 @@
                 var pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY, this.pointerUpPredicate, false, this.cameraToUseForPointers);
 
                 if (pickResult.hit && pickResult.pickedMesh) {
-                    if (this.onPointerPick && this._pickedDownMesh != null && pickResult.pickedMesh == this._pickedDownMesh) {
-                        this.onPointerPick(evt, pickResult);
+                    if (this._pickedDownMesh != null && pickResult.pickedMesh == this._pickedDownMesh) {
+                        if (this.onPointerPick) {
+                            this.onPointerPick(evt, pickResult);
+                        }
+                        if (this.onPointerObservable.hasObservers()) {
+                            let pi = new PointerInfo(PointerEventType.PointerPick, evt, pickResult);
+                            this.onPointerObservable.notifyObservers(pi);
+                        }
                     }
                     if (pickResult.pickedMesh.actionManager) {
                         pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
@@ -748,6 +799,11 @@
                     this.onPointerUp(evt, pickResult);
                 }
 
+                if (this.onPointerObservable.hasObservers()) {
+                    let pi = new PointerInfo(PointerEventType.PointerUp, evt, pickResult);
+                    this.onPointerObservable.notifyObservers(pi);
+                }
+
                 this._startingPointerTime = 0;
                 
                 // Sprites