浏览代码

sixDofDragBehavior

Trevor Baron 7 年之前
父节点
当前提交
b030170b50

+ 2 - 1
Tools/Gulp/config.json

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

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

@@ -27,7 +27,7 @@
 - Get a root mesh from an asset container, load a mesh from a file with a single string url ([TrevorDev](https://github.com/TrevorDev))
 - 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))
+- PointerDragBehavior, SixDofDragBehavior, MultiPointerScaleBehavior to enable drag and drop/scaling with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo and GizmoManager classes used to manipulate meshes in a scene. Position, rotation, scale, and bounding box gizmos ([TrevorDev](https://github.com/TrevorDev))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 - AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))

+ 2 - 3
src/Behaviors/Mesh/babylon.multiPointerScaleBehavior.ts

@@ -80,12 +80,11 @@ module BABYLON {
             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){
+                    if(change.length()>0.01){
                         ownerNode.scaling.addInPlace(change);
                     }
                 }
-            })
-
+            });
         }
         /**
          *  Detaches the behavior from the mesh

+ 143 - 0
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -0,0 +1,143 @@
+module BABYLON {
+    /**
+     * A behavior that when attached to a mesh will allow the mesh to be dragged around based on directiona and origin of the pointer's ray
+     */
+    export class SixDofDragBehavior implements Behavior<Mesh> {
+        private static _virtualScene:Scene;
+        private _ownerNode:Mesh;
+        private _sceneRenderObserver:Nullable<Observer<Scene>> = null;
+        private _scene:Scene;
+        private _targetPosition = new Vector3(0,0,0);
+        private _virtualOriginMesh:AbstractMesh;
+        private _virtualDragMesh:AbstractMesh;
+        private _pointerObserver:Nullable<Observer<PointerInfo>>;
+        // How much faster the object should move when its further away
+        private _sixDofZDragFactor = 5;
+        /**
+         * If the behavior is currently in a dragging state
+         */
+        public dragging = false;
+        /**
+         * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
+         */
+        public currentDraggingPointerID = -1;
+
+
+        constructor(){
+        }
+        
+        /**
+         *  The name of the behavior
+         */
+        public get name(): string {
+            return "SixDofDrag";
+        }
+
+        /**
+         *  Initializes the behavior
+         */
+        public init() {}
+
+        /**
+         * 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;
+            this._scene = this._ownerNode.getScene();
+            if(!SixDofDragBehavior._virtualScene){
+                SixDofDragBehavior._virtualScene = new BABYLON.Scene(this._scene.getEngine());
+                this._scene.getEngine().scenes.pop();
+            }
+        
+            var pickedMesh:Nullable<AbstractMesh> = null;
+            var lastSixDofOriginPosition = new BABYLON.Vector3(0,0,0);
+
+            // Setup virtual meshes to be used for dragging without dirtying the existing scene
+            this._virtualOriginMesh = new BABYLON.AbstractMesh("", SixDofDragBehavior._virtualScene);
+            this._virtualOriginMesh.rotationQuaternion = new Quaternion();
+            this._virtualDragMesh = new BABYLON.AbstractMesh("", SixDofDragBehavior._virtualScene);
+            this._virtualDragMesh.rotationQuaternion = new Quaternion();
+
+            var pickPredicate = (m:AbstractMesh)=>{
+                return this._ownerNode == m || m.isDescendantOf(this._ownerNode);
+            }
+            
+            this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState)=>{                
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if(!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)){
+                        pickedMesh = pointerInfo.pickInfo.pickedMesh;
+                        lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
+
+                        // Set position and orientation of the controller
+                        this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
+                        this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.subtract(pointerInfo.pickInfo.ray.direction));
+
+                        // Attach the virtual drag mesh to the virtual origin mesh so it can be dragged
+                        this._virtualOriginMesh.removeChild(this._virtualDragMesh);
+                        this._virtualDragMesh.position.copyFrom(pickedMesh.absolutePosition);
+                        if(!pickedMesh.rotationQuaternion){
+                            pickedMesh.rotationQuaternion = new Quaternion();
+                        }
+                        this._virtualDragMesh.rotationQuaternion!.copyFrom(pickedMesh.rotationQuaternion);
+                        this._virtualOriginMesh.addChild(this._virtualDragMesh);
+
+                        // Update state
+                        this.dragging = true;
+                        this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
+                    }
+                }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
+                    if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
+                        this.dragging = false;
+                        this.currentDraggingPointerID = -1;
+                        pickedMesh = null;
+                        this._virtualOriginMesh.removeChild(this._virtualDragMesh);
+                    }
+                }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
+                    if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh){
+                        // Calculate controller drag distance in controller space
+                        var originDragDifference = pointerInfo.pickInfo.ray.origin.subtract(lastSixDofOriginPosition);
+                        lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
+                        var localOriginDragDifference = Vector3.TransformCoordinates(originDragDifference, Matrix.Invert(this._virtualOriginMesh.getWorldMatrix().getRotationMatrix()));
+                        
+                        this._virtualOriginMesh.addChild(this._virtualDragMesh);
+                        // Determine how much the controller moved to/away towards the dragged object and use this to move the object further when its further away
+                        var zDragDistance = Vector3.Dot(localOriginDragDifference, this._virtualOriginMesh.position.normalizeToNew());
+                        this._virtualDragMesh.position.z -= this._virtualDragMesh.position.z < 1 ? zDragDistance*this._sixDofZDragFactor : zDragDistance*this._sixDofZDragFactor*this._virtualDragMesh.position.z;
+                        if(this._virtualDragMesh.position.z < 0){
+                            this._virtualDragMesh.position.z = 0;
+                        }
+                        
+                        // Update the controller position
+                        this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
+                        this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.subtract(pointerInfo.pickInfo.ray.direction));
+                        this._virtualOriginMesh.removeChild(this._virtualDragMesh)
+                    
+                        // Move the virtualObjectsPosition into the picked mesh's space if needed
+                        this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
+                        if(pickedMesh.parent){
+                            Vector3.TransformCoordinatesToRef(this._targetPosition, Matrix.Invert(pickedMesh.parent.getWorldMatrix()), this._targetPosition);
+                        }
+                    }
+                }
+            });
+
+            // On every frame move towards target scaling to avoid jitter caused by vr controllers
+            this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
+                if(this.dragging && pickedMesh){
+                    // Slowly move mesh to avoid jitter
+                    pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(0.2));
+                }
+            });
+        }
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        public detach(): void {
+            this._scene.onPointerObservable.remove(this._pointerObserver);
+            this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
+            this._virtualOriginMesh.dispose()
+            this._virtualDragMesh.dispose();
+        }
+    }
+}

+ 13 - 0
src/Gamepad/Controllers/babylon.poseEnabledController.ts

@@ -258,6 +258,19 @@ module BABYLON {
             if (!this._mesh.rotationQuaternion) {
                 this._mesh.rotationQuaternion = new Quaternion();
             }
+
+            // Sync controller mesh and pointing pose node's state with controller
+            this.update();
+            if(this._pointingPoseNode){
+                var parents = [];
+                var obj:Node = this._pointingPoseNode;
+                while(obj.parent){
+                    parents.push(obj.parent); 
+                    obj = obj.parent;
+                }
+                parents.reverse().forEach((p)=>{p.computeWorldMatrix(true)});
+            }
+
             this._meshAttachedObservable.notifyObservers(mesh);
         }
 

+ 4 - 2
src/Gamepad/Controllers/babylon.windowsMotionController.ts

@@ -348,14 +348,14 @@ module BABYLON {
 
                 if (meshLoaded) {
                     meshLoaded(this._defaultModel);
-                }
+                }                    
             }, null, (scene: Scene, message: string) => {
                 Tools.Log(message);
                 Tools.Warn('Failed to retrieve controller model from the remote server: ' + path + filename);
                 if (!forceDefault) {
                     this.initControllerMesh(scene, meshLoaded, true);
                 }
-            });
+            });            
         }
 
         /**
@@ -476,6 +476,8 @@ module BABYLON {
             loadedMeshInfo.pointingPoseNode = getChildByName(rootNode, this._mapping.pointingPoseMeshName);
             if (!loadedMeshInfo.pointingPoseNode) {
                 Tools.Warn('Missing pointing pose mesh with name: ' + this._mapping.pointingPoseMeshName);
+            }else{
+                this._pointingPoseNode = loadedMeshInfo.pointingPoseNode;
             }
 
             return loadedMeshInfo;