瀏覽代碼

Merge pull request #4389 from TrevorDev/gizmoManagerClass

gizmo manager
David Catuhe 7 年之前
父節點
當前提交
697b8b2726

+ 28 - 9
Tools/Gulp/config.json

@@ -95,6 +95,8 @@
             "physics",
             "textureFormats",
             "debug",
+            "utilityLayer",
+            "gizmos",
             "morphTargets",
             "octrees",
             "vr",
@@ -1020,15 +1022,7 @@
                 "../../src/Debug/babylon.rayHelper.js",
                 "../../src/Debug/babylon.debugLayer.js",
                 "../../src/Debug/babylon.physicsViewer.js",
-                "../../src/Rendering/babylon.boundingBoxRenderer.js",
-                "../../src/Rendering/babylon.utilityLayerRenderer.js",
-                "../../src/Gizmos/babylon.gizmo.js",
-                "../../src/Gizmos/babylon.axisDragGizmo.js",
-                "../../src/Gizmos/babylon.axisScaleGizmo.js",
-                "../../src/Gizmos/babylon.planeRotationGizmo.js",
-                "../../src/Gizmos/babylon.positionGizmo.js",
-                "../../src/Gizmos/babylon.rotationGizmo.js",
-                "../../src/Gizmos/babylon.scaleGizmo.js"
+                "../../src/Rendering/babylon.boundingBoxRenderer.js"
             ],
             "dependUpon": [
                 "shaderMaterial",
@@ -1039,6 +1033,31 @@
             ],
             "shaders": []
         },
+        "utilityLayer": {
+            "files": [
+                "../../src/Rendering/babylon.utilityLayerRenderer.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
+        "gizmos": {
+            "files": [
+                "../../src/Gizmos/babylon.gizmo.js",
+                "../../src/Gizmos/babylon.axisDragGizmo.js",
+                "../../src/Gizmos/babylon.axisScaleGizmo.js",
+                "../../src/Gizmos/babylon.planeRotationGizmo.js",
+                "../../src/Gizmos/babylon.positionGizmo.js",
+                "../../src/Gizmos/babylon.rotationGizmo.js",
+                "../../src/Gizmos/babylon.scaleGizmo.js",
+                "../../src/Gizmos/babylon.gizmoManager.js"
+            ],
+            "dependUpon": [
+                "core",
+                "utilityLayer",
+                "meshBehaviors"
+            ]
+        },
         "morphTargets": {
             "files": [
                 "../../src/Morph/babylon.morphTarget.js",

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

@@ -22,7 +22,7 @@
 - UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
 - AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
 - Pointer drag behavior to enable drag and drop with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
-- Gizmo class used to manipulate meshes in a scene, position, rotation, scale gizmos ([TrevorDev](https://github.com/TrevorDev))
+- Gizmo and gizmoManager class used to manipulate meshes in a scene, position, rotation, scale gizmos ([TrevorDev](https://github.com/TrevorDev))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 
 ### glTF Loader

+ 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);
 

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

@@ -0,0 +1,82 @@
+module BABYLON {
+    /**
+     * Helps setup gizmo's in the scene to rotate/scale/position meshes
+     */
+    export class GizmoManager implements IDisposable{
+
+        private _gizmoLayer:UtilityLayerRenderer;
+        // Set of gizmos that are currently in the scene for each mesh
+        private _gizmoSet:{[meshID: string]: {positionGizmo: PositionGizmo, rotationGizmo: RotationGizmo}} = {}
+        private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
+
+        /**
+         * 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(private scene:Scene, options?:{singleGizmo?:boolean}){
+            this._gizmoLayer = new UtilityLayerRenderer(scene);
+
+            // Options parsing
+            if(!options){
+                options = {}
+            }
+            if(options.singleGizmo === undefined){
+                options.singleGizmo = true;
+            }
+
+            // Instatiate/dispose gizmos based on pointer actions
+            this._pointerObserver = scene.onPointerObservable.add((pointerInfo, state)=>{
+                if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN){
+                    if(pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh){
+                        if(!this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId]){
+                            if(options!.singleGizmo){
+                                this._clearGizmos();
+                            }                            
+                            // Enable gizmo when mesh is selected
+                            this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId] = {positionGizmo: new PositionGizmo(this._gizmoLayer), rotationGizmo: new RotationGizmo(this._gizmoLayer)}
+                            this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                            this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                        }else{
+                            if(!options!.singleGizmo){
+                                // Disable gizmo when clicked again
+                                this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.dispose();
+                                this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.dispose();
+                                delete this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId];
+                            }
+                        }
+                    }else {
+                        if(options!.singleGizmo){
+                            // Disable gizmo when clicked away
+                            if(pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
+                                var gizmoPick = this._gizmoLayer.utilityLayerScene.pickWithRay(pointerInfo.pickInfo.ray);
+                                if(gizmoPick && !gizmoPick.hit){
+                                    this._clearGizmos();
+                                }
+                            }
+                        }
+                    }
+                }
+            })
+        }
+
+        /**
+         * Disposes of the gizmo manager
+         */
+        public dispose(){
+            this.scene.onPointerObservable.remove(this._pointerObserver);
+            this._clearGizmos();
+            this._gizmoLayer.dispose();
+        }
+
+        private _clearGizmos(){
+            for(var key in this._gizmoSet){
+                if(this._gizmoSet.hasOwnProperty(key)){
+                    this._gizmoSet[key].positionGizmo.dispose();
+                    this._gizmoSet[key].rotationGizmo.dispose();
+                    delete this._gizmoSet[key];
+                }
+            }
+        }
+    }
+}

+ 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);
 

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

@@ -12,9 +12,13 @@ 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>>;
+        private _originalPointerObserver:Nullable<Observer<PointerInfoPre>>;
         /**
          * Instantiates a UtilityLayerRenderer
          * @param originalScene the original scene that will be rendered on top of
@@ -24,6 +28,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();
+            this._originalPointerObserver = 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;
 
@@ -58,6 +106,9 @@ module BABYLON {
             if(this._sceneDisposeObserver){
                 this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
             }
+            if(this._originalPointerObserver){
+                this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
+            }
             this.utilityLayerScene.dispose();
         }
 

+ 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