Пример #1
0
        /// <summary>
        /// Process the motor each frame so that it can update the bone rotations.
        /// This is the function that should be overridden in each motor
        /// </summary>
        /// <param name="rDeltaTime">Delta time to use for the update</param>
        /// <param name="rUpdate">Determines if it is officially time to do the update</param>
        protected override void Update(float rDeltaTime, bool rUpdate)
        {
            if (mBones.Count == 0)
            {
                return;
            }

            // Process the motor info at start up
            if (!mIsInitialized)
            {
                // Ensure we have the correct amount of bone infos... we should
                while (_BoneInfo.Count < mBones.Count)
                {
                    RotationBone lBoneInfo = new RotationBone();
                    _BoneInfo.Add(lBoneInfo);
                }

                // Initialize the euler angles that are our base
                for (int i = 0; i < mBones.Count; i++)
                {
                    _BoneInfo[i].Euler        = Vector3.zero;
                    _BoneInfo[i].BaseRotation = mBones[i]._Transform.rotation;

                    //_BoneInfo[i].Euler = mBones[i]._Transform.rotation.eulerAngles;
                }

                // Flag that we've initialized
                mIsInitialized = true;
            }

            // If it's time to update, determine the positions we need to be
            // at and lerp towards them.
            if (rUpdate)
            {
                // Process each bone
                for (int i = 0; i < mBones.Count; i++)
                {
                    BoneControllerBone lBone     = mBones[i];
                    RotationBone       lBoneInfo = _BoneInfo[i];

                    // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
                    Quaternion lCurrentRotation = lBone.Transform.rotation * lBone.ToBoneForward;

                    // Rotation we're moving towards
                    Quaternion lTargetRotation = lBone._Transform.rotation;

                    // Speed this frame
                    Vector3 lRotationSpeed = lBoneInfo.RotationSpeed * rDeltaTime;

                    // Update the angles we're rotating to
                    lBoneInfo.Euler += lRotationSpeed;

                    // Rotate based on the axis
                    if (lBoneInfo.RotationAxis == EnumIKBoneRotationAxis.BONE)
                    {
                        lTargetRotation = _BoneInfo[i].BaseRotation * Quaternion.Euler(lBoneInfo.Euler);
                    }
                    else if (lBoneInfo.RotationAxis == EnumIKBoneRotationAxis.MODEL)
                    {
                        lTargetRotation = _BoneInfo[i].BaseRotation * Quaternion.Euler(lBoneInfo.Euler) * lBone._ToBoneForward;
                    }
                    else
                    {
                        lTargetRotation = lTargetRotation * lBone._ToBoneForward;
                    }

                    // Rotation as determined by the target
                    lBoneInfo.RotationTarget = Quaternion.Lerp(lCurrentRotation, lTargetRotation, _Weight * lBoneInfo.Weight);

                    // Slowly move towards the rotation we determined
                    lBoneInfo.Rotation = Quaternion.Lerp(lBoneInfo.Rotation, lBoneInfo.RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? lBoneInfo.RotationLerp : 1f));

                    // Set the world rotation
                    lBone.SetWorldRotation(lBoneInfo.Rotation, Quaternion.identity, _BoneWeight);
                }
            }
            // If it's not on a consistant update, we just want to reset the
            // last rotations that we found.
            else
            {
                for (int i = 0; i < mBones.Count; i++)
                {
                    BoneControllerBone lBone = mBones[i];
                    if (lBone == null)
                    {
                        continue;
                    }

                    lBone.SetWorldRotation(_BoneInfo[i].Rotation, Quaternion.identity, _BoneWeight);
                }
            }
        }
        /// <summary>
        /// Rotates the foot to match the ground. This function will use the
        /// collision values as best as it can
        /// </summary>
        /// <param name="rOwnerTransform"></param>
        /// <param name="rFoot"></param>
        /// <param name="rToes"></param>
        /// <param name="rFootTarget"></param>
        /// <param name="rGroundNormal"></param>
        /// <param name="rHeelCollision"></param>
        /// <param name="rToeCollision"></param>
        private void RotateFoot(Transform rOwnerTransform, BoneControllerBone rLowerLeg, BoneControllerBone rFoot, BoneControllerBone rToes, Vector3 rFootTarget, Vector3 rGroundNormal, bool rHeelCollision, bool rToeCollision)
        {
            BoneControllerBone     lBone     = rFoot;
            FootPlacementMotorBone lBoneInfo = _BoneInfo[mBones.IndexOf(lBone)];

            // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
            Quaternion lCurrentRotation = lBone.Transform.rotation * lBone.ToBoneForward;

            // Rotation based on the target position
            Quaternion lTargetRotation = lCurrentRotation;

            // Usually we don't want to rotate if we're moving. This way we don't cause floppy foot
            Vector3 lMovement = mSkeleton.transform.position - mLastPosition;

            if (_RotateFootOnMovement || (Mathf.Abs(lMovement.x) <= 0.001f && Mathf.Abs(lMovement.z) <= 0.001f))
            {
                // If we're meant to rotate the feet, do it
                float lGroundAngle = Vector3.Angle(rGroundNormal, mSkeleton.transform.up);
                if ((_RotateFootToGroundMinAngle == 0f || Mathf.Abs(lGroundAngle) > _RotateFootToGroundMinAngle) && Mathf.Abs(lGroundAngle) < 60f)
                {
                    if (rHeelCollision && rToeCollision)
                    {
                        Vector3 lGroundForward = Vector3.Cross(rGroundNormal, lCurrentRotation * -Vector3.right);

                        //float lDot = Vector3.Dot(mSkeleton.transform.forward, lGroundForward);
                        //if (lDot < 0f) { lGroundForward = -lGroundForward; }

                        lTargetRotation = Quaternion.LookRotation(lGroundForward, rGroundNormal) * _FootForwardToBind;
                    }
                    else if (rHeelCollision || rToeCollision)
                    {
                        if (rToes == null || !_RotateFootRequiresBoth)
                        {
                            if (rHeelCollision)
                            {
                                //Quaternion lHeelRotation = Quaternion.AngleAxis(90f, rFoot.WorldBindRotation * Vector3.right);
                                //Vector3 lToeForward = sCollisionInfo1.point + ((lHeelRotation * rGroundNormal).normalized * rFoot._Length);
                                //lTargetRotation = Quaternion.LookRotation(lToeForward - rFootTarget, rGroundNormal);

                                Vector3 lGroundForward = Vector3.Cross(rGroundNormal, lCurrentRotation * -Vector3.right);

                                //float lDot = Vector3.Dot(mSkeleton.transform.forward, lGroundForward);
                                //if (lDot < 0f) { lGroundForward = -lGroundForward; }

                                lTargetRotation = Quaternion.LookRotation(lGroundForward, rGroundNormal) * _FootForwardToBind;
                            }
                            else
                            {
                                //lTargetRotation = Quaternion.LookRotation(sCollisionInfo2.point - rFootTarget, rGroundNormal);

                                Vector3 lGroundForward = Vector3.Cross(rGroundNormal, lCurrentRotation * -Vector3.right);

                                //float lDot = Vector3.Dot(mSkeleton.transform.forward, lGroundForward);
                                //if (lDot < 0f) { lGroundForward = -lGroundForward; }

                                lTargetRotation = Quaternion.LookRotation(lGroundForward, rGroundNormal) * _FootForwardToBind;
                            }
                        }
                    }
                }
            }

            // Rotation as determined by the target
            lBoneInfo.RotationTarget = Quaternion.Lerp(lCurrentRotation, lTargetRotation, _Weight * lBoneInfo.Weight);

            // Slowly move towards the rotation we determined
            lBoneInfo.Rotation = Quaternion.Lerp(lBoneInfo.Rotation, lBoneInfo.RotationTarget, (!mIsFirstUpdate ? lBoneInfo.RotationLerp : 1f));

            // Set the world rotation
            lBone.SetWorldRotation(lBoneInfo.Rotation, _BoneWeight);
            if (lBone.ApplyLimitsInFrame)
            {
                lBone.ApplyLimitsInFrame = _ApplyLimits;
            }
        }
        /// <summary>
        /// Process the motor each frame so that it can update the bone rotations.
        /// This is the function that should be overridden in each motor
        /// </summary>
        /// <param name="rDeltaTime">Delta time to use for the update</param>
        /// <param name="rUpdate">Determines if it is officially time to do the update</param>
        protected override void Update(float rDeltaTime, bool rUpdate)
        {
            // TRT 02/04/2016 a - Protection for some errors I saw when going from debug to running
            if (mBones.Count < 3)
            {
                return;
            }
            if (mSkeleton == null)
            {
                return;
            }
            if (object.ReferenceEquals(mSkeleton, null))
            {
                return;
            }
            if (object.ReferenceEquals(mSkeleton.gameObject, null))
            {
                return;
            }

            // Shortcuts for easy access
            BoneControllerBone lUpperLeg = mBones[0];
            BoneControllerBone lLowerLeg = mBones[1];
            BoneControllerBone lFoot     = mBones[2];
            BoneControllerBone lToes     = (mBones.Count > 3 ? mBones[3] : null);

            // Get out if we don't have valid bones
            if (lUpperLeg == null || lLowerLeg == null || lFoot == null)
            {
                return;
            }

            // Ensure we have valid values
            if (lToes != null)
            {
                if (_FootToeDistance == 0f)
                {
                    _FootToeDistance = mBones[3]._Transform.position.y - mBones[2]._Transform.position.y;
                }
            }

            if (_FootForwardToBind == Quaternion.identity)
            {
                Vector3    lBindGroundForward   = Vector3.Cross(mSkeleton.transform.up, lFoot.WorldBindRotation * -Vector3.right);
                Quaternion lBindForwardRotation = Quaternion.LookRotation(lBindGroundForward, mSkeleton.transform.up);
                _FootForwardToBind = Quaternion.Inverse(lBindForwardRotation) * lFoot.WorldBindRotation;
            }

            // Ensure we have the correct amount of bone infos... we should
            while (_BoneInfo.Count < mBones.Count)
            {
                FootPlacementMotorBone lBoneInfo = new FootPlacementMotorBone();
                _BoneInfo.Add(lBoneInfo);
            }

            // If it's time to update, cast out to find the collision point and
            // generate the new positions.
            if (rUpdate)
            {
                bool      lUseCurrentRotation = true;
                Transform lOwnerTransform     = mSkeleton.gameObject.transform;

                // Heel cast
                Vector3 lHeelStart = lFoot.Transform.position;
                lHeelStart = lHeelStart + (lOwnerTransform.up * _RaycastStartDistance);

                float   lHeelRaycastDistance = _RaycastStartDistance + _FootToeDistance + _ToeSoleDistance + (_AllowLegExtension ? _RaycastExtensionDistance : 0f);
                Vector3 lHeelEnd             = lHeelStart - (lOwnerTransform.up * lHeelRaycastDistance);

                bool lHeelCollision = RaycastExt.SafeRaycast(lHeelStart, -lOwnerTransform.up, out sCollisionInfo1, lHeelRaycastDistance, _GroundingLayers, mSkeleton._RootTransform, mSkeleton.BoneTransforms);

                // Toe cast
                bool    lToeCollision = false;
                Vector3 lToeEnd       = Vector3.zero;

                if (lToes != null)
                {
                    Vector3 lToeStart = lToes.Transform.position;
                    lToeStart = lToeStart + (lOwnerTransform.up * _RaycastStartDistance);

                    float lToeRaycastDistance = _RaycastStartDistance + _ToeSoleDistance + (_AllowLegExtension ? _RaycastExtensionDistance : 0f);
                    lToeEnd = lToeStart - (lOwnerTransform.up * lToeRaycastDistance);

                    lToeCollision = RaycastExt.SafeRaycast(lToeStart, -lOwnerTransform.up, out sCollisionInfo2, lToeRaycastDistance, _GroundingLayers, mSkeleton._RootTransform, mSkeleton.BoneTransforms);
                }

                // Prepare some variables in case we'll need to continue
                Vector3 lFootTarget   = Vector3.zero;
                Vector3 lGroundNormal = Vector3.up;

                // We only need to process if there is a collision
                if (lHeelCollision || lToeCollision)
                {
                    lUseCurrentRotation = false;

                    // Test if we actually hit anything
                    bool lUseHeel = true;
                    if (!lHeelCollision || (lToeCollision && (sCollisionInfo2.point.y - lToeEnd.y > sCollisionInfo1.point.y - lHeelEnd.y)))
                    {
                        lUseHeel = false;
                    }

                    lGroundNormal = (lUseHeel ? sCollisionInfo1 : sCollisionInfo2).normal;

                    // Determine the actual foot bone target
                    if (lUseHeel)
                    {
                        lFootTarget = sCollisionInfo1.point + (lOwnerTransform.up * _FootToeDistance);
                    }
                    else
                    {
                        lFootTarget = sCollisionInfo2.point + ((lFoot.Transform.position - lToes.Transform.position).normalized * lFoot.Length);
                    }

                    // If we aren't allowed to extend the leg, but we need to... stop
                    if (!_AllowLegExtension)
                    {
                        // TRT 02/04/2016 a - When than animation curls the leg and pulls the foot up, we
                        // don't want to force the foot to the ground. So, we disable the IK. The problem is
                        // if there's a tiny shuffling animation, we could flicker the IK on and off.

                        float lLegFootAnimationDistance = (lFoot._Transform.position - lUpperLeg._Transform.position).sqrMagnitude;
                        float lLegFootTargetDistance    = (lFootTarget - lUpperLeg._Transform.position).sqrMagnitude;

                        //if (lLegFootNew >= lLegFootOld)
                        float lLegDelta = lLegFootTargetDistance - lLegFootAnimationDistance;
                        if (lLegDelta > _MaxDeltaDistance)
                        {
                            lUseCurrentRotation = true;
                        }
                    }
                }

                // If we're using the current rotations, we need to remove the targets that
                // may have been set. We do this so we can smoothly blend to the current rotation
                // as set by animations.
                if (lUseCurrentRotation)
                {
                    if (lUpperLeg != null)
                    {
                        BoneControllerBone     lBone     = lUpperLeg;
                        FootPlacementMotorBone lBoneInfo = _BoneInfo[mBones.IndexOf(lBone)];

                        lBoneInfo.RotationTarget = lBone.Transform.rotation * lBone.ToBoneForward;
                        lBoneInfo.Rotation       = Quaternion.Lerp(lBoneInfo.Rotation, lBoneInfo.RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? lBoneInfo.RotationLerp : 1f));
                        lBone.SetWorldRotation(lBoneInfo.Rotation, _BoneWeight);

                        if (lBone.ApplyLimitsInFrame)
                        {
                            lBone.ApplyLimitsInFrame = _ApplyLimits;
                        }
                    }

                    if (lLowerLeg != null)
                    {
                        BoneControllerBone     lBone     = lLowerLeg;
                        FootPlacementMotorBone lBoneInfo = _BoneInfo[mBones.IndexOf(lBone)];

                        lBoneInfo.RotationTarget = lBone.Transform.rotation * lBone.ToBoneForward;
                        lBoneInfo.Rotation       = Quaternion.Lerp(lBoneInfo.Rotation, lBoneInfo.RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? lBoneInfo.RotationLerp : 1f));
                        lBone.SetWorldRotation(lBoneInfo.Rotation, _BoneWeight);

                        if (lBone.ApplyLimitsInFrame)
                        {
                            lBone.ApplyLimitsInFrame = _ApplyLimits;
                        }
                    }

                    if (lFoot != null && _RotateFootToGround)
                    {
                        RotateFoot(lOwnerTransform, lLowerLeg, lFoot, lToes, lFootTarget, lGroundNormal, lHeelCollision, lToeCollision);
                    }
                }
                // If we get there, we need to bend the legs because there is a collision
                // or an extension
                else
                {
                    // Only perform the solve if there enough movement involved. Otherwise,
                    // we're wasting resources.
                    //float lTargetDistance = Vector3.Distance(lFoot.Transform.position, lFootTarget);

                    // TRT 02/04/2016 a - With minor movements in animation, we can see the solver popping on and off.
                    // So, we'll force the solver to run each frame no matter what. It's not that big of a hit.

                    //if (lTargetDistance > FootGround2BoneMotor.MIN_TARGET_DISTANCE)
                    {
                        //HingeSwingAndTwistJoint lLowerJoint = lLowerLeg.Joint as HingeSwingAndTwistJoint;

                        // Since we have a target, solve
                        IKSolverState lState = IKSolverState.Allocate();
                        lState.TargetPosition  = lFootTarget;
                        lState.UseBindRotation = _UseBindRotation;
                        lState.UsePlaneNormal  = _UsePlaneNormal;
                        lState.IsDebugEnabled  = _IsDebugEnabled;

                        lState.Bones.Add(lUpperLeg);
                        lState.Bones.Add(lLowerLeg);

                        lState.BoneBendAxes.Add(_BoneInfo[0].BendAxis);
                        lState.BoneBendAxes.Add(_BoneInfo[1].BendAxis);

                        CosineSolver.SolveIK(ref lState);

                        // Process the results of the solve. We use the enumerator to
                        // avoid garbage from the ForEach
                        Dictionary <BoneControllerBone, Quaternion> .Enumerator lEnumerator = lState.Rotations.GetEnumerator();
                        while (lEnumerator.MoveNext())
                        {
                            BoneControllerBone lBone = lEnumerator.Current.Key;

                            FootPlacementMotorBone lBoneInfo = _BoneInfo[mBones.IndexOf(lBone)];

                            // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
                            Quaternion lCurrentRotation = lBone.Transform.rotation * lBone.ToBoneForward;

                            // Rotation as determined by the target
                            Quaternion lTargetRotation = lState.Rotations[lBone] * Quaternion.Euler(0f, 0f, lBoneInfo.Twist);

                            // Determine the final rotation based on weight
                            lBoneInfo.RotationTarget = Quaternion.Lerp(lCurrentRotation, lTargetRotation, _Weight * lBoneInfo.Weight);

                            // Slowly move towards the rotation we determined
                            lBoneInfo.Rotation = Quaternion.Lerp(lBoneInfo.Rotation, lBoneInfo.RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? lBoneInfo.RotationLerp : 1f));

                            // Set the world rotation
                            lBone.SetWorldRotation(lBoneInfo.Rotation, _BoneWeight);
                            if (lBone.ApplyLimitsInFrame)
                            {
                                lBone.ApplyLimitsInFrame = _ApplyLimits;
                            }
                        }

                        //// Process the results of the solve
                        //foreach (BoneControllerBone lBone in lState.Rotations.Keys)
                        //{
                        //    FootPlacementMotorBone lBoneInfo = _BoneInfo[mBones.IndexOf(lBone)];

                        //    // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
                        //    Quaternion lCurrentRotation = lBone.Transform.rotation * lBone.ToBoneForward;

                        //    // Rotation as determined by the target
                        //    Quaternion lTargetRotation = lState.Rotations[lBone] * Quaternion.Euler(0f, 0f, lBoneInfo.Twist);

                        //    // Determine the final rotation based on weight
                        //    lBoneInfo.RotationTarget = Quaternion.Lerp(lCurrentRotation, lTargetRotation, _Weight * lBoneInfo.Weight);

                        //    // Slowly move towards the rotation we determined
                        //    lBoneInfo.Rotation = Quaternion.Lerp(lBoneInfo.Rotation, lBoneInfo.RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? lBoneInfo.RotationLerp : 1f));

                        //    // Set the world rotation
                        //    lBone.SetWorldRotation(lBoneInfo.Rotation, _BoneWeight);
                        //    if (lBone.ApplyLimitsInFrame) { lBone.ApplyLimitsInFrame = _ApplyLimits; }
                        //}

                        //DebugDraw.DrawLineOverlay(lUpperLeg.Transform.position, lUpperLeg.Transform.position + (lUpperLeg.Transform.rotation * lUpperLeg.ToBoneForward * (Vector3.forward * 0.5f)), 0.02f, Color.blue, 0.5f);
                        //DebugDraw.DrawLineOverlay(lUpperLeg.Transform.position, lUpperLeg.Transform.position + (lUpperLeg.Transform.rotation * lUpperLeg.ToBoneForward * (Vector3.up * 0.5f)), 0.02f, Color.green, 0.5f);
                        //DebugDraw.DrawLineOverlay(lUpperLeg.Transform.position, lUpperLeg.Transform.position + (lUpperLeg.Transform.rotation * lUpperLeg.ToBoneForward * (Vector3.right * 0.5f)), 0.02f, Color.red, 0.5f);

                        // Set the foot rotations. This may change based on the collisions
                        if (lFoot != null && _RotateFootToGround)
                        {
                            RotateFoot(lOwnerTransform, lLowerLeg, lFoot, lToes, lFootTarget, lGroundNormal, lHeelCollision, lToeCollision);
                        }

                        // Clean up
                        IKSolverState.Release(lState);
                    }
                }

                // Keep track of the last position so we can test for movement
                mLastPosition = mSkeleton.transform.position;
            }
            // If it's not on a consistant update, we just want to reset the
            // last rotations that we found.
            else
            {
                for (int i = 0; i < mBones.Count; i++)
                {
                    BoneControllerBone lBone = mBones[i];
                    if (lBone == null || lBone == lToes)
                    {
                        continue;
                    }

                    if (lBone == lFoot)
                    {
                        Vector3 lMovement = mSkeleton.transform.position - mLastPosition;
                        if (!_RotateFootOnMovement && (Mathf.Abs(lMovement.x) > 0.001f || Mathf.Abs(lMovement.z) > 0.001f))
                        {
                            continue;
                        }
                    }

                    lBone.SetWorldRotation(_BoneInfo[i].Rotation, _BoneWeight);
                }
            }
        }
Пример #4
0
        /// <summary>
        /// Process the motor each frame so that it can update the bone rotations.
        /// This is the function that should be overridden in each motor
        /// </summary>
        /// <param name="rDeltaTime">Delta time to use for the update</param>
        /// <param name="rUpdate">Determines if it is officially time to do the update</param>
        protected override void Update(float rDeltaTime, bool rUpdate)
        {
            if (mBones == null || mBones.Count < 2)
            {
                return;
            }

            if (_TargetTransform == null && _TargetTransformName.Length > 0)
            {
                GameObject lObject = GameObject.Find(_TargetTransformName);
                if (lObject != null)
                {
                    _TargetTransform = lObject.transform;
                }
            }

            if (_TargetTransform != null && !_TargetTransform.gameObject.activeInHierarchy)
            {
                return;
            }

            // Ensure we have the correct amount of bone infos... we should
            while (_BoneInfo.Count < mBones.Count)
            {
                LimbReachMotorBone lBoneInfo = new LimbReachMotorBone();
                _BoneInfo.Add(lBoneInfo);
            }

            // If it's time to update, determine the positions we need to be
            // at and lerp towards them.
            if (rUpdate)
            {
                // Grab the target. Priority is given to the transform
                Vector3 lTargetPosition = (_TargetTransform != null ? _TargetTransform.position : _TargetPosition);
                if (lTargetPosition == Vector3.zero)
                {
                    return;
                }

                // Simplify the bone names
                BoneControllerBone lBoneChainRoot = mBones[0];
                BoneControllerBone lBoneChainEnd  = mBones[1];

                // If we have valid bones, solve
                if (lBoneChainRoot != null && lBoneChainEnd != null)
                {
                    //HingeSwingAndTwistJoint lEndJoint = lBoneChainEnd.Joint as HingeSwingAndTwistJoint;

                    IKSolverState lState = IKSolverState.Allocate();
                    lState.TargetPosition  = lTargetPosition;
                    lState.UseBindRotation = _UseBindRotation;
                    lState.UsePlaneNormal  = _UsePlaneNormal;
                    lState.IsDebugEnabled  = _IsDebugEnabled;

                    lState.Bones.Add(lBoneChainRoot);
                    lState.Bones.Add(lBoneChainEnd);

                    lState.BoneBendAxes.Add(_BoneInfo[0].BendAxis);
                    lState.BoneBendAxes.Add(_BoneInfo[1].BendAxis);

                    CosineSolver.SolveIK(ref lState, _Bone2Extension);

                    // Process the results of the solve. We use the enumerator to
                    // avoid garbage from the ForEach
                    Dictionary <BoneControllerBone, Quaternion> .Enumerator lEnumerator = lState.Rotations.GetEnumerator();
                    while (lEnumerator.MoveNext())
                    {
                        BoneControllerBone lBone = lEnumerator.Current.Key;

                        int lIndex = mBones.IndexOf(lBone);

                        // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
                        Quaternion lCurrentRotation = lBone.Transform.rotation * lBone.ToBoneForward;

                        // Rotation based on the target position
                        Quaternion lTargetRotation = lState.Rotations[lBone] * Quaternion.Euler(0f, 0f, _BoneInfo[lIndex].Twist);

                        // Rotation as determined by the target
                        _BoneInfo[lIndex].RotationTarget = Quaternion.Lerp(lCurrentRotation, lTargetRotation, _Weight * _BoneInfo[lIndex].Weight);

                        // Slowly move towards the rotation we determined
                        _BoneInfo[lIndex].Rotation = Quaternion.Lerp(_BoneInfo[lIndex].Rotation, _BoneInfo[lIndex].RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? _BoneInfo[lIndex].RotationLerp : 1f));

                        // Set the world rotation
                        lBone.SetWorldRotation(_BoneInfo[lIndex].Rotation, _BoneWeight);
                        if (lBone.ApplyLimitsInFrame)
                        {
                            lBone.ApplyLimitsInFrame = _ApplyLimits;
                        }
                    }

                    //foreach (BoneControllerBone lBone in lState.Rotations.Keys)
                    //{
                    //    int lIndex = mBones.IndexOf(lBone);

                    //    // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
                    //    Quaternion lCurrentRotation = lBone.Transform.rotation * lBone.ToBoneForward;

                    //    // Rotation based on the target position
                    //    Quaternion lTargetRotation = lState.Rotations[lBone] * Quaternion.Euler(0f, 0f, _BoneInfo[lIndex].Twist);

                    //    // Rotation as determined by the target
                    //    _BoneInfo[lIndex].RotationTarget = Quaternion.Lerp(lCurrentRotation, lTargetRotation, _Weight * _BoneInfo[lIndex].Weight);

                    //    // Slowly move towards the rotation we determined
                    //    _BoneInfo[lIndex].Rotation = Quaternion.Lerp(_BoneInfo[lIndex].Rotation, _BoneInfo[lIndex].RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? _BoneInfo[lIndex].RotationLerp : 1f));

                    //    // Set the world rotation
                    //    lBone.SetWorldRotation(_BoneInfo[lIndex].Rotation, _BoneWeight);
                    //    if (lBone.ApplyLimitsInFrame) { lBone.ApplyLimitsInFrame = _ApplyLimits; }
                    //}

                    IKSolverState.Release(lState);
                }
            }
            // If it's not on a consistant update, we just want to reset the
            // last rotations that we found.
            else
            {
                for (int i = 0; i < mBones.Count; i++)
                {
                    BoneControllerBone lBone = mBones[i];
                    if (lBone == null)
                    {
                        continue;
                    }

                    lBone.SetWorldRotation(_BoneInfo[i].Rotation, _BoneWeight);
                }
            }
        }
        /// <summary>
        /// Process the motor each frame so that it can update the bone rotations.
        /// This is the function that should be overridden in each motor
        /// </summary>
        /// <param name="rDeltaTime">Delta time to use for the update</param>
        /// <param name="rUpdate">Determines if it is officially time to do the update</param>
        protected override void Update(float rDeltaTime, bool rUpdate)
        {
            if (mBones.Count == 0)
            {
                return;
            }

            // Ensure we always have one more than the number of bones. The
            // last one is the target position for the last bone.
            while (_BoneInfo.Count <= mBones.Count)
            {
                BoneControllerBone lBone = (_BoneInfo.Count < mBones.Count ? mBones[_BoneInfo.Count] : null);
                AddBoneInfo(mBones.Count, lBone);
            }

            // If it's time to update, determine the positions we need to be
            // at and lerp towards them.
            if (rUpdate)
            {
                // The first bone doesn't move on it's own. It's dragged by it's parent
                _BoneInfo[0].Velocity     = _BoneInfo[0].Position - _BoneInfo[0].PrevPosition;
                _BoneInfo[0].PrevPosition = _BoneInfo[0].Position;
                _BoneInfo[0].Position     = mBones[0]._Transform.position;

                // Initialize the data if it isn't
                for (int i = 1; i < _BoneInfo.Count; i++)
                {
                    // Initialize any bone info that hasn't currently been set.
                    if (_BoneInfo[i].PrevPosition.sqrMagnitude == 0f)
                    {
                        if (i < mBones.Count)
                        {
                            _BoneInfo[i].Velocity     = Vector3.zero;
                            _BoneInfo[i].PrevPosition = mBones[i]._Transform.position;
                            _BoneInfo[i].Position     = mBones[i]._Transform.position;
                        }
                        else
                        {
                            int lPrevIndex = i - 1;
                            _BoneInfo[i].Velocity     = Vector3.zero;
                            _BoneInfo[i].PrevPosition = mBones[lPrevIndex]._Transform.position + (mBones[lPrevIndex]._Transform.rotation * (mBones[lPrevIndex].BoneForward * mBones[lPrevIndex].Length));
                            _BoneInfo[i].Position     = mBones[lPrevIndex]._Transform.position + (mBones[lPrevIndex]._Transform.rotation * (mBones[lPrevIndex].BoneForward * mBones[lPrevIndex].Length));
                        }
                    }
                }

                // Drag the bones one after the other
                //bool lCollision = false;
                for (int i = 0; i < mBones.Count; i++)
                {
                    // Collisions will stop movement
                    if (_IsCollisionEnabled)
                    {
                        _BoneInfo[i].Collision = ProcessBoneCollisions(i);
                    }

                    // If we're not colliding, allow the bones to move
                    if (!_BoneInfo[i].Collision)
                    {
                        // Add gravity to pull the bones down
                        if (!_IsGravityEnabled)
                        {
                            _BoneInfo[i + 1].Velocity = Vector3.zero;
                        }
                        else
                        {
                            _BoneInfo[i + 1].Velocity = _Gravity * rDeltaTime * GetGravity(i);
                        }

                        // Add pull velocity so that the bones chase after the positions
                        _BoneInfo[i + 1].Velocity += (_BoneInfo[i + 1].Position - _BoneInfo[i + 1].PrevPosition);

                        // Determine the new position (which may be too much)
                        Vector3 lNewPosition = _BoneInfo[i + 1].PrevPosition + _BoneInfo[i + 1].Velocity;

                        // Ensure the new position stays connected with the previous bones
                        Vector3 lDirection    = (lNewPosition - _BoneInfo[i].Position).normalized;
                        Vector3 lDragPosition = _BoneInfo[i].Position + (lDirection * mBones[i].Length);

                        // Finally, set the new position
                        _BoneInfo[i + 1].Position = lDragPosition;
                    }
                }

                // With the bone positions determined, now we can compute the final positions
                for (int i = 0; i < mBones.Count; i++)
                {
                    // The original pose the bones were bound to
                    Vector3 lBaseEndPosition = Vector3.zero;
                    if (_BoneInfo[i].UseBindPosition)
                    {
                        lBaseEndPosition = _BoneInfo[i].Position + (mBones[i].WorldBindRotation * (Vector3.forward * mBones[i].Length));
                    }
                    else
                    {
                        lBaseEndPosition = (i < mBones.Count - 1 ? mBones[i + 1].Transform.position : mBones[i].WorldEndPosition);
                    }

                    // Determine the bending based on the stiffness of each bone
                    float lStiffness = GetStiffness(i);
                    if (_BoneInfo[i].Collision)
                    {
                        lStiffness = 0f;
                    }

                    Vector3 lNewEndPosition = Vector3.Lerp(_BoneInfo[i + 1].Position, lBaseEndPosition, lStiffness);

                    // Since we lerped, the value may not actually be the right position
                    // based on our bone length. So, we need to reprocess the lengths again
                    Vector3 lDirection = (lNewEndPosition - _BoneInfo[i].Position).normalized;
                    lNewEndPosition = _BoneInfo[i].Position + (lDirection * mBones[i].Length);

                    // Finally, set the final positions
                    _BoneInfo[i + 1].Position = lNewEndPosition;

                    // Rotation based on the positions
                    Vector3 lTargetForward = _BoneInfo[i + 1].Position - _BoneInfo[i].Position;
                    if (lTargetForward.sqrMagnitude != 0f)
                    {
                        lTargetForward = lTargetForward.normalized;

                        // TRT 2/21/2016 - Updated to reflect bone's actual values
                        //_BoneInfo[i].RotationTarget = Quaternion.LookRotation(lTargetForward.normalized, mBones[i].Transform.up);

                        // TRT 3/8/2016 - Wasn't using the current bone's actual values :(
                        //_BoneInfo[i].RotationTarget = Quaternion.LookRotation(lTargetForward.normalized, mBones[i]._BoneUp);

                        // TRT 7/9/2016 - Wasn't trying to get back to original rotation
                        //Vector3 lCurrentUp = (mBones[i].Transform.rotation * mBones[i].ToBoneForward) * Vector3.up;
                        //_BoneInfo[i].RotationTarget = Quaternion.LookRotation(lTargetForward, lCurrentUp);

                        // TRT 7/9/2016 - Want to make sure the bones try to twist to thier original rotation too
                        Vector3 lCurrentUp = (mBones[i].Transform.rotation * mBones[i].ToBoneForward) * Vector3.up;

                        if (_BoneInfo[i].UntwistLerp > 0f)
                        {
                            Vector3 lBindUp = mBones[i].WorldBindRotation * Vector3.up;
                            lCurrentUp = Vector3.Lerp(lCurrentUp, lBindUp, _BoneInfo[i].UntwistLerp);
                        }

                        _BoneInfo[i].RotationTarget = Quaternion.LookRotation(lTargetForward, lCurrentUp);

                        //Debug.DrawLine(mBones[i].Transform.position, mBones[i].Transform.position + lTargetForward, Color.black);
                        //Debug.DrawLine(mBones[i].Transform.position, mBones[i].Transform.position + lCurrentUp, Color.magenta);
                    }
                    // In the case of a 0 length bone, we just use the current rotation
                    else
                    {
                        _BoneInfo[i].RotationTarget = mBones[i].Transform.rotation * mBones[i].ToBoneForward;
                    }

                    // Slowly move towards the rotation we determined
                    _BoneInfo[i].Rotation = Quaternion.Lerp(_BoneInfo[i].Rotation, _BoneInfo[i].RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? _BoneInfo[i].RotationLerp : 1f));

                    // Set the world rotation
                    mBones[i].SetWorldRotation(_BoneInfo[i].Rotation, Quaternion.identity, _BoneWeight);

                    // Clean up
                    _BoneInfo[i].PrevPosition = _BoneInfo[i].Position;
                }

                // Update that last extra bone info
                _BoneInfo[_BoneInfo.Count - 1].PrevPosition = _BoneInfo[_BoneInfo.Count - 1].Position;
            }
            // If it's not on a consistant update, we just want to reset the
            // last rotations that we found.
            else
            {
                for (int i = 0; i < mBones.Count; i++)
                {
                    BoneControllerBone lBone = mBones[i];
                    if (lBone == null)
                    {
                        continue;
                    }

                    lBone.SetWorldRotation(_BoneInfo[i].Rotation, Quaternion.identity, _BoneWeight);
                }
            }
        }
Пример #6
0
        /// <summary>
        /// Process the motor each frame so that it can update the bone rotations.
        /// This is the function that should be overridden in each motor
        /// </summary>
        /// <param name="rDeltaTime">Delta time to use for the update</param>
        /// <param name="rUpdate">Determines if it is officially time to do the update</param>
        protected override void Update(float rDeltaTime, bool rUpdate)
        {
            // Get out if there are no valid bones
            if (mBones.Count == 0 || mBones[0] == null || mBones[0]._Transform == null)
            {
                return;
            }

            // If it's time to update, determine the positions we need to be
            // at and lerp towards them.
            if (rUpdate)
            {
                // Extract out the target info and resulting rotation
                Vector3 lTargetPosition = _TargetPosition;
                if (_TargetTransform != null)
                {
                    if (_UseAsDirection)
                    {
                        lTargetPosition = mBones[0]._Transform.position + (_TargetTransform.forward * 2f);
                    }
                    else
                    {
                        lTargetPosition = _TargetTransform.position;
                    }
                }

                // Forward direction that we want to be looking from
                Vector3 lAnchorPosition = mBones[0]._Transform.position;

                // The target we want to rotate to
                Vector3    lToTarget       = (lTargetPosition - lAnchorPosition).normalized;
                Quaternion lTargetRotation = Quaternion.LookRotation(lToTarget, (_InvertRotations ? -1 : 1) * (mSkeleton != null && mSkeleton.transform != null ? mSkeleton.transform.up : Vector3.up));

                // Start rotating each of the bones independantly
                for (int i = mBones.Count - 1; i >= 0; i--)
                {
                    if (_BoneInfo.Count <= i)
                    {
                        continue;
                    }

                    BoneControllerBone lBone = mBones[i];
                    if (lBone == null)
                    {
                        continue;
                    }

                    // This swings around the bone around it's bone's current x and y axis and twists around
                    // the current bone direction
                    Quaternion lRotationOffset = lBone.BindRotation * lBone.ToBoneForward;
                    lRotationOffset = lRotationOffset * Quaternion.AngleAxis(_BoneInfo[i].RotationOffset.y, Vector3.up);
                    lRotationOffset = lRotationOffset * Quaternion.AngleAxis(_BoneInfo[i].RotationOffset.x, Vector3.right);
                    lRotationOffset = lRotationOffset * Quaternion.AngleAxis(_BoneInfo[i].RotationOffset.z, Vector3.forward);

                    // The current rotation we will lerp from. We remove the trailing rotation offset because we'll add it later
                    Quaternion lCurrentRotation = mBones[i].Transform.rotation * mBones[i].ToBoneForward * Quaternion.Inverse(lRotationOffset);

                    // Target rotation. We want to convert from "Bone Forward" to "Bone Up"-forward since it's the up direction
                    // that will point to the target. Then, we need to remove part of the offset since it will be re-added.
                    Quaternion lBoneTargetRotation = lTargetRotation * Quaternion.AngleAxis(180f, Vector3.up) * Quaternion.AngleAxis(-90f, Vector3.right) * Quaternion.Inverse(lBone.BindRotation * lBone.ToBoneForward);

                    // Based on the weight, use the current rotation or our target rotation
                    _BoneInfo[i].RotationTarget = Quaternion.Lerp(lCurrentRotation, lBoneTargetRotation, _Weight * _BoneInfo[i].Weight);

                    // Slowly move towards the targets we determined
                    float lLerp = _BoneInfo[i].RotationLerp;

#if UNITY_EDITOR
                    // If we're editing, don't lerp. Jut move
                    if (!EditorApplication.isPlaying)
                    {
                        lLerp = 1f;
                    }
#endif

                    // Slowly move towards the rotation we determined
                    _BoneInfo[i].Rotation = Quaternion.Lerp(_BoneInfo[i].Rotation, _BoneInfo[i].RotationTarget, (_IsFixedUpdateEnabled && !mIsFirstUpdate ? lLerp : 1f));

                    // Set the world rotation
                    lBone.SetWorldRotation(_BoneInfo[i].Rotation * lRotationOffset, _BoneWeight);
                }
            }
            // If it's not on a consistant update, we just want to reset the
            // last rotations that we found.
            else
            {
                for (int i = mBones.Count - 1; i >= 0; i--)
                {
                    if (_BoneInfo.Count <= i)
                    {
                        continue;
                    }

                    BoneControllerBone lBone = mBones[i];
                    if (lBone == null)
                    {
                        continue;
                    }

                    // This swings around the bone around it's bone's current x and y axis and twists around
                    // the current bone direction
                    Quaternion lRotationOffset = lBone.BindRotation * lBone.ToBoneForward;
                    lRotationOffset = lRotationOffset * Quaternion.AngleAxis(_BoneInfo[i].RotationOffset.y, Vector3.up);
                    lRotationOffset = lRotationOffset * Quaternion.AngleAxis(_BoneInfo[i].RotationOffset.x, Vector3.right);
                    lRotationOffset = lRotationOffset * Quaternion.AngleAxis(_BoneInfo[i].RotationOffset.z, Vector3.forward);

                    mBones[i].SetWorldRotation(_BoneInfo[i].Rotation * lRotationOffset, _BoneWeight);
                }
            }
        }