5
$\begingroup$

I have written some code that calculates the world space coordinate I want to move the bone to but i am having trouble figuring out how to get that back into the bones coordinates.

from bpy import context, data
from mathutils import Vector

def test_empty(matrix, name):
    # view matrix by applying it to an object
    obj_empty = data.objects.new(name, None)
    context.scene.objects.link(obj_empty)
    obj_empty.matrix_world = matrix


armature = data.objects["Armature"]
pbones = armature.pose.bones
root = pbones["root"]
hips = pbones["hips"]
test = pbones["test"]

# calculate root
root_matrix = armature.matrix_world * root.matrix
test_empty(root_matrix, "Root")

# calculate hips
hips_matrix = armature.matrix_world * hips.matrix
test_empty(hips_matrix, "Hips")

# calculate bone
test_matrix = armature.matrix_world * test.matrix
test_empty(test_matrix, "Test")

# get offset of hips from root bone
offset = root_matrix.translation - hips_matrix.translation
print(offset)

# calculate new location in local space
new_position = test_matrix.translation + Vector((0,offset.y,0))

obj_empty = data.objects.new("TEST", None)
context.scene.objects.link(obj_empty)
obj_empty.location = new_position

# obviously something wrong here
new_position = armature.matrix_world.inverted() * new_position

# move bone to new location
test.location = new_position
$\endgroup$

1 Answer 1

9
$\begingroup$

Use Object.convert_space

There is a handy method on the object to do this for you. Example below, adds an empty at each pose bone (to test pose to world) and then uses this to convert back to pose and checks against bones matrix.

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

for pb in ob.pose.bones:
    # convert from pose to world
    mw = ob.convert_space(pose_bone=pb, 
            matrix=pb.matrix, 
            from_space='POSE', 
            to_space='WORLD')
    mt = bpy.data.objects.new(pb.name, None)
    mt.name = pb.name
    mt.matrix_world = mw
    scene.objects.link(mt)
    # now back from world to pose
    mp = ob.convert_space(pose_bone=pb,
            matrix=mw,
            from_space = 'WORLD',
            to_space = 'POSE')

    print("Pose bone matrix", pb.matrix)
    print("Matrix World to pb.matrix", mp)
    print((mp - pb.matrix)) # zero matrix if all is good

To move a bone to the cursor

pb = ob.pose.bones.get("root")
mw = ob.convert_space(pose_bone=pb, 
        matrix=pb.matrix, 
        from_space='POSE', 
        to_space='WORLD')
mw.translation = scene.cursor_location
pb.matrix = ob.convert_space(pose_bone=pb, 
        matrix=mw, 
        from_space='WORLD', 
        to_space='POSE')

Note: If the pose bone has a joined parent bone this may not work as expected as it wont break that bond.

Links

How can I manually calculate bpy.types.PoseBone.matrix using Blender's Python API?

$\endgroup$
2
  • $\begingroup$ in 2.90 : pose.bones.get returns "TypeError: this type doesn't support IDProperties" $\endgroup$
    – Phil
    May 30, 2022 at 15:16
  • $\begingroup$ and "AttributeError: 'PoseBone' object has no attribute 'convert_space'" $\endgroup$
    – Phil
    May 30, 2022 at 15:17

You must log in to answer this question.

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