Developer Documentation
|
A skeleton represents a hierarchical tree structure of joints. The joints can be accessed through the skeleton. Therefore the skeleton class provides an iterator over its joints. Additional ways to access joints from the skeleton are defined in the Joint Access Section.
The joint class does not directly store information about the position and orientation of the joint, since this is dependent on the current pose. The joint class can be used to traverse from a joint to its neighbors in the tree structure and to set the selection state of a joint. Each joint is equipped with a unique id which is guaranteed to lie in a range between , where is the number of joints. So when a joint is deleted from the skeleton the ids may change.
A skeleton consists of a set of different poses. Initially a skeleton has only one attached pose. The so called reference pose. This pose defines the original position and orientation of each joint in the skeleton. For each animation defined on the skeleton poses store the position of joints in each frame of the animation. So for every frame of the animation we have an associated pose.
The referencePose() can directly be accessed from the skeleton. In order to access specific poses from the animation we first have to get a handle for that pose. These handles are called AnimationHandles and they store the index of an animation and index of a frame (or pose). All functions needed to access animations and poses from a skeleton are defined in the Animation Section.
A pose of the skeleton defines the skeleton configuration for one frame in the animation. Thus if we want to change the position of joints this is only meaningful when a pose is given. The Pose class can then be used to alter the transformation of joints. Basically there are two matrices for every joint which store its alignment.
For every joint in the skeleton we define a matrix to prescribe the global position and orientation of the joint. Hence, is also referred to as the global matrix:
where is a rotation matrix and denotes the global joint position. The global matrix can also be seen as a transformation for the change of a basis. The global matrix transforms points from the local coordinate frame of the joint to global Cartesian coordinates.
The representation of joints using global matrices is sufficient to represent the geometry of a skeleton, but for computational efficiency we add another representation. We also express the position and orientation of a joint relative to its parent joint. This local representation of the joints is also stored in matrices, yielding a local matrix for every joint :
The functions to access and alter the transformations are defined in the Pose Editing section of the PoseT class.
Note: When using the functions PoseT::setLocalMatrix and PoseT::setLocalTranslation the second parameter '_keepLocalChildPositions' defines if a change of the local matrix, keeps the local matrices of the child joints and thus updates the global matrices of the child joints or vice versa.
Note: When using the functions PoseT::setGlobalMatrix and PoseT::setGlobalTranslation the second parameter '_keepGlobalChildPositions' defines if a change of the global matrix, keeps the global matrices of the child joints and thus updates the local matrices of the child joints or vice versa.
The Pose class also provides unified matrices. These matrices are stored to minimize computations since they are used often when the skeleton is equipped with a skin mesh. The unified matrix maps a point from global coordinates in the reference pose to global coordinates in another pose. If the global matrix of a joint in reference pose is denoted as and the global matrix in a target pose is denoted as then the unified matrix is given as:
Functions to access the unified matrices of a pose are defined in the Unified Matrices section.
This section shows and explains a sample code to construct a simple skeletal animation. Images from the resulting animation are shown below:
To generate such an animation we first have to define the structure of the skeleton:
Now the structure of the skeleton is defined, but it does not have an animation and the position of all joints in the reference pose is . In the next step we set the correct positions for the reference pose:
The reference pose of the skeleton is now completely defined, but we still don't have an animation. It is created and added to the skeleton as follows:
We have added an animation with 100 frames and a playback rate of 25 frames per second. The transformations for every joint and every frame of the animation however is still an identity matrix and therefore only one point is visible when this animation is played. To change this we now have to change the transformations of all joints for the complete animation:
Now the animation is complete. In order to use these code snippets two additional things are needed. For one we need two includes:
and after the animation is constructed we have to inform OpenFlipper about the changes:
In this tutorial we extend the first tutorial by adding a mesh which deforms with the rotation of the skeleton. For simplicity we just construct a mesh without faces. Additionally, the skin weights for the points of the mesh are also computed using a very simple method and therefore the result does not look perfect. The result is shown below:
The first thing we need to do is to add a mesh object:
The connection between joints of the skeleton and vertices of the mesh is stored in the skin weights. They describe the weighted influence of different joints on one vertex. The weights are stored as a mesh property and we need to add the property first:
Now we can go on and add vertices to the mesh and set their skin weights. The points are added above and below the skeleton and we add 50 points between two joints both above and below:
This simple skin weight definition shifts the center of the rotation between the first and the second joint, but a 'correct' weight computation would exceed the range of this tutorial. To run the code two more things have to be considered. First, we have to add an include for the skinweights:
And additionally we have to inform OpenFlipper about the changes that have been made:
Note: The RPC function call can be left out, but then the user has to connect skeleton and skin with the 'Attach Skin to Skeleton' Button in the Skeletal Animation Toolbox.
The complete sourcecode of this tutorial can be found here.