How to query/determine the roll of a bone in Pose/Object mode?

There’s a similar thread here, asking about this: Bone Roll in Python api - Add Feature?

In short, EditBone objects have access to the roll parameter, but Bone and PoseBone objects do not. This is particularly odd, to me, because Bone has a MatrixFromAxisRoll function that needs this information to produce meaningful results.

I will look into modifying Blender to access this information from Python (any help in this regard would be appreciated, seems simple enough to add).

In the meantime, I would like to learn how to correctly determine this value without changing modes, prefereably using values I already have, like PoseBone.vector. Thank you.

This isn’t exactly what I was writing about, but here is the way Blender calculates the PoseBone.matrix: EDIT: doesn’t actually

def PoseBoneMatrix(obArm, pb):
    oMat_accumulated = Matrix.Identity(4)
    tMat_accumulated = Matrix.Identity(4)
    curPB = pb
    cMat = Matrix.Translation(curPB.head - curPB.bone.head_local)
    while (curPB):
        oMat = curPB.bone.matrix.to_4x4()
        if (curPB.parent):
            tMat = Matrix.Translation(curPB.bone.head + curPB.bone.parent.vector)
        else:
            tMat = Matrix.Translation(curPB.bone.head)
        oMat_accumulated = oMat @ oMat_accumulated
        tMat_accumulated = (cMat @ tMat) @ tMat_accumulated
        curPB = curPB.parent
        cMat = Matrix.Identity(4)
    mat = (tMat_accumulated @ oMat_accumulated)# .inverted() @ pb.matrix_channel
                                               #   this makes it actually useful
    return mat

Note that this is a script that I reverse-engineered by brute force, and while it does produce identical results, it probably isn’t the way Blender does it internally (and it’s probably calculated in C). I’m including this because it may be useful to other people. I’ll update this when I figure out a better way to calculate the matrix in pose mode. EDIT: I was wrong :stuck_out_tongue:

Anyways, the following code returns correct results for the roll, from a few brief tests:

    #identical results here, but you may want to use a custom-calculated matrix
    # I'm trying to figure out the math for a matrix that accounts for parent
    # pose-space transformations, which would provide accurates results after
    # e.g. y-rotation in pose-mode, or parent's rotation, etc.
    mat  = pb.matrix #PoseBoneMatrix( obArm, pb)
    axis, roll = pb.bone.AxisRollFromMatrix(mat.to_3x3())
    print (round(roll, 5) * -57.29578)

A couple of things to note: the matrix must be converted to 3x3 (because it should not include translation data). Bone.matrix can’t be used because it gives the orientation relative to the parent bone, PoseBone.matrix must be used, since you need the orientation relative to the armature object. The EditBone’s roll parameter is degrees in the UI, so I’ve converted it to degrees here, but the actual value is in radians IIRC. Note that I’ve multiplied it by a negative number; for whatever reason this needs to be multiplied by -1 to be correct.

This should be enough to help other people out if they have this problem, but it’s an annoying workaround (even though it’s only really one line of code). pb.bone.AxisRollFromMatrix(pb.matrix.to_3x3())[1] is an awkward way of writing pb.roll.

EDIT: there was some stuff wrong with my initial code, I should eventually update this with correct code

1 Like