Bone Matrices.
A bone is defined along its local Y axis. The roll is a rotation around the y axis.
Let's say for example sake, we add a default single bone armature, and give bone a roll of 45 degrees.
Edit Mode
>>> C.active_bone
bpy.data.armatures['Armature.001']...EditBone
>>> C.active_bone.roll = radians(45)
>>> degrees(C.active_bone.roll)
45.00000125223908
If we look at its matrix
>>> [degrees(a) for a in C.active_bone.matrix.to_euler()]
[90.00000250447816, -0.0, 45.00000125223908]
The 90 about X explains why it is standing up, and the roll we set is about Z.
If the matrix is set to Identity (zero position) the bone would point in the Y direction. A bone vector is (0, bone.length, 0)
where default length is 1.
Pose mode.
Ok off to pose mode. The edit bone settings are the zero settings, aka rest pose. We set the pose bones matrix_basis
when we alter loc, rot, scale in posebone properties panel. With no pose set each pose bone will have a size 4 identity matrix.
>>> C.active_pose_bone.matrix_basis
Matrix(((1.0, 0.0, 0.0, 0.0),
(0.0, 1.0, 0.0, 0.0),
(0.0, 0.0, 1.0, 0.0),
(0.0, 0.0, 0.0, 1.0)))
and a familiar set of results from the PoseBone.matrix
. Note the matrix here is a 4x4 so as well as rotation has translation and scale.
>>> [degrees(a) for a in C.active_pose_bone.matrix.to_euler()]
[90.00000250447816, -0.0, 45.00000125223908]
Ok lets rotate (roll) the pose bone with RYY45 ie rotate bone by 45 degrees about its local Y axis.
>>> [degrees(a) for a in C.active_pose_bone.matrix_basis.to_euler()]
[0.0, 45.00000466733367, 0.0]
and
>>> [degrees(a) for a in C.active_pose_bone.matrix.to_euler()]
[90.00000250447816, -0.0, 89.999995674289]
Notice how we get 90 now for the rotation Z roll, which takes into account 45 for the edit bone roll, plus 45 for the pose bone roll,
Alternatively Using Bone.AxisRollFromMatrix
Arbitrarily placed bone with 41 degree bone roll
>>> degrees(C.active_bone.roll) # in edit mode
41.33893447974762
switch to pose mode
>>> pb = C.active_pose_bone
>>> pb
bpy.data.objects['Armature'].pose.bones["Bone"]
>>> b = pb.bone
>>> b
bpy.data.armatures['Armature.002'].bones["Bone"]
pose bone locally rolled 30 degrees
>> [degrees(a) for a in pb.matrix_basis.to_euler()]
[-9.504621014570856e-07, 30.00000424992064, 1.5754678639128658e-06]
edit bone roll from pose bone
>>> axis, angle = b.AxisRollFromMatrix(b.matrix, axis=pb.y_axis)
>>> degrees(angle)
41.33893106465304
Combined roll
>>> axis, angle = b.AxisRollFromMatrix(pb.matrix.to_3x3(), axis=pb.y_axis)
>>> degrees(angle)
71.33894214476285