CCD IK is Jerky and sometimes spazzy

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
Old 08 August 2012   #1
CCD IK is Jerky and sometimes spazzy

Hello. I am writing an IK solver for a 3D rendering engine that I am designing. My solver works correctly for the most part, but sometimes it will make the animation look jerky or completely spazz out (this usually happens when the joints have to bend over each other more).

This is my solver function:

D3DXMATRIX effTrans, destTrans, curBoneTrans;
	D3DXVECTOR3 effPos, destPos, curBonePos;

	D3DXVECTOR3 curBoneToEff, curBoneToDest;
	D3DXVECTOR3 cross, refAxis;

	D3DXVECTOR4 boneToEff, boneToDest;

	D3DXQUATERNION rot, origEffRot, tmpRotQ;

	D3DXMATRIX tmpRot, curBoneTransInv;

	destTrans = _destBone->getCombinedTrans();
	destPos = D3DXVECTOR3(destTrans(3,0), destTrans(3,1), destTrans(3,2));

	origEffRot = _effBone->getRot();

	double dot, angle, distance2;
	float x;

	for (int i=0;i<_numBone;i++)
	{
		_boneList[i]->UpdateIK()
	}
	_effBone->UpdateIK();

	for (int i=0;i<_iteration;i++)
	{
		for (int b=0;b<_numBone;b++)
		{
			//get end effector position
			effTrans = _effBone->getCombinedTrans();
			effPos = D3DXVECTOR3(effTrans(3,0), effTrans(3,1), effTrans(3,2));

			//get current bone position
			curBoneTrans = _boneList[b]->getCombinedTrans();
			curBonePos = D3DXVECTOR3(curBoneTrans(3,0), curBoneTrans(3,1), curBoneTrans(3,2));

			//create inverse of bone position
			D3DXMatrixInverse(&curBoneTransInv, NULL, &curBoneTrans);


			//create point-to-point vector from bone inverse matrix
			D3DXVec3Transform(&boneToEff, &effPos, &curBoneTransInv);
			D3DXVec3Transform(&boneToDest, &destPos, &curBoneTransInv);

			//transfer the vectors over
			curBoneToEff = (D3DXVECTOR3)boneToEff;
			curBoneToDest = (D3DXVECTOR3)boneToDest;
			
			
			//get distance between direction vectors
			distance2 = D3DXVec3Length(&(curBoneToDest-curBoneToEff));
			distance2 *= distance2;
			if (distance2 < IK_MIN_DIST)
			{
				//stop if end effector is close enough
				i = _iteration;
				break;
			}

			D3DXVec3Normalize(&curBoneToEff, &curBoneToEff);
			D3DXVec3Normalize(&curBoneToDest, &curBoneToDest);

			//get dot product and angle between the 2 vectors
			dot = D3DXVec3Dot(&curBoneToDest, &curBoneToEff);
			if (dot > 1.0f)
			{
				continue;
			}
			angle = acos(dot);
			if (fabs(angle) < IK_MIN_ANGLE)
			{
				continue;
			}
			if (angle < -_angleConstraint)
			{
				angle = -_angleConstraint;
			}
			else if (angle > _angleConstraint)
			{
				angle = _angleConstraint;
			}

			//get the cross product (rotation axis)
			D3DXVec3Cross(&cross, &curBoneToEff, &curBoneToDest);

			//get length of axis
			distance2 = D3DXVec3Length(&cross);
			distance2 *= distance2;
			if (distance2 < IK_MIN_AXIS && i > 0)
			{
				//axis is too small (direction dest to target is too close) skip to next bone
				continue;
			}

			D3DXVec3Normalize(&cross, &cross);
			//build rotation from axis and angle
			D3DXQuaternionRotationAxis(&rot, &cross, (float)angle);

			//check is current bone is limited to x
			if (_boneList[b]->getIsLimitX())
			{
				quatToEuler(rot, NULL, NULL, &x);

				if (x < -PI)
				{
					x = -PI;
				}
				if (-IK_MIN_ROT_SUM < x)
				{
					x = -IK_MIN_ROT_SUM;
				}					

				D3DXMatrixRotationX(&tmpRot, x);
				D3DXQuaternionRotationMatrix(&rot, &tmpRot);
			}
				
				D3DXQuaternionNormalize(&rot, &rot);

				//apply rotation to bone
				//combine with previous rotation for multiple iterations
				tmpRotQ = _boneList[b]->getRot();
				tmpRotQ *= rot;
				D3DXQuaternionNormalize(&rot, &rot);
				_boneList[b]->setRot(tmpRotQ);

			for (int j=b;j>=0;j--)
			{
				_boneList[j]->UpdateFromIK();
			}
			_effBone->UpdateFromIK();
		}
	}
	_effBone->setRot(origEffRot);
	_effBone->UpdateFromIK();


The UpdateIK() function uses this algorithm to update the bone's combined transform:

D3DXMatrixRotationQuaternion(&combTrans, &combRot);
	combTrans(3,0) = translate(3,0) + offset.x;
	combTrans(3,1) = translate(3,1) + offset.y;
	combTrans(3,2) = translate(3,2) + offset.z;

	if (_parent)
		combTrans *= _parent->getCombinedTrans();



Here is a video example of how the IK will be jerky or spazz out. Look at the left leg. The video is the model's bone structure.

http://youtu.be/w56-Kv0akNo
 
Old 09 September 2012   #2
Honestly, I haven't read the code.
But looking at the video, and assuming you're using the pretty standard law of cosine 3 point IK for 2 joints chains, it simply looks like you have a preferred plane implementation with no directionality.

Given two points, two lengths and a plane for them live on, there's always two solutions to law of cos or practically any other stateless (no temporal dimension) IK solver. The knee might go one way or the other basically.

Make sure a direction is available for the IK to solve the knee consistently, and that direction doesn't become ambiguous (never aligned to the solver's plane).
__________________
Come, Join the Cult http://www.cultofrig.com - Rigging from First Principles
 
Old 09 September 2012   #3
Thread automatically closed

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
__________________
CGTalk Policy/Legalities
Note that as CGTalk Members, you agree to the terms and conditions of using this website.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 08:13 PM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.