1
$\begingroup$

I think this question has been ask a few times but haven't been able to produce an answer.

So for an addon I'm building I need to send the bone rotations of an armature. Those rotations are expected to be in global coordinates. So after doing to bone rotations that I need, I collect the rotations like so:

        for bone in context.object.pose.bones:

            bone_idx: int = get_bone_index(skeleton_info, bone)

            idx = list(context.object.pose.bones).index(bone)
            m = matrices[idx]
            axis_angle = bone.rotation_quaternion.to_axis_angle()
            v_axis = axis_angle[0]
            result = (bone.id_data.matrix_world.to_3x3().inverted()
                      @ m.inverted()
                      @ v_axis)

            result.normalize()
            
            result = result * axis_angle[1]
            pose_np[bone_idx*3] = result[0]
            pose_np[bone_idx*3+1] = result[1]
            pose_np[bone_idx*3+2] = result[2]

As you can see I get the quaternion local value, get the axis angle and perform some transformations on the vector representing that axis in order to get to the system of coordinates I want.

The thing is the matrices I expect contain weird values. I don't really understand how to use the matrices inside the PoseBone class to achieve what I want to do... The usage of matrix, matrix_local and matrix_channel confuses me especially to see how each bone accumulates transformations and whether if I need to call the inverse of the parent's object matrix world... Could you please give me some hints on what I might be doing wrong?

Thanks!

$\endgroup$

3 Answers 3

1
$\begingroup$

Not sure what you need. Try this for rotation between local space and world space

for pbone in context.object.pose.bones:
    # This is the transform from bone local space to world space
    world = context.object.matrix_world @ pbone.matrix

    # If there are non-uniform scalings or shears in `world`, this won't work...
    _loc, rot, _scale = world.decompose()
    
    print(rot.to_axis_angle())

If you want the relative rotation from the rest pose to the current pose in world space, maybe this

for pbone in context.object.pose.bones:
    world = context.object.matrix_world @ pbone.matrix
    world_rest = context.object.matrix_world @ pbone.bone.matrix_local

    _loc, rot, _scale = world.decompose()
    _loc, rot_rest, _scale = world_rest.decompose()
    
    print(rot.rotation_difference(rot_rest).to_axis_angle())
$\endgroup$
1
$\begingroup$

I ended up doing the following:

        for pose_bone in context.object.pose.bones:

            # We'll translate from bone coords to avatar coords
            # using the matrix_local matrix in the actual bone
            # (not the pose bone) which contains the rest pose matrix

            # From matrix_local we'll extract the rotation
            rot = pose_bone.bone.matrix_local.decompose()[1]
            rot = rot.to_matrix().to_3x3()

            # Apply the rotation to the direction axis angle
            axis_angle = pose_bone.rotation_quaternion.to_axis_angle()
            new_axis = rot @ axis_angle[0]
            new_axis.normalize()

            # Get the actual axis angle multiplying the magnitude
            # with the unitary vector calculated earlier
            result = new_axis * axis_angle[1]

            # Assign the rotations to the locations avatar-core expects
            # on the pose numpy array
            bone_idx: int = get_bone_index(skeleton_info, pose_bone)
            pose_np[bone_idx*3] = result[0]
            pose_np[bone_idx*3+1] = result[1]
            pose_np[bone_idx*3+2] = result[2]
$\endgroup$
0
$\begingroup$

Object.convert_space

There is a convenience method available on objects to help convert spaces. For pose bones, can pass the pose bone, its pose matrix pb.matrix and convert it from 'POSE' space to world.

Little example script, to run in 'OBJECT' mode, only because it is placing empties using calculated world matrix. The add empty operator requires 'OBJECT' mode.

import bpy
context = bpy.context
ob = context.object

for pb in ob.pose.bones:
    mw = ob.convert_space(
            pose_bone=pb,
            matrix=pb.matrix,
            from_space='POSE',
            to_space='WORLD',
            )


    bpy.ops.object.empty_add()
    mt = context.object
    mt.empty_display_type = 'ARROWS'
    mt.matrix_world = mw

Note this aligns empties with blender bones, such the shaft of the bone is in the Y axis of the world matrix.

Or from a bones local space with

mw = ob.convert_space(
        pose_bone=pb,
        matrix=pb.matrix_basis,
        from_space='LOCAL',
        to_space='WORLD',
        )            

Should you require to change this to can use bpy_extras.io_utils.axis_conversion to transform to another space

>>> bpy_extras.io_utils.axis_conversion(
axis_conversion(from_forward='Y', from_up='Z', to_forward='Y', to_up='Z')
Each argument us an axis in ['X', 'Y', 'Z', '-X', '-Y', '-Z']
where the first 2 are a source and the second 2 are the target.

Same as above with resultant world matrix pointing z axis along bone.

import bpy
context = bpy.context
ob = context.object
from bpy_extras.io_utils import axis_conversion

A = axis_conversion(
    to_up='Y',
    to_forward='Z'
    ).to_4x4()
        
for pb in ob.pose.bones:
    mw = ob.convert_space(
            pose_bone=pb,
            matrix=pb.matrix,
            from_space='POSE',
            to_space='WORLD',
            )


    bpy.ops.object.empty_add()
    mt = context.object
    mt.empty_display_type = 'ARROWS'
    mt.matrix_world = mw @ A

Python, How to get pose bone to rotate with another pose bone from a different armature? Finding Rotational difference

Python How to make pose bone rotation toward a point?

How can I move a PoseBone to a specific world space position?

$\endgroup$
1
  • $\begingroup$ Thanks! Didn't know such function existed! I'll take a look at it to see if it fits our needs! $\endgroup$
    – AlFranco
    Apr 22, 2021 at 12:59

You must log in to answer this question.

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