瀏覽代碼

Merge pull request #4641 from TrevorDev/rotationAlongDragPlaneBugFix

use alternate picked point when rotating along drag plane
David Catuhe 7 年之前
父節點
當前提交
e2aae54aac

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

@@ -7,7 +7,7 @@
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
-- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, rotation, scale and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
+- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, scale, rotation and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
 - New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh. [Doc](http://doc.babylonjs.com/how_to/meshbehavior) ([TrevorDev](https://github.com/TrevorDev))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
   - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))

+ 38 - 0
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -9,6 +9,14 @@ module BABYLON {
         private _pointerObserver:Nullable<Observer<PointerInfo>>;
         private static _planeScene:Scene;
         /**
+         * The maximum tolerated angle between the drag plane and dragging pointer rays to trigger pointer events. Set to 0 to allow any angle (default: 0)
+         */
+        public maxDragAngle = 0;
+        /**
+         * @hidden
+         */
+        public _useAlternatePickedPointAboveMaxDragAngle = false;
+        /**
          * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
          */
         public currentDraggingPointerID = -1;
@@ -95,6 +103,7 @@ module BABYLON {
         public init() {}
 
         private _tmpVector = new Vector3(0,0,0);
+        private _alternatePickedPoint = new Vector3(0,0,0);
         private _worldDragAxis = new Vector3(0,0,0);
         /**
          * Attaches the drag behavior the passed in mesh
@@ -200,6 +209,35 @@ module BABYLON {
             if(!ray){
                 return null;
             }
+
+            // Calculate angle between plane normal and ray
+            var angle = Math.acos(Vector3.Dot(this._dragPlane.forward, ray.direction));
+            // Correct if ray is casted from oposite side
+            if(angle > Math.PI/2){
+                angle = Math.PI - angle;
+            }
+
+            // If the angle is too perpendicular to the plane pick another point on the plane where it is looking
+            if(this.maxDragAngle > 0 && angle > this.maxDragAngle){
+                if(this._useAlternatePickedPointAboveMaxDragAngle){
+                    // Invert ray direction along the towards object axis
+                    this._tmpVector.copyFrom(ray.direction);
+                    (<Mesh>this._attachedNode).absolutePosition.subtractToRef(ray.origin, this._alternatePickedPoint);
+                    this._alternatePickedPoint.normalize();
+                    this._alternatePickedPoint.scaleInPlace(-2*Vector3.Dot(this._alternatePickedPoint, this._tmpVector));
+                    this._tmpVector.addInPlace(this._alternatePickedPoint);
+                    
+                    // Project resulting vector onto the drag plane and add it to the attached nodes absolute position to get a picked point
+                    var dot = Vector3.Dot(this._dragPlane.forward, this._tmpVector);
+                    this._dragPlane.forward.scaleToRef(-dot, this._alternatePickedPoint);
+                    this._alternatePickedPoint.addInPlace(this._tmpVector);
+                    this._alternatePickedPoint.addInPlace((<Mesh>this._attachedNode).absolutePosition);
+                    return this._alternatePickedPoint
+                }else{
+                    return null;
+                }
+            }
+
             var pickResult = PointerDragBehavior._planeScene.pickWithRay(ray, (m)=>{return m == this._dragPlane})
             if (pickResult && pickResult.hit && pickResult.pickedMesh && pickResult.pickedPoint) {
                 return pickResult.pickedPoint;

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

@@ -80,21 +80,7 @@ module BABYLON {
                         }
                     }
                     
-                    var invertCount = 0;
-                    if(this.attachedMesh.scaling["x"] < 0){
-                        invertCount++;
-                    }
-                    if(this.attachedMesh.scaling["y"] < 0){
-                        invertCount++;
-                    }
-                    if(this.attachedMesh.scaling["z"] < 0){
-                        invertCount++;
-                    }
-                    if(invertCount % 2 == 0){
-                        this.attachedMesh.scaling.addInPlace(tmpVector);
-                    }else{
-                        this.attachedMesh.scaling.subtractInPlace(tmpVector);
-                    }
+                    this.attachedMesh.scaling.addInPlace(tmpVector);
 
                     if(snapped){
                         tmpSnapEvent.snapDistance = this.snapDistance*dragSteps;

+ 6 - 2
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -182,8 +182,12 @@ module BABYLON {
                                 
                                 // Update scale and position
                                 this.attachedMesh.scaling.addInPlace(deltaScale);
-                                this.attachedMesh.getAbsolutePosition().addToRef(worldMoveDirection, this._tmpVector)
-                                this.attachedMesh.setAbsolutePosition(this._tmpVector);
+                                if(this.attachedMesh.scaling.x < 0 || this.attachedMesh.scaling.y < 0 || this.attachedMesh.scaling.z < 0){
+                                    this.attachedMesh.scaling.subtractInPlace(deltaScale);
+                                }else{
+                                    this.attachedMesh.getAbsolutePosition().addToRef(worldMoveDirection, this._tmpVector)
+                                    this.attachedMesh.setAbsolutePosition(this._tmpVector);
+                                }
                             }
                         })
 

+ 18 - 2
src/Gizmos/babylon.gizmo.ts

@@ -9,6 +9,7 @@ module BABYLON {
         protected _rootMesh:Mesh;
         private _attachedMesh:Nullable<AbstractMesh>;
         private _scaleFactor = 3;
+        private _tmpMatrix = new Matrix();
         /**
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
          * * When set, interactions will be enabled
@@ -50,9 +51,24 @@ module BABYLON {
                 if(this.attachedMesh){
                     if(this.updateGizmoRotationToMatchAttachedMesh){
                         if(!this._rootMesh.rotationQuaternion){
-                            this._rootMesh.rotationQuaternion = new BABYLON.Quaternion();
+                            this._rootMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._rootMesh.rotation.y, this._rootMesh.rotation.x, this._rootMesh.rotation.z);
                         }
-                        Quaternion.FromRotationMatrixToRef(this.attachedMesh.getWorldMatrix().getRotationMatrix(), this._rootMesh.rotationQuaternion);
+
+                        // Remove scaling before getting rotation matrix to get rotation matrix unmodified by scale
+                        tempVector.copyFrom(this.attachedMesh.scaling);
+                        if(this.attachedMesh.scaling.x < 0){
+                            this.attachedMesh.scaling.x *= -1;
+                        }
+                        if(this.attachedMesh.scaling.y < 0){
+                            this.attachedMesh.scaling.y *= -1;
+                        }
+                        if(this.attachedMesh.scaling.z < 0){
+                            this.attachedMesh.scaling.z *= -1;
+                        }
+                        this.attachedMesh.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
+                        this.attachedMesh.scaling.copyFrom(tempVector);
+                        this.attachedMesh.computeWorldMatrix();
+                        Quaternion.FromRotationMatrixToRef(this._tmpMatrix, this._rootMesh.rotationQuaternion);
                     }
                     if(this.updateGizmoPositionToMatchAttachedMesh){
                         this._rootMesh.position.copyFrom(this.attachedMesh.absolutePosition);

+ 15 - 7
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -52,13 +52,15 @@ module BABYLON {
             // Add drag behavior to handle events when the gizmo is dragged
             this.dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal});
             this.dragBehavior.moveAttached = false;
+            this.dragBehavior.maxDragAngle =  Math.PI*9/20;
+            this.dragBehavior._useAlternatePickedPointAboveMaxDragAngle = true;
             this._rootMesh.addBehavior(this.dragBehavior);
 
-            var lastDragPosition:Nullable<Vector3> = null;
+            var lastDragPosition = new Vector3();
 
             this.dragBehavior.onDragStartObservable.add((e)=>{
                 if(this.attachedMesh){
-                    lastDragPosition = e.dragPlanePoint;
+                    lastDragPosition.copyFrom(e.dragPlanePoint);
                 }
             })
 
@@ -69,9 +71,9 @@ module BABYLON {
             var tmpSnapEvent = {snapDistance: 0};
             var currentSnapDragDistance = 0;
             this.dragBehavior.onDragObservable.add((event)=>{
-                if(this.attachedMesh && lastDragPosition){
+                if(this.attachedMesh){
                     if(!this.attachedMesh.rotationQuaternion){
-                        this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
+                        this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y, this.attachedMesh.rotation.x, this.attachedMesh.rotation.z);
                     }
                     // Calc angle over full 360 degree (https://stackoverflow.com/questions/43493711/the-angle-between-two-3d-vectors-with-a-result-range-0-360)
                     var newVector = event.dragPlanePoint.subtract(this.attachedMesh.position).normalize();
@@ -113,10 +115,16 @@ module BABYLON {
                      var quaternionCoefficient = Math.sin(angle/2)
                      var amountToRotate = new BABYLON.Quaternion(planeNormalTowardsCamera.x*quaternionCoefficient,planeNormalTowardsCamera.y*quaternionCoefficient,planeNormalTowardsCamera.z*quaternionCoefficient,Math.cos(angle/2));
 
-                     // Rotate selected mesh quaternion over fixed axis
-                     this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate,this.attachedMesh.rotationQuaternion);
+                     if(this.updateGizmoRotationToMatchAttachedMesh){
+                        // Rotate selected mesh quaternion over fixed axis
+                        this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate,this.attachedMesh.rotationQuaternion);
+                     }else{
+                         // Rotate selected mesh quaternion over rotated axis
+                        amountToRotate.multiplyToRef(this.attachedMesh.rotationQuaternion,this.attachedMesh.rotationQuaternion);
+                     }
+                     
 
-                    lastDragPosition = event.dragPlanePoint;
+                    lastDragPosition.copyFrom(event.dragPlanePoint);
                     if(snapped){
                         tmpSnapEvent.snapDistance = angle;
                         this.onSnapObservable.notifyObservers(tmpSnapEvent);