0
\$\begingroup\$

I'm working on adding IK to my engine and right now I'm implementing the FABRIK algorithm to give full support to more than three bones.

The algorithm works by giving the joints positions, the start position (first joint), the target, and a threshold. I have already wrote the code and, as far as I can tell, it is correct.

When I use the algorithm I feed the world positions of each bones in the current frame and the target to reach and then the algorithm returns the position each bone should be in.

The issue is that the positions returned are also in WS and my animation library, Cal3D's setTranslation function, requires the relative translation of the bone (if I'm not wrong, this is the position relative to the father bone).

Does anybody know how I should transform the WS my bone is supposed to be at to the relative position I must give to the setTranslation method? Is that possible?

Thank you very much.

\$\endgroup\$
  • \$\begingroup\$ What WS stands for ? \$\endgroup\$ – ColdSteel Jun 10 '19 at 2:22
  • \$\begingroup\$ My bad, sorry about it. It stands for world space. I'm trying to translate the world space positions of the bones to the relative position for each bone. \$\endgroup\$ – Devem Jun 10 '19 at 14:01
  • \$\begingroup\$ Ok, so as far as I get it, you have to get a WorldSpace Transform of the root bone, and then convert all the transforms to the RootBoneCoordinate system. \$\endgroup\$ – ColdSteel Jun 10 '19 at 15:30
0
\$\begingroup\$

First of all thanks to ColdSteel for his/her help. :)

I ended up solving the issue by applying the following algorithm. I will list the points as I find it clearer this way. Note that I'm not exactly sure if this is the best (or even the correct solution) but it worked quite well for my case.

  • Get the absolute positions of the bones according to the fabrik algorithm.
  • Now, for each bone starting from the first one up to the second last (included), we need to calculate the necessary rotation to look at to the next bone. For example, for the upper arm, calculate the look at to point to the forearm one (or whatever is the next one in your skeleton).

    // Loop through all the bones minus the last one.
    // bonesIKPosition has the positions accoriding to the FABRIK algorithm
    for (int i = 0; i < bonesIKPosition.size()-1; ++i){
       int boneID = boneIds[i];
       int boneToPick = i + 1;
    
       TBoneCorrection corrector = TBoneCorrection();
       corrector.bone_id = boneID;
       corrector.local_axis_to_correct = Vector3(1.0, 0.0, 0.0f);
       corrector.amount = 1.f;
       corrector.apply(skeleton, bonesIKPosition[boneToPick], amount);
    }
    

While I can't share the code of the look at corrector (not mine, sorry!), I will explain what it does step by step so you might be able to do it yourself. (I think there are probably some tutorials or videos about this online but not sure). The Bone corrector applies a lookat correction to a bone so it will point in a given direction, to do so it uses the following parameters:

  • The skeleton we are applying the correction to.
  • The bone_id to know which bone will be affected by the look at. (Which one we want rotate).
  • The position to point at (the next bone in the hierarchy in our case).
  • The amount to apply the correction (same as with the amount before, we set it to 1.0f here as we want the correction to be instant in the case of this type of ik (we are already applying the correction to the position to look at to).
  • And the local axis to correct, which means the axis of the bone (in bone space) which we want to be pointing to the given direction (in this case the x vector which is the forward vector of the bone in our skeleton.

The algorithm is as follows:

  • Get the absolute position of the bone we are applying the look at to.
  • Get the direction in ws between the position we want to point to and the absolute position of the bone (the point before).
  • Get the absolute rotation of the bone. Invert it to get the absolute to local rotation.
  • Use this rotation that rotates from absolute to local to convert the direction in ws. Now, you should have the direction in bone space.
  • Now, you should use this direction and the local axis to correct to build a quaternion to rotate the bone as necessary so it point where it needs to. To build it you should do:
    • Calculate the cross product between the direction and the local axis, this will be the rotation axis.
    • Get the angle between the direction and local axis to correct, search a function online if necessary but you probably have one in your vector class or somewhere else.
    • Finally, use your Quaternion class and see if there is a function that allows you to create a quaternion from a rotation axis and an angle, if so, now you have the rotation that needs to be added to the bone so it points where you want to.

Use this rotation and apply it over the current rotation of your bone. Something like this:

CalQuaternion rotationToAddToBone = deltaRotationToAdd; // Delta rotation is the rotation we need to apply to the bone and just calculated before.
rotationToAddToBone *= bone->getRotation(); // Combine the rotation with the one from the bone.
bone->setRotation(rotationToAddToBone ); // set the final rotation to the bone.
bone->calculateState(); // This is a call function so the rotation also affects the children bones.

You are done!

Hopefully it may help somebody in a similar situation.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.