Browse Source

Merge pull request #4657 from TrevorDev/boundingBoxNestedFixes

Bounding box nested fixes
David Catuhe 7 years ago
parent
commit
0d4aacb429

+ 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, scale, rotation 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, rotation, scale 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))

+ 12 - 12
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -70,19 +70,19 @@ module BABYLON {
          */
         public useObjectOrienationForDragging = true;
 
+        private _options:{dragAxis?:Vector3, dragPlaneNormal?:Vector3};
         /**
          * 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. If no options are specified the drag plane will always face the ray's origin (eg. camera)
          */
-        constructor(private options:{dragAxis?:Vector3, dragPlaneNormal?:Vector3}){
+        constructor(options?:{dragAxis?:Vector3, dragPlaneNormal?:Vector3}){
+            this._options = options ? options : {};
+            
             var optionCount = 0;
-            if(options === undefined){
-                options = {}
-            }
-            if(options.dragAxis){
+            if(this._options.dragAxis){
                 optionCount++;
             }
-            if(options.dragPlaneNormal){
+            if(this._options.dragPlaneNormal){
                 optionCount++;
             }
             if(optionCount > 1){
@@ -167,9 +167,9 @@ module BABYLON {
                             }
                             
                             // depending on the drag mode option drag accordingly
-                            if(this.options.dragAxis){
+                            if(this._options.dragAxis){
                                 // Convert local drag axis to world
-                                Vector3.TransformCoordinatesToRef(this.options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._worldDragAxis);
+                                Vector3.TransformCoordinatesToRef(this._options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._worldDragAxis);
 
                                 // Project delta drag from the drag plane onto the drag axis
                                 pickedPoint.subtractToRef(this.lastDragPosition, this._tmpVector);
@@ -257,8 +257,8 @@ module BABYLON {
         // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
         private _updateDragPlanePosition(ray:Ray, dragPlanePosition:Vector3){
             this._pointA.copyFrom(dragPlanePosition);
-            if(this.options.dragAxis){
-                this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this.options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._localAxis) : this._localAxis.copyFrom(this.options.dragAxis);
+            if(this._options.dragAxis){
+                this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this._options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._localAxis) : this._localAxis.copyFrom(this._options.dragAxis);
 
                 // Calculate plane normal in direction of camera but perpendicular to drag axis
                 this._pointA.addToRef(this._localAxis, this._pointB); // towards drag axis
@@ -275,8 +275,8 @@ module BABYLON {
                 this._dragPlane.position.copyFrom(this._pointA);
                 this._pointA.subtractToRef(this._lookAt, this._lookAt);
                 this._dragPlane.lookAt(this._lookAt);
-            }else if(this.options.dragPlaneNormal){
-                this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this.options.dragPlaneNormal, this._attachedNode.getWorldMatrix().getRotationMatrix(),this._localAxis) : this._localAxis.copyFrom(this.options.dragPlaneNormal);
+            }else if(this._options.dragPlaneNormal){
+                this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this._options.dragPlaneNormal, this._attachedNode.getWorldMatrix().getRotationMatrix(),this._localAxis) : this._localAxis.copyFrom(this._options.dragPlaneNormal);
                 this._dragPlane.position.copyFrom(this._pointA);
                 this._pointA.subtractToRef(this._localAxis, this._lookAt);
                 this._dragPlane.lookAt(this._lookAt);

+ 15 - 6
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -14,9 +14,9 @@ module BABYLON {
         private _moving = false;
         private _startingOrientation = new Quaternion();
         /**
-         * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 5)
+         * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 3)
          */
-         private zDragFactor = 5;
+         private zDragFactor = 3;
         /**
          * If the behavior is currently in a dragging state
          */
@@ -74,7 +74,11 @@ module BABYLON {
             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;
+                        if(this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE){
+                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.position)
+                        }
+                        
+                        pickedMesh = this._ownerNode;
                         lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
 
                         // Set position and orientation of the controller
@@ -108,15 +112,20 @@ module BABYLON {
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh){
+                        var zDragFactor = this.zDragFactor;
+                        if(this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE){
+                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.position)
+                            zDragFactor = 0;
+                        }
+
                         // 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()));
+                        var localOriginDragDifference = -Vector3.Dot(originDragDifference, pointerInfo.pickInfo.ray.direction);
 
                         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.zDragFactor : zDragDistance*this.zDragFactor*this._virtualDragMesh.position.z;
+                        this._virtualDragMesh.position.z -= this._virtualDragMesh.position.z < 1 ? localOriginDragDifference*this.zDragFactor : localOriginDragDifference*zDragFactor*this._virtualDragMesh.position.z;
                         if(this._virtualDragMesh.position.z < 0){
                             this._virtualDragMesh.position.z = 0;
                         }

+ 1 - 1
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -921,7 +921,7 @@ module BABYLON {
                 }
 
                 this.raySelectionPredicate = (mesh) => {
-                    return mesh.isVisible;
+                    return mesh.isVisible && mesh.isPickable;
                 }
 
                 this.meshSelectionPredicate = (mesh) => {

+ 109 - 23
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -43,7 +43,7 @@ module BABYLON {
          * Fired when a rotation sphere or scale box drag is needed
          */
         public onDragEndObservable = new Observable<{}>();
-        
+        private _anchorMesh:AbstractMesh;
         /**
          * Creates an BoundingBoxGizmo
          * @param gizmoLayer The utility layer the gizmo will be added to
@@ -55,6 +55,7 @@ module BABYLON {
             // Do not update the gizmo's scale so it has a fixed size to the object its attached to
             this._updateScale = false;
 
+            this._anchorMesh = new AbstractMesh("anchor", gizmoLayer.utilityLayerScene);
             // Create Materials
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -122,6 +123,9 @@ module BABYLON {
                         if(!this.attachedMesh.rotationQuaternion){
                             this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y,this.attachedMesh.rotation.x,this.attachedMesh.rotation.z);
                         }
+                        if(!this._anchorMesh.rotationQuaternion){
+                            this._anchorMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._anchorMesh.rotation.y,this._anchorMesh.rotation.x,this._anchorMesh.rotation.z);
+                        }
                        
                         // Do not allow the object to turn more than a full circle
                         totalTurnAmountOfDrag+=projectDist;
@@ -133,7 +137,11 @@ module BABYLON {
                             }else{
                                 Quaternion.RotationYawPitchRollToRef(0,projectDist,0, this._tmpQuaternion);
                             }
-                            this.attachedMesh.rotationQuaternion!.multiplyInPlace(this._tmpQuaternion);
+
+                            // Rotate around center of bounding box
+                            this._anchorMesh.addChild(this.attachedMesh);
+                            this._anchorMesh.rotationQuaternion!.multiplyToRef(this._tmpQuaternion,this._anchorMesh.rotationQuaternion!);
+                            this._anchorMesh.removeChild(this.attachedMesh);
                         }
                     }
                 });
@@ -169,25 +177,19 @@ module BABYLON {
                         _dragBehavior.onDragObservable.add((event)=>{
                             this.onDragObservable.notifyObservers({});
                             if(this.attachedMesh){
-                                // Current boudning box dimensions
-                                var boundingInfo = this.attachedMesh.getBoundingInfo().boundingBox;
-                                var boundBoxDimensions = boundingInfo.maximum.subtract(boundingInfo.minimum).multiplyInPlace(this.attachedMesh.scaling);
-                                
-                                // Get the change in bounding box size/2 and add this to the mesh's position to offset from scaling with center pivot point
                                 var deltaScale = new Vector3(event.dragDistance,event.dragDistance,event.dragDistance);
                                 deltaScale.scaleInPlace(this._scaleDragSpeed);
-                                var scaleRatio = deltaScale.divide(this.attachedMesh.scaling).scaleInPlace(0.5);
-                                var moveDirection = boundBoxDimensions.multiply(scaleRatio).multiplyInPlace(dragAxis);
-                                var worldMoveDirection = Vector3.TransformCoordinates(moveDirection, this.attachedMesh.getWorldMatrix().getRotationMatrix());
-                                
-                                // Update scale and position
-                                this.attachedMesh.scaling.addInPlace(deltaScale);
-                                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);
+                                this._updateBoundingBox(); 
+
+                                 // Scale from the position of the opposite corner                   
+                                box.absolutePosition.subtractToRef(this._anchorMesh.position, this._tmpVector);
+                                this._anchorMesh.position.subtractInPlace(this._tmpVector);
+                                this._anchorMesh.addChild(this.attachedMesh);
+                                this._anchorMesh.scaling.addInPlace(deltaScale);
+                                if(this._anchorMesh.scaling.x < 0 || this._anchorMesh.scaling.y < 0 || this._anchorMesh.scaling.z < 0){
+                                    this._anchorMesh.scaling.subtractInPlace(deltaScale);
                                 }
+                                this._anchorMesh.removeChild(this.attachedMesh);
                             }
                         })
 
@@ -231,6 +233,16 @@ module BABYLON {
             })
             this._updateBoundingBox();
         }
+        
+        protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
+            if(value){
+                // Reset anchor mesh to match attached mesh's scale
+                // This is needed to avoid invalid box/sphere position on first drag
+                this._anchorMesh.addChild(value);
+                this._anchorMesh.removeChild(value);
+                this._updateBoundingBox();
+            }
+        }
 
         private _selectNode(selectedMesh:Nullable<Mesh>){
             this._rotateSpheresParent.getChildMeshes()
@@ -239,13 +251,46 @@ module BABYLON {
             })
         }
 
+        private _recurseComputeWorld(mesh:AbstractMesh){
+            mesh.computeWorldMatrix(true);
+            mesh.getChildMeshes().forEach((m)=>{
+                this._recurseComputeWorld(m);
+            });
+        }
+
         private _updateBoundingBox(){
-            if(this.attachedMesh){
-                // Update bounding dimensions/positions
-                var boundingInfo = this.attachedMesh.getBoundingInfo().boundingBox;
-                var boundBoxDimensions = boundingInfo.maximum.subtract(boundingInfo.minimum).multiplyInPlace(this.attachedMesh.scaling);
-                this._boundingDimensions.copyFrom(boundBoxDimensions);
+            if(this.attachedMesh){             
+                // Rotate based on axis
+                if(!this.attachedMesh.rotationQuaternion){
+                    this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y,this.attachedMesh.rotation.x,this.attachedMesh.rotation.z);
+                }
+                if(!this._anchorMesh.rotationQuaternion){
+                    this._anchorMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._anchorMesh.rotation.y,this._anchorMesh.rotation.x,this._anchorMesh.rotation.z);
+                }
+                this._anchorMesh.rotationQuaternion.copyFrom(this.attachedMesh.rotationQuaternion);
+
+                // Store original position and reset mesh to origin before computing the bounding box
+                this._tmpQuaternion.copyFrom(this.attachedMesh.rotationQuaternion);
+                this._tmpVector.copyFrom(this.attachedMesh.position);
+                this.attachedMesh.rotationQuaternion.set(0,0,0,1);
+                this.attachedMesh.position.set(0,0,0);
+                
+                // Update bounding dimensions/positions   
+                var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors();
+                boundingMinMax.max.subtractToRef(boundingMinMax.min, this._boundingDimensions);
+
+                // Update gizmo to match bounding box scaling and rotation
                 this._lineBoundingBox.scaling.copyFrom(this._boundingDimensions);
+                this._lineBoundingBox.position.set((boundingMinMax.max.x+boundingMinMax.min.x)/2,(boundingMinMax.max.y+boundingMinMax.min.y)/2,(boundingMinMax.max.z+boundingMinMax.min.z)/2);
+                this._rotateSpheresParent.position.copyFrom(this._lineBoundingBox.position);
+                this._scaleBoxesParent.position.copyFrom(this._lineBoundingBox.position);
+                this._lineBoundingBox.computeWorldMatrix();
+                this._anchorMesh.position.copyFrom(this._lineBoundingBox.absolutePosition);
+
+                // restore position/rotation values
+                this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
+                this.attachedMesh.position.copyFrom(this._tmpVector);
+                this._recurseComputeWorld(this.attachedMesh);
             }
 
             // Update rotation sphere locations
@@ -329,5 +374,46 @@ module BABYLON {
             this._scaleBoxesParent.dispose();
             super.dispose();
         } 
+
+        /**
+         * Makes a mesh not pickable and wraps the mesh inside of a bounding box mesh that is pickable. (This is useful to avoid picking within complex geometry)
+         * @param mesh the mesh to wrap in the bounding box mesh and make not pickable
+         * @returns the bounding box mesh with the passed in mesh as a child
+         */
+        public static MakeNotPickableAndWrapInBoundingBox(mesh:Mesh):Mesh{
+            var makeNotPickable = (root:AbstractMesh) => {
+                root.isPickable = false;
+                root.getChildMeshes().forEach((c) => {
+                    makeNotPickable(c);
+                });
+            }
+            makeNotPickable(mesh);
+
+            // Reset position to get boudning box from origin with no rotation
+            if(!mesh.rotationQuaternion){
+                mesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(mesh.rotation.y,mesh.rotation.x,mesh.rotation.z);
+            }
+            var oldPos = mesh.position.clone()
+            var oldRot = mesh.rotationQuaternion.clone();
+            mesh.rotationQuaternion.set(0,0,0,1);
+            mesh.position.set(0,0,0)
+
+            // Update bounding dimensions/positions   
+            var box = BABYLON.MeshBuilder.CreateBox("box", {size: 1}, mesh.getScene());
+            var boundingMinMax = mesh.getHierarchyBoundingVectors();
+            boundingMinMax.max.subtractToRef(boundingMinMax.min, box.scaling);
+            box.position.set((boundingMinMax.max.x+boundingMinMax.min.x)/2,(boundingMinMax.max.y+boundingMinMax.min.y)/2,(boundingMinMax.max.z+boundingMinMax.min.z)/2);
+            
+            // Restore original positions
+            mesh.addChild(box);
+            mesh.rotationQuaternion.copyFrom(oldRot);
+            mesh.position.copyFrom(oldPos);
+
+            // Reverse parenting
+            mesh.removeChild(box);
+            box.addChild(mesh);
+            box.visibility = 0;
+            return box;
+        }
     }
 }