Trevor Baron 7 лет назад
Родитель
Сommit
5e67ac80a0

+ 2 - 1
Tools/Gulp/config.json

@@ -313,7 +313,8 @@
         },
         "meshBehaviors": {
             "files": [
-                "../../src/Behaviors/Mesh/babylon.pointerDragBehavior.js"
+                "../../src/Behaviors/Mesh/babylon.pointerDragBehavior.js",
+                "../../src/Behaviors/Mesh/babylon.multiPointerScaleBehavior.js"
             ],
             "dependUpon": [
                 "behaviors"

+ 102 - 0
src/Behaviors/Mesh/babylon.multiPointerScaleBehavior.ts

@@ -0,0 +1,102 @@
+module BABYLON {
+    /**
+     * A behavior that when attached to a mesh will allow the mesh to be scaled
+     */
+    export class MultiPointerScaleBehavior implements Behavior<Mesh> {
+        private _dragBehaviorA:PointerDragBehavior;
+        private _dragBehaviorB:PointerDragBehavior;
+        private _startDistance = 0;
+        private _initialScale = new Vector3(0,0,0);
+        private _targetScale = new Vector3(0,0,0);
+        private _ownerNode:Mesh;
+        private _sceneRenderObserver:Nullable<Observer<Scene>> = null;
+
+        constructor(){
+            this._dragBehaviorA = new BABYLON.PointerDragBehavior({});
+            this._dragBehaviorA.moveAttached = false;
+            this._dragBehaviorB = new BABYLON.PointerDragBehavior({});
+            this._dragBehaviorB.moveAttached = false;
+        }
+        
+        /**
+         *  The name of the behavior
+         */
+        public get name(): string {
+            return "MultiPointerScale";
+        }
+
+        /**
+         *  Initializes the behavior
+         */
+        public init() {}
+
+        private _getCurrentDistance(){
+            return this._dragBehaviorA.lastDragPosition.subtract(this._dragBehaviorB.lastDragPosition).length();
+        }
+
+        /**
+         * Attaches the scale behavior the passed in mesh
+         * @param ownerNode The mesh that will be scaled around once attached
+         */
+        public attach(ownerNode: Mesh): void {
+            this._ownerNode = ownerNode;
+
+            // Create 2 drag behaviors such that each will only be triggered by a separate pointer
+            this._dragBehaviorA.onDragStartObservable.add((e)=>{
+                if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
+                    if(this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID){
+                        this._dragBehaviorA.releaseDrag();
+                    }else{
+                        this._initialScale.copyFrom(ownerNode.scaling)
+                        this._startDistance = this._getCurrentDistance();
+                    }
+                }
+            });
+            this._dragBehaviorB.onDragStartObservable.add((e)=>{
+                if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
+                    if(this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID){
+                        this._dragBehaviorB.releaseDrag();
+                    }else{
+                        this._initialScale.copyFrom(ownerNode.scaling)
+                        this._startDistance = this._getCurrentDistance();
+                    }
+                }
+            });
+
+            // Once both drag behaviors are active scale based on the distance between the two pointers
+            [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior)=>{
+                behavior.onDragObservable.add(()=>{
+                    if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
+                        var ratio = this._getCurrentDistance()/this._startDistance;
+                        this._initialScale.scaleToRef(ratio, this._targetScale);
+                    }
+                });
+            })
+
+            ownerNode.addBehavior(this._dragBehaviorA);
+            ownerNode.addBehavior(this._dragBehaviorB);
+
+            // On every frame move towards target scaling to avoid jitter caused by vr controllers
+            this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
+                if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
+                    var change = this._targetScale.subtract(ownerNode.scaling).scaleInPlace(0.1);
+                    if(change.length()>0.1){
+                        ownerNode.scaling.addInPlace(change);
+                    }
+                }
+            })
+
+        }
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        public detach(): void {
+            this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
+            [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior)=>{
+                behavior.onDragStartObservable.clear();
+                behavior.onDragObservable.clear();
+                this._ownerNode.removeBehavior(behavior);
+            });
+        }
+    }
+}

+ 35 - 20
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -8,7 +8,18 @@ module BABYLON {
         private _scene:Scene;
         private _pointerObserver:Nullable<Observer<PointerInfo>>;
         private static _planeScene:Scene;
-        private _draggingID = -1;
+        /**
+         * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
+         */
+        public currentDraggingPointerID = -1;
+        /**
+         * The last position where the pointer hit the drag plane in world space
+         */
+        public lastDragPosition:Vector3;
+        /**
+         * If the behavior is currently in a dragging state
+         */
+        public dragging = false;
         // Debug mode will display drag planes to help visualize behavior
         private _debugMode = false;
         private _maxDragAngle = Math.PI/5;
@@ -20,15 +31,15 @@ module BABYLON {
          *  * dragPlaneNormal normal of the current drag plane used during the drag
          *  * dragPlanePoint in world space where the drag intersects the drag plane
          */
-        public onDragObservable = new Observable<{delta:Vector3, dragPlanePoint:Vector3, dragPlaneNormal:Vector3, dragDistance:number}>()
+        public onDragObservable = new Observable<{delta:Vector3, dragPlanePoint:Vector3, dragPlaneNormal:Vector3, dragDistance:number, pointerId:number}>()
         /**
          *  Fires each time a drag begins (eg. mouse down on mesh)
          */
-        public onDragStartObservable = new Observable<{dragPlanePoint:Vector3}>()
+        public onDragStartObservable = new Observable<{dragPlanePoint:Vector3, pointerId:number}>()
         /**
          *  Fires each time a drag ends (eg. mouse release after drag)
          */
-        public onDragEndObservable = new Observable<{dragPlanePoint:Vector3}>()
+        public onDragEndObservable = new Observable<{dragPlanePoint:Vector3, pointerId:number}>()
         /**
          *  If the attached mesh should be moved when dragged
          */
@@ -95,8 +106,7 @@ module BABYLON {
             this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
 
             // State of the drag
-            var dragging = false;
-            var lastPosition = new BABYLON.Vector3(0,0,0);
+            this.lastDragPosition = new BABYLON.Vector3(0,0,0);
             var delta = new BABYLON.Vector3(0,0,0);
             var dragLength = 0;
 
@@ -110,24 +120,23 @@ module BABYLON {
                 }
                 
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if(!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)){
+                    
+                    if(!this.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>pointerInfo.event).pointerId;
-                            lastPosition.copyFrom(pickedPoint);
-                            this.onDragStartObservable.notifyObservers({dragPlanePoint: pickedPoint});
+                            this.dragging = true;
+                            this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
+                            this.lastDragPosition.copyFrom(pickedPoint);
+                            this.onDragStartObservable.notifyObservers({dragPlanePoint: pickedPoint, pointerId: this.currentDraggingPointerID});
                         }
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
-                    if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId){
-                        dragging = false;
-                        this._draggingID = -1;
-                        this.onDragEndObservable.notifyObservers({dragPlanePoint: lastPosition});
+                    if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
+                        this.releaseDrag();
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
-                    if(this._draggingID == (<PointerEvent>pointerInfo.event).pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
+                    if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray){
                         var pickedPoint = this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
                         
                          // Get angle between drag plane and ray. Only update the drag plane at non steep angles to avoid jumps in delta position
@@ -143,23 +152,29 @@ module BABYLON {
                                 var worldDragAxis = Vector3.TransformCoordinates(this.options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix());
 
                                 // Project delta drag from the drag plane onto the drag axis
-                                dragLength = BABYLON.Vector3.Dot(pickedPoint.subtract(lastPosition), worldDragAxis)
+                                dragLength = BABYLON.Vector3.Dot(pickedPoint.subtract(this.lastDragPosition), worldDragAxis)
                                 worldDragAxis.scaleToRef(dragLength, delta);
                             }else{
                                 dragLength = delta.length();
-                                pickedPoint.subtractToRef(lastPosition, delta);
+                                pickedPoint.subtractToRef(this.lastDragPosition, delta);
                             }
                             if(this.moveAttached){
                                 (<Mesh>this._attachedNode).position.addInPlace(delta);
                             }
-                            this.onDragObservable.notifyObservers({dragDistance: dragLength, delta: delta, dragPlanePoint: pickedPoint, dragPlaneNormal: this._dragPlane.forward});
-                            lastPosition.copyFrom(pickedPoint);
+                            this.onDragObservable.notifyObservers({dragDistance: dragLength, delta: delta, dragPlanePoint: pickedPoint, dragPlaneNormal: this._dragPlane.forward, pointerId: this.currentDraggingPointerID});
+                            this.lastDragPosition.copyFrom(pickedPoint);
                         }
                     }
                 }
             });
         }
 
+        public releaseDrag(){
+            this.dragging = false;
+            this.onDragEndObservable.notifyObservers({dragPlanePoint: this.lastDragPosition, pointerId: this.currentDraggingPointerID});
+            this.currentDraggingPointerID = -1;
+        }
+
         private _pickWithRayOnDragPlane(ray:Nullable<Ray>){
             if(!ray){
                 return null;