1
$\begingroup$

I have 2 rigs with the exact same bone topology, but with different rest poses, see left image below. The T-Pose Rig is displayed in green, the A-Pose rig is displayed in black. Now i want to align the pose bones of the T-pose rig to the edit bones of the A-Pose rig as shown in the right image below.:

I tried to do this by copying the edit_bone.matrix values of the A-Pose rig to the pose bone matrices of the T-Pose rig and on first sight this seems to work perfectly (if all bones are disconnected).

        #Assume the context.object is the t_rig
        for bone in t_rig.pose.bones:
            bone.matrix = a_rig.data.bones[bone.name].matrix_local
            #need to do this, not sure why:
            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.mode_set(mode='POSE')

But...

I have a problem with the Bone Roll. Here is what happens on the upper arm bone in my example:

For the T-Pose Rig the bone roll z-axis of the arm bones is aligned to the world z-axis, while for the A-Pose rig the bone roll is rotated by 45 degree:

So, when i simply copy the bone matrices, then the bone rolls gets rotated as well. But this has an undesired impact on the bound meshes: the mesh gets rotated with the bone roll:

What i really want to achieve is to copy the matrices without changing the longitudinal rotation of the bones so that the arms are not rotated:

One way to do this is by working through the T-pose rig from top to bottom and for each pose bone do:

  • move the pose bone head to the corresponding location on the A-Pose rig
  • Calculate the rotation difference between the pose bone on the t-pose rig and the edit_bone on the a-pose rig
  • Apply the rotation difference to the pose bone on the T-Pose rig.

However i wonder if there is a better way to align the pose bones of one rig to the edit bones of another rig and keeping the longitudinal bone rotation unchanged.

$\endgroup$
  • $\begingroup$ Maybe you can save the roll before copying the matrix (roll = t_rig.data.edit_bones[bone.name].roll) then reset it after the copy (t_rig.data.edit_bones[bone.name].roll = roll) ? $\endgroup$ – lemon Sep 28 '16 at 16:11
  • $\begingroup$ The bone rolls of all bones is 0 for both rigs. Only the longitudinal rotations of the bones differ. so saving the roll from one rig and reapply after copy makes no difference :( $\endgroup$ – Gaia Clary Sep 28 '16 at 16:20
  • $\begingroup$ If you use ordinary parenting (inherit rotation, translation, scale) and do not use any constraints, this answer should apply for this case, too. $\endgroup$ – pink vertex Sep 29 '16 at 8:58
1
$\begingroup$

This requires a lot of matrix math which is complicated by the fact that the mathematical relationship between the various python-accessible matrices and fields is almost undocumented.

Here is an excerpt from http://web.purplefrog.com/~thoth/blender/python-cookbook/pose-match.html that should accomplish your mission:

import bpy
from mathutils import *


def matrix_scale(scale_vec):
    return Matrix([[scale_vec[0],0,0,0],
                   [0,scale_vec[1],0,0],
                   [0,0,scale_vec[2],0],
                   [0,0,0,1]
    ])

def matrix_for_bone_from_parent(bone, ao):
    eb1 = ao.data.bones[bone.name]
    E = eb1.matrix_local # * Matrix.Scale(eb1.length,4)
    ebp = ao.data.bones[bone.name].parent
    E_p = ebp.matrix_local # * Matrix.Scale(ebp.length,4)
    return E_p.inverted() * E

def matrix_the_hard_way(pose_bone, ao):
    if pose_bone.rotation_mode == 'QUATERNION':
        mr = pose_bone.rotation_quaternion.to_matrix().to_4x4()
    else:
        mr = pose_bone.rotation_euler.to_matrix().to_4x4()
    m1 = Matrix.Translation(pose_bone.location) * mr * matrix_scale(pose_bone.scale)

    E = ao.data.bones[pose_bone.name].matrix_local
    if pose_bone.parent is None:
        return E * m1
    else:
        m2 = matrix_the_hard_way(pose_bone.parent, ao)
        E_p = ao.data.bones[pose_bone.parent.name].matrix_local
        return m2 * E_p.inverted() * E * m1

def pose_to_match(arm, goal):
    """
    pose arm so that its bones line up with the REST pose of goal
    """

    matrix_os= {}
    for to_match in goal.data.bones:
        matrix_os[to_match.name] = to_match.matrix_local
        #print([ "matrix", to_match.name, matrix_os[to_match.name] ] )

    #xyz' = s * m * m(parent) * xyz

    for to_pose in arm.pose.bones:
        if to_pose.parent is None:
            len2 = arm.data.bones[to_pose.name].length
            len1 = goal.data.bones[to_pose.name].length
            to_pose.matrix = matrix_os[to_pose.name] * Matrix.Scale(len1/len2, 4)
        else:
            # we can not set .matrix, because a lot of stuff behind the scenes has not yet
            # caught up with our alterations, and it ends up doing math on outdated numbers
            mp = matrix_the_hard_way(to_pose.parent, arm) * matrix_for_bone_from_parent(to_pose, arm)
            m2 = mp.inverted()* matrix_os[to_pose.name] * Matrix.Scale(goal.data.bones[to_pose.name].length, 4)
            loc,rot,scale = m2.decompose()
            to_pose.location = loc
            if 'QUATERNION' == to_pose.rotation_mode:
                to_pose.rotation_quaternion = rot
            else:
                to_pose.rotation_euler = rot.to_euler(to_pose.rotation_mode)
            to_pose.scale = scale / arm.data.bones[to_pose.name].length



#
#
#

pose_to_match(bpy.data.objects['gamma'], bpy.data.objects['beta'])
$\endgroup$
0
$\begingroup$

I think I'm in a similar situation, actually every google search I make I end sooner or later on one of your posts, that looks exactly my problem.

For this issue, I think the solution would be to make a script that:

  1. for every bone in T-Pose add a bone constraint to copy the transforms in the A-Pose. (not sure if this works in all skeletons, but at least it worked flawless in my samples)
  2. Apply Visual Transform to Pose.
  3. Remove all the bone constraints added in Step 1.

The T-Pose (that still keeps the Rest Pose as it was) should be now posed in the A-Pose configuration. Now the pose could be saved into the Pose Library.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.