1
votes

I am trying to implement skeletal animation in OpenGL. At this stage, I am trying to have my bone system deform a mesh. I believe the problem is with how I create the matrix Bone::getMatrix().

Background:

Each bone has an offset to it's parent, and if a bone does not have a parent the offset will instead be the world position of the entire skeleton. A collection of bones comprise a skeleton. Currently, for testing purposes, I have 3 bones in a skeleton deforming a skinned mesh.

Again, the issue is that when deforming the mesh, the visual result does not produce the desired effect.

Code:

glm::mat4 Bone::getMatrix(){

    Bone* parent = getParent();
    glm::mat4   rot  = glm::mat4(1.0f);
    glm::mat4   trans= glm::mat4(1.0f);

    glm::vec3 orient = getOrientation();


    //Create Rotation Matrix;
    rot *= glm::rotate(orient.x,1.0f,0.0f,0.0f);
    rot *= glm::rotate(orient.y,0.0f,1.0f,0.0f);
    rot *= glm::rotate(orient.z,0.0f,0.0f,1.0f);

    //Create Translation Matrix
    if(parent  != nullptr){
        glm::vec3 OFF = offset;
        trans = glm::translate(OFF.x,OFF.y,OFF.z);
    }else{
        glm::vec3 pos = getPosition();
        //trans = glm::translate(pos.x,pos.y,pos.z);
    }


    glm::mat4 PM = glm::mat4(1.0f);
    if(parent != nullptr){
        PM *= glm::inverse( parent->getMatrix() );
    }else{
    }
    return PM * trans * rot ;
}

Deforming the mesh:

void Armature::draw(const float& dt){
    if(mesh == nullptr){
        postMsg("reference copy to mesh is nullptr","draw",34);
        return;
    }

    ArrayVertex3* mesh_Vertex = mesh->getVertex3();
    ArrayVertex3 render_vertex;

    if(mesh_Vertex == nullptr){
        postMsg("mesh vertex structure is null during draw","draw",30);
    }else{
        render_vertex.reserve(mesh_Vertex->size());
    }

    int i=0,j=0,k=0;

    glPushMatrix();
        glTranslatef(10,0,0);

        glBegin(GL_POINTS);

        for( i =0; i<this->getBoneCount(); i++){
            glVertex3fv( glm::value_ptr(    glm::vec3(  glm::vec4() * getBone(i)->getMatrix()  )   ));
        }
        glEnd();

        glBegin(GL_LINES);

        for( i =1; i<this->getBoneCount(); i++){

        //  glVertex3fv( glm::value_ptr(getBone(i)->getTranslationMatrix() * glm::mat3(getBone(i)->getRotationMatrix()) ));
        //  glVertex3fv( glm::value_ptr(getBone(i-1)->getTranslationMatrix() * glm::mat3(getBone(i-1)->getRotationMatrix()) ));
        }
        glEnd();

    glPopMatrix();

    //Loop all vertices
    for( i = 0 ; i <  int(mesh->getNumberVertexes()); i ++){
        render_vertex[i].vtx = glm::vec3(0.0f,  0.0f,  0.0f);


        int inf_count = (*mesh_Vertex)[i].numInfluences;

        //Loop all this vertices influences
        for( j=0; j < inf_count; j ++){
            Bone *bone = getBone(    (*mesh_Vertex)[i].boneIDs[j]    );
            float weight = (*mesh_Vertex)[i].boneWeights[j];

            glm::vec4 original_vert = glm::vec4((*mesh_Vertex)[i].vtx,1.0f);
            glm::vec4 original_norm = glm::vec4((*mesh_Vertex)[i].norm,0.0f);


            glm::vec3 T = bone->getTranslationMatrix();

            render_vertex[i].vtx += glm::vec3(  bone->getMatrix()  *  original_vert )   * (weight)  ;


            //Transform normal
            render_vertex[i].norm =  glm::vec3(  bone->getRotationMatrix()  * original_norm )   ;


        }


        (*mesh_Vertex)[i].vtx = render_vertex[i].vtx;
        (*mesh_Vertex)[i].norm = glm::normalize(render_vertex[i].norm);

    }
}

Result enter image description here

The rotation is not done according to its joint location

1

1 Answers

2
votes

I fount your bone matrix function is a bit chaotic. From my understanding, all the vertexes need to be transformed by global transformation matrix. A global transformation matrix is the concatenation of all the local transformation matrix. The following code is used to calculate global transformation. This article is useful. http://graphics.ucsd.edu/courses/cse169_w05/2-Skeleton.htm

Some of my previous work using GLSL + assimp + glm http://www.youtube.com/watch?v=bXBfVl-msYw&list=HL1385555185&feature=mh_lolz

glm::mat4 T = glm::translate(bone->offset);
glm::mat4 R = glm::toMat4(bone->rotate); 
glm::mat4 S = glm::scale(bone->scale);
glm::mat4 localTransform = T * R * S; 

if(bone->getID()!=0)
{
    bone->globalTransform = bone->getParent()->globalTransform * localTransform;
}
else
{
    bone->globalTransform = localTransform;
}

for (uint i = 0 ; i < bone->getChildren().size() ; i++) {
    updateTransform(bone->getChildren()[i]);
}