Преглед изворни кода

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe пре 8 година
родитељ
комит
42ed9ec994

+ 400 - 180
src/Bones/babylon.boneLookController.ts

@@ -3,114 +3,168 @@ module BABYLON {
 
         private static _tmpVecs: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
         private static _tmpQuat = Quaternion.Identity();
-        private static _tmpMat1 = Matrix.Identity();
-        private static _tmpMat2 = Matrix.Identity();
-
+        private static _tmpMats: Matrix[] = [Matrix.Identity(), Matrix.Identity(), Matrix.Identity(), Matrix.Identity(), Matrix.Identity()];
+        
+        /**
+         * The target Vector3 that the bone will look at.
+         */
         public target: Vector3;
+
+        /**
+         * The mesh that the bone is attached to.
+         */
         public mesh: AbstractMesh;
+
+        /**
+         * The bone that will be looking to the target.
+         */
         public bone: Bone;
+
+        /**
+         * The up axis of the coordinate system that is used when the bone is rotated.
+         */
         public upAxis: Vector3 = Vector3.Up();
+
+        /**
+         * The space that the up axis is in - BABYLON.Space.BONE, BABYLON.Space.LOCAL (default), or BABYLON.Space.WORLD.
+         */
+        public upAxisSpace: Space = Space.LOCAL;
+
+        /**
+         * Used to make an adjustment to the yaw of the bone.
+         */
+        public adjustYaw = 0;
+
+        /**
+         * Used to make an adjustment to the pitch of the bone.
+         */
+        public adjustPitch = 0;
+
+        /**
+         * Used to make an adjustment to the roll of the bone.
+         */
+        public adjustRoll = 0;
+
+        /**
+         * The amount to slerp (spherical linear interpolation) to the target.  Set this to a value between 0 and 1 (a value of 1 disables slerp).
+         */
         public slerpAmount = 1;
 
-        private _adjustRotY = 0;
-        private _adjustRotX = 0;
-        private _adjustRotZ = 0;
-        private _minRotY:number;
-        private _maxRotY:number;
-        private _minRotX:number;
-        private _maxRotX:number;
-        private _minRotYSin:number;
-        private _minRotYCos:number;
-        private _maxRotYSin:number;
-        private _maxRotYCos:number;
-        private _minRotXTan:number;
-        private _maxRotXTan:number;
-        private _minRotZ:number;
-        private _maxRotZ:number;
-        private _minRotZSin:number;
-        private _minRotZCos:number;
-        private _maxRotZSin:number;
-        private _maxRotZCos:number;
+        private _minYaw:number;
+        private _maxYaw:number;
+        private _minPitch:number;
+        private _maxPitch:number;
+        private _minYawSin:number;
+        private _minYawCos:number;
+        private _maxYawSin:number;
+        private _maxYawCos:number;
+        private _midYawConstraint:number;
+        private _minPitchTan:number;
+        private _maxPitchTan:number;
+        
         private _boneQuat:Quaternion = Quaternion.Identity();
         private _slerping = false;
-
-        get minRotationY():number{
-            return this._minRotY - this._adjustRotY;
-        }
-
-        set minRotationY(value:number){
-            this._minRotY = value + this._adjustRotY;
-            this._minRotYSin = Math.sin(this._minRotY);
-            this._minRotYCos = Math.cos(this._minRotY);
-        }
-
-        get maxRoationY():number{
-            return this._maxRotY - this._adjustRotY;
-        }
-
-        set maxRotationY(value:number){
-            this._maxRotY = value + this._adjustRotY;
-            this._maxRotYSin = Math.sin(this._maxRotY);
-            this._maxRotYCos = Math.cos(this._maxRotY);
+        private _transformYawPitch:Matrix;
+        private _transformYawPitchInv:Matrix;
+        private _firstFrameSkipped = false;
+        private _yawRange:number;
+        private _fowardAxis: Vector3 = Vector3.Forward();
+
+        /**
+         * Get/set the minimum yaw angle that the bone can look to.
+         */
+        get minYaw():number{
+            return this._minYaw;
         }
 
-        get minRotationX():number{
-            return this._minRotX - this._adjustRotX;
-        }
-
-        set minRotationX(value:number){
-            this._minRotX = value + this._adjustRotX;
-            this._minRotXTan = Math.tan(this._minRotX);
+        set minYaw(value:number){
+            this._minYaw = value;
+            this._minYawSin = Math.sin(value);
+            this._minYawCos = Math.cos(value);
+            if(this._maxYaw != null){
+                this._midYawConstraint = this._getAngleDiff(this._minYaw, this._maxYaw)*.5 + this._minYaw;
+                this._yawRange = this._maxYaw - this._minYaw;
+            }
         }
 
-        get maxRotationX():number{
-            return this._maxRotX - this._adjustRotX;
+        /**
+         * Get/set the maximum yaw angle that the bone can look to.
+         */
+        get maxYaw():number{
+            return this._maxYaw;
         }
 
-        set maxRotationX(value:number){
-            this._maxRotX = value + this._adjustRotX;
-            this._maxRotXTan = Math.tan(this._maxRotX);
+        set maxYaw(value:number){
+            this._maxYaw = value;
+            this._maxYawSin = Math.sin(value);
+            this._maxYawCos = Math.cos(value);
+            if(this._minYaw != null){
+                this._midYawConstraint = this._getAngleDiff(this._minYaw, this._maxYaw)*.5 + this._minYaw;
+                this._yawRange = this._maxYaw - this._minYaw;
+            }
         }
 
-        get minRotationZ():number{
-            return this._minRotZ - this._adjustRotZ;
+        /**
+         * Get/set the minimum pitch angle that the bone can look to.
+         */
+        get minPitch():number{
+            return this._minPitch;
         }
 
-        set minRotationZ(value:number){
-            this._minRotZ = value + this._adjustRotZ;
-            this._minRotZSin = Math.sin(this._minRotZ);
-            this._minRotZCos = Math.cos(this._minRotZ);
+        set minPitch(value:number){
+            this._minPitch = value;
+            this._minPitchTan = Math.tan(value);
         }
 
-        get maxRotationZ():number{
-            return this._maxRotZ - this._adjustRotZ;
+        /**
+         * Get/set the maximum pitch angle that the bone can look to.
+         */
+        get maxPitch():number{
+            return this._maxPitch;
         }
 
-        set maxRotationZ(value:number){
-            this._maxRotZ = value + this._adjustRotZ;
-            this._maxRotZSin = Math.sin(this._maxRotZ);
-            this._maxRotZCos = Math.cos(this._maxRotZ);
+        set maxPitch(value:number){
+            this._maxPitch = value;
+            this._maxPitchTan = Math.tan(value);
         }
 
+        /**
+         * Create a BoneLookController
+         * @param mesh the mesh that the bone belongs to
+         * @param bone the bone that will be looking to the target
+         * @param target the target Vector3 to look at
+         * @param settings optional settings:
+         * - maxYaw: the maximum angle the bone will yaw to
+         * - minYaw: the minimum angle the bone will yaw to
+         * - maxPitch: the maximum angle the bone will pitch to
+         * - minPitch: the minimum angle the bone will yaw to
+         * - slerpAmount: set the between 0 and 1 to make the bone slerp to the target.
+         * - upAxis: the up axis of the coordinate system
+         * - upAxisSpace: the space that the up axis is in - BABYLON.Space.BONE, BABYLON.Space.LOCAL (default), or BABYLON.Space.WORLD.
+         * - yawAxis: set yawAxis if the bone does not yaw on the y axis
+         * - pitchAxis: set pitchAxis if the bone does not pitch on the x axis
+         * - adjustYaw: used to make an adjustment to the yaw of the bone
+         * - adjustPitch: used to make an adjustment to the pitch of the bone
+         * - adjustRoll: used to make an adjustment to the roll of the bone
+         **/
         constructor(mesh: AbstractMesh, 
                     bone: Bone, 
                     target: Vector3, 
-                    options?: { 
-                        adjustRotationX?: number, 
-                        adjustRotationY?: number, 
-                        adjustRotationZ?: number, 
-                        slerpAmount?: number, 
-                        maxRotationY?:number, 
-                        minRotationY?:number,
-                        maxRotationX?:number, 
-                        minRotationX?:number,
-                        maxRotationZ?:number, 
-                        minRotationZ?:number,
+                    options?: {
                         adjustYaw?: number, 
                         adjustPitch?: number, 
-                        adjustRoll?: number 
-                    } 
-                    ){
+                        adjustRoll?: number, 
+                        slerpAmount?: number, 
+                        maxYaw?:number, 
+                        minYaw?:number, 
+                        maxPitch?:number, 
+                        minPitch?:number,
+                        upAxis?:Vector3,
+                        upAxisSpace?:Space,
+                        yawAxis?:Vector3,
+                        pitchAxis?:Vector3,
+                        showSpaceAxes?:boolean
+                    }){
 
             this.mesh = mesh;
             this.bone = bone;
@@ -119,158 +173,280 @@ module BABYLON {
             if(options){
 
                 if(options.adjustYaw){
-                    this._adjustRotY = options.adjustYaw;
+                    this.adjustYaw = options.adjustYaw;
                 }
 
                 if(options.adjustPitch){
-                    this._adjustRotX = options.adjustPitch;
+                    this.adjustPitch = options.adjustPitch;
                 }
 
                 if(options.adjustRoll){
-                    this._adjustRotZ = options.adjustRoll;
+                    this.adjustRoll = options.adjustRoll;
                 }
 
-                if(options.adjustRotationY){
-                    this._adjustRotY = options.adjustRotationY;
+                if(options.maxYaw != null){
+                    this.maxYaw = options.maxYaw;
+                }else{
+                    this.maxYaw = Math.PI;
                 }
 
-                if(options.adjustRotationX){
-                    this._adjustRotX = options.adjustRotationX;
+                if(options.minYaw != null){
+                    this.minYaw = options.minYaw;
+                }else{
+                    this.minYaw = -Math.PI;
                 }
 
-                if(options.adjustRotationZ){
-                    this._adjustRotZ = options.adjustRotationZ;
+                if(options.maxPitch != null){
+                    this.maxPitch = options.maxPitch;
+                }else{
+                    this.maxPitch = Math.PI;
                 }
 
-                if(options.maxRotationY != undefined){
-                    this.maxRotationY = options.maxRotationY;
+                if(options.minPitch != null){
+                    this.minPitch = options.minPitch;
+                }else{
+                    this.minPitch = -Math.PI;
                 }
 
-                if(options.minRotationY != undefined){
-                    this.minRotationY = options.minRotationY;
+                if(options.slerpAmount != null){
+                    this.slerpAmount = options.slerpAmount;
                 }
 
-                if(options.maxRotationX != undefined){
-                    this.maxRotationX = options.maxRotationX;
+                if(options.upAxis != null){
+                    this.upAxis = options.upAxis;
                 }
 
-                if(options.minRotationX != undefined){
-                    this.minRotationX = options.minRotationX;
+                if(options.upAxisSpace != null){
+                    this.upAxisSpace = options.upAxisSpace;
                 }
 
-                if(options.maxRotationZ != undefined){
-                    this.maxRotationZ = options.maxRotationZ;
-                }
+                if(options.yawAxis != null || options.pitchAxis != null){
 
-                if(options.minRotationZ != undefined){
-                    this.minRotationZ = options.minRotationZ;
-                }
+                    var newYawAxis = Axis.Y;
+                    var newPitchAxis = Axis.X;
 
-                if(options.slerpAmount != undefined){
-                    this.slerpAmount = options.slerpAmount;
+                    if(options.yawAxis != null){
+                        newYawAxis = options.yawAxis.clone();
+                        newYawAxis.normalize();
+                    }
+
+                    if(options.pitchAxis != null){
+                        newPitchAxis = options.pitchAxis.clone();
+                        newPitchAxis.normalize();
+                    }
+
+                    var newRollAxis = Vector3.Cross(newPitchAxis, newYawAxis);
+
+                    this._transformYawPitch = Matrix.Identity();
+                    Matrix.FromXYZAxesToRef(newPitchAxis, newYawAxis, newRollAxis, this._transformYawPitch);
+
+                    this._transformYawPitchInv = this._transformYawPitch.clone();
+                    this._transformYawPitch.invert();
+                    
                 }
 
             }
 
+            if(!bone.getParent() && this.upAxisSpace == Space.BONE){
+                this.upAxisSpace = Space.LOCAL;
+            }
+
         }
 
+        /**
+         * Update the bone to look at the target.  This should be called before the scene is rendered (use scene.registerBeforeRender()).
+         */
         public update (): void {
-                
+
+            //skip the first frame when slerping so that the mesh rotation is correct
+            if(this.slerpAmount < 1 && !this._firstFrameSkipped){
+                this._firstFrameSkipped = true;
+                return;
+            }
+
             var bone = this.bone;
-            var target = this.target;
-            var mat1 = BoneLookController._tmpMat1;
-            var mat2 = BoneLookController._tmpMat2;
+            var bonePos = BoneLookController._tmpVecs[0];
+            bone.getAbsolutePositionToRef(this.mesh, bonePos);
 
+            var target = this.target;
+            var _tmpMat1 = BoneLookController._tmpMats[0];
+            var _tmpMat2 = BoneLookController._tmpMats[1];
+            
+            var mesh = this.mesh;
             var parentBone = bone.getParent();
 
-            if(parentBone){
-                if(this._maxRotX != undefined || this._minRotX != undefined){
-                    var localTarget = BoneLookController._tmpVecs[4];
-                    var _tmpVec5 = BoneLookController._tmpVecs[5];
-                    parentBone.getLocalPositionFromAbsoluteToRef(target, this.mesh, localTarget);
-                    bone.getPositionToRef(Space.LOCAL, null, _tmpVec5);
-                    localTarget.x -= _tmpVec5.x;
-                    localTarget.y -= _tmpVec5.y;
-                    localTarget.z -= _tmpVec5.z;
-                    var xzlen = Math.sqrt(localTarget.x*localTarget.x + localTarget.z*localTarget.z);
-                    var rotX = Math.atan2(localTarget.y, xzlen);
-
-                    if(rotX > this._maxRotX){
-                        localTarget.y = this._maxRotXTan*xzlen + _tmpVec5.y;
-                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
-                        target = localTarget;
-                    }else if(rotX < this._minRotX){
-                        localTarget.y = this._minRotXTan*xzlen + _tmpVec5.y;
-                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
-                        target = localTarget;
-                    }
+            var upAxis = BoneLookController._tmpVecs[1];
+            upAxis.copyFrom(this.upAxis);
+
+            if(this.upAxisSpace == Space.BONE){
+                if (this._transformYawPitch){
+                    Vector3.TransformCoordinatesToRef(upAxis, this._transformYawPitchInv, upAxis);
                 }
+                parentBone.getDirectionToRef(upAxis, this.mesh, upAxis);
+            }else if(this.upAxisSpace == Space.LOCAL){
+                mesh.getDirectionToRef(upAxis, upAxis);
+                if(mesh.scaling.x != 1 || mesh.scaling.y != 1 || mesh.scaling.z != 1){
+                    upAxis.normalize();
+                }
+            }
+
+            var checkYaw = false;
+            var checkPitch = false;
 
-                if(this._maxRotY != undefined || this._minRotY != undefined){
-                    var localTarget = BoneLookController._tmpVecs[6];
-                    var _tmpVec7 = BoneLookController._tmpVecs[7];
-                    parentBone.getLocalPositionFromAbsoluteToRef(target, this.mesh, localTarget);
-                    bone.getPositionToRef(Space.LOCAL, null, _tmpVec7);
-                    localTarget.x -= _tmpVec7.x;
-                    localTarget.z -= _tmpVec7.z;
-                    var rotY = Math.atan2(localTarget.x, localTarget.z);
+            if(this._maxYaw != Math.PI || this._minYaw != -Math.PI){
+                checkYaw = true;
+            }
+            if(this._maxPitch != Math.PI || this._minPitch != -Math.PI){
+                checkPitch = true;
+            }
+
+            if(checkYaw || checkPitch){
+
+                var _tmpMat3 = BoneLookController._tmpMats[2];
+                var _tmpMat3Inv = BoneLookController._tmpMats[3];
+
+                if(this.upAxisSpace == Space.BONE && upAxis.y == 1){
+
+                    parentBone.getRotationMatrixToRef(Space.WORLD, this.mesh, _tmpMat3);
                     
-                    if(rotY > this._maxRotY){
-                        var xzlen = Math.sqrt(localTarget.x*localTarget.x + localTarget.z*localTarget.z);
-                        localTarget.z = this._maxRotYCos*xzlen;
-                        localTarget.x = this._maxRotYSin*xzlen;
-                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
-                        target = localTarget;
-                    }else if(rotY < this._minRotY){
-                        var xzlen = Math.sqrt(localTarget.x*localTarget.x + localTarget.z*localTarget.z);
-                        localTarget.z = this._minRotYCos*xzlen;
-                        localTarget.x = this._minRotYSin*xzlen;
-                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
-                        target = localTarget;
+                }else if(this.upAxisSpace == Space.LOCAL && upAxis.y == 1 && !parentBone){
+
+                    _tmpMat3.copyFrom(mesh.getWorldMatrix());
+
+                }else{
+
+                    var forwardAxis = BoneLookController._tmpVecs[2];
+                    forwardAxis.copyFrom(this._fowardAxis);
+                    
+                    if (this._transformYawPitch) {
+                        Vector3.TransformCoordinatesToRef(forwardAxis, this._transformYawPitchInv, forwardAxis);
                     }
+
+                    if(parentBone){
+                        parentBone.getDirectionToRef(forwardAxis, this.mesh, forwardAxis);
+                    }else{
+                        mesh.getDirectionToRef(forwardAxis, forwardAxis);
+                    }
+
+                    var rightAxis = Vector3.Cross(upAxis, forwardAxis);
+                    rightAxis.normalize();
+                    var forwardAxis = Vector3.Cross(rightAxis, upAxis);
+
+                    Matrix.FromXYZAxesToRef(rightAxis, upAxis, forwardAxis, _tmpMat3);
+                    
                 }
 
-                if(this._maxRotZ != undefined || this._minRotZ != undefined){
-                    var localTarget = BoneLookController._tmpVecs[8];
-                    var _tmpVec9 = BoneLookController._tmpVecs[9];
-                    parentBone.getLocalPositionFromAbsoluteToRef(target, this.mesh, localTarget);
-                    bone.getPositionToRef(Space.LOCAL, null, _tmpVec9);
-                    localTarget.x -= _tmpVec9.x;
-                    localTarget.y -= _tmpVec9.y;
-                    var rotZ = Math.atan2(localTarget.y, localTarget.x);
+                _tmpMat3.invertToRef(_tmpMat3Inv);
+                
+                var xzlen:number;
+
+                if(checkPitch){
+                    var localTarget = BoneLookController._tmpVecs[3];
+                    Vector3.TransformCoordinatesToRef(target.subtract(bonePos), _tmpMat3Inv, localTarget);
+
+                    var xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
+                    var pitch = Math.atan2(localTarget.y, xzlen);
+                    var newPitch = pitch;
+
+                    if(pitch > this._maxPitch){
+                        localTarget.y = this._maxPitchTan*xzlen;
+                        newPitch = this._maxPitch;
+                    }else if(pitch < this._minPitch){
+                        localTarget.y = this._minPitchTan*xzlen;
+                        newPitch = this._minPitch;
+                    }
                     
-                    if(rotZ > this._maxRotZ){
-                        var xylen = Math.sqrt(localTarget.x*localTarget.x + localTarget.y*localTarget.y);
-                        localTarget.x = this._maxRotZCos*xylen;
-                        localTarget.y = this._maxRotZSin*xylen;
-                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
-                        target = localTarget;
-                    }else if(rotZ < this._minRotZ){
-                        var xylen = Math.sqrt(localTarget.x*localTarget.x + localTarget.y*localTarget.y);
-                        localTarget.x = this._minRotZCos*xylen;
-                        localTarget.y = this._minRotZSin*xylen;
-                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
+                    if(pitch != newPitch){
+                        Vector3.TransformCoordinatesToRef(localTarget, _tmpMat3, localTarget);
+                        localTarget.addInPlace(bonePos);
                         target = localTarget;
                     }
                 }
 
+                if(checkYaw){
+                    var localTarget = BoneLookController._tmpVecs[4];
+                    Vector3.TransformCoordinatesToRef(target.subtract(bonePos), _tmpMat3Inv, localTarget);
+
+                    var yaw = Math.atan2(localTarget.x, localTarget.z);
+                    var newYaw = yaw;
+
+                    if(yaw > this._maxYaw || yaw < this._minYaw){
+                        
+                        if(xzlen == null){
+                            xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
+                        }
+
+                        if(yaw > this._maxYaw){
+                            localTarget.z = this._maxYawCos*xzlen;
+                            localTarget.x = this._maxYawSin*xzlen;
+                            newYaw = this._maxYaw;
+                        }else if(yaw < this._minYaw){
+                            localTarget.z = this._minYawCos*xzlen;
+                            localTarget.x = this._minYawSin*xzlen;
+                            newYaw = this._minYaw;
+                        }
+                    }
+
+                    if(this._slerping && this._yawRange > Math.PI){
+                        //are we going to be crossing into the min/max region
+                        var _tmpVec8 = BoneLookController._tmpVecs[8];
+                        _tmpVec8.copyFrom(Axis.Z);
+                        if (this._transformYawPitch) {
+                            Vector3.TransformCoordinatesToRef(_tmpVec8, this._transformYawPitchInv, _tmpVec8);
+                        }
+
+                        var boneRotMat = BABYLON.BoneLookController._tmpMats[4];
+                        this._boneQuat.toRotationMatrix(boneRotMat);
+                        this.mesh.getWorldMatrix().multiplyToRef(boneRotMat, boneRotMat);
+                        BABYLON.Vector3.TransformCoordinatesToRef(_tmpVec8, boneRotMat, _tmpVec8);
+                        BABYLON.Vector3.TransformCoordinatesToRef(_tmpVec8, _tmpMat3Inv, _tmpVec8);
+
+                        var boneYaw = Math.atan2(_tmpVec8.x, _tmpVec8.z);
+                        var ang1 = this._getAngleBetween(boneYaw, yaw);
+                        var ang2 = this._getAngleBetween(boneYaw, this._midYawConstraint);
+
+                        if(ang1 > ang2){
+
+                            if (xzlen == null) {
+                                xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
+                            }
+
+                            var ang3 = this._getAngleBetween(boneYaw, this._maxYaw);
+                            var ang4 = this._getAngleBetween(boneYaw, this._minYaw);
+
+                            if(ang4 < ang3){
+                                newYaw = boneYaw+Math.PI*.75;
+                                localTarget.z = Math.cos(newYaw) * xzlen;
+                                localTarget.x = Math.sin(newYaw) * xzlen;
+                            }else{
+                                newYaw = boneYaw-Math.PI*.75;
+                                localTarget.z = Math.cos(newYaw) * xzlen;
+                                localTarget.x = Math.sin(newYaw) * xzlen;
+                            }
+                        }
+                    }
+
+                    if(yaw != newYaw){
+                        Vector3.TransformCoordinatesToRef(localTarget, _tmpMat3, localTarget);
+                        localTarget.addInPlace(bonePos);
+                        target = localTarget;
+                    }
+                }
 
             }
 
-            var bonePos = BoneLookController._tmpVecs[0];
-            var zaxis = BoneLookController._tmpVecs[1];
-            var xaxis = BoneLookController._tmpVecs[2];
-            var yaxis = BoneLookController._tmpVecs[3];
+            var zaxis = BoneLookController._tmpVecs[5];
+            var xaxis = BoneLookController._tmpVecs[6];
+            var yaxis = BoneLookController._tmpVecs[7];
             var _tmpQuat = BoneLookController._tmpQuat;
 
-            bone.getAbsolutePositionToRef(this.mesh, bonePos);
             target.subtractToRef(bonePos, zaxis);
             zaxis.normalize();
-            Vector3.CrossToRef(this.upAxis, zaxis, xaxis);
+            Vector3.CrossToRef(upAxis, zaxis, xaxis);
             xaxis.normalize();
             Vector3.CrossToRef(zaxis, xaxis, yaxis);
             yaxis.normalize();
-            Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
+            Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, _tmpMat1);
 
             if(xaxis.x === 0 && xaxis.y === 0 && xaxis.z === 0){
                 return;
@@ -284,25 +460,69 @@ module BABYLON {
                 return;
             }
 
-            if (this._adjustRotY || this._adjustRotX || this._adjustRotZ) {
-                Matrix.RotationYawPitchRollToRef(this._adjustRotY, this._adjustRotX, this._adjustRotZ, mat2);
-                mat2.multiplyToRef(mat1, mat1);
+            if (this.adjustYaw || this.adjustPitch || this.adjustRoll) {
+                Matrix.RotationYawPitchRollToRef(this.adjustYaw, this.adjustPitch, this.adjustRoll, _tmpMat2);
+                _tmpMat2.multiplyToRef(_tmpMat1, _tmpMat1);
             }
 
             if (this.slerpAmount < 1) {
                 if (!this._slerping) {
                     this.bone.getRotationQuaternionToRef(Space.WORLD, this.mesh, this._boneQuat);
                 }
-                Quaternion.FromRotationMatrixToRef(mat1, _tmpQuat);
+                if(this._transformYawPitch){
+                    this._transformYawPitch.multiplyToRef(_tmpMat1, _tmpMat1);
+                }
+                Quaternion.FromRotationMatrixToRef(_tmpMat1, _tmpQuat);
                 Quaternion.SlerpToRef(this._boneQuat, _tmpQuat, this.slerpAmount, this._boneQuat);
+                
                 this.bone.setRotationQuaternion(this._boneQuat, Space.WORLD, this.mesh);
                 this._slerping = true;
             } else {
-                this.bone.setRotationMatrix(mat1, Space.WORLD, this.mesh);
+                if(this._transformYawPitch){
+                    this._transformYawPitch.multiplyToRef(_tmpMat1, _tmpMat1);
+                }
+                this.bone.setRotationMatrix(_tmpMat1, Space.WORLD, this.mesh);
                 this._slerping = false;
             }
 
         }
 
+        private _getAngleDiff(ang1, ang2):number {
+
+            var angDiff = ang2 - ang1;
+            angDiff %= Math.PI*2;
+            
+            if(angDiff > Math.PI){
+                angDiff -= Math.PI*2;
+            }else if (angDiff < -Math.PI){
+                angDiff += Math.PI*2;
+            }
+            
+            return angDiff;
+        }
+
+        private _getAngleBetween(ang1, ang2):number {
+
+            ang1 %= (2 * Math.PI);
+            ang1 = (ang1 < 0) ? ang1 + (2 * Math.PI) : ang1;
+
+            ang2 %= (2 * Math.PI);
+            ang2 = (ang2 < 0) ? ang2 + (2 * Math.PI) : ang2;
+
+            var ab = 0;
+
+            if(ang1 < ang2){
+                ab = ang2 - ang1;
+            }else{
+                ab = ang1 - ang2;
+            }
+
+            if(ab > Math.PI){
+                ab = Math.PI*2 - ab;
+            }
+
+            return ab;
+        }
+
     }
 }

+ 7 - 3
src/Materials/babylon.effect.ts

@@ -28,6 +28,9 @@
             this._meshRank = rank;
             this._mesh = mesh;
 
+            if (rank < this._currentRank) {
+                this._currentRank = rank;
+            }
             if (rank > this._maxRank) {
                 this._maxRank = rank;
             }
@@ -40,9 +43,10 @@
         public reduce(currentDefines: string): string {
 
             var currentFallbacks = this._defines[this._currentRank];
-
-            for (var index = 0; index < currentFallbacks.length; index++) {
-                currentDefines = currentDefines.replace("#define " + currentFallbacks[index], "");
+            if (currentFallbacks) {
+                for (var index = 0; index < currentFallbacks.length; index++) {
+                    currentDefines = currentDefines.replace("#define " + currentFallbacks[index], "");
+                }
             }
 
             if (this._mesh && this._currentRank === this._meshRank) {

+ 2 - 1
src/Math/babylon.math.ts

@@ -4336,7 +4336,8 @@
 
     export enum Space {
         LOCAL = 0,
-        WORLD = 1
+        WORLD = 1,
+        BONE = 2
     }
 
     export class Axis {

+ 3 - 1
src/Mesh/babylon.geometry.ts

@@ -401,7 +401,9 @@
                 if (numOfMeshes === 1) {
                     this._vertexBuffers[kind].create();
                 }
-                this._vertexBuffers[kind].getBuffer().references = numOfMeshes;
+                var buffer = this._vertexBuffers[kind].getBuffer();
+                if (buffer)
+                    buffer.references = numOfMeshes;
 
                 if (kind === VertexBuffer.PositionKind) {
                     mesh._resetPointsArrayCache();

+ 76 - 14
src/PostProcess/babylon.standardRenderingPipeline.ts

@@ -14,6 +14,7 @@ module BABYLON {
         public textureAdderPostProcess: PostProcess = null;
 
         public textureAdderFinalPostProcess: PostProcess = null;
+        public lensFlareFinalPostProcess: PostProcess = null;
 
         public lensFlarePostProcess: PostProcess = null;
         public lensFlareComposePostProcess: PostProcess = null;
@@ -21,26 +22,46 @@ module BABYLON {
         public depthOfFieldPostProcess: PostProcess = null;
 
         // Values
+        @serialize()
         public brightThreshold: number = 1.0;
 
+        @serialize()
         public blurWidth: number = 2.0;
+        @serialize()
+        public horizontalBlur: boolean = false;
+        @serialize()
         public gaussianCoefficient: number = 0.25;
+        @serialize()
         public gaussianMean: number = 1.0;
+        @serialize()
         public gaussianStandardDeviation: number = 1.0;
 
+        @serialize()
         public exposure: number = 1.0;
+        @serializeAsTexture("lensTexture")
         public lensTexture: Texture = null;
 
+        @serializeAsTexture("lensColorTexture")
         public lensColorTexture: Texture = null;
+        @serialize()
         public lensFlareStrength: number = 20.0;
+        @serialize()
         public lensFlareGhostDispersal: number = 1.4;
+        @serialize()
         public lensFlareHaloWidth: number = 0.7;
+        @serialize()
         public lensFlareDistortionStrength: number = 16.0;
+        @serializeAsTexture("lensStarTexture")
         public lensStarTexture: Texture = null;
+        @serializeAsTexture("lensFlareDirtTexture")
         public lensFlareDirtTexture: Texture = null;
 
+        @serialize()
         public depthOfFieldDistance: number = 10.0;
 
+        @serialize()
+        public depthOfFieldBlurWidth: number = 2.0;
+
         // IAnimatable
         public animations: Animation[] = [];
 
@@ -50,6 +71,7 @@ module BABYLON {
         private _scene: Scene;
 
         private _depthRenderer: DepthRenderer = null;
+        private _currentDepthOfFieldSource: PostProcess = null;
 
         // Getters and setters
         private _depthOfFieldEnabled: boolean = true;
@@ -73,6 +95,7 @@ module BABYLON {
             this._depthOfFieldEnabled = enabled;
         }
 
+        @serialize()
         public get DepthOfFieldEnabled(): boolean {
             return this._depthOfFieldEnabled;
         }
@@ -86,6 +109,8 @@ module BABYLON {
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRLensFlareCompose", this._scene.cameras);
+
+                this._setDepthOfFieldSavePostProcess("HDRPostLensFlareDepthOfFieldSource");
             }
             else if (!enabled && this._lensFlareEnabled) {
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlare", this._scene.cameras);
@@ -93,11 +118,14 @@ module BABYLON {
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRLensFlareCompose", this._scene.cameras);
+
+                this._setDepthOfFieldSavePostProcess("HDRBaseDepthOfFieldSource");
             }
 
             this._lensFlareEnabled = enabled;
         }
 
+        @serialize()
         public get LensFlareEnabled(): boolean {
             return this._lensFlareEnabled;
         }
@@ -118,7 +146,7 @@ module BABYLON {
 
             // Create pass post-processe
             if (!originalPostProcess) {
-                this.originalPostProcess = new PostProcess("HDRPass", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), true, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_FLOAT);
+                this.originalPostProcess = new PostProcess("HDRPass", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_FLOAT);
             }
             else {
                 this.originalPostProcess = originalPostProcess;
@@ -142,14 +170,18 @@ module BABYLON {
             this._createTextureAdderPostProcess(scene, ratio);
 
             // Create depth-of-field source post-process
-            this.textureAdderFinalPostProcess = new PostProcess("HDRDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), true, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
-            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRDepthOfFieldSource", () => { return this.textureAdderFinalPostProcess; }, true));
+            this.textureAdderFinalPostProcess = new PostProcess("HDRDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBaseDepthOfFieldSource", () => { return this.textureAdderFinalPostProcess; }, true));
 
             // Create lens flare post-process
             this._createLensFlarePostProcess(scene, ratio);
 
+            // Create depth-of-field source post-process post lens-flare and disable it now
+            this.lensFlareFinalPostProcess = new PostProcess("HDRPostLensFlareDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRPostLensFlareDepthOfFieldSource", () => { return this.lensFlareFinalPostProcess; }, true));
+
             // Create gaussian blur used by depth-of-field
-            this._createGaussianBlurPostProcesses(scene, ratio / 2, 5);
+            this._createGaussianBlurPostProcesses(scene, ratio / 2, 5, "depthOfFieldBlurWidth");
 
             // Create depth-of-field post-process
             this._createDepthOfFieldPostProcess(scene, ratio);
@@ -166,6 +198,19 @@ module BABYLON {
             this.DepthOfFieldEnabled = false;
         }
 
+        // Sets depth-of-field save post-process
+        private _setDepthOfFieldSavePostProcess(name: string): void {
+            this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRPostLensFlareDepthOfFieldSource", this._scene.cameras);
+
+            this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, name, this._scene.cameras);
+
+            switch (name) {
+                case "HDRBaseDepthOfFieldSource": this._currentDepthOfFieldSource = this.textureAdderFinalPostProcess; break;
+                case "HDRPostLensFlareDepthOfFieldSource": this._currentDepthOfFieldSource = this.lensFlareFinalPostProcess; break;
+                default: break;
+            }
+        }
+
         // Down Sample X4 Post-Processs
         private _createDownSampleX4PostProcess(scene: Scene, ratio: number): void {
             var downSampleX4Offsets = new Array<number>(32);
@@ -215,7 +260,7 @@ module BABYLON {
         }
 
         // Create gaussian blur H&V post-processes
-        private _createGaussianBlurPostProcesses(scene: Scene, ratio: number, indice: number): void {
+        private _createGaussianBlurPostProcesses(scene: Scene, ratio: number, indice: number, blurWidthKey: string = "blurWidth"): void {
             var blurOffsets = new Array<number>(9);
             var blurWeights = new Array<number>(9);
             var uniforms: string[] = ["blurOffsets", "blurWeights", "blurWidth"];
@@ -245,16 +290,22 @@ module BABYLON {
 
                     effect.setArray("blurOffsets", blurOffsets);
                     effect.setArray("blurWeights", blurWeights);
-                    effect.setFloat("blurWidth", this.blurWidth);
+
+                    if (height) {
+                        effect.setFloat("blurWidth", this.horizontalBlur ? 1.0 : this[blurWidthKey]);
+                    }
+                    else {
+                        effect.setFloat("blurWidth", this[blurWidthKey]);
+                    }
                 };
             };
 
             // Create horizontal gaussian blur post-processes
-            var gaussianBlurHPostProcess = new PostProcess("HDRGaussianBlurH" + ratio, "standard", uniforms, [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_H", Engine.TEXTURETYPE_UNSIGNED_INT);
+            var gaussianBlurHPostProcess = new PostProcess("HDRGaussianBlurH_" + ratio + "_" + indice, "standard", uniforms, [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_H", Engine.TEXTURETYPE_UNSIGNED_INT);
             gaussianBlurHPostProcess.onApply = callback(false);
 
             // Create vertical gaussian blur post-process
-            var gaussianBlurVPostProcess = new PostProcess("HDRGaussianBlurV" + ratio, "standard", uniforms, [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_V", Engine.TEXTURETYPE_UNSIGNED_INT);
+            var gaussianBlurVPostProcess = new PostProcess("HDRGaussianBlurV_" + ratio + "_" + indice, "standard", uniforms, [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_V", Engine.TEXTURETYPE_UNSIGNED_INT);
             gaussianBlurVPostProcess.onApply = callback(true);
 
             // Add to pipeline
@@ -268,8 +319,6 @@ module BABYLON {
 
         // Create texture adder post-process
         private _createTextureAdderPostProcess(scene: Scene, ratio: number): void {
-            var lastGaussianBlurPostProcess = this.gaussianBlurVPostProcesses[3];
-
             this.textureAdderPostProcess = new PostProcess("HDRTextureAdder", "standard", ["exposure"], ["otherSampler", "lensSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define TEXTURE_ADDER", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.textureAdderPostProcess.onApply = (effect: Effect) => {
                 effect.setTextureFromPostProcess("otherSampler", this.originalPostProcess);
@@ -284,13 +333,13 @@ module BABYLON {
 
         // Create lens flare post-process
         private _createLensFlarePostProcess(scene: Scene, ratio: number): void {
-            this.lensFlarePostProcess = new PostProcess("HDRLensFlare", "standard", ["strength", "ghostDispersal", "haloWidth", "resolution", "distortionStrength"], ["lensColorSampler"], ratio / 2, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), true, "#define LENS_FLARE", Engine.TEXTURETYPE_UNSIGNED_INT);
-            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRLensFlare", () => { return this.lensFlarePostProcess; }, false));
+            this.lensFlarePostProcess = new PostProcess("HDRLensFlare", "standard", ["strength", "ghostDispersal", "haloWidth", "resolution", "distortionStrength"], ["lensColorSampler"], ratio / 2, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define LENS_FLARE", Engine.TEXTURETYPE_UNSIGNED_INT);
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRLensFlare", () => { return this.lensFlarePostProcess; }, true));
 
             this._createGaussianBlurPostProcesses(scene, ratio / 4, 4);
 
             this.lensFlareComposePostProcess = new PostProcess("HDRLensFlareCompose", "standard", ["lensStarMatrix"], ["otherSampler", "lensDirtSampler", "lensStarSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define LENS_FLARE_COMPOSE", Engine.TEXTURETYPE_UNSIGNED_INT);
-            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRLensFlareCompose", () => { return this.lensFlareComposePostProcess; }, false));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRLensFlareCompose", () => { return this.lensFlareComposePostProcess; }, true));
 
             var resolution = new Vector2(0, 0);
 
@@ -353,7 +402,7 @@ module BABYLON {
         private _createDepthOfFieldPostProcess(scene: Scene, ratio: number): void {
             this.depthOfFieldPostProcess = new PostProcess("HDRDepthOfField", "standard", ["distance"], ["otherSampler", "depthSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DEPTH_OF_FIELD", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.depthOfFieldPostProcess.onApply = (effect: Effect) => {
-                effect.setTextureFromPostProcess("otherSampler", this.textureAdderFinalPostProcess);
+                effect.setTextureFromPostProcess("otherSampler", this._currentDepthOfFieldSource);
                 effect.setTexture("depthSampler", this._depthRenderer.getDepthMap());
 
                 effect.setFloat("distance", this.depthOfFieldDistance);
@@ -391,5 +440,18 @@ module BABYLON {
 
             super.dispose();
         }
+
+        // Serialize rendering pipeline
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this);
+            serializationObject.customType = "StandardRenderingPipeline";
+
+            return serializationObject;
+        }
+
+        // Parse serialized pipeline
+        public static Parse(source: any, scene: Scene, rootUrl: string): StandardRenderingPipeline {
+            return SerializationHelper.Parse(() => new StandardRenderingPipeline(source._name, scene, source._ratio), source, scene, rootUrl);
+        }
     }
 }