Jump to content
  • Advertisement
Sign in to follow this  
lucky6969b

[IK] Calculating the local position of the hand wrt the handle

This topic is 903 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Dear all,

I wonder whether this is the correct way to calculate the IK

of attaching the left hand to the left handle of the trolley?

I have some doubts, because I've read some posts about this already.

 

I want to calculate when the worker is in the frame of the trolley,

So I need the position of the trolley's handle

D3DXVECTOR3 handlePos(handleFrame->matCombined._41, handleFrame->matCombined._42, handleFrame->matCombined.43) ;

of the trolley frame hierarchy...

And I need to grab the inverse of the worker's hand, which is given by

D3DXMatrixInverse(&workerHandInverse, NULL, &workerHand->matCombined);

of the worker frame hierarchy. I then transform the trolley's handle position

by

D3DXVec3TransformCoord(&newPos, &handlePos, &workerHandInverse);

Then both objects are in the same frame.

 

 

I then create an animation set and start blending them together by

interpolating between trolley's handle position and newPos?

 

BTW, should I use the code in the book called Character Animation With Direct3D to calculate the IK pose

or just intepolating between the 2 positions, and the biped will handle the rest for me?

Jack

Edited by lucky6969b

Share this post


Link to post
Share on other sites
Advertisement

Sorry, I was editing the source code

 

void TestApp::CreateArmMove() {
    

    ID3DXKeyframedAnimationSet* pAnimSet = NULL;
    
    D3DXCreateKeyframedAnimationSet("ArmMove", 100, D3DXPLAY_PINGPONG, 20, 0, NULL, &pAnimSet);


    FRAME* f = (FRAME*)D3DXFrameFind(this->m_pMesh.GetFrameRoot(), "Bip001_L_UpperArm");

    D3DXVECTOR3 vpos(f->matCombined._41, f->matCombined._42, f->matCombined._43);

    D3DXVECTOR3 vMoveDir(0, 0.01, 0);

    D3DXKEY_VECTOR3 pos[6];
    D3DXKEY_QUATERNION quat[5];


    D3DXVECTOR3 xaxis(1.0, 0.0, 0.0);
    D3DXVECTOR3 yaxis(0.0, 1.0, 0.0);
    D3DXVECTOR3 zaxis(0.0, 0.0, 1.0);

    D3DXVECTOR3 yzaxis(0.0, 1.0, -1.0);

    D3DXVec3Normalize(&yzaxis, &yzaxis);

    D3DXQUATERNION q[5];
    D3DXVECTOR3 p[5];

    for (int i = 0; i < 5; i++) {
        p[i] = vpos + i;
        D3DXQuaternionRotationAxis(&q[i], &yzaxis, -i*0.1);
    }



 
    
    for (int i = 0; i < 5; i++)
    {
        pos[i].Time = i * pAnimSet->GetSourceTicksPerSecond();
        pos[i].Value = p[i];

        quat[i].Time = i * pAnimSet->GetSourceTicksPerSecond();
        quat[i].Value = q[i];

    }

    DWORD idx;
    pAnimSet->RegisterAnimationSRTKeys("Bip001_L_UpperArm", 0, 5, 0, NULL, quat, NULL, &idx);
    m_pAnimController->RegisterAnimationSet(pAnimSet);    

    DWORD dwNumAnimSet = m_pAnimController->GetMaxNumAnimationSets();

 
    m_pAnimController->ResetTime();

    ID3DXAnimationSet* anim1 = NULL;
    ID3DXAnimationSet* anim2 = NULL;

    m_pAnimController->GetAnimationSet(0, &anim1);
    m_pAnimController->GetAnimationSet(1, &anim2);

    m_pAnimController->SetTrackAnimationSet(0, anim1);
    m_pAnimController->SetTrackAnimationSet(1, anim2);

    float w = (rand() % 1000) / 1000.0f;
    m_pAnimController->SetTrackWeight(0, w);
    m_pAnimController->SetTrackWeight(1, 1.0-w);

    m_pAnimController->SetTrackSpeed(0, (rand() % 1000) / 500.0f);
    m_pAnimController->SetTrackSpeed(1, (rand() % 1000) / 500.0f);

    m_pAnimController->SetTrackPriority(0, D3DXPRIORITY_HIGH);
    m_pAnimController->SetTrackPriority(1, D3DXPRIORITY_HIGH);

    m_pAnimController->SetTrackEnable(0, true);
    m_pAnimController->SetTrackEnable(1, true);

 
    
}

 

#include <sstream>
#include "IK.h"
#include "..\CMesh.h"

IK::IK(CMesh* pSkinnedMesh) {
    m_pSkinnedMesh = pSkinnedMesh;

    m_pShoulderBone = (FRAME*)D3DXFrameFind(m_pSkinnedMesh->GetFrameRoot(), "Bip001_L_UpperArm");
    m_pElbowBone = (FRAME *)D3DXFrameFind(m_pSkinnedMesh->GetFrameRoot(), "Bip001_L_Forearm");
    m_pHandBone = (FRAME*)D3DXFrameFind(m_pSkinnedMesh->GetFrameRoot(), "Bip001_L_Hand");

    m_pRShoulderBone = (FRAME*)D3DXFrameFind(m_pSkinnedMesh->GetFrameRoot(), "Bip001_R_UpperArm");
    m_pRElbowBone = (FRAME *)D3DXFrameFind(m_pSkinnedMesh->GetFrameRoot(), "Bip001_R_Forearm");
    m_pRHandBone = (FRAME*)D3DXFrameFind(m_pSkinnedMesh->GetFrameRoot(), "Bip001_R_Hand");
}

 
void IK::UpdateArmIK(D3DXVECTOR3 target, float s)
{
    if (m_pShoulderBone == NULL || m_pElbowBone == NULL || m_pHandBone == NULL)
        return;

    D3DXVECTOR3 hinge(0, 0, -1);
    //D3DXVec3Normalize(&hinge, &hinge);


    {
        stringstream oss;
        D3DXVECTOR3 startPos(m_pHandBone->matCombined._41, m_pHandBone->matCombined._42, m_pHandBone->matCombined._43);

        oss << "Start Pos@ : " << startPos.x << " " << startPos.y << " " << startPos.z << endl;

        OutputDebugString(oss.str().c_str());


        D3DXVECTOR3 vDist = startPos - target;

        float fdist = D3DXVec3Length(&vDist);

        
        oss << fdist << " is Beyond Reach\n";
        if (fdist > 1.0f) {
            OutputDebugString(oss.str().c_str());
            return;
        }

        // intepolate here
        //D3DXVECTOR3 finalPos;

        //if (s < 1.0f)
        //    D3DXVec3Lerp(&finalPos, &startPos, &target, s);
        //else
        //    finalPos = target;

        ApplyArmIK(hinge, target, LEFT_ARM);

        // how to create animation set?
        
    }

    
 
}


void IK::ApplyArmIK(D3DXVECTOR3 &hingeAxis, D3DXVECTOR3 &target, Limbs limb)
{
 
    // The two joints algorithm works in two steps first we bend the middle(joint) bone so that the distance from
    // start bone to target position will be correct.
    // We blindly assume that the user given hingeAxis always will be perpendicular to both start and joint vectors.
    // - This will always be hold for elbows and knees for a human character.
    // After that we use the "look-at-IK" algorithm to turn the start bone so that we grab the target

    // Setup some vectors and positions
    D3DXVECTOR3 startPosition = D3DXVECTOR3(m_pShoulderBone->matCombined._41, m_pShoulderBone->matCombined._42, m_pShoulderBone->matCombined._43);
    D3DXVECTOR3 jointPosition = D3DXVECTOR3(m_pElbowBone->matCombined._41, m_pElbowBone->matCombined._42, m_pElbowBone->matCombined._43);
    D3DXVECTOR3 endPosition = D3DXVECTOR3(m_pHandBone->matCombined._41, m_pHandBone->matCombined._42, m_pHandBone->matCombined._43);

    D3DXVECTOR3 startToTarget = target - startPosition;
    D3DXVECTOR3 startToJoint = jointPosition - startPosition;
    D3DXVECTOR3 jointToEnd = endPosition - jointPosition;

    float distStartToTarget = D3DXVec3Length(&startToTarget);
    float distStartToJoint = D3DXVec3Length(&startToJoint);
    float distJointToEnd = D3DXVec3Length(&jointToEnd);

    // Calculate joint bone rotation
    // Calcualte current angle and wanted angle
    float wantedJointAngle = 0.0f;

    if (distStartToTarget >= distStartToJoint + distJointToEnd)
    {
        // Target out of reach
        wantedJointAngle = D3DXToRadian(180.0f);
    }
    else
    {
        //Calculate wanted joint angle (Using the Law of Cosines)
        float cosAngle = (distStartToJoint * distStartToJoint + distJointToEnd * distJointToEnd - distStartToTarget * distStartToTarget) / (2.0f * distStartToJoint * distJointToEnd);
        wantedJointAngle = acosf(cosAngle);
    }

    //Normalize vectors
    D3DXVECTOR3 nmlStartToJoint = startToJoint;
    D3DXVECTOR3 nmlJointToEnd = jointToEnd;
    D3DXVec3Normalize(&nmlStartToJoint, &nmlStartToJoint);
    D3DXVec3Normalize(&nmlJointToEnd, &nmlJointToEnd);

    //Calculate the current joint angle
    float currentJointAngle = acosf(D3DXVec3Dot(&(-nmlStartToJoint), &nmlJointToEnd));

    //Calculate rotation matrix
    float diffJointAngle = wantedJointAngle - currentJointAngle;
    D3DXMATRIX rotation;
    D3DXMatrixRotationAxis(&rotation, &hingeAxis, diffJointAngle);

    //Apply elbow transformation
    m_pElbowBone->TransformationMatrix = rotation * m_pElbowBone->TransformationMatrix;

    // Calcuate new end position
    // Calculate this in world position and transform it later to start bones local space
    D3DXMATRIX tempMatrix = m_pElbowBone->matCombined;
    tempMatrix._41 = 0.0f;
    tempMatrix._42 = 0.0f;
    tempMatrix._43 = 0.0f;
    tempMatrix._44 = 1.0f;

    D3DXVECTOR3 worldHingeAxis;
    D3DXVECTOR3 newJointToEnd;
    D3DXVec3TransformCoord(&worldHingeAxis, &hingeAxis, &tempMatrix);
    D3DXMatrixRotationAxis(&rotation, &worldHingeAxis, diffJointAngle);
    D3DXVec3TransformCoord(&newJointToEnd, &jointToEnd, &rotation);

    D3DXVECTOR3 newEndPosition;
    D3DXVec3Add(&newEndPosition, &newJointToEnd, &jointPosition);

    // Calculate start bone rotation
    D3DXMATRIX mtxToLocal;
    D3DXMatrixInverse(&mtxToLocal, NULL, &m_pShoulderBone->matCombined);

    D3DXVECTOR3 localNewEnd;
    D3DXVECTOR3 localTarget;
    D3DXVec3TransformCoord(&localNewEnd, &newEndPosition, &mtxToLocal);
    D3DXVec3TransformCoord(&localTarget, &target, &mtxToLocal);
    D3DXVec3Normalize(&localNewEnd, &localNewEnd);
    D3DXVec3Normalize(&localTarget, &localTarget);

    D3DXVECTOR3 localAxis;
    D3DXVec3Cross(&localAxis, &localNewEnd, &localTarget);
    if (D3DXVec3Length(&localAxis) == 0.0f)
        return;

    D3DXVec3Normalize(&localAxis, &localAxis);
    float localAngle = acosf(D3DXVec3Dot(&localNewEnd, &localTarget));

    // Apply the rotation that makes the bone turn
    D3DXMatrixRotationAxis(&rotation, &localAxis, localAngle);
    m_pShoulderBone->matCombined = rotation * m_pShoulderBone->matCombined;
    m_pShoulderBone->TransformationMatrix = rotation * m_pShoulderBone->TransformationMatrix;

    // Update matrices of child bones.
    if (m_pShoulderBone->pFrameFirstChild)
        m_pSkinnedMesh->UpdateFrame((FRAME*)m_pShoulderBone->pFrameFirstChild,
            &m_pShoulderBone->matCombined);
}

 

 

How do I convert the UpdateArmIK animation into keyframes specified in the function right above it?

Thanks

Jack



    


        
 

Edited by lucky6969b

Share this post


Link to post
Share on other sites

What I understood is that you want to drive an animation of a hand so that it reaches for and "grab" a trolley. The position of that hand should be calculated by interpolation between its start position and the target position. The posing of the arm should then be calculated by inverse kinematic.

 

The variable handleFrame->matCombined is the world transform of the target, and workerHand->matCombined is the world transform of the hand, right? Then the positions inside those transforms are in the same co-ordinate space, namely the global space, and could be interpolated directly.

 

Now for some reason you want to use another space for the interpolation. The transformation you apply in the OP is the inverse of workerHand->matCombined. Hence your new space is the hand local space. Notice that the start position of the hand is at (0,0,0,1) herein. Is that really the space you need? It seems me not to be a good choice for IK. I would expect some parent level of workerHand to be used instead.

Share this post


Link to post
Share on other sites

Hello Haegarr,

Thanks for responding to my question. Hope you have a wonderful year in 2016.

I am wondering why the hand IK is shaking instabilily throughout the animation.

I try to establish the IK like this

 

Update:

Okay, found the bug, the reason was the target was beyond reach which makes the IK instable.

   FRAME* handFrame = (FRAME*)D3DXFrameFind(m_pMesh.GetFrameRoot(), "Bip001_L_Hand");

    
    D3DXVECTOR3 outPos;
    
    D3DXVECTOR3 handPos(handFrame->matCombined._41, handFrame->matCombined._42, handFrame->matCombined._43);
    D3DXVECTOR3 targetHandPos = handPos + D3DXVECTOR3(0, 1, 0);

    D3DXVec3Lerp(&outPos, &targetHandPos, &handPos, m_fFrameNo);

    // and pose it with ik

    ik->UpdateArmIK(outPos);

    if (m_fFrameNo > 1.0f) {
        m_fFrameNo = 1.0f;
    }
    else
    {
        m_fFrameNo += 0.1f;
    }
Edited by lucky6969b

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!