Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucio Rossi <lucio.rossi75@gmail.com>2016-07-28 19:32:36 +0300
committerLucio Rossi <lucio.rossi75@gmail.com>2016-07-28 19:32:36 +0300
commitefd386991e12320590ae9943496d4a90961f132f (patch)
tree23706efe1a8b06555c36569fd6422ce0a89c5587
parentd1dafb1ec7fe5444360d575186f9e1bc31eff90b (diff)
Pitchipoy FK/IK switch implemented
-rw-r--r--rigify/generate.py51
-rw-r--r--rigify/rig_ui_pitchipoy_template.py717
-rw-r--r--rigify/rigs/pitchipoy/limbs/ui.py126
3 files changed, 864 insertions, 30 deletions
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 ========================
+
+# <pep8 compliant>
+
+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'
+ )