sebastien пре 7 година
родитељ
комит
d0fe2945a5

+ 2 - 1
Tools/Gulp/config.json

@@ -411,7 +411,8 @@
         "sprites": {
             "files": [
                 "../../src/Sprites/babylon.spriteManager.js",
-                "../../src/Sprites/babylon.sprite.js"
+                "../../src/Sprites/babylon.sprite.js",
+                "../../src/Sprites/babylon.spriteSceneComponent.js"
             ],
             "dependUpon": [
                 "core"

+ 1 - 1
src/Rendering/babylon.renderingManager.ts

@@ -76,7 +76,7 @@
             info.camera = this._scene.activeCamera;
 
             // Dispatch sprites
-            if (renderSprites) {
+            if (this._scene.spriteManagers && renderSprites) {
                 for (let index = 0; index < this._scene.spriteManagers.length; index++) {
                     var manager = this._scene.spriteManagers[index];
                     this.dispatchSprites(manager);

+ 3 - 0
src/Sprites/babylon.spriteManager.ts

@@ -57,6 +57,9 @@
         }
 
         constructor(public name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon: number = 0.01, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
+            if (!scene._getComponent(SceneComponentConstants.NAME_SPRITE)) {
+                scene._addComponent(new SpriteSceneComponent(scene));
+            }
             this._capacity = capacity;
             this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
             this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;

+ 303 - 0
src/Sprites/babylon.spriteSceneComponent.ts

@@ -0,0 +1,303 @@
+module BABYLON {
+    export interface ActionManager {
+
+    }
+    
+    export interface Scene {
+        /** @hidden */
+        _pointerOverSprite: Nullable<Sprite>;
+
+        /** @hidden */
+        _pickedDownSprite: Nullable<Sprite>;
+
+        /** @hidden */
+        _tempSpritePickingRay: Nullable<Ray>;
+
+        /**
+         * All of the sprite managers added to this scene
+         * @see http://doc.babylonjs.com/babylon101/sprites
+         */
+        spriteManagers: Array<ISpriteManager>;
+
+        /**
+         * An event triggered when sprites rendering is about to start
+         * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
+         */
+        onBeforeSpritesRenderingObservable: Observable<Scene>;
+
+        /**
+         * An event triggered when sprites rendering is done
+         * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
+         */
+        onAfterSpritesRenderingObservable: Observable<Scene>;
+
+        /** @hidden */
+        _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** Launch a ray to try to pick a sprite in the scene
+         * @param x position on screen
+         * @param y position on screen
+         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
+         * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
+         * @returns a PickingInfo
+         */
+        pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** Use the given ray to pick a sprite in the scene
+         * @param ray The ray (in world space) to use to pick meshes
+         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
+         * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
+         * @returns a PickingInfo
+         */
+        pickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** 
+         * Force the sprite under the pointer
+         * @param sprite defines the sprite to use
+         */
+        setPointerOverSprite(sprite: Nullable<Sprite>): void;
+
+        /** 
+         * Gets the sprite under the pointer
+         * @returns a Sprite or null if no sprite is under the pointer
+         */
+        getPointerOverSprite(): Nullable<Sprite>;
+    }
+
+    Scene.prototype._internalPickSprites = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        if (!PickingInfo) {
+            return null;
+        }
+
+        var pickingInfo = null;
+
+        if (!camera) {
+            if (!this.activeCamera) {
+                return null;
+            }
+            camera = this.activeCamera;
+        }
+
+        if (this.spriteManagers.length > 0) {
+            for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
+                var spriteManager = this.spriteManagers[spriteIndex];
+
+                if (!spriteManager.isPickable) {
+                    continue;
+                }
+
+                var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
+                if (!result || !result.hit)
+                    continue;
+
+                if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance)
+                    continue;
+
+                pickingInfo = result;
+
+                if (fastCheck) {
+                    break;
+                }
+            }
+        }
+
+        return pickingInfo || new PickingInfo();
+    }
+
+    Scene.prototype.pickSprite = function(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        this.createPickingRayInCameraSpaceToRef(x, y, this._tempSpritePickingRay!, camera);
+
+        return this._internalPickSprites(this._tempSpritePickingRay!, predicate, fastCheck, camera);
+    }
+
+    Scene.prototype.pickSpriteWithRay = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        if (!this._tempSpritePickingRay) {
+            return null;
+        }
+
+        if (!camera) {
+            if (!this.activeCamera) {
+                return null;
+            }
+            camera = this.activeCamera;
+        }
+
+        Ray.TransformToRef(ray, camera.getViewMatrix(), this._tempSpritePickingRay);
+
+        return this._internalPickSprites(this._tempSpritePickingRay, predicate, fastCheck, camera);
+    }
+
+    /**
+     * Force the sprite under the pointer
+     * @param sprite defines the sprite to use
+     */
+    Scene.prototype.setPointerOverSprite = function(sprite: Nullable<Sprite>): void {
+        if (this._pointerOverSprite === sprite) {
+            return;
+        }
+
+        if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
+            this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
+        }
+
+        this._pointerOverSprite = sprite;
+        if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
+            this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
+        }
+    }
+
+    /** 
+     * Gets the sprite under the pointer
+     * @returns a Sprite or null if no sprite is under the pointer
+     */
+    Scene.prototype.getPointerOverSprite = function(): Nullable<Sprite> {
+        return this._pointerOverSprite;
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage sprites
+     * in a given scene.
+     */
+    export class SpriteSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_SPRITE;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /** @hidden */
+        private _spritePredicate: (sprite: Sprite) => boolean;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this.scene.spriteManagers = new Array<ISpriteManager>();
+            this.scene._tempSpritePickingRay = Ray ? Ray.Zero() : null;
+            this.scene.onBeforeSpritesRenderingObservable = new Observable<Scene>();
+            this.scene.onAfterSpritesRenderingObservable = new Observable<Scene>();
+            this._spritePredicate = (sprite: Sprite): boolean => {
+                return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
+            };
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._pointerMoveStage.registerStep(SceneComponentConstants.STEP_POINTERMOVE_SPRITE, this, this._pointerMove);
+            this.scene._pointerDownStage.registerStep(SceneComponentConstants.STEP_POINTERDOWN_SPRITE, this, this._pointerDown);
+            this.scene._pointerUpStage.registerStep(SceneComponentConstants.STEP_POINTERUP_SPRITE, this, this._pointerUp);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            /** Nothing to do for sprites */
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            this.scene.onBeforeSpritesRenderingObservable.clear();
+            this.scene.onAfterSpritesRenderingObservable.clear();
+
+            let spriteManagers = this.scene.spriteManagers;
+            while (spriteManagers.length) {
+                spriteManagers[0].dispose();
+            }
+        }
+
+        private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+            var result = this.scene.pickSprite(x, y, this._spritePredicate, fastCheck, camera);
+            if (result) {
+                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
+            }
+            return result;
+        }
+
+        private _pointerMove(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, canvas: HTMLCanvasElement): void {
+            var scene = this.scene;
+            if (isMeshPicked) {
+                scene.setPointerOverSprite(null);
+            } else {
+                pickResult = this._pickSpriteButKeepRay(pickResult, unTranslatedPointerX, unTranslatedPointerY, false, scene.cameraToUseForPointers || undefined);
+
+                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
+                    scene.setPointerOverSprite(pickResult.pickedSprite);
+                    if (scene._pointerOverSprite && scene._pointerOverSprite.actionManager && scene._pointerOverSprite.actionManager.hoverCursor) {
+                        canvas.style.cursor = scene._pointerOverSprite.actionManager.hoverCursor;
+                    } else {
+                        canvas.style.cursor = scene.hoverCursor;
+                    }
+                } else {
+                    scene.setPointerOverSprite(null);
+                    // Restore pointer
+                    canvas.style.cursor = scene.defaultCursor;
+                }
+            }
+        }
+
+        private _pointerDown(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): void {
+            var scene = this.scene;
+            scene._pickedDownSprite = null;
+            if (scene.spriteManagers.length > 0) {
+                pickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
+
+                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
+                    if (pickResult.pickedSprite.actionManager) {
+                        scene._pickedDownSprite = pickResult.pickedSprite;
+                        switch (evt.button) {
+                            case 0:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                            case 1:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                            case 2:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                        }
+                        if (pickResult.pickedSprite.actionManager) {
+                            pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                        }
+                    }
+                }
+            }
+        }
+
+        private _pointerUp(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): void {
+            var scene = this.scene;
+            if (scene.spriteManagers.length > 0) {
+                let spritePickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
+
+                if (spritePickResult) {
+                    if (spritePickResult.hit && spritePickResult.pickedSprite) {
+                        if (spritePickResult.pickedSprite.actionManager) {
+                            spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
+                            if (spritePickResult.pickedSprite.actionManager) {
+                                if (!this.scene._isPointerSwiping()) {
+                                    spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
+                                }
+                            }
+                        }
+                    }
+                    if (scene._pickedDownSprite && scene._pickedDownSprite.actionManager && scene._pickedDownSprite !== spritePickResult.pickedSprite) {
+                        scene._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(scene._pickedDownSprite, scene, evt));
+                    }
+                }
+            }
+        }
+    }
+}

+ 39 - 225
src/babylon.scene.ts

@@ -278,10 +278,6 @@
          */
         public disableOfflineSupportExceptionRules = new Array<RegExp>();
 
-        // Events
-
-        private _spritePredicate: (sprite: Sprite) => boolean;
-
         /**
         * An event triggered when the scene is disposed.
         */
@@ -416,18 +412,6 @@
         public onAfterParticlesRenderingObservable = new Observable<Scene>();
 
         /**
-        * An event triggered when sprites rendering is about to start
-        * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
-        */
-        public onBeforeSpritesRenderingObservable = new Observable<Scene>();
-
-        /**
-        * An event triggered when sprites rendering is done
-        * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
-        */
-        public onAfterSpritesRenderingObservable = new Observable<Scene>();
-
-        /**
         * An event triggered when SceneLoader.Append or SceneLoader.Load or SceneLoader.ImportMesh were successfully executed
         */
         public onDataLoadedObservable = new Observable<Scene>();
@@ -805,11 +789,6 @@
         * Gets or sets a boolean indicating if sprites are enabled on this scene
         */
         public spritesEnabled = true;
-        /**
-        * All of the sprite managers added to this scene
-        * @see http://doc.babylonjs.com/babylon101/sprites
-        */
-        public spriteManagers = new Array<ISpriteManager>();
 
         // Skeletons
         private _skeletonsEnabled = true;
@@ -1055,13 +1034,11 @@
         private _selectionOctree: Octree<AbstractMesh>;
 
         private _pointerOverMesh: Nullable<AbstractMesh>;
-        private _pointerOverSprite: Nullable<Sprite>;
 
         private _debugLayer: DebugLayer;
 
         private _pickedDownMesh: Nullable<AbstractMesh>;
         private _pickedUpMesh: Nullable<AbstractMesh>;
-        private _pickedDownSprite: Nullable<Sprite>;
         private _externalData: StringDictionary<Object>;
         private _uid: Nullable<string>;
 
@@ -1187,6 +1164,21 @@
          * Defines the actions happening when Geometries are rebuilding.
          */
         public _rebuildGeometryStage = Stage.Create<SimpleStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer move event happens.
+         */
+        public _pointerMoveStage = Stage.Create<PointerMoveStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer down event happens.
+         */
+        public _pointerDownStage = Stage.Create<PointerUpDownStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer up event happens.
+         */
+        public _pointerUpStage = Stage.Create<PointerUpDownStageAction>();
 
         /**
          * Creates a new Scene
@@ -1539,14 +1531,6 @@
         }
 
         // Pointers handling
-        private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            var result = this.pickSprite(x, y, predicate, fastCheck, camera);
-            if (result) {
-                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
-            }
-            return result;
-        }
-
         private _setRayOnPointerInfo(pointerInfo: PointerInfo) {
             if (pointerInfo.pickInfo) {
                 if (!pointerInfo.pickInfo.ray) {
@@ -1579,37 +1563,15 @@
                 return this;
             }
 
-            if (pickResult && pickResult.hit && pickResult.pickedMesh) {
-                this.setPointerOverSprite(null);
-
-                this.setPointerOverMesh(pickResult.pickedMesh);
-
-                if (this._pointerOverMesh && this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                    if (this._pointerOverMesh.actionManager.hoverCursor) {
-                        canvas.style.cursor = this._pointerOverMesh.actionManager.hoverCursor;
-                    } else {
-                        canvas.style.cursor = this.hoverCursor;
-                    }
-                } else {
-                    canvas.style.cursor = this.defaultCursor;
-                }
+            var isMeshPicked = (pickResult && pickResult.hit && pickResult.pickedMesh) ? true : false;
+            if (isMeshPicked) {
+                this.setPointerOverMesh(pickResult!.pickedMesh);
             } else {
                 this.setPointerOverMesh(null);
-                // Sprites
-                pickResult = this._pickSpriteButKeepRay(pickResult, this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
+            }
 
-                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
-                    this.setPointerOverSprite(pickResult.pickedSprite);
-                    if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
-                        canvas.style.cursor = this._pointerOverSprite.actionManager.hoverCursor;
-                    } else {
-                        canvas.style.cursor = this.hoverCursor;
-                    }
-                } else {
-                    this.setPointerOverSprite(null);
-                    // Restore pointer
-                    canvas.style.cursor = this.defaultCursor;
-                }
+            for (let step of this._pointerMoveStage) {
+                step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, isMeshPicked, canvas);
             }
 
             if (pickResult) {
@@ -1688,8 +1650,7 @@
                             if (pickResult && pickResult.hit && pickResult.pickedMesh && actionManager) {
                                 if (this._totalPointersPressed !== 0 &&
                                     ((Date.now() - this._startingPointerTime) > Scene.LongPressDelay) &&
-                                    (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold &&
-                                        Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold)) {
+                                    !this._isPointerSwiping()) {
                                     this._startingPointerTime = 0;
                                     actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                 }
@@ -1810,6 +1771,12 @@
             return this._pointerCaptures[pointerId];
         }
 
+        /** @hidden */
+        public _isPointerSwiping(): boolean {
+            return Math.abs(this._startingPointerPosition.x - this._pointerX) > Scene.DragMovementThreshold ||
+                   Math.abs(this._startingPointerPosition.y - this._pointerY) > Scene.DragMovementThreshold;
+        }
+
         /**
         * Attach events to the canvas (To handle actionManagers triggers and raise onPointerMove, onPointerDown and onPointerUp
         * @param attachUp defines if you want to attach events to pointerup
@@ -1855,8 +1822,7 @@
                 }
                 if (checkPicking) {
                     let btn = evt.button;
-                    clickInfo.hasSwiped = Math.abs(this._startingPointerPosition.x - this._pointerX) > Scene.DragMovementThreshold ||
-                        Math.abs(this._startingPointerPosition.y - this._pointerY) > Scene.DragMovementThreshold;
+                    clickInfo.hasSwiped = this._isPointerSwiping();
 
                     if (!clickInfo.hasSwiped) {
                         let checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
@@ -1903,8 +1869,7 @@
                             ) {
                                 // pointer has not moved for 2 clicks, it's a double click
                                 if (!clickInfo.hasSwiped &&
-                                    Math.abs(this._previousStartingPointerPosition.x - this._startingPointerPosition.x) < Scene.DragMovementThreshold &&
-                                    Math.abs(this._previousStartingPointerPosition.y - this._startingPointerPosition.y) < Scene.DragMovementThreshold) {
+                                    !this._isPointerSwiping()) {
                                     this._previousStartingPointerTime = 0;
                                     this._doubleClickOccured = true;
                                     clickInfo.doubleClick = true;
@@ -1950,10 +1915,6 @@
                 cb(clickInfo, this._currentPickResult);
             };
 
-            this._spritePredicate = (sprite: Sprite): boolean => {
-                return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
-            };
-
             this._onPointerMove = (evt: PointerEvent) => {
 
                 this._updatePointerPosition(evt);
@@ -2015,30 +1976,8 @@
 
                 this._processPointerDown(pickResult, evt);
 
-                // Sprites
-                this._pickedDownSprite = null;
-                if (this.spriteManagers.length > 0) {
-                    pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
-
-                    if (pickResult && pickResult.hit && pickResult.pickedSprite) {
-                        if (pickResult.pickedSprite.actionManager) {
-                            this._pickedDownSprite = pickResult.pickedSprite;
-                            switch (evt.button) {
-                                case 0:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                                case 1:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                                case 2:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                            }
-                            if (pickResult.pickedSprite.actionManager) {
-                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                            }
-                        }
-                    }
+                for (let step of this._pointerDownStage) {
+                    step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, evt);
                 }
             };
 
@@ -2098,26 +2037,9 @@
 
                     this._processPointerUp(pickResult, evt, clickInfo);
 
-                    // Sprites
                     if (!clickInfo.ignore) {
-                        if (this.spriteManagers.length > 0) {
-                            let spritePickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
-
-                            if (spritePickResult) {
-                                if (spritePickResult.hit && spritePickResult.pickedSprite) {
-                                    if (spritePickResult.pickedSprite.actionManager) {
-                                        spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, this, evt));
-                                        if (spritePickResult.pickedSprite.actionManager) {
-                                            if (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold) {
-                                                spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, this, evt));
-                                            }
-                                        }
-                                    }
-                                }
-                                if (this._pickedDownSprite && this._pickedDownSprite.actionManager && this._pickedDownSprite !== spritePickResult.pickedSprite) {
-                                    this._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(this._pickedDownSprite, this, evt));
-                                }
-                            }
+                        for (let step of this._pointerUpStage) {
+                            step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, evt);
                         }
                     }
 
@@ -4886,6 +4808,10 @@
             this._beforeCameraUpdateStage.clear();
             this._gatherRenderTargetsStage.clear();
             this._rebuildGeometryStage.clear();
+            this._pointerMoveStage.clear();
+            this._pointerDownStage.clear();
+            this._pointerUpStage.clear();
+
             for (let component of this._components) {
                 component.dispose();
             }
@@ -4936,8 +4862,6 @@
             this.onAfterActiveMeshesEvaluationObservable.clear();
             this.onBeforeParticlesRenderingObservable.clear();
             this.onAfterParticlesRenderingObservable.clear();
-            this.onBeforeSpritesRenderingObservable.clear();
-            this.onAfterSpritesRenderingObservable.clear();
             this.onBeforeDrawPhaseObservable.clear();
             this.onAfterDrawPhaseObservable.clear();
             this.onBeforePhysicsObservable.clear();
@@ -5009,11 +4933,6 @@
                 this.particleSystems[0].dispose();
             }
 
-            // Release sprites
-            while (this.spriteManagers.length) {
-                this.spriteManagers[0].dispose();
-            }
-
             // Release postProcesses
             while (this.postProcesses.length) {
                 this.postProcesses[0].dispose();
@@ -5329,46 +5248,6 @@
             return pickingInfos;
         }
 
-        private _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            if (!PickingInfo) {
-                return null;
-            }
-
-            var pickingInfo = null;
-
-            if (!camera) {
-                if (!this.activeCamera) {
-                    return null;
-                }
-                camera = this.activeCamera;
-            }
-
-            if (this.spriteManagers.length > 0) {
-                for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
-                    var spriteManager = this.spriteManagers[spriteIndex];
-
-                    if (!spriteManager.isPickable) {
-                        continue;
-                    }
-
-                    var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
-                    if (!result || !result.hit)
-                        continue;
-
-                    if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance)
-                        continue;
-
-                    pickingInfo = result;
-
-                    if (fastCheck) {
-                        break;
-                    }
-                }
-            }
-
-            return pickingInfo || new PickingInfo();
-        }
-
         private _tempPickingRay: Nullable<Ray> = Ray ? Ray.Zero() : null;
 
         /** Launch a ray to try to pick a mesh in the scene
@@ -5393,49 +5272,11 @@
             return result;
         }
 
-        /** Launch a ray to try to pick a sprite in the scene
-         * @param x position on screen
-         * @param y position on screen
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
-         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
-         * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
-         * @returns a PickingInfo
-         */
-        public pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            this.createPickingRayInCameraSpaceToRef(x, y, this._tempPickingRay!, camera);
-
-            return this._internalPickSprites(this._tempPickingRay!, predicate, fastCheck, camera);
-        }
-
         private _cachedRayForTransform: Ray;
 
-        /** Use the given ray to pick a sprite in the scene
-         * @param ray The ray (in world space) to use to pick meshes
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
-         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
-         * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
-         * @returns a PickingInfo
-         */
-        public pickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            if (!this._cachedRayForTransform) {
-                this._cachedRayForTransform = Ray.Zero();
-            }
-
-            if (!camera) {
-                if (!this.activeCamera) {
-                    return null;
-                }
-                camera = this.activeCamera;
-            }
-
-            Ray.TransformToRef(ray, camera.getViewMatrix(), this._cachedRayForTransform);
-
-            return this._internalPickSprites(this._cachedRayForTransform, predicate, fastCheck, camera);
-        }       
-
         /** Use the given ray to pick a mesh in the scene
          * @param ray The ray to use to pick meshes
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param predicate Predicate function used to determine eligible meshes. Can be set to null. In this case, a mesh must have isPickable set to true
          * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null
          * @returns a PickingInfo
          */
@@ -5520,33 +5361,6 @@
             return this._pointerOverMesh;
         }
 
-        /** 
-         * Force the sprite under the pointer
-         * @param sprite defines the sprite to use
-         */
-        public setPointerOverSprite(sprite: Nullable<Sprite>): void {
-            if (this._pointerOverSprite === sprite) {
-                return;
-            }
-
-            if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
-                this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
-            }
-
-            this._pointerOverSprite = sprite;
-            if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
-                this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
-            }
-        }
-
-        /** 
-         * Gets the sprite under the pointer
-         * @returns a Sprite or null if no sprite is under the pointer
-         */
-        public getPointerOverSprite(): Nullable<Sprite> {
-            return this._pointerOverSprite;
-        }
-
         // Physics
 
         /** 

+ 15 - 0
src/babylon.sceneComponent.ts

@@ -14,6 +14,7 @@
         public static readonly NAME_GEOMETRYBUFFERRENDERER = "GeometryBufferRenderer";
         public static readonly NAME_DEPTHRENDERER = "DepthRenderer";
         public static readonly NAME_POSTPROCESSRENDERPIPELINEMANAGER = "PostProcessRenderPipelineManager";
+        public static readonly NAME_SPRITE = "Sprite";
 
         public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;
 
@@ -44,6 +45,10 @@
         public static readonly STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER = 2;
 
         public static readonly STEP_REBUILDGEOMETRY_POSTPROCESSRENDERPIPELINEMANAGER = 0;
+
+        public static readonly STEP_POINTERMOVE_SPRITE = 0;
+        public static readonly STEP_POINTERDOWN_SPRITE = 0;
+        public static readonly STEP_POINTERUP_SPRITE = 0;
     }
 
     /**
@@ -141,6 +146,16 @@
     export type RenderTargetsStageAction = (renderTargets: SmartArrayNoDuplicate<RenderTargetTexture>) => void;
 
     /** 
+     * Strong typing of a pointer move action.
+     */
+    export type PointerMoveStageAction = (unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, canvas: HTMLCanvasElement) => void;
+
+    /** 
+     * Strong typing of a pointer up/down action.
+     */
+    export type PointerUpDownStageAction = (unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent) => void;
+
+    /** 
      * Repressentation of a stage in the scene (Basically a list of ordered steps) 
      * @hidden
      */