Forward Kinematic

Summary

Introduction

This project aim to show how to use forward kinematic. I won’t go through the nitty-gritty details here, I will write a separate blog post (TBD) explaining all the code thoroughly with all the technical details.

I used as reference a 3D hand model. I inserted two key-frames, for each bone, using Blender: an initial and a final position. I implemented the hand hierarchy using a tree data structure called Skeleton and another structure Bone for the nodes. Although the hand is loaded through an external library called Assimp, I did not make use of any Assimp’s data structure for rendering and/or animating. Information about Bones and Animation Channels in Assimp have been imported in my own local data structure.

The animation is obtained traversing the whole tree every frame and thus calculating the final transformation matrix. It is then sent to the vertex shader along with the bone weights to update the individual position of vertices.

Hand Animation

Implementation

There are two main classes involved in the process: Skeleton.h and Bone.h:

  • Skeleton.h → Hierarchical data structure. It contains bones loaded from Assimp. The Skeleton class has a method, called importSkeletonBone, which accepts an Assimp Node (aiNode). It traverses the Assimp internal tree structure to copy locally the information about the hierarchy using
    Bone as a node.
  • Bone.h → used as a tree node. Contains information about its parent, its children (implemented as a vector<Bone> although vector a double pointer Bone** could have been used instead), key-frames, bone name and a transformation matrix.
  • AnimationPose.h  → For each key frame, defined in the modeler (Blender or any other), are defined a translation which is interpolated with the next, two rotations which are spherical interpolated and a scale operation. In this class there is the code needed to perform operations with keyframes.

To animate the model a recursive function it is needed to traverse the tree (the skeleton in this case). The following function, implemented in Skeleton.h is the main responsible for updating the tree:

void updateSkeleton(Bone* bone = nullptr)
{
   if (d_num_of_bones == 0) return;
   root_bone_check(&bone);

   bone->globalTransform =  bone->getParentTransform() * bone->transform * bone->localTransform;
   bone->finalTransform = m_inverse_global * bone->globalTransform  * bone->boneOffset;  

   for (int i = 0; i < bone->children.size(); i++) {
	updateSkeleton (&bone->children[i]);
   }
}

Video