From efd386991e12320590ae9943496d4a90961f132f Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 28 Jul 2016 18:32:36 +0200 Subject: Pitchipoy FK/IK switch implemented --- rigify/generate.py | 51 ++- rigify/rig_ui_pitchipoy_template.py | 717 ++++++++++++++++++++++++++++++++++++ rigify/rigs/pitchipoy/limbs/ui.py | 126 ++++++- 3 files changed, 864 insertions(+), 30 deletions(-) create mode 100644 rigify/rig_ui_pitchipoy_template.py diff --git a/rigify/generate.py b/rigify/generate.py index 4fb83f5d..0536377e 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -32,6 +32,8 @@ from .utils import create_root_widget from .utils import random_id from .utils import copy_attributes from .rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER +from .rig_ui_pitchipoy_template import UI_P_SLIDERS, layers_P_ui, UI_P_REGISTER + RIG_MODULE = "rigs" ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to. @@ -385,18 +387,35 @@ def generate_rig(context, metarig): print( l.name ) layer_layout += [(l.name, l.row)] - # Generate the UI script - if "rig_ui.py" in bpy.data.texts: - script = bpy.data.texts["rig_ui.py"] - script.clear() + + if isPitchipoy(metarig): + + # Generate the UI Pitchipoy script + if "rig_ui.py" in bpy.data.texts: + script = bpy.data.texts["rig_ui.py"] + script.clear() + else: + script = bpy.data.texts.new("rig_ui.py") + script.write(UI_P_SLIDERS % rig_id) + for s in ui_scripts: + script.write("\n " + s.replace("\n", "\n ") + "\n") + script.write(layers_P_ui(vis_layers, layer_layout)) + script.write(UI_P_REGISTER) + script.use_module = True + else: - script = bpy.data.texts.new("rig_ui.py") - script.write(UI_SLIDERS % rig_id) - for s in ui_scripts: - script.write("\n " + s.replace("\n", "\n ") + "\n") - script.write(layers_ui(vis_layers, layer_layout)) - script.write(UI_REGISTER) - script.use_module = True + # Generate the UI script + if "rig_ui.py" in bpy.data.texts: + script = bpy.data.texts["rig_ui.py"] + script.clear() + else: + script = bpy.data.texts.new("rig_ui.py") + script.write(UI_SLIDERS % rig_id) + for s in ui_scripts: + script.write("\n " + s.replace("\n", "\n ") + "\n") + script.write(layers_ui(vis_layers, layer_layout)) + script.write(UI_REGISTER) + script.use_module = True # Run UI script exec(script.as_string(), {}) @@ -451,3 +470,13 @@ def param_name(param_name, rig_type): """ Get the actual parameter name, sans-rig-type. """ return param_name[len(rig_type) + 1:] + +def isPitchipoy(metarig): + """ Returns True if metarig is type pitchipoy. + """ + pbones=metarig.pose.bones + for pb in pbones: + words = pb.rigify_type.partition('.') + if words[0] == 'pitchipoy': + return True + return False \ No newline at end of file diff --git a/rigify/rig_ui_pitchipoy_template.py b/rigify/rig_ui_pitchipoy_template.py new file mode 100644 index 00000000..da3bda7e --- /dev/null +++ b/rigify/rig_ui_pitchipoy_template.py @@ -0,0 +1,717 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# + +UI_P_SLIDERS = ''' +import bpy +from mathutils import Matrix, Vector +from math import acos, pi, radians + +rig_id = "%s" + + +############################ +## Math utility functions ## +############################ + +def perpendicular_vector(v): + """ Returns a vector that is perpendicular to the one given. + The returned vector is _not_ guaranteed to be normalized. + """ + # Create a vector that is not aligned with v. + # It doesn't matter what vector. Just any vector + # that's guaranteed to not be pointing in the same + # direction. + if abs(v[0]) < abs(v[1]): + tv = Vector((1,0,0)) + else: + tv = Vector((0,1,0)) + + # Use cross prouct to generate a vector perpendicular to + # both tv and (more importantly) v. + return v.cross(tv) + + +def rotation_difference(mat1, mat2): + """ Returns the shortest-path rotational difference between two + matrices. + """ + q1 = mat1.to_quaternion() + q2 = mat2.to_quaternion() + angle = acos(min(1,max(-1,q1.dot(q2)))) * 2 + if angle > pi: + angle = -angle + (2*pi) + return angle + +def tail_distance(angle,bone_ik,bone_fk): + """ Returns the distance between the tails of two bones + after rotating bone_ik in AXIS_ANGLE mode. + """ + rot_mod=bone_ik.rotation_mode + if rot_mod != 'AXIS_ANGLE': + bone_ik.rotation_mode = 'AXIS_ANGLE' + bone_ik.rotation_axis_angle[0] = angle + bpy.context.scene.update() + + dv = (bone_fk.tail - bone_ik.tail).length + + bone_ik.rotation_mode = rot_mod + return dv + +def find_min_range(bone_ik,bone_fk,f=tail_distance,delta=pi/8): + """ finds the range where lies the minimum of function f applied on bone_ik and bone_fk + at a certain angle. + """ + rot_mod=bone_ik.rotation_mode + if rot_mod != 'AXIS_ANGLE': + bone_ik.rotation_mode = 'AXIS_ANGLE' + + start_angle = bone_ik.rotation_axis_angle[0] + angle = start_angle + while (angle > (start_angle - 2*pi)) and (angle < (start_angle + 2*pi)): + l_dist = f(angle-delta,bone_ik,bone_fk) + c_dist = f(angle,bone_ik,bone_fk) + r_dist = f(angle+delta,bone_ik,bone_fk) + if min((l_dist,c_dist,r_dist)) == c_dist: + bone_ik.rotation_mode = rot_mod + return (angle-delta,angle+delta) + else: + angle=angle+delta + +def ternarySearch(f, left, right, bone_ik, bone_fk, absolutePrecision): + """ + Find minimum of unimodal function f() within [left, right] + To find the maximum, revert the if/else statement or revert the comparison. + """ + while True: + #left and right are the current bounds; the maximum is between them + if abs(right - left) < absolutePrecision: + return (left + right)/2 + + leftThird = left + (right - left)/3 + rightThird = right - (right - left)/3 + + if f(leftThird, bone_ik, bone_fk) > f(rightThird, bone_ik, bone_fk): + left = leftThird + else: + right = rightThird + +######################################### +## "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. + TODO: try to handle cases with axis-scaled parents better. + """ + rest = pose_bone.bone.matrix_local.copy() + rest_inv = rest.inverted() + if pose_bone.parent: + par_mat = pose_bone.parent.matrix.copy() + par_inv = par_mat.inverted() + par_rest = pose_bone.parent.bone.matrix_local.copy() + else: + par_mat = Matrix() + par_inv = Matrix() + par_rest = Matrix() + + # Get matrix in bone's current transform space + smat = rest_inv * (par_rest * (par_inv * mat)) + + # Compensate for non-local location + #if not pose_bone.bone.use_local_location: + # loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion() + # smat.translation = loc + + return smat + + +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_translation(pose_bone, mat): + """ Sets the pose bone's translation to the same translation as the given matrix. + Matrix should be given in bone's local space. + """ + if pose_bone.bone.use_local_location == True: + pose_bone.location = mat.to_translation() + else: + loc = mat.to_translation() + + 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() + pose_bone.location = q * loc + + +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. + """ + q = mat.to_quaternion() + + 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) + + +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() + + +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 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. + """ + 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') + + +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') + +def correct_rotation(bone_ik, bone_fk): + """ Corrects the ik rotation in ik2fk snapping functions + """ + + alfarange = find_min_range(bone_ik,bone_fk) + alfamin = ternarySearch(tail_distance,alfarange[0],alfarange[1],bone_ik,bone_fk,0.1) + + rot_mod = bone_ik.rotation_mode + if rot_mod != 'AXIS_ANGLE': + bone_ik.rotation_mode = 'AXIS_ANGLE' + bone_ik.rotation_axis_angle[0] = alfamin + bone_ik.rotation_mode = rot_mod + +############################## +## 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 + + # Get a vector perpendicular to ikv + pv = perpendicular_vector(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 + (ikv/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 ik_first and match_bone + angle = rotation_difference(ik_first.matrix, match_bone.matrix) + + # Try compensating for the rotation difference in both directions + pv1 = Matrix.Rotation(angle, 4, ikv) * pv + set_pole(pv1) + ang1 = rotation_difference(ik_first.matrix, match_bone.matrix) + + pv2 = Matrix.Rotation(-angle, 4, ikv) * pv + set_pole(pv2) + ang2 = rotation_difference(ik_first.matrix, match_bone.matrix) + + # Do the one with the smaller angle + if ang1 < ang2: + set_pole(pv1) + + +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 + """ + 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]] + + # Stretch + #if handi['auto_stretch'] == 0.0: + # uarm['stretch_length'] = handi['stretch_length'] + #else: + # diff = (uarmi.vector.length + farmi.vector.length) / (uarm.vector.length + farm.vector.length) + # uarm['stretch_length'] *= diff + + # Upper arm position + match_pose_translation(uarm, uarmi) + match_pose_rotation(uarm, uarmi) + match_pose_scale(uarm, uarmi) + + # Forearm position + #match_pose_translation(hand, handi) + match_pose_rotation(farm, farmi) + match_pose_scale(farm, farmi) + + # Hand position + match_pose_translation(hand, handi) + match_pose_rotation(hand, handi) + match_pose_scale(hand, handi) + + +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]] + + # Stretch + #handi['stretch_length'] = uarm['stretch_length'] + + # Hand position + match_pose_translation(handi, hand) + match_pose_rotation(handi, hand) + match_pose_scale(handi, hand) + + # Upper Arm position + match_pose_translation(uarmi, uarm) + match_pose_rotation(uarmi, uarm) + match_pose_scale(uarmi, uarm) + + # Rotation Correction + correct_rotation(uarmi, uarm) + +# farmi.constraints["IK"].pole_target = obj +# farmi.constraints["IK"].pole_subtarget = farm.name +# farmi.constraints["IK"].pole_angle = -1.74533 +# +# bpy.ops.object.mode_set(mode='POSE') +# bpy.ops.pose.select_all(action='DESELECT') +# uarmi.bone.select = True +# bpy.ops.pose.visual_transform_apply() +# farmi.constraints["IK"].pole_target = None + + # 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]] + footi = obj.pose.bones[ik[2]] + mfooti = obj.pose.bones[ik[3]] + + # Stretch + #if footi['auto_stretch'] == 0.0: + # thigh['stretch_length'] = footi['stretch_length'] + #else: + # diff = (thighi.vector.length + shini.vector.length) / (thigh.vector.length + shin.vector.length) + # thigh['stretch_length'] *= diff + + # Thigh position + match_pose_translation(thigh, thighi) + 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) + set_pose_scale(foot, footmat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + +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 + """ + 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]] + mfooti = obj.pose.bones[ik[4]] + foot = obj.pose.bones[fk[3]] + + # Stretch + #footi['stretch_length'] = thigh['stretch_length'] + + # Clear footroll + set_pose_rotation(footroll, Matrix()) + + # Foot position + mat = mfooti.bone.matrix_local.inverted() * footi.bone.matrix_local + footmat = get_pose_matrix_in_other_space(foot.matrix, footi) * mat + set_pose_translation(footi, footmat) + set_pose_rotation(footi, footmat) + set_pose_scale(footi, footmat) + bpy.ops.object.mode_set(mode='OBJECT') + bpy.ops.object.mode_set(mode='POSE') + + # Thigh position + match_pose_translation(thighi, thigh) + match_pose_rotation(thighi, thigh) + match_pose_scale(thighi, thigh) + + # Rotation Correction + correct_rotation(thighi,thigh) + +# shini.constraints["IK"].pole_target = obj +# shini.constraints["IK"].pole_subtarget = shin.name +# shini.constraints["IK"].pole_angle = -1.74533 +# +# bpy.ops.object.mode_set(mode='POSE') +# bpy.ops.pose.select_all(action='DESELECT') +# thighi.bone.select = True +# bpy.ops.pose.visual_transform_apply() +# shini.constraints["IK"].pole_target = None + + # Pole target position + #match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length)) + + +############################## +## IK/FK snapping operators ## +############################## + +class Rigify_Arm_FK2IK(bpy.types.Operator): + """ Snaps an FK arm to an IK arm. + """ + bl_idname = "pose.rigify_arm_fk2ik_" + rig_id + bl_label = "Rigify Snap FK arm to IK" + bl_options = {'UNDO'} + + uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name") + farm_fk = bpy.props.StringProperty(name="Forerm FK Name") + hand_fk = bpy.props.StringProperty(name="Hand FK Name") + + 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") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + fk2ik_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +class Rigify_Arm_IK2FK(bpy.types.Operator): + """ Snaps an IK arm to an FK arm. + """ + bl_idname = "pose.rigify_arm_ik2fk_" + rig_id + bl_label = "Rigify Snap IK arm to FK" + bl_options = {'UNDO'} + + uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name") + farm_fk = bpy.props.StringProperty(name="Forerm FK Name") + hand_fk = bpy.props.StringProperty(name="Hand FK Name") + + 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") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + #ik2fk_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik, self.pole]) + ik2fk_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +class Rigify_Leg_FK2IK(bpy.types.Operator): + """ Snaps an FK leg to an IK leg. + """ + bl_idname = "pose.rigify_leg_fk2ik_" + rig_id + bl_label = "Rigify Snap FK leg to IK" + bl_options = {'UNDO'} + + thigh_fk = bpy.props.StringProperty(name="Thigh FK Name") + shin_fk = bpy.props.StringProperty(name="Shin FK Name") + 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") + foot_ik = bpy.props.StringProperty(name="Foot IK Name") + mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name") + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + fk2ik_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.foot_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.mfoot_ik]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +class Rigify_Leg_IK2FK(bpy.types.Operator): + """ Snaps an IK leg to an FK leg. + """ + bl_idname = "pose.rigify_leg_ik2fk_" + rig_id + bl_label = "Rigify Snap IK leg to FK" + bl_options = {'UNDO'} + + thigh_fk = bpy.props.StringProperty(name="Thigh FK Name") + shin_fk = bpy.props.StringProperty(name="Shin FK Name") + mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name") + foot_fk = bpy.props.StringProperty(name="Foot FK Name") + thigh_ik = bpy.props.StringProperty(name="Thigh IK Name") + shin_ik = bpy.props.StringProperty(name="Shin IK Name") + foot_ik = bpy.props.StringProperty(name="Foot IK Name") + footroll = bpy.props.StringProperty(name="Foot Roll Name") + #pole = bpy.props.StringProperty(name="Pole IK Name") + mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name") + + + @classmethod + def poll(cls, context): + return (context.active_object != None and context.mode == 'POSE') + + def execute(self, context): + use_global_undo = context.user_preferences.edit.use_global_undo + context.user_preferences.edit.use_global_undo = False + try: + #ik2fk_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.footroll, self.pole, self.mfoot_ik]) + ik2fk_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.mfoot_fk, self.foot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.footroll, self.mfoot_ik]) + finally: + context.user_preferences.edit.use_global_undo = use_global_undo + return {'FINISHED'} + + +################### +## Rig UI Panels ## +################### + +class RigUI(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Rig Main Properties" + bl_idname = rig_id + "_PT_rig_ui" + + @classmethod + def poll(self, context): + if context.mode != 'POSE': + return False + try: + return (context.active_object.data.get("rig_id") == rig_id) + except (AttributeError, KeyError, TypeError): + return False + + def draw(self, context): + layout = self.layout + pose_bones = context.active_object.pose.bones + try: + selected_bones = [bone.name for bone in context.selected_pose_bones] + selected_bones += [context.active_pose_bone.name] + except (AttributeError, TypeError): + return + + def is_selected(names): + # Returns whether any of the named bones are selected. + if type(names) == list: + for name in names: + if name in selected_bones: + return True + elif names in selected_bones: + return True + return False + + +''' + + +def layers_P_ui(layers, layout): + """ Turn a list of booleans + a list of names into a layer UI. + """ + + code = ''' +class RigLayers(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Rig Layers" + bl_idname = rig_id + "_PT_rig_layers" + + @classmethod + def poll(self, context): + try: + return (context.active_object.data.get("rig_id") == rig_id) + except (AttributeError, KeyError, TypeError): + return False + + def draw(self, context): + layout = self.layout + col = layout.column() +''' + rows = {} + for i in range(28): + if layers[i]: + if layout[i][1] not in rows: + rows[layout[i][1]] = [] + rows[layout[i][1]] += [(layout[i][0], i)] + + keys = list(rows.keys()) + keys.sort() + + for key in keys: + code += "\n row = col.row()\n" + i = 0 + for l in rows[key]: + if i > 3: + code += "\n row = col.row()\n" + i = 0 + code += " row.prop(context.active_object.data, 'layers', index=%s, toggle=True, text='%s')\n" % (str(l[1]), l[0]) + i += 1 + + # Root layer + code += "\n row = col.row()" + code += "\n row.separator()" + code += "\n row = col.row()" + code += "\n row.separator()\n" + code += "\n row = col.row()\n" + code += " row.prop(context.active_object.data, 'layers', index=28, toggle=True, text='Root')\n" + + return code + + +UI_P_REGISTER = ''' + +def register(): + bpy.utils.register_class(Rigify_Arm_FK2IK) + bpy.utils.register_class(Rigify_Arm_IK2FK) + bpy.utils.register_class(Rigify_Leg_FK2IK) + bpy.utils.register_class(Rigify_Leg_IK2FK) + bpy.utils.register_class(RigUI) + bpy.utils.register_class(RigLayers) + +def unregister(): + bpy.utils.unregister_class(Rigify_Arm_FK2IK) + bpy.utils.unregister_class(Rigify_Arm_IK2FK) + bpy.utils.unregister_class(Rigify_Leg_FK2IK) + bpy.utils.unregister_class(Rigify_Leg_IK2FK) + bpy.utils.unregister_class(RigUI) + bpy.utils.unregister_class(RigLayers) + +register() +''' diff --git a/rigify/rigs/pitchipoy/limbs/ui.py b/rigify/rigs/pitchipoy/limbs/ui.py index b086e666..894bf32f 100644 --- a/rigify/rigs/pitchipoy/limbs/ui.py +++ b/rigify/rigs/pitchipoy/limbs/ui.py @@ -1,13 +1,29 @@ -script = """ +script_arm = """ controls = [%s] tweaks = [%s] -ik_ctrl = '%s' +ik_ctrl = [%s] fk_ctrl = '%s' parent = '%s' # IK/FK Switch on all Control Bones if is_selected( controls ): layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) + props = layout.operator("pose.rigify_arm_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_ctrl + ")") + props.uarm_fk = controls[1] + props.farm_fk = controls[2] + props.hand_fk = controls[3] + props.uarm_ik = controls[0] + props.farm_ik = ik_ctrl[1] + props.hand_ik = controls[4] + props = layout.operator("pose.rigify_arm_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_ctrl + ")") + props.uarm_fk = controls[1] + props.farm_fk = controls[2] + props.hand_fk = controls[3] + props.uarm_ik = controls[0] + props.farm_ik = ik_ctrl[1] + props.hand_ik = controls[4] + #props.pole = ik_arm[3] + # BBone rubber hose on each Respective Tweak for t in tweaks: @@ -23,14 +39,56 @@ if is_selected( fk_ctrl ): layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) """ -def create_script( bones, limb_type=None): # limb_type arg is added for future fk/ik - # switch to add in UI scripts - # scripts are different between arms and - # legs and paws +script_leg = """ +controls = [%s] +tweaks = [%s] +ik_ctrl = [%s] +fk_ctrl = '%s' +parent = '%s' +# IK/FK Switch on all Control Bones +if is_selected( controls ): + layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) + props = layout.operator("pose.rigify_leg_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_ctrl + ")") + props.thigh_fk = controls[1] + props.shin_fk = controls[2] + props.foot_fk = controls[3] + props.mfoot_fk = controls[7] + props.thigh_ik = controls[0] + props.shin_ik = ik_ctrl[1] + props.foot_ik = ik_ctrl[2] + props.mfoot_ik = ik_ctrl[2] + props = layout.operator("pose.rigify_leg_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_ctrl + ")") + props.thigh_fk = controls[1] + props.shin_fk = controls[2] + props.foot_fk = controls[3] + props.mfoot_fk = controls[7] + props.thigh_ik = controls[0] + props.shin_ik = ik_ctrl[1] + props.foot_ik = controls[6] + #props.pole = ik_leg[3] + props.footroll = controls[5] + props.mfoot_ik = ik_ctrl[2] + +# BBone rubber hose on each Respective Tweak +for t in tweaks: + if is_selected( t ): + layout.prop( pose_bones[ t ], '["%s"]', slider = True ) + +# IK Stretch on IK Control bone +if is_selected( ik_ctrl ): + layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) + +# FK limb follow +if is_selected( fk_ctrl ): + layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) +""" + +def create_script( bones, limb_type=None): # All ctrls have IK/FK switch controls = [ bones['ik']['ctrl']['limb'] ] + bones['fk']['ctrl'] controls += bones['ik']['ctrl']['terminal'] + controls += [ bones['fk']['mch'] ] controls_string = ", ".join(["'" + x + "'" for x in controls]) @@ -39,17 +97,47 @@ def create_script( bones, limb_type=None): # limb_type arg is added for fut tweaks_string = ", ".join(["'" + x + "'" for x in tweaks]) # IK ctrl has IK stretch - ik_ctrl = bones['ik']['ctrl']['terminal'][-1] - - return script % ( - controls_string, - tweaks_string, - ik_ctrl, - bones['fk']['ctrl'][0], - bones['parent'], - 'IK/FK', - 'rubber_tweak', - 'IK_Strertch', - 'FK_limb_follow' - ) + ik_ctrl = [ bones['ik']['ctrl']['terminal'][-1] ] + ik_ctrl += [ bones['ik']['mch_ik'] ] + ik_ctrl += [ bones['ik']['mch_target'] ] + + ik_ctrl_string = ", ".join(["'" + x + "'" for x in ik_ctrl]) + + if limb_type == 'arm': + return script_arm % ( + controls_string, + tweaks_string, + ik_ctrl_string, + bones['fk']['ctrl'][0], + bones['parent'], + 'IK/FK', + 'rubber_tweak', + 'IK_Strertch', + 'FK_limb_follow' + ) + + elif limb_type == 'leg': + return script_leg % ( + controls_string, + tweaks_string, + ik_ctrl_string, + bones['fk']['ctrl'][0], + bones['parent'], + 'IK/FK', + 'rubber_tweak', + 'IK_Strertch', + 'FK_limb_follow' + ) + elif limb_type == 'paw': + return script_leg % ( + controls_string, + tweaks_string, + ik_ctrl_string, + bones['fk']['ctrl'][0], + bones['parent'], + 'IK/FK', + 'rubber_tweak', + 'IK_Strertch', + 'FK_limb_follow' + ) -- cgit v1.2.3