0
$\begingroup$

I have an animated rig and I am trying to calculate the transformation matrix of a particular pose bone. Here are the relevant values at a frame:

bone.location -> Vector((0.25549933314323425, 0.3193724751472473, 1.4211256504058838))

bone.rotation_quaternion -> Quaternion((0.9943733811378479, -0.09154314547777176, -0.019470931962132454, -0.04962115362286568))

`bone.scale -> Vector((1, 1, 1))

And the actual transformation matrix at the same from I get with bone.matrix is:

Matrix(((0.9943171739578247, 0.10224877297878265, -0.02963779866695404, 0.25549933314323425),
        (-0.08072073012590408, 0.5426188111305237, -0.8360914587974548, -1.1943329572677612),
        (-0.06940729171037674, 0.8337326049804688, 0.5477888584136963, 0.4846082329750061),
        (0.0, 0.0, 0.0, 1.0)))

Now I need to get the above matrix with the given rotation and location values, but am unable to get it.

I tried the following

matrix = (Matrix.Rotation(bone.rotation_quaternion.angle, 4, bone.rotation_quaternion.axis) @ Matrix.Translation(bone.location))

and

matrix = (Matrix.Translation(bone.location) @ bone.rotation_quaternion.to_matrix().to_4x4())

Both the above approaches give different results.

So given bone location and translation how do I get the matrix that matches with bone.matrix. I need this because I need the matrix at a different frame. And I can't get it directly, I can only get location and Quaternion rotation using fcurve.evaluate.

$\endgroup$

1 Answer 1

4
$\begingroup$

Calculating PoseBone.matrix from Fcurves

The right-hand side of the first expression you tried is in reverse order. Also it's PoseBone.matrix_basis, not PoseBone.matrix.

matrix_basis = Matrix.Translation(bone.location) @ Matrix.Rotation(bone.rotation_quaternion.angle, 4, bone.rotation_quaternion.axis)

Recursive call is required to obtain matrix from matrix_basis. And here is an example of doing this without view_layer.update() or scene.frame_set(). The script below is a modified version of that sample.

 
import bpy
from mathutils import Matrix, Vector, Quaternion


def get_matrix_basis_from_fcurve(pbone, fcurves, frame):

    vecs = [Vector(), Vector([1,1,1,1])]
    props = ('location', 'scale')
    for j in range(len(props)):
        data_path = pbone.path_from_id(props[j])
        for i in range(3):
            fc = fcurves.find(data_path, index=i)
            if fc is None:
                continue
            vecs[j][i] = fc.evaluate(frame)
    trans = Matrix.Translation(vecs[0])
    scale = Matrix.Diagonal(vecs[1])


    data_path = pbone.path_from_id('rotation_quaternion')
    quat = Quaternion()
    temp_quat = Quaternion()
    for i in range(4):
        fc = fcurves.find(data_path, index=i)
        if fc is None:
            break
        temp_quat[i] = fc.evaluate(frame)
    else:
        quat = temp_quat
    rot = quat.to_matrix().to_4x4()
        
    return trans @ rot @ scale


def get_pose_matrices(obj, matrix_map, frame):
    "Assign pose space matrices of all bones at once, ignoring constraints."

    fcurves = obj.animation_data.action.fcurves

    def rec(pbone):

        matrix_basis = get_matrix_basis_from_fcurve(pbone, fcurves, frame)

        # Compute the updated pose matrix from local and new parent matrix
        if pbone.parent:
            matrix_map[pbone.name] = pbone.bone.convert_local_to_pose(
                    matrix_basis,
                    pbone.bone.matrix_local,
                    parent_matrix=matrix_map[pbone.parent.name],
                    parent_matrix_local=pbone.parent.bone.matrix_local,
            )
        else:
            matrix_map[pbone.name] = pbone.bone.convert_local_to_pose(
                    matrix_basis,
                    pbone.bone.matrix_local,
                )

        # Recursively process children, passing the new matrix through
        for child in pbone.children:
            rec(child)

    # Scan all bone trees from their roots
    for pbone in obj.pose.bones:
        if not pbone.parent:
            rec(pbone)
            

obj = bpy.context.object
matrix_map = {}
frame = 12
get_pose_matrices(obj, matrix_map, frame)
print(matrix_map[obj.data.bones[0].name])
 
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .