How to calculate the bone direction and roll from matrix and vice versa (glTF importer)?

I want to convert an existing bone matrix into a direction vector and a roll angle, because when I import a glTF file from Blender then I have the bones matrices including the directions, but I don’t have the “roll” for each bone, I have no idea how to calculate the roll, any help please?

void boneMatrix_to_headTailRoll(
    const glm::mat4 &matrix,
    float length,
    glm::vec3 &out_head,
    glm::vec3 &out_tail,
    float &out_roll)

Another simple question, referencing from Blender’s bone, how to calculate the bone matrix from 2 points ‘head’, ‘tail’ and an angle ‘roll’ such as the origin of the matrix is the ‘head’, use GLM C++? In general, asking this question will solve 2 problems: “Add a bone in C++” and “Half answer the first question by doing reverse engineering”, but the problem is I indicate a weird situation in the image (double solutions).

glm::mat4 boneHeadTailRoll_to_matrix(
    const glm::vec3 &head,
    const glm::vec3 &tail,
    float roll)

Note, I tried to use glm::yawPitchRoll() but it’s very different than the Blender bone matrix rotation.

A “roll” relative to what? What is “up” with regard to this “roll”? That is, what direction does an angle of 0 mean?

It would really be better to simply not need whatever you’re trying to do. That is, change the code that wants this direction+roll to not want it and take some other information.

@Alfonse_Reinheart > The “roll” I choose is like in this image but “Will my code support glTF animation exactly like in Blender animation?” (the “roll” value is not like in Blender). It will take several days to write an animation code, that’s why I ask the question, so in case it fails then someone already answered my question.

void boneHeadTailRoll_to_matrix(const glm::vec3 &head, const glm::vec3 &tail, float roll, glm::mat4 &out_matrix, float &out_length) {
    // Referenced from [How to get Yaw, Pitch and Roll from a 3D vector](
    glm::vec3 d = tail - head;
    out_length = glm::length(d);
    if (out_length != 0.0f) {
        d *= 1.0f / out_length;
    float pitch = asin(-d.y);
    float yaw = atan2(d.x, d.z);
    glm::mat4 M = glm::yawPitchRoll(yaw, pitch, roll);
    M = glm::rotate(M, glm::pi<float>() / 2, glm::vec3(1, 0, 0));
    out_matrix = glm::translate(glm::mat4(1.0f), head) * M;
    // Note: The matrix can be suddenly rotated 180 degree.

void boneMatrix_to_headTailRoll(const glm::mat4 &matrix, float length, glm::vec3 &out_head, glm::vec3 &out_tail, float &out_roll) {
    out_head = glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]);
    out_tail = out_head + length * glm::vec3(matrix[1][0], matrix[1][1], matrix[1][2]);
    glm::mat4 mat_roll0;
    float l;
    boneHeadTailRoll_to_matrix(out_head, out_tail, 0.0f, mat_roll0, l);
    glm::vec3 X = glm::vec3(mat_roll0[2][0], mat_roll0[2][1], mat_roll0[2][2]);
    glm::vec3 Y = glm::vec3(mat_roll0[0][0], mat_roll0[0][1], mat_roll0[0][2]);
    glm::vec3 v = glm::vec3(matrix[2][0], matrix[2][1], matrix[2][2]);
    out_roll = atan2(glm::dot(Y, v), glm::dot(X, v));

I think I found a solution: Always use Matrix in my codes whenever possible instead of using (head, tail, roll) which can make the code very complex, but only use it in the GUI because it’s easier to read.

That works. And it doesn’t suffer issues associated with decomposing arbitrary rotations into per-axis component rotation angles (such as gimbal lock).

Alternatively, since your goal was:

Given this, and your complexity comment above, …

Consider just using a quaternion to represent each rotation. It gives you the exact direction vector and rotation angle around that direction vector, which sounds like exactly what you’re looking for. Moreover, it only consumes 4 floats instead of 9 for a full mat3x3. And, like a matrix, it’s a completely general representation. You can even use these in your shader for skinning.

Moreover, you can ++ this by using dual quaternions in your shader for skinning (Dual Quaternion Skinning), providing smooth rotation and translation interpolation for joint skinning, at a mem cost of only 7 floats per transform (vs. 16 or 12 for a mat4x4 or mat3x4). This does cost you a little in shader execution cost, but gets you the benefit of no joint collapse typical of Linear Blend Skinning (LBS). There may be newer, cooler things besides DQS you can do here as well … I haven’t had my head in skinning techniques for years.

1 Like

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.