0
votes

If I have a geometry with single bone I can render it with animation but if it has 2 bones the vertices do not animated correctly, I load a JSON format exported from blender, as I understand the child bone relative matrix multiplied by its parent absolute matrix then multiplied by it relative matrix inverse then each bone matrix multiplied by the key frame matrix to get the final matrix then send these matrices to the vertex shader. I set the weight and bone index as attributes with the VBO.

for (int y = 0; y < g.getBones().size(); y++) {
    com.thatulborooj.opengl.objects.Bone bone = new com.thatulborooj.opengl.objects.Bone();
    Bone gBone = g.getBones().get(y);
    float[] t = new float[] { gBone.getPosition().get(0), gBone.getPosition().get(0), gBone.getPosition().get(0) };
    float[] r = new float[] { gBone.getRotation().get(0), gBone.getRotation().get(1), gBone.getRotation().get(2), gBone.getRotation().get(3) };
    float[] s = gBone.getScale() == null ? new float[] { 1, 1, 1 } : new float[] { gBone.getScale().get(0), gBone.getScale().get(1), gBone.getScale().get(2) };
    float[] nMatrix = createMat4(t, r, s);
    float[] inverseMatrix = new float[16];
    if (gBone.getParent() == -1) {
        bone.setBindMatrix(nMatrix);
        invertM(inverseMatrix, 0, nMatrix, 0);
        float[] fMatrix = new float[16];
        multiplyMM(fMatrix, 0, nMatrix, 0, inverseMatrix, 0);
        bone.setFinalMatrix(fMatrix);
    } else {
        float[] pMatrix = mesh.getBones().get(gBone.getParent()).getBindMatrix();
        float[] bMatrix = new float[16];
        multiplyMM(bMatrix, 0, pMatrix, 0, nMatrix, 0);
        bone.setBindMatrix(bMatrix);
        invertM(inverseMatrix, 0, bMatrix, 0);
        float[] fMatrix = new float[16];
        multiplyMM(fMatrix, 0, bMatrix, 0, inverseMatrix, 0);
        bone.setFinalMatrix(fMatrix);
        }
        bone.setName(gBone.getName());
        bone.setParent(gBone.getParent());
        mesh.getBones().add(bone);
    }
}

this is the render code

    if (animated) {
        if (playing) {
            if (curFrame < frames) {
                for (int i = 0; i < bones.size(); i++) {
                    Key key = animations.get(0).getHierarchy().get(i).getKeys().get(curFrame);
                    setIdentityM(aMatrix, 0);
                    multiplyMM(aMatrix, 0, bones.get(i).getFinalMatrix(), 0, key.getMatrix(), 0);
                    for (int q = 0; q < 16; q++) {
                        bs[i][q] = aMatrix[q];
                    }
                }
                curFrame++;
            } else
                curFrame = 0;
            shaderProgram.setBones(bs);
        }
    }

this is the function which make the matrix from position, quaternion and scale:

public static float[] createMat4(float[] t, float[] r, float[] s) {
    float[] mat4 = new float[16];
    float[] T = new float[16];
    float[] R = quaternionToMatrix(r);
    float[] S = new float[16];
    setIdentityM(T, 0);
    setIdentityM(S, 0);
    translateM(T, 0, t[0], t[1], t[2]);
    scaleM(S, 0, s[0], s[1], s[2]);
    float[] temp = new float[16];
    multiplyMM(temp, 0, T, 0, R, 0);
    multiplyMM(mat4, 0, temp, 0, S, 0);
    return mat4;
}

private static float[] quaternionToMatrix(float[] q) {
    float[] m = new float[16];
    final float xx = q[0] * q[0];
    final float xy = q[0] * q[1];
    final float xz = q[0] * q[2];
    final float xw = q[0] * q[3];
    final float yy = q[1] * q[1];
    final float yz = q[1] * q[2];
    final float yw = q[1] * q[3];
    final float zz = q[2] * q[2];
    final float zw = q[2] * q[3];
    // Set matrix from quaternion
    m[0] = 1 - 2 * (yy + zz);
    m[1] = 2 * (xy - zw);
    m[2] = 2 * (xz + yw);
    m[3] = 0;
    m[4] = 2 * (xy + zw);
    m[5] = 1 - 2 * (xx + zz);
    m[6] = 2 * (yz - xw);
    m[7] = 0;
    m[8] = 2 * (xz - yw);
    m[9] = 2 * (yz + xw);
    m[10] = 1 - 2 * (xx + yy);
    m[11] = 0;
    m[12] = 0;
    m[13] = 0;
    m[14] = 0;
    m[15] = 1;
    return m;
}

I set the bones array uniform by this line:

private void setBonesUniforms() {
    for (int i = 0; i < bones.length; i++) {
        int uBonesLocation = glGetUniformLocation(program, "bones[" + i + "]");
        glUniformMatrix4fv(uBonesLocation, 1, false, bones[i], 0);
    }
}

finally this is the vertex shader:

vec4 newVertex=vertexPosition;
vec4 newNormal=vertexNormal;
if(animated==1){
    int index;
    index=int(skinindex.x);
    newVertex = (bones[index]  * skinweight.x) * vertexPosition;
    newNormal = (bones[index]  * skinweight.x) * vertexNormal;

    index=int(skinindex.y);
    newVertex += (bones[index] * skinweight.y) * vertexPosition;
    newNormal += (bones[index] * skinweight.y) * vertexNormal;
}
newVertex=vec4(newVertex.xyz, 1.0);

I noticed that changing the child bone matrix with any floats do not change the result.

1

1 Answers

0
votes

You can use AssimpLib it is easy to use and supports most formats including collada with skeletal animations