/////////////////////////////////////////////////////////////////////////////// // Procedure: CheckDOFRestrictions // Purpose: Make sure link is within valid DOF // Arguments: Link /////////////////////////////////////////////////////////////////////////////// void COGLView::CheckDOFRestrictions(t_Bone *link) { /// Local Variables /////////////////////////////////////////////////////////// tVector euler; // PLACE TO STORE EULER ANGLES /////////////////////////////////////////////////////////////////////////////// // FIRST STEP IS TO CONVERT LINK QUATERNION BACK TO EULER ANGLES QuatToEuler(&link->quat, &euler); // CHECK THE DOF SETTINGS if (euler.x > (float)link->max_rx) euler.x = (float)link->max_rx; if (euler.x < (float)link->min_rx) euler.x = (float)link->min_rx; if (euler.y > (float)link->max_ry) euler.y = (float)link->max_ry; if (euler.y < (float)link->min_ry) euler.y = (float)link->min_ry; if (euler.z > (float)link->max_rz) euler.z = (float)link->max_rz; if (euler.z < (float)link->min_rz) euler.z = (float)link->min_rz; // BACK TO QUATERNION EulerToQuaternion(&euler, &link->quat); } /////////////////////////////////////////////////////////////////////////////// // Procedure: ComputeCCDLink // Purpose: Compute an IK Solution to an end effector position in 3D // Arguments: End Target (x,y,z) // Returns: TRUE if a solution exists, FALSE if the position isn't in reach /////////////////////////////////////////////////////////////////////////////// BOOL COGLView::ComputeCCDLink(tVector endPos) { /// Local Variables /////////////////////////////////////////////////////////// tVector rootPos,curEnd,desiredEnd,targetVector,curVector,crossResult; double cosAngle,turnAngle,turnDeg; int link,tries; tQuaternion aquat; /////////////////////////////////////////////////////////////////////////////// // START AT THE LAST LINK IN THE CHAIN link = EFFECTOR_POS - 1; tries = 0; // LOOP COUNTER SO I KNOW WHEN TO QUIT do { // THE COORDS OF THE X,Y,Z POSITION OF THE ROOT OF THIS BONE IS IN THE MATRIX // TRANSLATION PART WHICH IS IN THE 12,13,14 POSITION OF THE MATRIX rootPos.x = m_Link[link].matrix.m[12]; rootPos.y = m_Link[link].matrix.m[13]; rootPos.z = m_Link[link].matrix.m[14]; // POSITION OF THE END EFFECTOR curEnd.x = m_Link[EFFECTOR_POS].matrix.m[12]; curEnd.y = m_Link[EFFECTOR_POS].matrix.m[13]; curEnd.z = m_Link[EFFECTOR_POS].matrix.m[14]; // DESIRED END EFFECTOR POSITION desiredEnd.x = (float)endPos.x; desiredEnd.y = (float)endPos.y; desiredEnd.z = (float)endPos.z; // SEE IF I AM ALREADY CLOSE ENOUGH if (VectorSquaredDistance(&curEnd, &desiredEnd) > IK_POS_THRESH) { // CREATE THE VECTOR TO THE CURRENT EFFECTOR POS curVector.x = curEnd.x - rootPos.x; curVector.y = curEnd.y - rootPos.y; curVector.z = curEnd.z - rootPos.z; // CREATE THE DESIRED EFFECTOR POSITION VECTOR targetVector.x = endPos.x - rootPos.x; targetVector.y = endPos.y - rootPos.y; targetVector.z = endPos.z - rootPos.z; // NORMALIZE THE VECTORS (EXPENSIVE, REQUIRES A SQRT) NormalizeVector(&curVector); NormalizeVector(&targetVector); // THE DOT PRODUCT GIVES ME THE COSINE OF THE DESIRED ANGLE cosAngle = DotProduct(&targetVector,&curVector); // IF THE DOT PRODUCT RETURNS 1.0, I DON'T NEED TO ROTATE AS IT IS 0 DEGREES if (cosAngle < 0.99999) { // USE THE CROSS PRODUCT TO CHECK WHICH WAY TO ROTATE CrossProduct(&curVector, &targetVector, &crossResult); NormalizeVector(&crossResult); turnAngle = acos((float)cosAngle); // GET THE ANGLE turnDeg = RADTODEG(turnAngle); // COVERT TO DEGREES // DAMPING if (m_Damping && turnDeg > m_Link[link].damp_width) turnDeg = m_Link[link].damp_width; AxisAngleToQuat(&crossResult,turnDeg,&aquat); MultQuaternions(&m_Link[link].quat, &aquat,&m_Link[link].quat); // HANDLE THE DOF RESTRICTIONS IF I WANT THEM if (m_DOF_Restrict) CheckDOFRestrictions(&m_Link[link]); // RECALC ALL THE MATRICES WITHOUT DRAWING ANYTHING drawScene(FALSE); // CHANGE THIS TO TRUE IF YOU WANT TO SEE THE ITERATION } if (--link < 0) link = EFFECTOR_POS - 1; // START OF THE CHAIN, RESTART } // QUIT IF I AM CLOSE ENOUGH OR BEEN RUNNING LONG ENOUGH } while (tries++ < MAX_IK_TRIES && VectorSquaredDistance(&curEnd, &desiredEnd) > IK_POS_THRESH); if (tries == MAX_IK_TRIES) return FALSE; else return TRUE; }