Needs help with my CCD system

Started by
0 comments, last by rgreene 20 years ago
I just got my CCD IK implementation working and I have a question about it. When I apply the algorithm to something like a character''s arm, you end up having twisting on some of the bones. While the system is technically solving the system (ie the bones are indeed pointing towards the goal point), the result looks poor. For implemeting a head-look-at solver, I ran into the same thing, however I was able to solve the sitaution by taking my output and rotating the bone about it''s axis so that it points as much "up" as it can (if that makes sense). However, with an arbitrary bone linkage, such as a characters arm, I don''t see how I can use the same kind of cheat. This is my main concern.
Advertisement
Well, I came up with a solution, but it requires a whole lot of extra math... here''s my ik algorithm:


tQuatBone *IKBones[3];
float initialBoneRotation[3];
int itteration, bone, i;
tPoint3 affectorPoint;
tPoint3 targetVector, goalVector, orthoAxis;
tPoint3 u, v, w;
tQuaternion q, inverseParent, baseRotation;
float theta;

if (!fTargetPoint)
return kFalse;

IKBones[0] = skeleton->mGetBone ("L Wrist01");
IKBones[1] = skeleton->mGetBone ("L ForeArm02");
IKBones[2] = skeleton->mGetBone ("L UpperArm02");

// get our initial rotations
for (bone = 0; bone < 3; bone++)
{
u.mSet (0, 1, 0);
v.mSet (0, 1, 0);
w.mSet (1, 0, 0);
IKBones[bone]->fParent->fOutput.mTransform (u);
IKBones[bone]->fParent->fOutput.mTransform (w);
IKBones[bone]->fOutput.mTransform (v);

w = w.mCrossProduct (u);
theta = acos (u.mDotProduct (v));

if (w.mDotProduct (v) > 0.0f)
initialBoneRotation[bone] = theta;
else
initialBoneRotation[bone] = 3.14159 + (3.14159 - theta);
}

for (itteration = 0; itteration < 10; itteration++)
{
for (bone = 0; bone < 3; bone++)
{
affectorPoint.mSet (IKBones[0]->fLength, 0, 0);
IKBones[0]->fOutput.mTransform (affectorPoint);
affectorPoint += IKBones[0]->fFinalWorldPosition;

targetVector = affectorPoint - IKBones[bone]->fFinalWorldPosition;
targetVector.mNormalize ();

goalVector = *fTargetPoint - IKBones[bone]->fFinalWorldPosition;
goalVector.mNormalize ();

orthoAxis = targetVector.mCrossProduct (goalVector);
orthoAxis.mNormalize ();

q.mCreateRotation (orthoAxis, acos (targetVector.mDotProduct (goalVector)));
inverseParent = IKBones[bone]->fParent->fOutput.mInverse ();
q = q * IKBones[bone]->fOutput;
IKBones[bone]->fBaseOutput = inverseParent * q;

for (i = bone; i >= 0; i--)
IKBones->mUpdateBone ();
}
}

// now fix up the rotations based on the initial rotations (for all bones but the lowest bone)
for (bone = 1; bone >= 0; bone--)
{
u.mSet (0, 1, 0);
v.mSet (0, 1, 0);
w.mSet (1, 0, 0);
IKBones[bone]->fParent->fOutput.mTransform (u);
IKBones[bone]->fParent->fOutput.mTransform (w);
IKBones[bone]->fOutput.mTransform (v);

w = w.mCrossProduct (u);
theta = acos (u.mDotProduct (v));

if (w.mDotProduct (v) > 0.0f)
q.mCreateRotation (1, 0, 0, -theta + initialBoneRotation[bone]);
else
q.mCreateRotation (1, 0, 0, -(3.14159 + (3.14159 - theta)) + initialBoneRotation[bone]);

IKBones[bone]->fBaseOutput *= q;

// send this down the heirarchy
for (i = bone; i >= 0; i--)
IKBones->mUpdateBone ();<br><br>}<br><br>// treat the lowest bone special<br>{<br> tPoint3 forward, up, right, idealUp;<br><br> forward.mSet (1, 0, 0);<br> up.mSet (0, 1, 0);<br> idealUp.mSet (0, 0, 1);<br><br> IKBones[2]->fOutput.mTransform (forward);<br> IKBones[2]->fOutput.mTransform (up);<br><br> idealUp -= forward * idealUp.mDotProduct (forward);<br> idealUp.mNormalize ();<br><br> right = idealUp.mCrossProduct (forward);<br><br> if (up.mDotProduct (right) > 0.0f)<br> q.mCreateRotation (1, 0, 0, acos (up.mDotProduct (idealUp)));<br><br> else<br> q.mCreateRotation (1, 0, 0, -acos (up.mDotProduct (idealUp)));<br><br> IKBones[2]->fBaseOutput *= q;<br> IKBones[2]->mUpdateBone ();<br>}<br><br><br>I spend a whole bunch of time fixing the bone-spinning… is there a bette way to do this? </i>

This topic is closed to new replies.

Advertisement