Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rotate a skinned model's bones in c++ using assimp?

I could load an animation of a skinned model using assimp by interpolating between key-frames. Now, I have been trying to orient or position bones from a user defined transformation matrix instead of just loading it from an animation file. Like, rotating an arm by a certain angle in which the angle would be specified by the user. I loaded the model in it's bind pose as:

void recursion(aiNode* gNode)
{
 std::string gNodeName(gNode->mName.data);
 if(boneMapping.find(gNodeName) != boneMapping.end())
 {
  //if node corresponds to a bone,
  Matrix4f boneMatrix = IdentityMatrix;
  aiNode* tempNode = gNode;
     //find combined transform of a bone
     while(tempNode != NULL)
     {
        Matrix4f NodeMatrix(tempNode->mTransformation);
        boneMatrix =  NodeMatrix * boneMatrix;
        tempNode = tempNode->mParent;
     }

     pBoneData[boneId].FinalTransform = GlobalInverseTransform * boneMatrix * pBoneData[boneId].OffsetMatrix;
  }
 for(int i = 0; i < gNode->mNumChildren; i++)
 { //repeat this process for child nodes
    recursion(gNode->mChildren[i]);
 }
}

To orient one of the meshes of a model using transformation matrix, I tried searching for name of the bone that corresponds to the parent-bone of the mesh and then replace it's node matrix with the required matrix. However, that didn't work at all as it deformed a bone at a different mesh.

enter image description here

The model to the right is in T pose and I intended to modify it by rotating the bone at the neck by a 45 deg angle, but it remained the same and the leg portion got deformed as seen in model to the left. So, any links to existing articles or answers could be really helpful.

like image 249
user3124361 Avatar asked Mar 21 '15 15:03

user3124361


1 Answers

I think the order of the multiplication matrix is wrong in this line:

boneMatrix =  NodeMatrix * boneMatrix;

it should be:

boneMatrix = boneMatrix * NodeMatrix;

In OpenGL the order of multiplication is reversed.

Although this approach doesn't sound right to me:

while(tempNode != NULL)
{
    Matrix4f NodeMatrix(tempNode->mTransformation);
    boneMatrix =  NodeMatrix * boneMatrix;
    tempNode = tempNode->mParent;
}

you are multiplying your local transformation by the bone matrix in bone space and not in world space. This is why your character appears deformed.

I solved this problem for my animation project, using assimp and OpenGL. I used a slightly different approach by saving all assimp information into my own data structure. (skeleton and bones). By the look of your code and the data structure you are using I guess you are basing your code upon this tutorial.

Here is the code I used, I'm sure you can use it to compare with your implementation:

glm::mat4 getParentTransform()
{
    if (this->parent)
        return parent->globalTransform;
    else 
        return glm::mat4(1.0f);
}

void updateSkeleton(Bone* bone = NULL)
{ 
    bone->globalTransform =  bone->getParentTransform() // This retrieve the transformation one level above in the tree
    * bone->transform //bone->transform is the assimp matrix assimp_node->mTransformation
    * bone->localTransform;  //this is your T * R matrix

    bone->finalTransform = inverseGlobal // which is scene->mRootNode->mTransformation from assimp
        * bone->globalTransform  //defined above
        * bone->boneOffset;  //which is ai_mesh->mBones[i]->mOffsetMatrix


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

EDIT:

For matrix conversion from Assimp to glm I used this function:

inline glm::mat4 aiMatrix4x4ToGlm(const aiMatrix4x4* from)
{
    glm::mat4 to;


    to[0][0] = (GLfloat)from->a1; to[0][1] = (GLfloat)from->b1;  to[0][2] = (GLfloat)from->c1; to[0][3] = (GLfloat)from->d1;
    to[1][0] = (GLfloat)from->a2; to[1][1] = (GLfloat)from->b2;  to[1][2] = (GLfloat)from->c2; to[1][3] = (GLfloat)from->d2;
    to[2][0] = (GLfloat)from->a3; to[2][1] = (GLfloat)from->b3;  to[2][2] = (GLfloat)from->c3; to[2][3] = (GLfloat)from->d3;
    to[3][0] = (GLfloat)from->a4; to[3][1] = (GLfloat)from->b4;  to[3][2] = (GLfloat)from->c4; to[3][3] = (GLfloat)from->d4;

    return to;
}

Hope it helps.

like image 62
codingadventures Avatar answered Nov 18 '22 07:11

codingadventures