Bladeren bron

update bounding box to handle nested meshes such as gltf files

Trevor Baron 7 jaren geleden
bovenliggende
commit
3cd8c73b75
1 gewijzigde bestanden met toevoegingen van 70 en 43 verwijderingen
  1. 70 43
      src/Gizmos/babylon.boundingBoxGizmo.ts

+ 70 - 43
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -233,40 +233,14 @@ module BABYLON {
             })
             this._updateBoundingBox();
         }
-
-        // http://www.html5gamedevs.com/topic/23468-get-size-of-mesh-while-taking-its-children-into-account/
-        private getMinMax(mesh:AbstractMesh, min:Vector3, max:Vector3, computeWorldMatrix:boolean) {
-            if (computeWorldMatrix) {
-                mesh.computeWorldMatrix(true);
-            }
-            
-            var bi = mesh.getBoundingInfo();
-            var minimum = bi.boundingBox.minimumWorld;
-            var maximum = bi.boundingBox.maximumWorld;
-            
-            if (minimum.x < min.x) {
-                min.x = minimum.x;
-            }
-            if (minimum.y < min.y) {
-                min.y = minimum.y;
-            }
-            if (minimum.z < min.z) {
-                min.z = minimum.z;
-            }
         
-            if (maximum.x > max.x) {
-                max.x = maximum.x;
-            }
-            if (maximum.y > max.y) {
-                max.y = maximum.y;
-            }
-            if (maximum.z > max.z) {
-                max.z = maximum.z;
-            }
-
-            var children = mesh.getChildMeshes();
-            for (var i = 0; i < children.length; i++){
-                this.getMinMax(children[i], min, max, computeWorldMatrix);
+        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();
             }
         }
 
@@ -277,38 +251,48 @@ module BABYLON {
             })
         }
 
+        private _recurseComputeWorld(mesh:AbstractMesh){
+            mesh.computeWorldMatrix(true);
+            mesh.getChildMeshes().forEach((m)=>{
+                this._recurseComputeWorld(m);
+            });
+        }
+
         private _updateBoundingBox(){
             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)
+                this.attachedMesh.position.set(0,0,0);
                 
                 // Update bounding dimensions/positions   
-                var min = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
-                var max = new BABYLON.Vector3(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
-                this.getMinMax(this.attachedMesh, min, max, true)
-                this._boundingDimensions.x = max.x-min.x
-                this._boundingDimensions.y = max.y-min.y
-                this._boundingDimensions.z = max.z-min.z
+                var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors();
+                this._boundingDimensions.x = boundingMinMax.max.x-boundingMinMax.min.x;
+                this._boundingDimensions.y = boundingMinMax.max.y-boundingMinMax.min.y;
+                this._boundingDimensions.z = boundingMinMax.max.z-boundingMinMax.min.z;
 
                 // Update gizmo to match bounding box scaling and rotation
                 this._lineBoundingBox.scaling.copyFrom(this._boundingDimensions);
-                this._lineBoundingBox.position.set((max.x+min.x)/2,(max.y+min.y)/2,(max.z+min.z)/2);
+                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.getMinMax(this.attachedMesh, min, max, true);
+                this._recurseComputeWorld(this.attachedMesh);
             }
 
             // Update rotation sphere locations
@@ -392,5 +376,48 @@ 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();
+            box.scaling.x = boundingMinMax.max.x-boundingMinMax.min.x;
+            box.scaling.y = boundingMinMax.max.y-boundingMinMax.min.y;
+            box.scaling.z = boundingMinMax.max.z-boundingMinMax.min.z;
+            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;
+        }
     }
 }