diff options
author | Nathan Vegdahl <cessen@cessen.com> | 2011-03-10 12:18:09 +0300 |
---|---|---|
committer | Nathan Vegdahl <cessen@cessen.com> | 2011-03-10 12:18:09 +0300 |
commit | 887868444d4b4cec6fdae8a3b6f277e5e35527be (patch) | |
tree | ed107dfb35d74a82d4b7b9506b0ee0e83855a942 /rigify/rig_ui_template.py | |
parent | e9efe82cd0af0a99567340bf1d8e458dfe1acb86 (diff) |
Rigify:
Clean-up of the IK/FK snapping code. Should be much more maintainable now.
Also changed rig id generation. Rig id's now consist of a random
alphanumeric string 8 characters long, with the smallest 8 digits of seconds
since the epoc (in hex) at the time of rig generation appended on the end.
This results in a 16-character string that is ludicrously unlikely to
have any collisions between rigs. 36^8 * 16^8, with the 16^8 being very well
distributed over time. Ah... paranoia.
Diffstat (limited to 'rigify/rig_ui_template.py')
-rw-r--r-- | rigify/rig_ui_template.py | 380 |
1 files changed, 193 insertions, 187 deletions
diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py index c9f4bdfc..282a7e98 100644 --- a/rigify/rig_ui_template.py +++ b/rigify/rig_ui_template.py @@ -24,17 +24,21 @@ from math import acos rig_id = "%s" -def get_pose_matrix_in_other_space(mat, pbs): - """ Returns the transform matrix relative to pbs's transform space. - In other words, you could take the matrix returned from this, slap - it into pbs, and it would have the same world transform as mat. - This is with constraints applied. +######################################### +## "Visual Transform" helper functions ## +######################################### + +def get_pose_matrix_in_other_space(mat, pose_bone): + """ Returns the transform matrix relative to pose_bone's current + transform space. In other words, presuming that mat is in + armature space, slapping the returned matrix onto pose_bone + should give it the armature-space transforms of mat. """ - rest_inv = pbs.bone.matrix_local.inverted() + rest_inv = pose_bone.bone.matrix_local.inverted() - if pbs.parent: - par_inv = pbs.parent.matrix.inverted() - par_rest = pbs.parent.bone.matrix_local.copy() + if pose_bone.parent: + par_inv = pose_bone.parent.matrix.inverted() + par_rest = pose_bone.parent.bone.matrix_local.copy() smat = rest_inv * (par_rest * (par_inv * mat)) else: @@ -43,135 +47,130 @@ def get_pose_matrix_in_other_space(mat, pbs): return smat -def get_local_matrix_with_constraints(pose_bone): - """ Returns the local matrix of the given pose bone, with constraints - applied. +def get_local_pose_matrix(pose_bone): + """ Returns the local transform matrix of the given pose bone. """ return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone) -def set_pose_rotation(pb, mat): - """ Sets the pose bone's rotation to the same rotation as the given matrix. - Matrix should be given in local space. - """ - q = mat.to_quaternion() - - if pb.rotation_mode == 'QUATERNION': - pb.rotation_quaternion = q - elif pb.rotation_mode == 'AXIS_ANGLE': - pb.rotation_axis_angle[0] = q.angle - pb.rotation_axis_angle[1] = q.axis[0] - pb.rotation_axis_angle[2] = q.axis[1] - pb.rotation_axis_angle[3] = q.axis[2] - else: - pb.rotation_euler = q.to_euler(pb.rotation_mode) - - -def set_pose_scale(pb, mat): - """ Sets the pose bone's scale to the same scale as the given matrix. - Matrix should be given in local space. - """ - pb.scale = mat.to_scale() - - -def set_pose_translation(pb, mat): +def set_pose_translation(pose_bone, mat): """ Sets the pose bone's translation to the same translation as the given matrix. - Matrix should be given in local space. + Matrix should be given in bone's local space. """ - if pb.bone.use_local_location == True: - pb.location = mat.to_translation() + if pose_bone.bone.use_local_location == True: + pose_bone.location = mat.to_translation() else: loc = mat.to_translation() - rest = pb.bone.matrix_local.copy() - if pb.bone.parent: - par_rest = pb.bone.parent.matrix_local.copy() + rest = pose_bone.bone.matrix_local.copy() + if pose_bone.bone.parent: + par_rest = pose_bone.bone.parent.matrix_local.copy() else: par_rest = Matrix() q = (par_rest.inverted() * rest).to_quaternion() - pb.location = loc * q + pose_bone.location = loc * q -def fk2ik_arm(obj, fk, ik): - """ Matches the fk bones in an arm rig to the ik bones. +def set_pose_rotation(pose_bone, mat): + """ Sets the pose bone's rotation to the same rotation as the given matrix. + Matrix should be given in bone's local space. """ - pb = obj.pose.bones + q = mat.to_quaternion() - uarm = pb[fk[0]] - farm = pb[fk[1]] - hand = pb[fk[2]] + if pose_bone.rotation_mode == 'QUATERNION': + pose_bone.rotation_quaternion = q + elif pose_bone.rotation_mode == 'AXIS_ANGLE': + pose_bone.rotation_axis_angle[0] = q.angle + pose_bone.rotation_axis_angle[1] = q.axis[0] + pose_bone.rotation_axis_angle[2] = q.axis[1] + pose_bone.rotation_axis_angle[3] = q.axis[2] + else: + pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode) - uarmi = pb[ik[0]] - farmi = pb[ik[1]] - handi = pb[ik[2]] - uarmmat = get_pose_matrix_in_other_space(uarmi.matrix, uarm) - set_pose_rotation(uarm, uarmmat) - set_pose_scale(uarm, uarmmat) - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.mode_set(mode='POSE') +def set_pose_scale(pose_bone, mat): + """ Sets the pose bone's scale to the same scale as the given matrix. + Matrix should be given in bone's local space. + """ + pose_bone.scale = mat.to_scale() - farmmat = get_pose_matrix_in_other_space(farmi.matrix, farm) - set_pose_rotation(farm, farmmat) - set_pose_scale(farm, farmmat) - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.mode_set(mode='POSE') - handmat = get_pose_matrix_in_other_space(handi.matrix, hand) - set_pose_rotation(hand, handmat) - set_pose_scale(hand, handmat) +def match_pose_translation(pose_bone, target_bone): + """ Matches pose_bone's visual translation to target_bone's visual + translation. + This function assumes you are in pose mode on the relevant armature. + """ + mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone) + set_pose_translation(pose_bone, mat) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') -def ik2fk_arm(obj, fk, ik): - """ Matches the ik bones in an arm rig to the fk bones. +def match_pose_rotation(pose_bone, target_bone): + """ Matches pose_bone's visual rotation to target_bone's visual + rotation. + This function assumes you are in pose mode on the relevant armature. """ - pb = obj.pose.bones - - uarm = pb[fk[0]] - farm = pb[fk[1]] - hand = pb[fk[2]] + mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone) + set_pose_rotation(pose_bone, mat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') - uarmi = pb[ik[0]] - farmi = pb[ik[1]] - handi = pb[ik[2]] - pole = pb[ik[3]] - # Hand position - handmat = get_pose_matrix_in_other_space(hand.matrix, handi) - set_pose_translation(handi, handmat) - set_pose_rotation(handi, handmat) - set_pose_scale(handi, handmat) +def match_pose_scale(pose_bone, target_bone): + """ Matches pose_bone's visual scale to target_bone's visual + scale. + This function assumes you are in pose mode on the relevant armature. + """ + mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone) + set_pose_scale(pose_bone, mat) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='POSE') - # Pole target position - a = uarm.matrix.to_translation() - b = farm.matrix.to_translation() + farm.vector - # Vector from the head of the upper arm to the - # tip of the forearm - armv = b - a +############################## +## IK/FK snapping functions ## +############################## + +def match_pole_target(ik_first, ik_last, pole, match_bone, length): + """ Places an IK chain's pole target to match ik_first's + transforms to match_bone. All bones should be given as pose bones. + You need to be in pose mode on the relevant armature object. + ik_first: first bone in the IK chain + ik_last: last bone in the IK chain + pole: pole target bone for the IK chain + match_bone: bone to match ik_first to (probably first bone in a matching FK chain) + length: distance pole target should be placed from the chain center + """ + a = ik_first.matrix.to_translation() + b = ik_last.matrix.to_translation() + ik_last.vector + + # Vector from the head of ik_first to the + # tip of ik_last + ikv = b - a - # Create a vector that is not aligned with armv. + # Create a vector that is not aligned with ikv. # It doesn't matter what vector. Just any vector # that's guaranteed to not be pointing in the same - # direction. - if abs(armv[0]) < abs(armv[1]) and abs(armv[0]) < abs(armv[2]): + # direction. In this case, we create a unit vector + # on the axis of the smallest component of ikv. + if abs(ikv[0]) < abs(ikv[1]) and abs(ikv[0]) < abs(ikv[2]): v = Vector((1,0,0)) - elif abs(armv[1]) < abs(armv[2]): + elif abs(ikv[1]) < abs(ikv[2]): v = Vector((0,1,0)) else: v = Vector((0,0,1)) - # cross v with armv to get a vector perpendicular to armv - pv = v.cross(armv).normalized() * (uarm.length + farm.length) + # Get a vector perpendicular to ikv + pv = v.cross(ikv).normalized() * length def set_pole(pvi): + """ Set pole target's position based on a vector + from the arm center line. + """ # Translate pvi into armature space - ploc = a + (armv/2) + pvi + ploc = a + (ikv/2) + pvi # Set pole target to location mat = get_pose_matrix_in_other_space(Matrix().Translation(ploc), pole) @@ -182,50 +181,99 @@ def ik2fk_arm(obj, fk, ik): set_pole(pv) - # Get the rotation difference between the ik and fk upper arms - q1 = uarm.matrix.to_quaternion() - q2 = uarmi.matrix.to_quaternion() + # Get the rotation difference between ik_first and match_bone + q1 = ik_first.matrix.to_quaternion() + q2 = match_bone.matrix.to_quaternion() angle = acos(min(1,max(-1,q1.dot(q2)))) * 2 # Compensate for the rotation difference - if angle > 0.00001: - pv *= Matrix.Rotation(angle, 4, armv).to_quaternion() + if angle > 0.0001: + pv *= Matrix.Rotation(angle, 4, ikv).to_quaternion() set_pole(pv) - q1 = uarm.matrix.to_quaternion() - q2 = uarmi.matrix.to_quaternion() + # Get rotation difference again, to see if we + # compensated in the right direction + q1 = ik_first.matrix.to_quaternion() + q2 = match_bone.matrix.to_quaternion() angle2 = acos(min(1,max(-1,q1.dot(q2)))) * 2 - if angle2 > 0.00001: - pv *= Matrix.Rotation((angle*(-2)), 4, armv).to_quaternion() + if angle2 > 0.0001: + # Compensate in the other direction + pv *= Matrix.Rotation((angle*(-2)), 4, ikv).to_quaternion() set_pole(pv) -def fk2ik_leg(obj, fk, ik): - """ Matches the fk bones in a leg rig to the ik bones. +def fk2ik_arm(obj, fk, ik): + """ Matches the fk bones in an arm rig to the ik bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names """ - pb = obj.pose.bones + uarm = obj.pose.bones[fk[0]] + farm = obj.pose.bones[fk[1]] + hand = obj.pose.bones[fk[2]] + uarmi = obj.pose.bones[ik[0]] + farmi = obj.pose.bones[ik[1]] + handi = obj.pose.bones[ik[2]] - thigh = pb[fk[0]] - shin = pb[fk[1]] - foot = pb[fk[2]] - mfoot = pb[fk[3]] + # Upper arm position + match_pose_rotation(uarm, uarmi) + match_pose_scale(uarm, uarmi) - thighi = pb[ik[0]] - shini = pb[ik[1]] - mfooti = pb[ik[2]] + # Forearm position + match_pose_rotation(farm, farmi) + match_pose_scale(farm, farmi) - thighmat = get_pose_matrix_in_other_space(thighi.matrix, thigh) - set_pose_rotation(thigh, thighmat) - set_pose_scale(thigh, thighmat) - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.mode_set(mode='POSE') + # Hand position + match_pose_rotation(hand, handi) + match_pose_scale(hand, handi) - shinmat = get_pose_matrix_in_other_space(shini.matrix, shin) - set_pose_rotation(shin, shinmat) - set_pose_scale(shin, shinmat) - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.mode_set(mode='POSE') +def ik2fk_arm(obj, fk, ik): + """ Matches the ik bones in an arm rig to the fk bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names + """ + uarm = obj.pose.bones[fk[0]] + farm = obj.pose.bones[fk[1]] + hand = obj.pose.bones[fk[2]] + uarmi = obj.pose.bones[ik[0]] + farmi = obj.pose.bones[ik[1]] + handi = obj.pose.bones[ik[2]] + pole = obj.pose.bones[ik[3]] + + # Hand position + match_pose_translation(handi, hand) + match_pose_rotation(handi, hand) + match_pose_scale(handi, hand) + + # Pole target position + match_pole_target(uarmi, farmi, pole, uarm, (uarmi.length + farmi.length)) + + +def fk2ik_leg(obj, fk, ik): + """ Matches the fk bones in a leg rig to the ik bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names + """ + thigh = obj.pose.bones[fk[0]] + shin = obj.pose.bones[fk[1]] + foot = obj.pose.bones[fk[2]] + mfoot = obj.pose.bones[fk[3]] + thighi = obj.pose.bones[ik[0]] + shini = obj.pose.bones[ik[1]] + mfooti = obj.pose.bones[ik[2]] + + # Thigh position + match_pose_rotation(thigh, thighi) + match_pose_scale(thigh, thighi) + + # Shin position + match_pose_rotation(shin, shini) + match_pose_scale(shin, shini) + + # Foot position mat = mfoot.bone.matrix_local.inverted() * foot.bone.matrix_local footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) * mat set_pose_rotation(foot, footmat) @@ -236,19 +284,19 @@ def fk2ik_leg(obj, fk, ik): def ik2fk_leg(obj, fk, ik): """ Matches the ik bones in a leg rig to the fk bones. + obj: armature object + fk: list of fk bone names + ik: list of ik bone names """ - pb = obj.pose.bones - - thigh = pb[fk[0]] - shin = pb[fk[1]] - mfoot = pb[fk[2]] - - thighi = pb[ik[0]] - shini = pb[ik[1]] - footi = pb[ik[2]] - footroll = pb[ik[3]] - pole = pb[ik[4]] - mfooti = pb[ik[5]] + thigh = obj.pose.bones[fk[0]] + shin = obj.pose.bones[fk[1]] + mfoot = obj.pose.bones[fk[2]] + thighi = obj.pose.bones[ik[0]] + shini = obj.pose.bones[ik[1]] + footi = obj.pose.bones[ik[2]] + footroll = obj.pose.bones[ik[3]] + pole = obj.pose.bones[ik[4]] + mfooti = obj.pose.bones[ik[5]] # Clear footroll set_pose_rotation(footroll, Matrix()) @@ -263,57 +311,12 @@ def ik2fk_leg(obj, fk, ik): bpy.ops.object.mode_set(mode='POSE') # Pole target position - a = thigh.matrix.to_translation() - b = shin.matrix.to_translation() + shin.vector - - # Vector from the head of the thigh to the - # tip of the shin - legv = b - a - - # Create a vector that is not aligned with legv. - # It doesn't matter what vector. Just any vector - # that's guaranteed to not be pointing in the same - # direction. - if abs(legv[0]) < abs(legv[1]) and abs(legv[0]) < abs(legv[2]): - v = Vector((1,0,0)) - elif abs(legv[1]) < abs(legv[2]): - v = Vector((0,1,0)) - else: - v = Vector((0,0,1)) + match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length)) - # Get a vector perpendicular to legv - pv = v.cross(legv).normalized() * (thigh.length + shin.length) - - def set_pole(pvi): - # Translate pvi into armature space - ploc = a + (legv/2) + pvi - - # Set pole target to location - mat = get_pose_matrix_in_other_space(Matrix().Translation(ploc), pole) - set_pose_translation(pole, mat) - - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.mode_set(mode='POSE') - - set_pole(pv) - - # Get the rotation difference between the ik and fk thighs - q1 = thigh.matrix.to_quaternion() - q2 = thighi.matrix.to_quaternion() - angle = acos(min(1,max(-1,q1.dot(q2)))) * 2 - - # Compensate for the rotation difference - if angle > 0.00001: - pv *= Matrix.Rotation(angle, 4, legv).to_quaternion() - set_pole(pv) - - q1 = thigh.matrix.to_quaternion() - q2 = thighi.matrix.to_quaternion() - angle2 = acos(min(1,max(-1,q1.dot(q2)))) * 2 - if angle2 > 0.00001: - pv *= Matrix.Rotation((angle*(-2)), 4, legv).to_quaternion() - set_pole(pv) +############################## +## IK/FK snapping operators ## +############################## class Rigify_Arm_FK2IK(bpy.types.Operator): """ Snaps an FK arm to an IK arm. @@ -358,7 +361,7 @@ class Rigify_Arm_IK2FK(bpy.types.Operator): uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name") farm_ik = bpy.props.StringProperty(name="Forearm IK Name") hand_ik = bpy.props.StringProperty(name="Hand IK Name") - pole = bpy.props.StringProperty(name="Pole IK Name") + pole = bpy.props.StringProperty(name="Pole IK Name") @classmethod def poll(cls, context): @@ -386,7 +389,6 @@ class Rigify_Leg_FK2IK(bpy.types.Operator): foot_fk = bpy.props.StringProperty(name="Foot FK Name") mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name") - thigh_ik = bpy.props.StringProperty(name="Thigh IK Name") shin_ik = bpy.props.StringProperty(name="Shin IK Name") mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name") @@ -443,6 +445,10 @@ bpy.utils.register_class(Rigify_Leg_FK2IK) bpy.utils.register_class(Rigify_Leg_IK2FK) +################### +## Rig UI Panels ## +################### + class RigUI(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' |