Browse Source

gizmo manager

Trevor Baron 7 years ago
parent
commit
7a69309037

+ 2 - 1
Tools/Gulp/config.json

@@ -1028,7 +1028,8 @@
                 "../../src/Gizmos/babylon.planeRotationGizmo.js",
                 "../../src/Gizmos/babylon.positionGizmo.js",
                 "../../src/Gizmos/babylon.rotationGizmo.js",
-                "../../src/Gizmos/babylon.scaleGizmo.js"
+                "../../src/Gizmos/babylon.scaleGizmo.js",
+                "../../src/Gizmos/babylon.gizmoManager.js"
             ],
             "dependUpon": [
                 "shaderMaterial",

+ 16 - 30
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -6,7 +6,7 @@ module BABYLON {
         private _attachedNode: Node; 
         private _dragPlane: Mesh;
         private _scene:Scene;
-        private _pointerObserver:Nullable<Observer<PointerInfoPre>>;
+        private _pointerObserver:Nullable<Observer<PointerInfo>>;
         private static _planeScene:Scene;
         private _draggingID = -1;
         
@@ -38,9 +38,9 @@ module BABYLON {
         
         /**
          * Creates a pointer drag behavior that can be attached to a mesh
-         * @param options The drag axis or normal of the plane that will be dragged across. pointerObservableScene can be used to listen to drag events from another scene(eg. if the attached mesh is in an overlay scene).
+         * @param options The drag axis or normal of the plane that will be dragged across.
          */
-        constructor(private options:{dragAxis?:Vector3, dragPlaneNormal?:Vector3, pointerObservableScene?:Scene}){
+        constructor(private options:{dragAxis?:Vector3, dragPlaneNormal?:Vector3}){
             var optionCount = 0;
             if(options.dragAxis){
                 optionCount++;
@@ -74,9 +74,6 @@ module BABYLON {
          */
         public attach(ownerNode: Mesh): void {
             this._scene = ownerNode.getScene();
-            if(!this.options.pointerObservableScene){
-                this.options.pointerObservableScene = this._scene;
-            }
             this._attachedNode = ownerNode;
 
             // Initialize drag plane to not interfere with existing scene
@@ -95,43 +92,32 @@ module BABYLON {
                 return this._attachedNode == m || m.isDescendantOf(this._attachedNode)
             }
 
-            this._pointerObserver = this.options.pointerObservableScene!.onPrePointerObservable.add((pointerInfoPre, eventState)=>{
+            this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState)=>{
                 if(!this.enabled){
                     return;
                 }
-                // Check if attached mesh is picked
-                var pickInfo = pointerInfoPre.ray ? this._scene.pickWithRay(pointerInfoPre.ray, pickPredicate) : this._scene.pick(this._scene.pointerX, this._scene.pointerY, pickPredicate);
-                if(pickInfo){
-                    pickInfo.ray = pointerInfoPre.ray;
-                    if(!pickInfo.ray){
-                        pickInfo.ray = this.options.pointerObservableScene!.createPickingRay(this._scene.pointerX, this._scene.pointerY, Matrix.Identity(), this._scene.activeCamera);
-                    }
-                    if(pickInfo.hit && pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN){
-                        pointerInfoPre.skipOnPointerObservable = true;
-                    }
-                }
                 
-                if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if(!dragging && pickInfo && pickInfo.hit && pickInfo.pickedMesh && pickInfo.ray){
-                        this._updateDragPlanePosition(pickInfo.ray);
-                        var pickedPoint = this._pickWithRayOnDragPlane(pickInfo.ray);
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if(!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)){
+                        this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                        var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
                         if(pickedPoint){
                             dragging = true;
-                            this._draggingID = (<PointerEvent>pointerInfoPre.event).pointerId;
+                            this._draggingID = (<PointerEvent>pointerInfo.event).pointerId;
                             lastPosition.copyFrom(pickedPoint);
                             this.onDragStartObservable.notifyObservers({dragPlanePoint: pickedPoint});
                         }
                     }
-                }else if(pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERUP){
-                    if(this._draggingID == (<PointerEvent>pointerInfoPre.event).pointerId){
+                }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
+                    if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId){
                         dragging = false;
                         this._draggingID = -1;
                         this.onDragEndObservable.notifyObservers({dragPlanePoint: lastPosition});
                     }
-                }else if(pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERMOVE){
-                    if(this._draggingID == (<PointerEvent>pointerInfoPre.event).pointerId && dragging && pickInfo && pickInfo.ray){
-                        var pickedPoint = this._pickWithRayOnDragPlane(pickInfo.ray);
-                        this._updateDragPlanePosition(pickInfo.ray);
+                }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
+                    if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
+                        var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                        this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
                         if (pickedPoint) {
                             // depending on the drag mode option drag accordingly
                             if(this.options.dragAxis){
@@ -195,7 +181,7 @@ module BABYLON {
          */
         public detach(): void {
             if(this._pointerObserver){
-                this._scene.onPrePointerObservable.remove(this._pointerObserver);
+                this._scene.onPointerObservable.remove(this._pointerObserver);
             }
         }
     }

+ 1 - 1
src/Gizmos/babylon.axisDragGizmo.ts

@@ -35,7 +35,7 @@ module BABYLON {
             this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
 
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
             this._dragBehavior.onDragObservable.add((event)=>{

+ 1 - 1
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -35,7 +35,7 @@ module BABYLON {
             this._rootMesh.lookAt(this._rootMesh.position.subtract(dragAxis));
 
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene});
+            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
 

+ 67 - 0
src/Gizmos/babylon.gizmoManager.ts

@@ -0,0 +1,67 @@
+module BABYLON {
+    /**
+     * Helps setup gizmo's in the scene to rotate/scale/position meshes
+     */
+    export class GizmoManager {
+        /**
+         * Instatiates a gizmo manager
+         * @param scene the scene to overlay the gizmos on top of
+         * @param options If only a single gizmo should exist at one time
+         */
+        constructor(scene:Scene, options?:{singleGizmo?:boolean}){
+            var gizmoLayer = new BABYLON.UtilityLayerRenderer(scene);
+
+            // Options parsing
+            if(!options){
+                options = {}
+            }
+            if(options.singleGizmo === undefined){
+                options.singleGizmo = true;
+            }
+            
+            // Set of gizmos that are currently in the scene for each mesh
+            var gizmoSet:{[meshID: string]: {positionGizmo: PositionGizmo, rotationGizmo: RotationGizmo}} = {}
+            var clearGizmos = ()=>{
+                for(var key in gizmoSet){
+                    gizmoSet[key].positionGizmo.dispose();
+                    gizmoSet[key].rotationGizmo.dispose();
+                    delete gizmoSet[key];
+                }
+            }
+
+            // Instatiate/dispose gizmos based on pointer actions
+            scene.onPointerObservable.add((pointerInfo, state)=>{
+                if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN){
+                    if(pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh){
+                        if(!gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId]){
+                            if(options!.singleGizmo){
+                                clearGizmos();
+                            }                            
+                            // Enable gizmo when mesh is selected
+                            gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId] = {positionGizmo: new PositionGizmo(gizmoLayer), rotationGizmo: new RotationGizmo(gizmoLayer)}
+                            gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                            gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                        }else{
+                            if(!options!.singleGizmo){
+                                // Disable gizmo when clicked again
+                                gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.dispose();
+                                gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.dispose();
+                                delete gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId];
+                            }
+                        }
+                    }else {
+                        if(options!.singleGizmo){
+                            // Disable gizmo when clicked away
+                            if(pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
+                                var gizmoPick = gizmoLayer.utilityLayerScene.pickWithRay(pointerInfo.pickInfo.ray);
+                                if(gizmoPick && !gizmoPick.hit){
+                                    clearGizmos();
+                                }
+                            }
+                        }
+                    }
+                }
+            })
+        }
+    }
+}

+ 1 - 1
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -29,7 +29,7 @@ module BABYLON {
             this._rootMesh.lookAt(this._rootMesh.position.subtract(planeNormal));
 
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal, pointerObservableScene: gizmoLayer.originalScene});
+            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal});
             this._dragBehavior.moveAttached = false;
             this._rootMesh.addBehavior(this._dragBehavior);
 

+ 48 - 1
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -12,7 +12,10 @@ module BABYLON {
          *  If the utility layer should automatically be rendered on top of existing scene
         */
         public shouldRender:boolean = true;
-
+        /**
+         * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
+         */
+        public onlyCheckPointerDownEvents = true;
         private _afterRenderObserver:Nullable<Observer<Scene>>;
         private _sceneDisposeObserver:Nullable<Observer<Scene>>;
         /**
@@ -24,6 +27,50 @@ module BABYLON {
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             originalScene.getEngine().scenes.pop();
 
+            // Detach controls on utility scene, events will be fired by logic below to handle picking priority
+            this.utilityLayerScene.detachControl();
+            originalScene.onPrePointerObservable.add((prePointerInfo, eventState)=>{
+                var utilityScenePick = prePointerInfo.ray ? this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                if(!prePointerInfo.ray && utilityScenePick){
+                    prePointerInfo.ray = utilityScenePick.ray;
+                }
+
+                // always fire the prepointer oversvable
+                this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo)
+
+                // allow every non pointer down event to flow to the utility layer
+                if(this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN){
+                    if(!prePointerInfo.skipOnPointerObservable){
+                        this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
+                    }
+                    return;
+                }
+
+                if(this.utilityLayerScene.autoClearDepthAndStencil){
+                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                    if(utilityScenePick && utilityScenePick.hit){
+                        
+                        if(!prePointerInfo.skipOnPointerObservable){
+                            this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
+                        }
+                        prePointerInfo.skipOnPointerObservable = true;
+                    }
+                }else{
+                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+
+                    // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
+                    if(originalScenePick && utilityScenePick){
+                        if(utilityScenePick.distance < originalScenePick.distance){
+                            if(!prePointerInfo.skipOnPointerObservable){
+                                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
+                            }
+                            prePointerInfo.skipOnPointerObservable = true;
+                        }
+                    }
+                }
+                
+            })
+
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
 

+ 5 - 2
src/babylon.scene.ts

@@ -5716,11 +5716,14 @@
             if (!PickingInfo) {
                 return null;
             }
-
-            return this._internalPick(world => {
+            var result = this._internalPick(world => {
                 this.createPickingRayToRef(x, y, world, this._tempPickingRay!, camera || null);
                 return this._tempPickingRay!;
             }, predicate, fastCheck);
+            if(result){
+                result.ray = this.createPickingRay(x, y, Matrix.Identity(), camera || null);
+            }
+            return result;
         }
 
         /** Launch a ray to try to pick a sprite in the scene