2
votes

I'm currently implementing Skeletal Animation into my 3D OpenGL Game Engine using the ASSIMP library, unfortunately it isn't going all that great. The problem I am currently facing is that the concatenated bone transforms (the parent-child relationship between bones) only act correctly when only one parent is being transformed.

What happens is that the thigh's bone transformation is carried out on global axes instead of the pelvis' local axes.

Here is my Skeleton class:

class Skeleton
{
public:
    Skeleton();

    unsigned int numBones;
    std::vector<std::string> names;
    std::vector<glm::mat4> offsets;


    std::vector<glm::mat4> transforms;
    std::vector<int> parents;

    std::string GetName(int bone);
    glm::mat4 GetWorldTransform(int bone);
    int GetID(std::string bone_name);
};

and here is the function I use to calculate the world transform for each bone:

glm::mat4 Skeleton::GetWorldTransform(int bone)
{
int p = parents[bone];
glm::mat4 result = glm::mat4(1.0);

result *= glm::inverse(offsets[bone]);
    result *= transforms[bone];
result *= offsets[bone];

while(p >= 0)   //The root bone has a parent of -1
{
    result *= glm::inverse(offsets[p]);
        result *= transforms[p];    //Apply The Parent's Transform
    result *= offsets[p];

    p = parents[p];
}
return result;
}

And in my Mesh class I have this function UpdateSkeleton():

void Mesh::UpdateSkeleton(unsigned int shaderID)
{
std::vector<glm::mat4> mats;

for(int i = 0; i < skeleton->numBones; i++)
{
    glm::mat4 matrix = glm::mat4(1.0);
        matrix *= skeleton->GetWorldTransform(i);
    mats.push_back(matrix);
}

if(mats.size() > 0)
    glUniformMatrix4fv(glGetUniformLocation(shaderID,"gBones"),mats.size(),GL_FALSE,glm::value_ptr(mats[0]));
}

Thank you!

1

1 Answers

1
votes

So I fixed it.

https://www.youtube.com/playlist?list=PLi9hkiiZDonvUcT_FRD8rJtuuGltL3xys

Basically the problem was that the concatenations were being done in the reverse order, I was doing them like this originally:

void Bone::UpdateParentTransform()
{
    parent_transform = glm::mat4(1.0);

    Node* node_id = parent_node;

    while(node_id != nullptr)
    {
        Bone* bone = mesh->FindBone(node_id->name);

        if(bone != nullptr)
        {
            parent_transform *= glm::inverse(bone->offset_matrix);
                parent_transform *= node_id->transformation;
            parent_transform *= (bone->offset_matrix);
            node_id = bone->parent_node;
        }
        else
            node_id = nullptr;
    }
}

Now I made it so they are performed in the reverse order, like so:

void Bone::UpdateParentTransform()
{
    parent_transform = glm::mat4(1.0);

    Node* node_id = node;

    std::vector<Bone*> bones;
    std::vector<Node*> parents;

    while(node_id != nullptr)
    {
        Bone* bone = mesh->FindBone(node_id->name);

        if(bone != nullptr)
        {
            bones.push_back(bone);
            parents.push_back(node_id);

            node_id = bone->parent_node;
        }
        else
            node_id = nullptr;
    }


    for(int i = parents.size()-1; i > 0; i--)
    {
        parent_transform *= glm::inverse(bones.at(i)->offset_matrix);
            parent_transform *= parents.at(i)->transformation;
        parent_transform *= (bones.at(i)->offset_matrix);
    }

}

If you have any questions about this or anything else regarding skeletal animation using ASSIMP please don't hesitate to ask me, you can contact me here or on my YouTube channel: RealityMultiplied

I'm going to start work on a tutorial series on how to do skeletal animation using ASSIMP right now, so I hope to have the first video up by tonight.

I hope to make this an easy to understand topic for those just beginning because it can seem daunting and just horrible overall.

EDIT: So I started working on the tutorials, but I'm going through a lot tough shit in my life right now and I don't think I'm going to be able to finish them. I hope they help at least a little bit, I'm sorry I couldn't do more.

http://realitymultiplied.blogspot.mx/