# Calculate a pose bone's transformation matrix from its F-curve without updating

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.

# 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])
 `