Beispiel #1
0
        public override void UpdateTracking()
        {
            if (_animator.runtimeAnimatorController == null)
            {
                for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
                {
                    GetIKSolver(ikBone).ResetIKChain();
                }
            }

            QuickIKSolver ikSolverHips = GetIKSolver(IKBone.Hips);
            QuickIKSolver ikSolverHead = GetIKSolver(IKBone.Head);

            //float chainLength = Vector3.Distance(_animator.GetBoneTransform(HumanBodyBones.Hips).position, _animator.GetBoneTransform(HumanBodyBones.Head).position);
            //Vector3 v = (ikSolverHips._targetLimb.position - ikSolverHead._targetLimb.position).normalized;
            //ikSolverHips._targetLimb.position = ikSolverHead._targetLimb.position + v * chainLength;

            //Update the IK for the body controllers
            ikSolverHips.UpdateIK();
            ikSolverHead.UpdateIK();

            GetIKSolver(IKBone.LeftHand).UpdateIK();
            GetIKSolver(IKBone.RightHand).UpdateIK();
            GetIKSolver(IKBone.LeftFoot).UpdateIK();
            GetIKSolver(IKBone.RightFoot).UpdateIK();

            //Update the IK for the fingers controllers
            UpdateIKFingers();

            //Update the IK for the face controllers
            for (IKBone ikBone = IKBone.LeftEye; ikBone <= IKBone.RightEye; ikBone++)
            {
                GetIKSolver(ikBone).UpdateIK();
            }
        }
        protected virtual void DrawIKSolver(QuickIKSolver ikSolver, bool isSolverFinger)
        {
            Handles.color = Color.magenta;
            if (ikSolver._boneUpper && ikSolver._boneMid)
            {
                Handles.DrawLine(ikSolver._boneUpper.position, ikSolver._boneMid.position);
            }
            if (ikSolver._boneMid && ikSolver._boneLimb)
            {
                Handles.DrawLine(ikSolver._boneMid.position, ikSolver._boneLimb.position);
            }

            Handles.color = Color.yellow;
            if (ikSolver._boneMid && ikSolver._targetHint)
            {
                Handles.DrawLine(ikSolver._boneMid.position, ikSolver._targetHint.position);
            }

            Handles.color = Color.cyan;
            if (ikSolver._boneUpper && ikSolver._targetLimb)
            {
                float   chainLength = Vector3.Distance(ikSolver._boneUpper.position, ikSolver._boneMid.position) + Vector3.Distance(ikSolver._boneMid.position, ikSolver._boneLimb.position);
                Vector3 v           = ikSolver._targetLimb.position - ikSolver._boneUpper.position;
                Vector3 p           = ikSolver._boneUpper.position + (v.normalized * Mathf.Min(v.magnitude, chainLength));
                Handles.DrawLine(ikSolver._boneUpper.position, p);
            }

            DrawIKTarget(ikSolver._targetLimb, Handles.CubeHandleCap, isSolverFinger);
            DrawIKTarget(ikSolver._targetHint, Handles.SphereHandleCap, isSolverFinger);
        }
        private static void SelectIKTarget(Transform ikTarget, QuickIKSolver ikSolver, IKBone ikBone)
        {
            _selectedIKTarget._transform = ikTarget;
            _selectedIKTarget._ikSolver  = ikSolver;
            _selectedIKTarget._ikBone    = ikBone;

            Selection.activeTransform = ikTarget;
        }
 protected virtual void DrawIKSolverProperties(QuickIKSolver ikSolver, string name)
 {
     ikSolver._enableIK = EditorGUILayout.Toggle(name, ikSolver._enableIK);
     if (ikSolver._enableIK)
     {
         DrawIKSolverPropertiesBase(ikSolver);
     }
 }
Beispiel #5
0
        public virtual void UpdateIKTargets()
        {
            if (Application.isPlaying)
            {
                //1) Update all the IKTargets taking into consideration its ControlType.
                for (IKBone ikBone = IKBone.Hips; ikBone < IKBone.LastBone; ikBone++)
                {
                    ControlType    cType  = GetIKControl(ikBone);
                    HumanBodyBones boneID = ToHumanBodyBones(ikBone);
                    GetIKSolver(ikBone)._enableIK = cType != ControlType.Animation;

                    if (cType == ControlType.Tracking)
                    {
                        QuickVRNode node = _vrPlayArea.GetVRNode(boneID);
                        if (node.IsTracked())
                        {
                            //Update the QuickVRNode's position
                            UpdateIKTargetPosFromUser(node, boneID);

                            //Update the QuickVRNode's rotation
                            UpdateIKTargetRotFromUser(node, boneID);

                            if (boneID == HumanBodyBones.Head)
                            {
                                QuickIKSolver ikSolverHead = GetIKSolver(IKBone.Head);
                                if (!_applyHeadPosition)
                                {
                                    ikSolverHead._weightIKPos = 0;
                                }
                                if (!_applyHeadRotation)
                                {
                                    ikSolverHead._weightIKRot = 0;
                                }
                            }
                            else if (boneID == HumanBodyBones.LeftEye || boneID == HumanBodyBones.RightEye)
                            {
                                QuickIKSolverEye ikSolver = (QuickIKSolverEye)_animator.GetComponent <QuickIKManager>().GetIKSolver(boneID);
                                ikSolver._weightBlink = ((QuickVRNodeEye)node).GetBlinkFactor();
                            }
                        }
                    }
                }

                //2) Special case. If the Hips is set to Tracking mode, we need to adjust the IKTarget position of the hips
                //in a way that the head will match the position of the camera provided by the HMD
                if (GetIKControl(IKBone.Hips) == ControlType.Tracking)
                {
                    QuickIKSolver ikSolverHips = GetIKSolver(IKBone.Hips);
                    QuickIKSolver ikSolverHead = GetIKSolver(IKBone.Head);
                    float         chainLength  = Vector3.Distance(_animator.GetBoneTransform(HumanBodyBones.Hips).position, _animator.GetBoneTransform(HumanBodyBones.Head).position);
                    Vector3       v            = (ikSolverHips._targetLimb.position - ikSolverHead._targetLimb.position).normalized;
                    ikSolverHips._targetLimb.position = ikSolverHead._targetLimb.position + v * chainLength;
                }

                UpdateVRCursors();
                _footprints.gameObject.SetActive(_useFootprints);
            }
        }
        private static void DrawIKTargets()
        {
            foreach (QuickIKManagerExecuteInEditMode ikManagerEditor in _ikManagers)
            {
                QuickIKManager ikManager = ikManagerEditor._ikManager;
                if (ikManager && ikManager.gameObject.activeInHierarchy)
                {
                    for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
                    {
                        QuickIKSolver ikSolver = ikManager.GetIKSolver(ikBone);
                        float         size;
                        if (ikBone >= IKBone.Hips && ikBone <= IKBone.RightFoot)
                        {
                            size = 0.05f;
                        }
                        else
                        {
                            size = 0.01f;
                        }

                        Handles.color = new Color(1, 0, 0, 0.5f);
                        if (Handles.Button(ikSolver._targetLimb.position, ikSolver._targetLimb.rotation, size, size, Handles.CubeHandleCap))
                        {
                            SelectIKTarget(ikSolver._targetLimb, ikSolver, ikBone);
                        }

                        if (ikSolver._targetHint)
                        {
                            Handles.color = new Color(0, 1, 0, 0.5f);
                            if (Handles.Button(ikSolver._targetHint.position, ikSolver._targetHint.rotation, size, size, Handles.SphereHandleCap))
                            {
                                SelectIKTarget(ikSolver._targetHint, ikSolver, ikBone);
                            }
                        }
                    }
                }
            }
        }
Beispiel #7
0
        public virtual void LoadTPose()
        {
            _animator.EnforceTPose();

            //Reset the IKTargets
            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                QuickIKSolver  ikSolver = GetIKSolver(ikBone);
                HumanBodyBones boneID   = ToHumanBodyBones(ikBone);
                ResetIKTarget(boneID, ikSolver._targetLimb);
                ikSolver._targetLimb.parent = GetIKTargetParent(boneID);

                if (ikSolver._targetLimb.childCount > 0)
                {
                    ikSolver._targetLimb.GetChild(0).rotation = ikSolver._boneLimb.rotation;
                }

                if (ikSolver._targetHint)
                {
                    ResetIKTarget(QuickHumanTrait.GetParentBone(boneID), ikSolver._targetHint);
                }
            }
        }
Beispiel #8
0
        protected virtual void OnDrawGizmos()
        {
            Gizmos.color = Color.cyan;
            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                QuickIKSolver ikSolver = GetIKSolver(ikBone);
                if (ikSolver._boneUpper && ikSolver._targetLimb)
                {
                    Gizmos.DrawLine(ikSolver._boneUpper.position, ikSolver._targetLimb.position);
                }
            }

            Gizmos.color = Color.magenta;
            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                QuickIKSolver ikSolver = GetIKSolver(ikBone);
                if (ikSolver._boneUpper && ikSolver._boneMid)
                {
                    Gizmos.DrawLine(ikSolver._boneUpper.position, ikSolver._boneMid.position);
                }
                if (ikSolver._boneMid && ikSolver._boneLimb)
                {
                    Gizmos.DrawLine(ikSolver._boneMid.position, ikSolver._boneLimb.position);
                }
            }

            Gizmos.color = Color.yellow;
            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                QuickIKSolver ikSolver = GetIKSolver(ikBone);
                if (ikSolver._boneMid && ikSolver._targetHint)
                {
                    Gizmos.DrawLine(ikSolver._boneMid.position, ikSolver._targetHint.position);
                }
            }
        }
Beispiel #9
0
        protected virtual void MirrorPose(QuickIKSolver srcIKSolver, QuickIKSolver dstIKSolver)
        {
            Transform srcParent = srcIKSolver._targetLimb.parent;
            Transform dstParent = dstIKSolver._targetLimb.parent;

            srcIKSolver._targetLimb.parent = dstIKSolver._targetLimb.parent = transform;

            Transform srcHintParent = null;
            Transform dstHintParent = null;

            if (srcIKSolver._targetHint && dstIKSolver._targetHint)
            {
                srcHintParent = srcIKSolver._targetHint.parent;
                dstHintParent = dstIKSolver._targetHint.parent;
                srcIKSolver._targetHint.parent = dstIKSolver._targetHint.parent = transform;
            }

            MirrorIKTarget(srcIKSolver._targetLimb, dstIKSolver._targetLimb);
            MirrorIKTarget(srcIKSolver._targetHint, dstIKSolver._targetHint);

            srcIKSolver.UpdateIK();
            dstIKSolver.UpdateIK();

            //Restore the parent for the IKTargetLimbs
            srcIKSolver._targetLimb.parent     = srcParent;
            dstIKSolver._targetLimb.parent     = dstParent;
            srcIKSolver._targetLimb.localScale = dstIKSolver._targetLimb.localScale = Vector3.one;

            //Restore the parent for the IKTargetHints
            if (srcIKSolver._targetHint && dstIKSolver._targetHint)
            {
                srcIKSolver._targetHint.parent     = srcHintParent;
                dstIKSolver._targetHint.parent     = dstHintParent;
                srcIKSolver._targetHint.localScale = dstIKSolver._targetHint.localScale = Vector3.one;
            }
        }
Beispiel #10
0
        protected virtual void CreateIKSolvers()
        {
            //Ensure that the ikSolvers are childs of _ikSolversRoot.
            foreach (QuickIKSolver ikSolver in GetComponentsInChildren <QuickIKSolver>(true))
            {
                ikSolver.transform.parent = _ikSolversRoot;
            }

            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                QuickIKSolver ikSolver = CreateIKSolver(ikBone);
                ikSolver.transform.SetSiblingIndex((int)ikBone);
            }

            for (IKBone ikBone = IKBone.LeftThumbDistal; ikBone <= IKBone.LeftLittleDistal; ikBone++)
            {
                GetIKSolver(ikBone)._targetHint.parent = _animator.GetBoneTransform(HumanBodyBones.LeftHand);
            }

            for (IKBone ikBone = IKBone.RightThumbDistal; ikBone <= IKBone.RightLittleDistal; ikBone++)
            {
                GetIKSolver(ikBone)._targetHint.parent = _animator.GetBoneTransform(HumanBodyBones.RightHand);
            }
        }
Beispiel #11
0
        protected override void UpdateIKFingers()
        {
            if (_vrPlayArea)
            {
                //if (_boneFingers == null)
                //{
                //    InitBoneFingers();
                //}

                //for (int j = 0; j < _boneFingers.Count; j+= 4)
                //{
                //    if (_boneFingers[j].Key != null)
                //    {
                //        for (int i = 0; i < 3; i++)
                //        {
                //            if ((i == 0 && (j == 0 || j == 20)) && QuickVRManager._handTrackingMode == QuickVRManager.HandTrackingMode.Controllers)
                //            {
                //                //HACK
                //                //Avoid applying the rotation to the thumb distal fingers as the results look weird. Look for a better method
                //                //of transfering the bone rotations when using the controllers.
                //                continue;
                //            }
                //            ApplyFingerRotation(_boneFingers[j + i], _boneFingers[j + i + 1]);
                //        }
                //    }
                //}


                //foreach (bool b in new bool[] { true, false })
                //{
                //    foreach (QuickHumanFingers f in QuickHumanTrait.GetHumanFingers())
                //    {
                //        List<QuickHumanBodyBones> fingerBones = QuickHumanTrait.GetBonesFromFinger(f, b);
                //        List<Quaternion> initialFingerBonesLocalRotations = new List<Quaternion>();

                //        for (int i = 0; i < fingerBones.Count - 1; i++)
                //        {
                //            QuickHumanBodyBones fBoneID = fingerBones[i];
                //            initialFingerBonesLocalRotations.Add(_animator.GetBoneTransform(fBoneID).localRotation);

                //            if (_animator.GetBoneTransform(fBoneID) && _vrPlayArea.GetVRNode(fBoneID).IsTracked())
                //            {
                //                ApplyFingerRotation(fBoneID, fingerBones[i + 1]);
                //            }
                //        }

                //        //At this point the finger is correctly aligned. Set the targets to match this.
                //        //HumanBodyBones boneID = (HumanBodyBones)fingerBones[2];
                //        //QuickIKSolver ikSolver = GetIKSolver(boneID);
                //        //Transform tBone = _animator.GetBoneTransform(boneID);

                //        //ikSolver._targetLimb.position = tBone.position;
                //        //ikSolver._targetLimb.GetChild(0).rotation = tBone.rotation;
                //        //ikSolver._targetHint.position = ikSolver._boneMid.position + (ikSolver._boneMid.position - ikSolver._boneUpper.position) + (ikSolver._boneMid.position - ikSolver._boneLimb.position);

                //        ////Restore the rotation of the bone fingers
                //        //ikSolver._boneUpper.localRotation = initialFingerBonesLocalRotations[0];
                //        //ikSolver._boneMid.localRotation = initialFingerBonesLocalRotations[1];
                //        //ikSolver._boneLimb.localRotation = initialFingerBonesLocalRotations[2];
                //    }
                //}



                //foreach (bool b in new bool[] { true, false })
                //{
                //    foreach (QuickHumanFingers f in QuickHumanTrait.GetHumanFingers())
                //    {
                //        List<QuickHumanBodyBones> fingerBones = QuickHumanTrait.GetBonesFromFinger(f, b);
                //        List<Quaternion> initialFingerBonesLocalRotations = new List<Quaternion>();

                //        for (int i = 0; i < fingerBones.Count - 1; i++)
                //        {
                //            QuickHumanBodyBones fBoneID = fingerBones[i];
                //            initialFingerBonesLocalRotations.Add(_animator.GetBoneTransform(fBoneID).localRotation);

                //            if (_animator.GetBoneTransform(fBoneID) && _vrPlayArea.GetVRNode(fBoneID).IsTracked())
                //            {
                //                ApplyFingerRotation(fBoneID, fingerBones[i + 1]);
                //            }
                //        }

                //        //At this point the finger is correctly aligned. Set the targets to match this.
                //        //HumanBodyBones boneID = (HumanBodyBones)fingerBones[2];
                //        //QuickIKSolver ikSolver = GetIKSolver(boneID);
                //        //Transform tBone = _animator.GetBoneTransform(boneID);

                //        //ikSolver._targetLimb.position = tBone.position;
                //        //ikSolver._targetLimb.GetChild(0).rotation = tBone.rotation;
                //        //ikSolver._targetHint.position = ikSolver._boneMid.position + (ikSolver._boneMid.position - ikSolver._boneUpper.position) + (ikSolver._boneMid.position - ikSolver._boneLimb.position);

                //        ////Restore the rotation of the bone fingers
                //        //ikSolver._boneUpper.localRotation = initialFingerBonesLocalRotations[0];
                //        //ikSolver._boneMid.localRotation = initialFingerBonesLocalRotations[1];
                //        //ikSolver._boneLimb.localRotation = initialFingerBonesLocalRotations[2];
                //    }
                //}

                //foreach (bool isLeft in new bool[] { true, false })
                //{
                //    foreach (QuickHumanFingers f in QuickHumanTrait.GetHumanFingers())
                //    {
                //        float fLength = _vrPlayArea.GetFingerLength(f, isLeft);
                //        if (fLength > 0)
                //        {
                //            List<QuickHumanBodyBones> fingerBones = QuickHumanTrait.GetBonesFromFinger(f, isLeft);
                //            QuickVRNode n0 = _vrPlayArea.GetVRNode(fingerBones[0]);
                //            QuickVRNode n1 = _vrPlayArea.GetVRNode(fingerBones[1]);
                //            QuickVRNode n2 = _vrPlayArea.GetVRNode(fingerBones[2]);

                //            QuickIKSolver ikSolver = GetIKSolver((HumanBodyBones)fingerBones[2]);

                //            if (n0.IsTracked() && n2.IsTracked())
                //            {
                //                float sf = ikSolver.GetChainLength() / fLength;
                //                Vector3 v = sf * (n2.transform.position - n0.transform.position);

                //                ikSolver._targetLimb.position = ikSolver._boneUpper.position + v;
                //                ikSolver._targetHint.position = ikSolver._boneMid.position + (n1.transform.position - n0.transform.position) + (n1.transform.position - n2.transform.position);
                //            }
                //        }
                //    }
                //}

                foreach (bool isLeft in new bool[] { true, false })
                {
                    foreach (QuickHumanFingers f in QuickHumanTrait.GetHumanFingers())
                    {
                        List <QuickHumanBodyBones> fingerBones = QuickHumanTrait.GetBonesFromFinger(f, isLeft);
                        QuickVRNode n0 = _vrPlayArea.GetVRNode(fingerBones[0]);
                        QuickVRNode n1 = _vrPlayArea.GetVRNode(fingerBones[1]);
                        QuickVRNode n2 = _vrPlayArea.GetVRNode(fingerBones[2]);

                        if (n0.IsTracked() && n1.IsTracked() && n2.IsTracked())
                        {
                            QuickIKSolver ikSolver = GetIKSolver((HumanBodyBones)fingerBones[2]);

                            Vector3 v = (n1.transform.position - n0.transform.position).normalized;
                            Vector3 w = (n2.transform.position - n1.transform.position).normalized;

                            ikSolver._targetLimb.position = ikSolver._boneUpper.position + v * ikSolver.GetUpperLength() + w * ikSolver.GetMidLength();
                            ikSolver._targetLimb.rotation = n2.transform.rotation;
                            ikSolver._targetHint.position = ikSolver._boneMid.position + n1.transform.up * DEFAULT_TARGET_HINT_FINGER_DISTANCE;
                            ikSolver._targetHint.rotation = n1.transform.rotation;
                            //ikSolver._targetHint.position = ikSolver._boneMid.position + (n1.transform.position - n0.transform.position) + (n1.transform.position - n2.transform.position);
                        }
                    }
                }
            }

            base.UpdateIKFingers();
        }
Beispiel #12
0
        public virtual void LoadAnimPose()
        {
            //Restore the TPose
            LoadTPose();

            _ikTargetsRoot.ResetTransformation();

            //Temporally set the parent of each ikTargetLimb to be the boneLimb. This way, the
            //target is automatically moved to the bone position when the animation is applied.
            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                QuickIKSolver ikSolver = GetIKSolver(ikBone);
                ikSolver._targetLimb.parent = ikSolver._boneLimb;
            }

            //If we have an animatorcontroller defined, the targets are moved at the position of the
            //initial frame of the current animation in such controller.
            if (_animator.runtimeAnimatorController)
            {
                Quaternion jawLocalRot = Quaternion.identity;
                Transform  tJaw        = _animator.GetBoneTransform(HumanBodyBones.Jaw);
                if (tJaw)
                {
                    jawLocalRot = tJaw.localRotation;
                }

                _animator.Update(0);

                //Force the mouth to be closed
                if (tJaw)
                {
                    tJaw.localRotation = jawLocalRot;
                }
            }

            //Check the rotation of the parents of the targets of the finger bones
            for (HumanBodyBones boneID = HumanBodyBones.LeftThumbDistal; boneID <= HumanBodyBones.LeftLittleDistal; boneID += 3)
            {
                Transform t = GetIKTargetParent(boneID);
                t.position = _animator.GetBoneTransform(boneID - 2).position;
                t.LookAt(_animator.GetBoneTransform(boneID - 1), transform.up);
            }
            for (HumanBodyBones boneID = HumanBodyBones.RightThumbDistal; boneID <= HumanBodyBones.RightLittleDistal; boneID += 3)
            {
                Transform t = GetIKTargetParent(boneID);
                t.position = _animator.GetBoneTransform(boneID - 2).position;
                t.LookAt(_animator.GetBoneTransform(boneID - 1), transform.up);
            }

            //Restore the ikTargetLimb real parent.
            for (IKBone ikBone = 0; ikBone < IKBone.LastBone; ikBone++)
            {
                HumanBodyBones boneID   = ToHumanBodyBones(ikBone);
                QuickIKSolver  ikSolver = GetIKSolver(ikBone);
                ikSolver._targetLimb.parent     = GetIKTargetParent(boneID);
                ikSolver._targetLimb.localScale = Vector3.one;
            }

            //Recalculate the ikTargetHint position of the arms and legs
            for (IKBone ikBone = IKBone.LeftHand; ikBone <= IKBone.RightFoot; ikBone++)
            {
                QuickIKSolver ikSolver = GetIKSolver(ikBone);
                if (ikSolver._targetHint)
                {
                    Vector3 u = (ikSolver._boneMid.position - ikSolver._boneLimb.position).normalized;
                    Vector3 v = (ikSolver._boneMid.position - ikSolver._boneUpper.position).normalized;

                    if (Vector3.Angle(u, v) < 175)
                    {
                        ikSolver._targetHint.position = ikSolver._boneMid.position + (u + v).normalized * DEFAULT_TARGET_HINT_DISTANCE;
                    }
                }
            }
        }
        protected virtual void DrawIKSolverPropertiesBase(QuickIKSolver ikSolver)
        {
            EditorGUILayout.BeginHorizontal();
            GUI.enabled = false;
            EditorGUILayout.ObjectField("IKTarget", ikSolver._targetLimb, typeof(Transform), true);
            GUI.enabled = true;
            if (DrawButton("Reset", GUILayout.Width(52)))
            {
                ikSolver.LoadPose();
                //_target.ResetIKTarget(boneID);
            }
            EditorGUILayout.EndHorizontal();

            ikSolver._weightIKPos = EditorGUILayout.Slider("IKPosWeight", ikSolver._weightIKPos, 0, 1);
            ikSolver._weightIKRot = EditorGUILayout.Slider("IKRotWeight", ikSolver._weightIKRot, 0, 1);

            if (ikSolver.GetType() == typeof(QuickIKSolverEye))
            {
                QuickIKSolverEye ikSolverEye = (QuickIKSolverEye)ikSolver;
                if (ikSolverEye._showAngleLimits = FoldoutBolt(ikSolverEye._showAngleLimits, "Angle Limits"))
                {
                    EditorGUI.indentLevel++;
                    ikSolverEye._angleLimitLeft  = EditorGUILayout.FloatField("Left", ikSolverEye._angleLimitLeft);
                    ikSolverEye._angleLimitRight = EditorGUILayout.FloatField("Right", ikSolverEye._angleLimitRight);
                    ikSolverEye._angleLimitDown  = EditorGUILayout.FloatField("Down", ikSolverEye._angleLimitDown);
                    ikSolverEye._angleLimitUp    = EditorGUILayout.FloatField("Up", ikSolverEye._angleLimitUp);
                    EditorGUI.indentLevel--;
                }
                ikSolverEye._leftRight = EditorGUILayout.Slider("Left - Right", ikSolverEye._leftRight, ikSolverEye._angleLimitLeft, ikSolverEye._angleLimitRight);
                ikSolverEye._downUp    = EditorGUILayout.Slider("Down - Up", ikSolverEye._downUp, ikSolverEye._angleLimitDown, ikSolverEye._angleLimitUp);

                if (ikSolverEye._showBlinking = FoldoutBolt(ikSolverEye._showBlinking, "Blinking"))
                {
                    EditorGUI.indentLevel++;
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.Space();
                    if (DrawButton("Add"))
                    {
                        ikSolverEye._blinking.Add(new QuickIKSolverEye.BlinkData());
                    }
                    if (DrawButton("Remove Last"))
                    {
                        if (ikSolverEye._blinking.Count > 0)
                        {
                            ikSolverEye._blinking.RemoveAt(ikSolverEye._blinking.Count - 1);
                        }
                    }
                    EditorGUILayout.EndHorizontal();

                    EditorGUILayout.Space();
                    for (int i = 0; i < ikSolverEye._blinking.Count; i++)
                    {
                        QuickIKSolverEye.BlinkData bData = ikSolverEye._blinking[i];

                        if (bData._showInInspector = FoldoutBolt(bData._showInInspector, "Element " + i.ToString()))
                        {
                            EditorGUI.indentLevel++;

                            bData._renderer = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Mesh", bData._renderer, typeof(SkinnedMeshRenderer), true);
                            if (bData._renderer && bData._renderer.sharedMesh)
                            {
                                Mesh          mesh   = bData._renderer.sharedMesh;
                                List <string> bNames = new List <string>();
                                bNames.Add("None");
                                for (int j = 0; j < mesh.blendShapeCount; j++)
                                {
                                    bNames.Add(mesh.GetBlendShapeName(j));
                                }

                                bData._blendshapeID = EditorGUILayout.Popup("Blendshape ID", bData._blendshapeID + 1, bNames.ToArray()) - 1;
                            }

                            EditorGUI.indentLevel--;
                        }
                    }

                    EditorGUILayout.Space();

                    EditorGUI.indentLevel--;
                }

                ikSolverEye._weightBlink = EditorGUILayout.Slider("BlinkWeight", ikSolverEye._weightBlink, 0, 1);
            }
        }