Any examples out there of how to do an IK solver for leg movement?
IK solvers have two problems: Either there are infinite solutions or none.
But because our body is made for walking / running we get away with a very simple 'solver' for this purpose.
Below is the simplified solution i use (a bit hard to read, but it shows necessary complexity).
Most probably you do not need to define and care for joint limits as long as your units do only basic movement,
and it should not be necessary to read papers on IK solvers, because they focus on harder problems.
How it works:
Take a line from hip to target and stretch leg along that line.
If target distance is shorter than leg length, bend knee accordingly.
Set foot orientation to target.
So you see it's very easy.
Complexity arises because bones and joints each have their own spaces we need to tackle and so forth.
You may wanna start with a 2D case to feel safe.
After the IK solver is working, you only need to care for target foot positions.
For now i keep the swivel angle to a constant value keeping the knees pointing forwards.
For turning and walking smaller circles it would look better to modify it a bit.
void Ragdoll::IKSolveLegChainEffSimplyfied (Chain &chain, BodyInfo *bodyInfos,
float swivelAngle, // Sit on a chair and keep your foot on the ground. You can rotate your knee arond the line from hip joint to ankle joint without moving foot or pelvis - that's why we need to provide swivelAngle to avoid infinite solutions
sVec3 tPos, // target ankle joint position
sQuat *tOrn) // target foot orientation
{
float limitShrink = 0.0; // tolerance to keep from max joint limits
Target &tUpper = chain.target[Chain::UPPER]; // bone upper leg
Target &tLower = chain.target[Chain::LOWER]; // bone lower leg
Target &tEff = chain.target[Chain::EFF]; // bone foot
RagdollJoint &jUpper = *tUpper.joint; // joint pelvis - upper leg (hip)
RagdollJoint &jLower = *tLower.joint; // joint upper leg - lower leg (knee)
RagdollJoint &jEff = *tEff.joint; // joint lower leg - foot (ankle)
sQuat qBase, qUpper, qLower, qEff;
qBase = bodyInfos[jUpper.index[1]].orn; // pelvis orientation in world space
qUpper = bodyInfos[jUpper.index[0]].orn; // upper leg orientation in world space
qLower = bodyInfos[jLower.index[0]].orn; // lower leg orientation in world space
qEff = bodyInfos[jEff.index[0]].orn; // foot orientation in world space
float lUpper = tUpper.boneLength;
float lLower = tLower.boneLength;
float lSum = lUpper + lLower;
float curTwist, curSwing;
sVec3 axis; float angle;
sQuat rot;
sQuat oB, oU, oL; // new bone orientations
sVec3 pB, pU, pL; // new bone positions
oB = qBase;
pU = tUpper.cPos;
// measure hip - target dist
sVec3 dUT = tPos - pU;
float lenUT = dUT.Length();
if (lenUT < FP_EPSILON) return;
dUT /= lenUT;
sQuat oBunmod;
sQuat oUunmod;
sVec3 dL, dU;
{
// upper...
float angU = 0;
if (lenUT < lSum) // bend knee if target dist is sharter than leg length
{
angU = acos (min(1.0, max(-1.0, (lenUT*lenUT + lUpper*lUpper - lLower*lLower) / (2.0 * lenUT * lUpper))));
}
oU = oB * jUpper.localRot1 * jUpper.localRot0.Inversed();
rot.FromVecToVecR (dUT, oU.XAxis()); // X axis = axis of rigid body capsules per bone
oU = rot * oU;
oU *= sQuat::rotationX (-swivelAngle);
oU *= sQuat::rotationZ (angU);
sQuat oUn;
jUpper.CalcTwistSwingLimitTarget (oUn, curTwist, curSwing, oU, oB, limitShrink); // makes oUn the relative rotation from oU to oB. Ensure this does not violate joint limits (never happens when walking or running)
oU = oUn;
oU.Normalize(); // oU finished
dU = oU.XAxis();
oUunmod = oU;
oU *= chain.upperOffset;
jUpper.CalcTwistSwingLimitTarget (oU, curTwist, curSwing, oU, oB, limitShrink);
oU.Normalize();
dU = oU.XAxis();
// lower...
pL = pU + dU * tUpper.boneLength;
sVec3 lTar = oU.Inversed().Rotate (sVec3(tPos - pL));
sVec3 lTarQ (lTar[0], lTar[1], 0);
float len = lTarQ.SqL();
if (len > FP_EPSILON)
{
len = sqrt(len);
lTarQ /= len;
angle = atan2 (lTarQ[1], lTarQ[0]); // knee angle
}
else angle = 0;
if (angle > jLower.twistLimitAngleP - limitShrink) angle = jLower.twistLimitAngleP - limitShrink; // knee joint limits
if (angle < jLower.twistLimitAngleN + limitShrink) angle = jLower.twistLimitAngleN + limitShrink;
oL = oU * sQuat::rotationZ (angle);
oL.Normalize(); // lB finished
}
// apply upper & lower leg (set joint targets for simulation)
{
sQuat oU1 = oB * jUpper.localRot1;
sQuat oU0 = oU * jUpper.localRot0;
sQuat rotUpper = oU1.Inversed() * oU0;
rotUpper.SafeNormalize();
jUpper.targetOrn = rotUpper;
sQuat oL1 = oU * jLower.localRot1;
sQuat oL0 = oL * jLower.localRot0;
sQuat rotLower = oL1.Inversed() * oL0;
rotLower.SafeNormalize();
jLower.targetOrn = rotLower;
}
// foot (just set to target and ensure joint limits)...
if (tOrn)
{
//float effSpeed = 1;
sQuat eB = *tOrn;
float limitTwist = jEff.CalcTwistSwingLimitTarget (eB, curTwist, curSwing, eB, oL, limitShrink); // joint limits
sQuat oE1 = oL * jEff.localRot1;
sQuat oE0 = eB * jEff.localRot0;
sQuat rotEff = oE1.Inversed() * oE0;
rotEff.SafeNormalize();
jEff.targetOrn = rotEff;
}
}
struct Target // just some joint data
{
sVec3 cPos; // position of the joint (not the bone)
float boneLength;
RagdollJoint* joint;
}