Home > Mobile >  Bones rotate around parent - OpenGL Animation/Skinning
Bones rotate around parent - OpenGL Animation/Skinning

Time:11-08

I am having an issue with my bone skinning where instead of my bones rotating around their local origin, they instead rotate around the their parent. Left - my engine. Right - blender

There are two locations in the pipeline that I suspect are at fault, but I cant tell where exactly it is. First, I have this bit of code that actually calculates the bone matrices:

void CalculateBoneTransform(DSkeletonBone sb, dmat4x4f parentTransform, std::vector<DSkeletonBone> boneOverrides)
{
    if (boneDataIndex.count(sb.name) == 0) return;

    Transform transform = sb.localTransform;
    for (auto& bo : boneOverrides)
    {
        if (bo.name == sb.name)
        {
            transform = bo.localTransform;
        }
    }

    dmat4x4f globalTransform = dmat4x4f::Mul(parentTransform, transform.GetMatrix());

    dmat4x4f finalTransform = dmat4x4f::Mul(globalTransform, sb.inverseTransform);

    finalTransforms[boneDataIndex[sb.name]] = finalTransform;

    for (auto& pbC : sb.children)
    {
        CalculateBoneTransform(pbC, globalTransform, boneOverrides);
    }
}

The boneOverrides come from anything that would like to override a bone (such as an animation). finalTransforms is the array that is pushed to the shader.

In my mesh/anim parser, I have this code that calculates the inverse bind pose matrices (using OpenFBX, but this is not relevant):

            glm::mat4 local = glm::inverse(GetMatFromVecs(
                boneData[skin->getCluster(i)->name].pos,
                boneData[skin->getCluster(i)->name].rot,
                boneData[skin->getCluster(i)->name].scl));

            for (Object* parent = skin->getCluster(i)->getLink()->getParent(); parent != NULL; parent = parent->getParent())
            {
                glm::mat4 parentInverseTransform = glm::inverse(GetMatFromVecs(
                    parent->getLocalTranslation(),
                    parent->getLocalRotation(),
                    parent->getLocalScaling()));

                local = parentInverseTransform * local;
            }
            boneData[skin->getCluster(i)->name].offset = local;

The getLocalXX functions get the item in local space.

And the GetMatFromVecs function referenced above:

    glm::mat4 GetMatFromVecs(Vec3 pos, Vec3 rot)
    {
        glm::mat4 m(1);
        m = glm::scale(m, glm::vec3(1,1,1));
        m = glm::rotate(m, (float)rot.x * 0.0174532925f, glm::vec3(1, 0, 0));
        m = glm::rotate(m, (float)rot.y * 0.0174532925f, glm::vec3(0, 1, 0));
        m = glm::rotate(m, (float)rot.z * 0.0174532925f, glm::vec3(0, 0, 1));
        m = glm::translate(m, glm::vec3(pos.x, pos.y, pos.z));
        return m;
    }

Notice I am rotating first, then translating (this is also the case with the GetMatrix() function referenced in the first snippit, by the way).

So my question is, why is this happening? Also, what are some causes of this type of thing in mesh skinning? My translations are local, but rotations are not.

CodePudding user response:

My issue was bad matrix math. I swapped out my matrix::mul function for glm's and got intended results.

  • Related