This second tutorial is simply going to be the basics of bones.
A very important concept you need to grasp in order to understand skeletal animation is the offset matrix.
-Offset Matrix: The offset matrix is the matrix that transforms from world space into bone space, in other words it is simply the bone's transformation relative to the world's origin.
Think of it as how far the bone is from the origin.
Like this:
When you first try to transform a bone, it will by default be transformed around the world's origin instead of the bone's origin.
Like this:
When we want...
But how do we get this?
We must first transform the bone by the inverse (think of it as the opposite) of the offset matrix so that the bone is at the origin.
Then we transform it by whatever transformation matrix we want (this is specified by you as the bone's transformation matrix).
And finally we transform it back to it's original position by multiplying it by the offset matrix.
Ok but let's dive into the actual code, the first thing we want to do is define a bone class, so create a new file bone.h and create a new class:
#ifndef BONE_H
#define BONE_H
#include <glm/glm.hpp>
#include <string>
#include <vector>
#include <assimp/scene.h>
#include <iostream>
class Mesh;
class Bone
{
public:
unsigned int id;
std::string name;
Mesh* mesh;
glm::mat4 offset_matrix;
glm::mat4 transform;
Bone(){name = ""; id = -2;}
Bone(Mesh* in_mesh, unsigned int in_id, std::string in_name, aiMatrix4x4 in_o_mat);
Bone(Mesh* in_mesh, unsigned int in_id, std::string in_name, glm::mat4 in_o_mat);
};
#endif
Now I know I said I wouldn't plop any chunks of code but I'm going to walk you through this step by step, and trust me there's more to come.
First we have the include guards and the includes themselves, those should be self-explanatory.
Then we have the id: this will come in handy later on when we have to locate this bone inside the vector of other bones called the skeleton -- the id is the bone's location inside the skeleton.
Next we have the name: this is just for the same purpose as the id, essentially, it is to help us locate the bone inside the skeleton later on. It's for convenience.
The Mesh* mesh is useful because the Mesh class will later on contain the vector of Bones called the skeleton. Giving the individual bones the ability to access this allows them to locate other bones in the skeleton and access their properties, this will come in handy later on.
The offset_matrix is what was explained above, the transform is simply the bone's transformation, and then we have the constructors. The -2 in the first constructor is just a number I picked, it works. That's what matters.
The other two constructors take in the mesh, the bone's id, the bone's name, and the bone's offset matrix -- there are two so the constructor can use both ASSIMP's matrices as well as GLM's. We'll write a convenience function to convert from ASSIMP to GLM pretty soon, so don't worry about that.
We will continue to add on to this class as the tutorial series goes on, this is just the bare bones.
Heehee.
Anyways, the next thing we have to do is write our bone.cpp file, we'll add on to this file later on as well, these are just the basics:
Very good explanation, it will be great if you would make tutorials about bone hierarchy and skining.
ReplyDelete