Permalink
Browse files

Support LimbIK(Foot/Wrist) Rotation Weight & Limit.

  • Loading branch information...
1 parent 9c2a0f6 commit a879088c0342781ea35adf1f9d31ddb5412a0ae2 Nora committed Mar 24, 2016
Showing with 157 additions and 10 deletions.
  1. +10 −0 Editor/FullBodyIKInspector.cs
  2. +30 −1 Scripts/FullBodyIK.cs
  3. +117 −9 Scripts/FullBodyIK/LimbIK.cs
@@ -325,6 +325,16 @@ void _OnInspectorGUI_Basic()
EditorUtil.GUI.Slider( "Elbow Front Inner Limit Angle", ref settings.limbIK.elbowFrontInnerLimitAngle, 0.0f, 90.0f );
EditorUtil.GUI.Slider( "Elbow Back Inner Limit Angle", ref settings.limbIK.elbowBackInnerLimitAngle, 0.0f, 90.0f );
+ GUILayout.Label( "Wrist limit", _guiStyle_section );
+ EditorUtil.GUI.Field( "Wrist Limit Enabled", ref settings.limbIK.wristLimitEnabled );
+ EditorUtil.GUI.Slider( "Wrist Limit Angle", ref settings.limbIK.wristLimitAngle, 0.0f, 180.0f );
+
+ GUILayout.Label( "Foot limit", _guiStyle_section );
+ EditorUtil.GUI.Slider( "Foot Limit Yaw", ref settings.limbIK.footLimitYaw, 0.0f, 89.99f );
+ EditorUtil.GUI.Slider( "Foot Limit Pitch Up", ref settings.limbIK.footLimitPitchUp, 0.0f, 89.99f );
+ EditorUtil.GUI.Slider( "Foot Limit Pitch Down", ref settings.limbIK.footLimitPitchDown, 0.0f, 89.99f );
+ EditorUtil.GUI.Slider( "Foot Limit Roll", ref settings.limbIK.footLimitRoll, 0.0f, 89.99f );
+
_Header( "HeadIK" );
EditorUtil.GUI.Slider( "Neck Limit Pitch Up", ref settings.headIK.neckLimitPitchUp, 0.0f, 89.99f );
EditorUtil.GUI.Slider( "Neck Limit Pitch Down", ref settings.headIK.neckLimitPitchDown, 0.0f, 89.99f );
View
@@ -267,6 +267,17 @@ public class LimbIK
// Arm elbow limit angles.(Automatic / Manual)
public float elbowFrontInnerLimitAngle = 5.0f;
public float elbowBackInnerLimitAngle = 0.0f;
+
+ // Wrist limit
+ public bool wristLimitEnabled = true;
+ public float wristLimitAngle = 90.0f;
+
+ // Foot limit
+ public bool footLimitEnabled = true;
+ public float footLimitYaw = 45.0f;
+ public float footLimitPitchUp = 45.0f;
+ public float footLimitPitchDown = 60.0f;
+ public float footLimitRoll = 45.0f;
}
[System.Serializable]
@@ -278,7 +289,7 @@ public class HeadIK
public float eyesToNeckPitchRate = 0.4f;
- public float headLimitYaw = 80.0f;
+ public float headLimitYaw = 60.0f;
public float headLimitPitchUp = 15.0f;
public float headLimitPitchDown = 15.0f;
public float headLimitRoll = 5.0f;
@@ -514,6 +525,11 @@ public class LimbIK
public CachedDegreesToSin elbowFrontInnerLimitTheta = CachedDegreesToSin.zero;
public CachedDegreesToSin elbowBackInnerLimitTheta = CachedDegreesToSin.zero;
+ public CachedDegreesToSin footLimitYawTheta = CachedDegreesToSin.zero;
+ public CachedDegreesToSin footLimitPitchUpTheta = CachedDegreesToSin.zero;
+ public CachedDegreesToSin footLimitPitchDownTheta = CachedDegreesToSin.zero;
+ public CachedDegreesToSin footLimitRollTheta = CachedDegreesToSin.zero;
+
public void Update( Settings.LimbIK settingsLimbIK )
{
// Optimize: Reduce C# fuction call.
@@ -545,6 +561,19 @@ public void Update( Settings.LimbIK settingsLimbIK )
if( elbowBackInnerLimitTheta._degrees != settingsLimbIK.elbowBackInnerLimitAngle ) {
elbowBackInnerLimitTheta._Reset( settingsLimbIK.elbowBackInnerLimitAngle );
}
+
+ if( footLimitYawTheta._degrees != settingsLimbIK.footLimitYaw ) {
+ footLimitYawTheta._Reset( settingsLimbIK.footLimitYaw );
+ }
+ if( footLimitPitchUpTheta._degrees != settingsLimbIK.footLimitPitchUp ) {
+ footLimitPitchUpTheta._Reset( settingsLimbIK.footLimitPitchUp );
+ }
+ if( footLimitPitchDownTheta._degrees != settingsLimbIK.footLimitPitchDown ) {
+ footLimitPitchDownTheta._Reset( settingsLimbIK.footLimitPitchDown );
+ }
+ if( footLimitRollTheta._degrees != settingsLimbIK.footLimitRoll ) {
+ footLimitRollTheta._Reset( settingsLimbIK.footLimitRoll );
+ }
}
}
@@ -105,7 +105,7 @@ public LimbIK( FullBodyIK fullBodyIK, LimbIKLocation limbIKLocation )
void _Prepare( FullBodyIK fullBodyIK )
{
- _endEffectorToWorldRotation = Inverse( _endEffector.defaultRotation ) * _endBone._defaultRotation;
+ SAFBIKQuatMultInv0( out _endEffectorToWorldRotation, ref _endEffector._defaultRotation, ref _endBone._defaultRotation );
// for _defaultCosTheta, _defaultSinTheta
_beginToBendingLength = _bendingBone._defaultLocalLength.length;
@@ -1026,8 +1026,20 @@ public bool Solve()
_arm_isSolvedLimbIK = false;
+ Quaternion bendingBonePrevRotation = Quaternion.identity;
+ Quaternion endBonePrevRotation = Quaternion.identity;
+ if( !_internalValues.resetTransforms ) {
+ float endRotationWeight = _endEffector.rotationEnabled ? _endEffector.rotationWeight : 0.0f;
+ if( endRotationWeight > IKEpsilon ) {
+ if( endRotationWeight < 1.0f - IKEpsilon ) {
+ bendingBonePrevRotation = _bendingBone.worldRotation;
+ endBonePrevRotation = _endBone.worldRotation;
+ }
+ }
+ }
+
bool r = _SolveInternal();
- r |= _SolveEndRotation();
+ r |= _SolveEndRotation( r, ref bendingBonePrevRotation, ref endBonePrevRotation );
r |= _RollInternal();
return r;
@@ -1132,17 +1144,113 @@ public bool _SolveInternal()
return true;
}
- bool _SolveEndRotation()
+ bool _SolveEndRotation( bool isSolved, ref Quaternion bendingBonePrevRotation, ref Quaternion endBonePrevRotation )
{
- if( !_endEffector.rotationEnabled ) {
- return false;
+ float endRotationWeight = _endEffector.rotationEnabled ? _endEffector.rotationWeight : 0.0f;
+ if( endRotationWeight > IKEpsilon ) {
+ Quaternion endEffectorWorldRotation = _endEffector.worldRotation;
+ Quaternion toRotation;
+ SAFBIKQuatMult( out toRotation, ref endEffectorWorldRotation, ref _endEffectorToWorldRotation );
+
+ if( endRotationWeight < 1.0f - IKEpsilon ) {
+ Quaternion fromRotation;
+ if( _internalValues.resetTransforms ) {
+ Quaternion bendingBoneWorldRotation = _bendingBone.worldRotation;
+ SAFBIKQuatMult3( out fromRotation, ref bendingBoneWorldRotation, ref _bendingBone._worldToBaseRotation, ref _endBone._baseToWorldRotation );
+ } else {
+ if( isSolved ) {
+ Quaternion bendingBoneWorldRotation = _bendingBone.worldRotation;
+ SAFBIKQuatMultNorm3Inv1( out fromRotation, ref bendingBoneWorldRotation, ref bendingBonePrevRotation, ref endBonePrevRotation );
+ } else {
+ fromRotation = endBonePrevRotation; // This is able to use endBonePrevRotation directly.
+ }
+ }
+ _endBone.worldRotation = Quaternion.Lerp( fromRotation, toRotation, endRotationWeight );
+ } else {
+ _endBone.worldRotation = toRotation;
+ }
+
+ _EndRotationLimit();
+ return true;
+ } else {
+ if( _internalValues.resetTransforms ) {
+ Quaternion fromRotation, bendingBoneWorldRotation = _bendingBone.worldRotation;
+ SAFBIKQuatMult3( out fromRotation, ref bendingBoneWorldRotation, ref _bendingBone._worldToBaseRotation, ref _endBone._baseToWorldRotation );
+ _endBone.worldRotation = fromRotation;
+ return true;
+ }
}
- // todo: rotationWeight
+ return false;
+ }
- var r = _endEffector.worldRotation * _endEffectorToWorldRotation;
- _endBone.worldRotation = r;
- return true;
+ void _EndRotationLimit()
+ {
+ if( _limbIKType == LimbIKType.Arm ) {
+ if( !_settings.limbIK.wristLimitEnabled ) {
+ return;
+ }
+ } else if( _limbIKType == LimbIKType.Leg ) {
+ if( !_settings.limbIK.footLimitEnabled ) {
+ return;
+ }
+ }
+
+ // Rotation Limit.
+ Quaternion tempRotation, endRotation, bendingRotation, localRotation;
+ tempRotation = _endBone.worldRotation;
+ SAFBIKQuatMult( out endRotation, ref tempRotation, ref _endBone._worldToBaseRotation );
+ tempRotation = _bendingBone.worldRotation;
+ SAFBIKQuatMult( out bendingRotation, ref tempRotation, ref _bendingBone._worldToBaseRotation );
+ SAFBIKQuatMultInv0( out localRotation, ref bendingRotation, ref endRotation );
+
+ if( _limbIKType == LimbIKType.Arm ) {
+ bool isLimited = false;
+ float limitAngle = _settings.limbIK.wristLimitAngle;
+
+ float angle;
+ Vector3 axis;
+ localRotation.ToAngleAxis( out angle, out axis );
+ if( angle < -limitAngle ) {
+ angle = -limitAngle;
+ isLimited = true;
+ } else if( angle > limitAngle ) {
+ angle = limitAngle;
+ isLimited = true;
+ }
+
+ if( isLimited ) {
+ localRotation = Quaternion.AngleAxis( angle, axis );
+ SAFBIKQuatMultNorm3( out endRotation, ref bendingRotation, ref localRotation, ref _endBone._baseToWorldRotation );
+ _endBone.worldRotation = endRotation;
+ }
+ } else if( _limbIKType == LimbIKType.Leg ) {
+ Matrix3x3 localBasis;
+ SAFBIKMatSetRot( out localBasis, ref localRotation );
+
+ Vector3 localDirY = localBasis.column1;
+ Vector3 localDirZ = localBasis.column2;
+
+ bool isLimited = false;
+ isLimited |= _LimitXZ_Square( ref localDirY,
+ _internalValues.limbIK.footLimitRollTheta.sin,
+ _internalValues.limbIK.footLimitRollTheta.sin,
+ _internalValues.limbIK.footLimitPitchUpTheta.sin,
+ _internalValues.limbIK.footLimitPitchDownTheta.sin );
+ isLimited |= _LimitXY_Square( ref localDirZ,
+ _internalValues.limbIK.footLimitYawTheta.sin,
+ _internalValues.limbIK.footLimitYawTheta.sin,
+ _internalValues.limbIK.footLimitPitchDownTheta.sin,
+ _internalValues.limbIK.footLimitPitchUpTheta.sin );
+
+ if( isLimited ) {
+ if( SAFBIKComputeBasisFromYZLockZ( out localBasis, ref localDirY, ref localDirZ ) ) {
+ SAFBIKMatGetRot( out localRotation, ref localBasis );
+ SAFBIKQuatMultNorm3( out endRotation, ref bendingRotation, ref localRotation, ref _endBone._baseToWorldRotation );
+ _endBone.worldRotation = endRotation;
+ }
+ }
+ }
}
bool _RollInternal()

0 comments on commit a879088

Please sign in to comment.