public static void SolveCCD(VirtualBone[] bones, Vector3 targetPosition, float weight, int iterations)
            {
                if (weight <= 0f)
                {
                    return;
                }

                // Iterating the solver
                for (int iteration = 0; iteration < iterations; iteration++)
                {
                    for (int i = bones.Length - 2; i > -1; i--)
                    {
                        Vector3 toLastBone = bones[bones.Length - 1].solverPosition - bones[i].solverPosition;
                        Vector3 toTarget   = targetPosition - bones[i].solverPosition;


                        Quaternion rotation = Quaternion.FromToRotation(toLastBone, toTarget);

                        if (weight >= 1)
                        {
                            //bones[i].transform.rotation = targetRotation;
                            VirtualBone.RotateBy(bones, i, rotation);
                        }
                        else
                        {
                            VirtualBone.RotateBy(bones, i, Quaternion.Lerp(Quaternion.identity, rotation, weight));
                        }
                    }
                }
            }
            public void Solve(bool isLeft)
            {
                chestRotation = Quaternion.LookRotation(rootRotation * chestForwardAxis, rootRotation * chestUpAxis);
                chestForward  = chestRotation * Vector3.forward;
                chestUp       = chestRotation * Vector3.up;

                //Debug.DrawRay (Vector3.up * 2f, chestForward);
                //Debug.DrawRay (Vector3.up * 2f, chestUp);

                if (hasShoulder && shoulderRotationWeight > 0f)
                {
                    switch (shoulderRotationMode)
                    {
                    case ShoulderRotationMode.YawPitch:
                        Vector3 sDir = position - shoulder.solverPosition;
                        sDir = sDir.normalized;

                        // Shoulder Yaw
                        float      yOA          = isLeft? yawOffsetAngle: -yawOffsetAngle;
                        Quaternion yawOffset    = Quaternion.AngleAxis((isLeft? -90f: 90f) + yOA, chestUp);
                        Quaternion workingSpace = yawOffset * chestRotation;

                        //Debug.DrawRay(Vector3.up * 2f, workingSpace * Vector3.forward);
                        //Debug.DrawRay(Vector3.up * 2f, workingSpace * Vector3.up);

                        Vector3 sDirWorking = Quaternion.Inverse(workingSpace) * sDir;

                        //Debug.DrawRay(Vector3.up * 2f, sDirWorking);

                        float yaw = Mathf.Atan2(sDirWorking.x, sDirWorking.z) * Mathf.Rad2Deg;

                        float dotY = Vector3.Dot(sDirWorking, Vector3.up);
                        dotY = 1f - Mathf.Abs(dotY);
                        yaw *= dotY;

                        yaw -= yOA;
                        float yawLimitMin = isLeft? -20f: -50f;
                        float yawLimitMax = isLeft? 50f: 20f;
                        yaw = DamperValue(yaw, yawLimitMin - yOA, yawLimitMax - yOA, 0.7f);                         // back, forward

                        Vector3    f           = shoulder.solverRotation * shoulder.axis;
                        Vector3    t           = workingSpace * (Quaternion.AngleAxis(yaw, Vector3.up) * Vector3.forward);
                        Quaternion yawRotation = Quaternion.FromToRotation(f, t);

                        //Debug.DrawRay(Vector3.up * 2f, f, Color.red);
                        //Debug.DrawRay(Vector3.up * 2f, t, Color.green);

                        //Debug.DrawRay(Vector3.up * 2f, yawRotation * Vector3.forward, Color.blue);
                        //Debug.DrawRay(Vector3.up * 2f, yawRotation * Vector3.up, Color.green);
                        //Debug.DrawRay(Vector3.up * 2f, yawRotation * Vector3.right, Color.red);

                        // Shoulder Pitch
                        Quaternion pitchOffset = Quaternion.AngleAxis(isLeft? -90f: 90f, chestUp);
                        workingSpace = pitchOffset * chestRotation;
                        workingSpace = Quaternion.AngleAxis(isLeft? pitchOffsetAngle: -pitchOffsetAngle, chestForward) * workingSpace;

                        //Debug.DrawRay(Vector3.up * 2f, workingSpace * Vector3.forward);
                        //Debug.DrawRay(Vector3.up * 2f, workingSpace * Vector3.up);

                        sDir        = position - (shoulder.solverPosition + chestRotation * (isLeft? Vector3.right: Vector3.left) * mag);
                        sDirWorking = Quaternion.Inverse(workingSpace) * sDir;

                        //Debug.DrawRay(Vector3.up * 2f, sDirWorking);

                        float pitch = Mathf.Atan2(sDirWorking.y, sDirWorking.z) * Mathf.Rad2Deg;

                        pitch -= pitchOffsetAngle;
                        pitch  = DamperValue(pitch, -45f - pitchOffsetAngle, 45f - pitchOffsetAngle);
                        Quaternion pitchRotation = Quaternion.AngleAxis(-pitch, workingSpace * Vector3.right);

                        //Debug.DrawRay(Vector3.up * 2f, pitchRotation * Vector3.forward, Color.green);
                        //Debug.DrawRay(Vector3.up * 2f, pitchRotation * Vector3.up, Color.green);

                        // Rotate bones
                        Quaternion sR = pitchRotation * yawRotation;
                        if (shoulderRotationWeight * positionWeight < 1f)
                        {
                            sR = Quaternion.Lerp(Quaternion.identity, sR, shoulderRotationWeight * positionWeight);
                        }
                        VirtualBone.RotateBy(bones, sR);

                        Stretching();

                        // Solve trigonometric
                        VirtualBone.SolveTrigonometric(bones, 1, 2, 3, position, GetBendNormal(position - upperArm.solverPosition), positionWeight);

                        float p = Mathf.Clamp(pitch * positionWeight * shoulderRotationWeight * shoulderTwistWeight * 2f, 0f, 180f);
                        shoulder.solverRotation = Quaternion.AngleAxis(p, shoulder.solverRotation * (isLeft? shoulder.axis: -shoulder.axis)) * shoulder.solverRotation;
                        upperArm.solverRotation = Quaternion.AngleAxis(p, upperArm.solverRotation * (isLeft? upperArm.axis: -upperArm.axis)) * upperArm.solverRotation;

                        // Additional pass to reach with the shoulders
                        //VirtualBone.SolveTrigonometric(bones, 0, 1, 3, position, Vector3.Cross(upperArm.solverPosition - shoulder.solverPosition, hand.solverPosition - shoulder.solverPosition), positionWeight * 0.5f);
                        break;

                    case ShoulderRotationMode.FromTo:
                        Quaternion shoulderRotation = shoulder.solverRotation;

                        Quaternion r = Quaternion.FromToRotation((upperArm.solverPosition - shoulder.solverPosition).normalized + chestForward, position - shoulder.solverPosition);
                        r = Quaternion.Slerp(Quaternion.identity, r, 0.5f * shoulderRotationWeight * positionWeight);
                        VirtualBone.RotateBy(bones, r);

                        Stretching();

                        VirtualBone.SolveTrigonometric(bones, 0, 2, 3, position, Vector3.Cross(forearm.solverPosition - shoulder.solverPosition, hand.solverPosition - shoulder.solverPosition), 0.5f * shoulderRotationWeight * positionWeight);
                        VirtualBone.SolveTrigonometric(bones, 1, 2, 3, position, GetBendNormal(position - upperArm.solverPosition), positionWeight);

                        // Twist shoulder and upper arm bones when holding hands up
                        Quaternion q           = Quaternion.Inverse(Quaternion.LookRotation(chestUp, chestForward));
                        Vector3    vBefore     = q * (shoulderRotation * shoulder.axis);
                        Vector3    vAfter      = q * (shoulder.solverRotation * shoulder.axis);
                        float      angleBefore = Mathf.Atan2(vBefore.x, vBefore.z) * Mathf.Rad2Deg;
                        float      angleAfter  = Mathf.Atan2(vAfter.x, vAfter.z) * Mathf.Rad2Deg;
                        float      pitchAngle  = Mathf.DeltaAngle(angleBefore, angleAfter);
                        if (isLeft)
                        {
                            pitchAngle = -pitchAngle;
                        }
                        pitchAngle = Mathf.Clamp(pitchAngle * shoulderRotationWeight * shoulderTwistWeight * 2f * positionWeight, 0f, 180f);

                        shoulder.solverRotation = Quaternion.AngleAxis(pitchAngle, shoulder.solverRotation * (isLeft? shoulder.axis: -shoulder.axis)) * shoulder.solverRotation;
                        upperArm.solverRotation = Quaternion.AngleAxis(pitchAngle, upperArm.solverRotation * (isLeft? upperArm.axis: -upperArm.axis)) * upperArm.solverRotation;
                        break;
                    }
                }
                else
                {
                    Stretching();

                    // Solve arm trigonometric
                    if (hasShoulder)
                    {
                        VirtualBone.SolveTrigonometric(bones, 1, 2, 3, position, GetBendNormal(position - upperArm.solverPosition), positionWeight);
                    }
                    else
                    {
                        VirtualBone.SolveTrigonometric(bones, 0, 1, 2, position, GetBendNormal(position - upperArm.solverPosition), positionWeight);
                    }
                }

                // Fix forearm twist relative to upper arm
                Quaternion forearmFixed = upperArm.solverRotation * forearmRelToUpperArm;
                Quaternion fromTo       = Quaternion.FromToRotation(forearmFixed * forearm.axis, hand.solverPosition - forearm.solverPosition);

                RotateTo(forearm, fromTo * forearmFixed, positionWeight);

                // Set hand rotation
                if (rotationWeight >= 1f)
                {
                    hand.solverRotation = rotation;
                }
                else if (rotationWeight > 0f)
                {
                    hand.solverRotation = Quaternion.Lerp(hand.solverRotation, rotation, rotationWeight);
                }
            }
Beispiel #3
0
            private void SolvePelvis()
            {
                if (pelvisPositionWeight <= 0f)
                {
                    return;
                }

                var headSolverRotation = head.solverRotation;
                var headSolverPosition = head.solverPosition;

                var middleLegPosition = (mySolver.legs[0].thigh.readPosition + mySolver.legs[1].thigh.readPosition) / 2;
                var hipLocalOffset    = Quaternion.Inverse(pelvis.readRotation) * (middleLegPosition - pelvis.readPosition);

                if (IkTweaksSettings.DoHipShifting)
                {
                    pelvis.solverPosition += pelvis.solverRotation * hipLocalOffset;
                    IKPositionPelvis      += IKRotationPelvis * hipLocalOffset;
                }

                if (IkTweaksSettings.PreStraightenSpine)
                {
                    for (var i = 1; i < bones.Length - 1; i++)
                    {
                        var rotation = Quaternion.FromToRotation(bones[i + 1].solverPosition - bones[i].solverPosition,
                                                                 bones[i].solverPosition - bones[i - 1].solverPosition);
                        VirtualBone.RotateBy(bones, i, rotation);
                    }
                }

                if (IkTweaksSettings.StraightenNeck)
                {
                    if (neckIndex >= 0)
                    {
                        var rotation = Quaternion.FromToRotation(bones[neckIndex + 1].solverPosition - bones[neckIndex].solverPosition,
                                                                 bones[neckIndex].solverPosition - bones[neckIndex - 1].solverPosition);
                        VirtualBone.RotateBy(bones, neckIndex, rotation);
                    }
                }

                var minAngle     = 0f;
                var maxAngle     = 1f;
                var currentAngle = 0f;

                for (var i = 0; i < bones.Length; i++)
                {
                    bonesShadow[i].solverPosition = bones[i].solverPosition;
                    bonesShadow[i].solverRotation = bones[i].solverRotation;
                }

                var targetDistance = (IKPositionPelvis - headSolverPosition).magnitude;

                for (var i = 0; i < relaxationIterations; i++)
                {
                    for (var j = 0; j < bones.Length; j++)
                    {
                        bones[j].solverPosition = bonesShadow[j].solverPosition;
                        bones[j].solverRotation = bonesShadow[j].solverRotation;
                    }

                    var currentIkTargetPos = IKPositionPelvis;

                    Vector3 delta = ((currentIkTargetPos + pelvisPositionOffset) - pelvis.solverPosition) *
                                    pelvisPositionWeight;
                    foreach (VirtualBone bone in bones)
                    {
                        bone.solverPosition += delta;
                    }

                    VirtualBone.RotateTo(bones, pelvisIndex, IKRotationPelvis);

                    var targetToHead = (headSolverPosition - currentIkTargetPos).normalized;
                    // var currentToHead = head.solverPosition - pelvis.solverPosition;
                    var currentToHead = (pelvis.solverRotation * Quaternion.Inverse(pelvis.readRotation) *
                                         (head.readPosition - pelvis.readPosition)).normalized;
                    var rotationNormal  = Vector3.Cross(currentToHead, targetToHead);
                    var rotationForward = Vector3.ProjectOnPlane(anchorRotation * Vector3.forward, currentToHead).normalized;
                    var bendDirection   = Vector3.ProjectOnPlane(targetToHead, currentToHead).normalized;
                    var bendForwardness = (Vector3.Dot(rotationForward, bendDirection) + 1) / 2;

                    var maxBendTotal = Mathf.Pow(Mathf.Clamp01(Mathf.Acos(Vector3.Dot(currentToHead, targetToHead)) * Mathf2.Rad2Deg / IkTweaksSettings.StraightSpineAngle), IkTweaksSettings.StraightSpinePower);

                    var maxSpineAngle = Mathf.Lerp(maxSpineAngleBack, maxSpineAngleFwd, bendForwardness) * maxBendTotal;
                    var maxNeckAngle  = Mathf.Lerp(maxNeckAngleBack, maxNeckAngleFwd, bendForwardness) * maxBendTotal;

                    var lastBoneToRotate = hipRotationPinning ? 1 : 0;
                    for (var j = bones.Length - 2; j > lastBoneToRotate; j--)
                    {
                        // var rotationNormal = Vector3.Cross(bones[j + 1].solverPosition - bones[j].solverPosition, headSolverPosition - bones[j].solverPosition);
                        var targetAngle = j == neckIndex
                                                        ? Mathf.Clamp01(currentAngle *neckBendPriority) * maxNeckAngle
                                                        : currentAngle * maxSpineAngle;

                        VirtualBone.RotateBy(bones, j, Quaternion.AngleAxis(targetAngle, rotationNormal));
                    }

                    if (hipRotationPinning)
                    {
                        var od = pelvis.solverPosition - bones[1].solverPosition;
                        var p  = Vector3.Dot(od, targetToHead);
                        var q  = od.sqrMagnitude - (bones[1].solverPosition - head.solverPosition).sqrMagnitude;
                        var t  = -p + Mathf.Sqrt(p * p - q);
                        var headRotateToTarget = pelvis.solverPosition + targetToHead * t;
                        VirtualBone.RotateBy(bones, 1,
                                             Quaternion.FromToRotation(head.solverPosition - bones[1].solverPosition,
                                                                       headRotateToTarget - bones[1].solverPosition));
                    }
                    else
                    {
                        VirtualBone.RotateBy(bones,
                                             Quaternion.FromToRotation(head.solverPosition - pelvis.solverPosition,
                                                                       headSolverPosition - IKPositionPelvis));
                    }

                    delta = headSolverPosition - head.solverPosition;
                    foreach (VirtualBone bone in bones)
                    {
                        bone.solverPosition += delta;
                    }

                    var currentDistance = (head.solverPosition - pelvis.solverPosition).magnitude;

                    if (currentDistance > targetDistance)
                    {
                        minAngle = currentAngle;
                    }
                    else
                    {
                        maxAngle = currentAngle;
                    }

                    currentAngle = (minAngle + maxAngle) / 2;
                }

                if (IkTweaksSettings.DoHipShifting)
                {
                    pelvis.solverPosition -= pelvis.solverRotation * hipLocalOffset;
                    IKPositionPelvis      -= IKRotationPelvis * hipLocalOffset;
                }

                head.solverRotation = headSolverRotation;
            }