Browse Source

attachToBoxBehavior

Trevor Baron 7 years ago
parent
commit
8a39a2eb98
2 changed files with 158 additions and 1 deletions
  1. 2 1
      Tools/Gulp/config.json
  2. 156 0
      src/Behaviors/Mesh/babylon.attachToBoxBehavior.ts

+ 2 - 1
Tools/Gulp/config.json

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

+ 156 - 0
src/Behaviors/Mesh/babylon.attachToBoxBehavior.ts

@@ -0,0 +1,156 @@
+module BABYLON {
+    /**
+     * @hidden
+     */
+    class FaceDirectionInfo {
+        constructor(public direction:Vector3, public rotatedDirection = new BABYLON.Vector3(), public diff = 0, public ignore = false){}
+    }
+    
+    /**
+     * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+     */
+    export class AttachToBoxBehavior implements BABYLON.Behavior<BABYLON.Mesh> {
+        /**
+         *  The name of the behavior
+         */
+        public name = "AttachToBoxBehavior";
+        /**
+         * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
+         */
+        public distanceAwayFromFace = 0.15;
+        /**
+         * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
+         */
+        public distanceAwayFromBottomOfFace = 0.15;
+        private _faceVectors = [new FaceDirectionInfo(BABYLON.Vector3.Up()), new FaceDirectionInfo(BABYLON.Vector3.Down()), new FaceDirectionInfo(BABYLON.Vector3.Left()), new FaceDirectionInfo(BABYLON.Vector3.Right()), new FaceDirectionInfo(BABYLON.Vector3.Forward()), new FaceDirectionInfo(BABYLON.Vector3.Forward().scaleInPlace(-1))];
+        private _target:Mesh;
+        private _scene:Scene;
+        private _onRenderObserver:Nullable<Observer<Scene>>;
+        /**
+         * Creates the AttachToBoxBehavior, used to attach UI to the closest face of the box to a camera
+         * @param ui The transform node that should be attched to the mesh
+         */
+        constructor(private ui: BABYLON.TransformNode){}
+         /**
+         *  Initializes the behavior
+         */
+        init(){}
+        private _tmpMatrix = new Matrix();
+        private _tmpVector = new Vector3();
+        private _closestFace(targetDirection:Vector3){
+            // Go over each face and calculate the angle between the face's normal and targetDirection
+            this._faceVectors.forEach((v)=>{
+                if(!this._target.rotationQuaternion){
+                    this._target.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._target.rotation.y, this._target.rotation.x, this._target.rotation.z);
+                }
+                this._target.rotationQuaternion.toRotationMatrix(this._tmpMatrix)
+                BABYLON.Vector3.TransformCoordinatesToRef(v.direction, this._tmpMatrix, v.rotatedDirection);
+                v.diff = BABYLON.Vector3.GetAngleBetweenVectors(v.rotatedDirection, targetDirection, BABYLON.Vector3.Cross(v.rotatedDirection, targetDirection));
+            });
+            // Return the face information of the one with the normal closeset to target direction
+            return this._faceVectors.reduce((min,p)=>{
+                if(min.ignore){
+                    return p;
+                }else if(p.ignore){
+                    return min;
+                }else{
+                    return min.diff < p.diff ? min : p
+                }
+            }, this._faceVectors[0]);
+        }
+        private _zeroVector = Vector3.Zero();
+        private _lookAtTmpMatrix = new Matrix();
+        private _lookAtToRef(pos:Vector3, up = new BABYLON.Vector3(0,1,0), ref:Quaternion){
+            BABYLON.Matrix.LookAtLHToRef(this._zeroVector, pos, up, this._lookAtTmpMatrix);
+            this._lookAtTmpMatrix.invert();
+            BABYLON.Quaternion.FromRotationMatrixToRef(this._lookAtTmpMatrix, ref);
+        }
+        /**
+         * Attaches the AttachToBoxBehavior to the passed in mesh
+         * @param target The mesh that the specified node will be attached to
+         */
+        attach(target: BABYLON.Mesh){
+            this._target = target;
+            this._scene = this._target.getScene();
+            
+            // Every frame, update the app bars position
+            this._onRenderObserver = this._scene.onBeforeRenderObservable.add(()=>{
+                if(!this._scene.activeCamera){
+                    return;
+                }
+
+                // Find the face closest to the cameras position
+                var cameraPos = this._scene.activeCamera.position;
+                if((<WebVRFreeCamera>this._scene.activeCamera).devicePosition){
+                    cameraPos = (<WebVRFreeCamera>this._scene.activeCamera).devicePosition;
+                }
+                var facing = this._closestFace(cameraPos.subtract(target.position));
+                if(this._scene.activeCamera.leftCamera){
+                    this._scene.activeCamera.leftCamera.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
+                }else{
+                    this._scene.activeCamera.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
+                }
+                
+                // Get camera up direction
+                BABYLON.Vector3.TransformCoordinatesToRef(BABYLON.Vector3.Up(), this._tmpMatrix, this._tmpVector);
+                // Ignore faces to not select a parrelel face for the up vector of the UI
+                this._faceVectors.forEach((v)=>{
+                    if(facing.direction.x && v.direction.x){
+                        v.ignore = true;
+                    }
+                    if(facing.direction.y && v.direction.y){
+                        v.ignore = true;
+                    }
+                    if(facing.direction.z && v.direction.z){
+                        v.ignore = true;
+                    }
+                })
+                var facingUp = this._closestFace(this._tmpVector);
+                // Unignore faces
+                this._faceVectors.forEach((v)=>{
+                    v.ignore = false;
+                });
+
+                // Position the app bar on that face
+                this.ui.position.copyFrom(target.position);
+                if(facing.direction.x){
+                    facing.rotatedDirection.scaleToRef((target.scaling.x/2)+this.distanceAwayFromFace, this._tmpVector);
+                    this.ui.position.addInPlace(this._tmpVector);
+                }
+                if(facing.direction.y){
+                    facing.rotatedDirection.scaleToRef((target.scaling.y/2)+this.distanceAwayFromFace, this._tmpVector);
+                    this.ui.position.addInPlace(this._tmpVector);
+                }
+                if(facing.direction.z){
+                    facing.rotatedDirection.scaleToRef((target.scaling.z/2)+this.distanceAwayFromFace, this._tmpVector);
+                    this.ui.position.addInPlace(this._tmpVector);
+                }
+                
+                // Rotate to be oriented properly to the camera
+                if(!this.ui.rotationQuaternion){
+                    this.ui.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.ui.rotation.y, this.ui.rotation.x, this.ui.rotation.z);
+                }
+                facing.rotatedDirection.scaleToRef(-1, this._tmpVector);
+                this._lookAtToRef(this._tmpVector, facingUp.rotatedDirection, this.ui.rotationQuaternion);
+                
+                // Place ui the correct distance from the bottom of the mesh
+                if(facingUp.direction.x){
+                    this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace-target.scaling.x/2, this._tmpVector);
+                }
+                if(facingUp.direction.y){
+                    this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace-target.scaling.y/2, this._tmpVector);
+                }
+                if(facingUp.direction.z){
+                    this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace-target.scaling.z/2, this._tmpVector);
+                }
+                this.ui.position.addInPlace(this._tmpVector);
+            })
+        }
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        detach(){
+            this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
+        }
+    }
+}