1 #====================== BEGIN GPL LICENSE BLOCK ======================
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 #======================= END GPL LICENSE BLOCK ========================
23 from mathutils import Matrix, Vector
24 from math import acos, pi, radians
29 ############################
30 ## Math utility functions ##
31 ############################
33 def perpendicular_vector(v):
34 """ Returns a vector that is perpendicular to the one given.
35 The returned vector is _not_ guaranteed to be normalized.
37 # Create a vector that is not aligned with v.
38 # It doesn't matter what vector. Just any vector
39 # that's guaranteed to not be pointing in the same
41 if abs(v[0]) < abs(v[1]):
46 # Use cross prouct to generate a vector perpendicular to
47 # both tv and (more importantly) v.
51 def rotation_difference(mat1, mat2):
52 """ Returns the shortest-path rotational difference between two
55 q1 = mat1.to_quaternion()
56 q2 = mat2.to_quaternion()
57 angle = acos(min(1,max(-1,q1.dot(q2)))) * 2
59 angle = -angle + (2*pi)
62 def tail_distance(angle,bone_ik,bone_fk):
63 """ Returns the distance between the tails of two bones
64 after rotating bone_ik in AXIS_ANGLE mode.
66 rot_mod=bone_ik.rotation_mode
67 if rot_mod != 'AXIS_ANGLE':
68 bone_ik.rotation_mode = 'AXIS_ANGLE'
69 bone_ik.rotation_axis_angle[0] = angle
70 bpy.context.scene.update()
72 dv = (bone_fk.tail - bone_ik.tail).length
74 bone_ik.rotation_mode = rot_mod
77 def find_min_range(bone_ik,bone_fk,f=tail_distance,delta=pi/8):
78 """ finds the range where lies the minimum of function f applied on bone_ik and bone_fk
81 rot_mod=bone_ik.rotation_mode
82 if rot_mod != 'AXIS_ANGLE':
83 bone_ik.rotation_mode = 'AXIS_ANGLE'
85 start_angle = bone_ik.rotation_axis_angle[0]
87 while (angle > (start_angle - 2*pi)) and (angle < (start_angle + 2*pi)):
88 l_dist = f(angle-delta,bone_ik,bone_fk)
89 c_dist = f(angle,bone_ik,bone_fk)
90 r_dist = f(angle+delta,bone_ik,bone_fk)
91 if min((l_dist,c_dist,r_dist)) == c_dist:
92 bone_ik.rotation_mode = rot_mod
93 return (angle-delta,angle+delta)
97 def ternarySearch(f, left, right, bone_ik, bone_fk, absolutePrecision):
99 Find minimum of unimodal function f() within [left, right]
100 To find the maximum, revert the if/else statement or revert the comparison.
103 #left and right are the current bounds; the maximum is between them
104 if abs(right - left) < absolutePrecision:
105 return (left + right)/2
107 leftThird = left + (right - left)/3
108 rightThird = right - (right - left)/3
110 if f(leftThird, bone_ik, bone_fk) > f(rightThird, bone_ik, bone_fk):
115 #########################################
116 ## "Visual Transform" helper functions ##
117 #########################################
119 def get_pose_matrix_in_other_space(mat, pose_bone):
120 """ Returns the transform matrix relative to pose_bone's current
121 transform space. In other words, presuming that mat is in
122 armature space, slapping the returned matrix onto pose_bone
123 should give it the armature-space transforms of mat.
124 TODO: try to handle cases with axis-scaled parents better.
126 rest = pose_bone.bone.matrix_local.copy()
127 rest_inv = rest.inverted()
129 par_mat = pose_bone.parent.matrix.copy()
130 par_inv = par_mat.inverted()
131 par_rest = pose_bone.parent.bone.matrix_local.copy()
137 # Get matrix in bone's current transform space
138 smat = rest_inv * (par_rest * (par_inv * mat))
140 # Compensate for non-local location
141 #if not pose_bone.bone.use_local_location:
142 # loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion()
143 # smat.translation = loc
148 def get_local_pose_matrix(pose_bone):
149 """ Returns the local transform matrix of the given pose bone.
151 return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone)
154 def set_pose_translation(pose_bone, mat):
155 """ Sets the pose bone's translation to the same translation as the given matrix.
156 Matrix should be given in bone's local space.
158 if pose_bone.bone.use_local_location == True:
159 pose_bone.location = mat.to_translation()
161 loc = mat.to_translation()
163 rest = pose_bone.bone.matrix_local.copy()
164 if pose_bone.bone.parent:
165 par_rest = pose_bone.bone.parent.matrix_local.copy()
169 q = (par_rest.inverted() * rest).to_quaternion()
170 pose_bone.location = q * loc
173 def set_pose_rotation(pose_bone, mat):
174 """ Sets the pose bone's rotation to the same rotation as the given matrix.
175 Matrix should be given in bone's local space.
177 q = mat.to_quaternion()
179 if pose_bone.rotation_mode == 'QUATERNION':
180 pose_bone.rotation_quaternion = q
181 elif pose_bone.rotation_mode == 'AXIS_ANGLE':
182 pose_bone.rotation_axis_angle[0] = q.angle
183 pose_bone.rotation_axis_angle[1] = q.axis[0]
184 pose_bone.rotation_axis_angle[2] = q.axis[1]
185 pose_bone.rotation_axis_angle[3] = q.axis[2]
187 pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode)
190 def set_pose_scale(pose_bone, mat):
191 """ Sets the pose bone's scale to the same scale as the given matrix.
192 Matrix should be given in bone's local space.
194 pose_bone.scale = mat.to_scale()
197 def match_pose_translation(pose_bone, target_bone):
198 """ Matches pose_bone's visual translation to target_bone's visual
200 This function assumes you are in pose mode on the relevant armature.
202 mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
203 set_pose_translation(pose_bone, mat)
204 bpy.ops.object.mode_set(mode='OBJECT')
205 bpy.ops.object.mode_set(mode='POSE')
208 def match_pose_rotation(pose_bone, target_bone):
209 """ Matches pose_bone's visual rotation to target_bone's visual
211 This function assumes you are in pose mode on the relevant armature.
213 mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
214 set_pose_rotation(pose_bone, mat)
215 bpy.ops.object.mode_set(mode='OBJECT')
216 bpy.ops.object.mode_set(mode='POSE')
219 def match_pose_scale(pose_bone, target_bone):
220 """ Matches pose_bone's visual scale to target_bone's visual
222 This function assumes you are in pose mode on the relevant armature.
224 mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
225 set_pose_scale(pose_bone, mat)
226 bpy.ops.object.mode_set(mode='OBJECT')
227 bpy.ops.object.mode_set(mode='POSE')
229 def correct_rotation(bone_ik, bone_fk):
230 """ Corrects the ik rotation in ik2fk snapping functions
233 alfarange = find_min_range(bone_ik,bone_fk)
234 alfamin = ternarySearch(tail_distance,alfarange[0],alfarange[1],bone_ik,bone_fk,0.1)
236 rot_mod = bone_ik.rotation_mode
237 if rot_mod != 'AXIS_ANGLE':
238 bone_ik.rotation_mode = 'AXIS_ANGLE'
239 bone_ik.rotation_axis_angle[0] = alfamin
240 bone_ik.rotation_mode = rot_mod
242 ##############################
243 ## IK/FK snapping functions ##
244 ##############################
246 def match_pole_target(ik_first, ik_last, pole, match_bone, length):
247 """ Places an IK chain's pole target to match ik_first's
248 transforms to match_bone. All bones should be given as pose bones.
249 You need to be in pose mode on the relevant armature object.
250 ik_first: first bone in the IK chain
251 ik_last: last bone in the IK chain
252 pole: pole target bone for the IK chain
253 match_bone: bone to match ik_first to (probably first bone in a matching FK chain)
254 length: distance pole target should be placed from the chain center
256 a = ik_first.matrix.to_translation()
257 b = ik_last.matrix.to_translation() + ik_last.vector
259 # Vector from the head of ik_first to the
263 # Get a vector perpendicular to ikv
264 pv = perpendicular_vector(ikv).normalized() * length
267 """ Set pole target's position based on a vector
268 from the arm center line.
270 # Translate pvi into armature space
271 ploc = a + (ikv/2) + pvi
273 # Set pole target to location
274 mat = get_pose_matrix_in_other_space(Matrix.Translation(ploc), pole)
275 set_pose_translation(pole, mat)
277 bpy.ops.object.mode_set(mode='OBJECT')
278 bpy.ops.object.mode_set(mode='POSE')
282 # Get the rotation difference between ik_first and match_bone
283 angle = rotation_difference(ik_first.matrix, match_bone.matrix)
285 # Try compensating for the rotation difference in both directions
286 pv1 = Matrix.Rotation(angle, 4, ikv) * pv
288 ang1 = rotation_difference(ik_first.matrix, match_bone.matrix)
290 pv2 = Matrix.Rotation(-angle, 4, ikv) * pv
292 ang2 = rotation_difference(ik_first.matrix, match_bone.matrix)
294 # Do the one with the smaller angle
299 def fk2ik_arm(obj, fk, ik):
300 """ Matches the fk bones in an arm rig to the ik bones.
302 fk: list of fk bone names
303 ik: list of ik bone names
305 uarm = obj.pose.bones[fk[0]]
306 farm = obj.pose.bones[fk[1]]
307 hand = obj.pose.bones[fk[2]]
308 uarmi = obj.pose.bones[ik[0]]
309 farmi = obj.pose.bones[ik[1]]
310 handi = obj.pose.bones[ik[2]]
312 if 'auto_stretch' in handi.keys():
313 # This is kept for compatibility with legacy rigify Human
315 if handi['auto_stretch'] == 0.0:
316 uarm['stretch_length'] = handi['stretch_length']
318 diff = (uarmi.vector.length + farmi.vector.length) / (uarm.vector.length + farm.vector.length)
319 uarm['stretch_length'] *= diff
322 match_pose_rotation(uarm, uarmi)
323 match_pose_scale(uarm, uarmi)
326 match_pose_rotation(farm, farmi)
327 match_pose_scale(farm, farmi)
330 match_pose_rotation(hand, handi)
331 match_pose_scale(hand, handi)
334 match_pose_translation(uarm, uarmi)
335 match_pose_rotation(uarm, uarmi)
336 match_pose_scale(uarm, uarmi)
339 #match_pose_translation(hand, handi)
340 match_pose_rotation(farm, farmi)
341 match_pose_scale(farm, farmi)
344 match_pose_translation(hand, handi)
345 match_pose_rotation(hand, handi)
346 match_pose_scale(hand, handi)
349 def ik2fk_arm(obj, fk, ik):
350 """ Matches the ik bones in an arm rig to the fk bones.
352 fk: list of fk bone names
353 ik: list of ik bone names
355 uarm = obj.pose.bones[fk[0]]
356 farm = obj.pose.bones[fk[1]]
357 hand = obj.pose.bones[fk[2]]
358 uarmi = obj.pose.bones[ik[0]]
359 farmi = obj.pose.bones[ik[1]]
360 handi = obj.pose.bones[ik[2]]
362 main_parent = obj.pose.bones[ik[4]]
364 if ik[3] != "" and main_parent['pole_vector']:
365 pole = obj.pose.bones[ik[3]]
372 # handi['stretch_length'] = uarm['stretch_length']
375 match_pose_translation(handi, hand)
376 match_pose_rotation(handi, hand)
377 match_pose_scale(handi, hand)
378 # Pole target position
379 match_pole_target(uarmi, farmi, pole, uarm, (uarmi.length + farmi.length))
383 match_pose_translation(handi, hand)
384 match_pose_rotation(handi, hand)
385 match_pose_scale(handi, hand)
388 match_pose_translation(uarmi, uarm)
389 match_pose_rotation(uarmi, uarm)
390 match_pose_scale(uarmi, uarm)
391 # Rotation Correction
392 correct_rotation(uarmi, uarm)
394 def fk2ik_leg(obj, fk, ik):
395 """ Matches the fk bones in a leg rig to the ik bones.
397 fk: list of fk bone names
398 ik: list of ik bone names
400 thigh = obj.pose.bones[fk[0]]
401 shin = obj.pose.bones[fk[1]]
402 foot = obj.pose.bones[fk[2]]
403 mfoot = obj.pose.bones[fk[3]]
404 thighi = obj.pose.bones[ik[0]]
405 shini = obj.pose.bones[ik[1]]
406 footi = obj.pose.bones[ik[2]]
407 mfooti = obj.pose.bones[ik[3]]
409 if 'auto_stretch' in footi.keys():
410 # This is kept for compatibility with legacy rigify Human
412 if footi['auto_stretch'] == 0.0:
413 thigh['stretch_length'] = footi['stretch_length']
415 diff = (thighi.vector.length + shini.vector.length) / (thigh.vector.length + shin.vector.length)
416 thigh['stretch_length'] *= diff
419 match_pose_rotation(thigh, thighi)
420 match_pose_scale(thigh, thighi)
423 match_pose_rotation(shin, shini)
424 match_pose_scale(shin, shini)
427 mat = mfoot.bone.matrix_local.inverted() * foot.bone.matrix_local
428 footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) * mat
429 set_pose_rotation(foot, footmat)
430 set_pose_scale(foot, footmat)
431 bpy.ops.object.mode_set(mode='OBJECT')
432 bpy.ops.object.mode_set(mode='POSE')
436 match_pose_translation(thigh, thighi)
437 match_pose_rotation(thigh, thighi)
438 match_pose_scale(thigh, thighi)
441 match_pose_rotation(shin, shini)
442 match_pose_scale(shin, shini)
445 mat = mfoot.bone.matrix_local.inverted() * foot.bone.matrix_local
446 footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) * mat
447 set_pose_rotation(foot, footmat)
448 set_pose_scale(foot, footmat)
449 bpy.ops.object.mode_set(mode='OBJECT')
450 bpy.ops.object.mode_set(mode='POSE')
453 def ik2fk_leg(obj, fk, ik):
454 """ Matches the ik bones in a leg rig to the fk bones.
456 fk: list of fk bone names
457 ik: list of ik bone names
459 thigh = obj.pose.bones[fk[0]]
460 shin = obj.pose.bones[fk[1]]
461 mfoot = obj.pose.bones[fk[2]]
463 foot = obj.pose.bones[fk[3]]
466 thighi = obj.pose.bones[ik[0]]
467 shini = obj.pose.bones[ik[1]]
468 footi = obj.pose.bones[ik[2]]
469 footroll = obj.pose.bones[ik[3]]
471 main_parent = obj.pose.bones[ik[6]]
473 if ik[4] != "" and main_parent['pole_vector']:
474 pole = obj.pose.bones[ik[4]]
477 mfooti = obj.pose.bones[ik[5]]
479 if (not pole) and (foot):
482 set_pose_rotation(footroll, Matrix())
485 mat = mfooti.bone.matrix_local.inverted() * footi.bone.matrix_local
486 footmat = get_pose_matrix_in_other_space(foot.matrix, footi) * mat
487 set_pose_translation(footi, footmat)
488 set_pose_rotation(footi, footmat)
489 set_pose_scale(footi, footmat)
490 bpy.ops.object.mode_set(mode='OBJECT')
491 bpy.ops.object.mode_set(mode='POSE')
494 match_pose_translation(thighi, thigh)
495 match_pose_rotation(thighi, thigh)
496 match_pose_scale(thighi, thigh)
498 # Rotation Correction
499 correct_rotation(thighi,thigh)
503 if 'stretch_lenght' in footi.keys() and 'stretch_lenght' in thigh.keys():
504 # Kept for compat with legacy rigify Human
505 footi['stretch_length'] = thigh['stretch_length']
508 set_pose_rotation(footroll, Matrix())
511 mat = mfooti.bone.matrix_local.inverted() * footi.bone.matrix_local
512 footmat = get_pose_matrix_in_other_space(mfoot.matrix, footi) * mat
513 set_pose_translation(footi, footmat)
514 set_pose_rotation(footi, footmat)
515 set_pose_scale(footi, footmat)
516 bpy.ops.object.mode_set(mode='OBJECT')
517 bpy.ops.object.mode_set(mode='POSE')
519 # Pole target position
520 match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length))
523 ################################
524 ## IK Rotation-Pole functions ##
525 ################################
527 def parse_bone_names(names_string):
528 if names_string[0] == '[' and names_string[-1] == ']':
529 return eval(names_string)
533 def rotPoleToggle(rig, limb_type, controls, ik_ctrl, fk_ctrl, parent, pole):
535 rig_id = rig.data['rig_id']
536 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
537 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
538 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
539 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
541 controls = parse_bone_names(controls)
542 ik_ctrl = parse_bone_names(ik_ctrl)
543 fk_ctrl = parse_bone_names(fk_ctrl)
544 parent = parse_bone_names(parent)
545 pole = parse_bone_names(pole)
547 pbones = bpy.context.selected_pose_bones
548 bpy.ops.pose.select_all(action='DESELECT')
552 new_pole_vector_value = not rig.pose.bones[parent]['pole_vector']
554 if b.name in controls or b.name in ik_ctrl:
555 if limb_type == 'arm':
558 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
559 rig.pose.bones[controls[4]].bone.select = not new_pole_vector_value
560 rig.pose.bones[parent].bone.select = not new_pole_vector_value
561 rig.pose.bones[pole].bone.select = new_pole_vector_value
563 kwargs1 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
564 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
565 'hand_ik': controls[4]}
566 kwargs2 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
567 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
568 'pole': pole, 'main_parent': parent}
572 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
573 rig.pose.bones[controls[6]].bone.select = not new_pole_vector_value
574 rig.pose.bones[controls[5]].bone.select = not new_pole_vector_value
575 rig.pose.bones[parent].bone.select = not new_pole_vector_value
576 rig.pose.bones[pole].bone.select = new_pole_vector_value
578 kwargs1 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
579 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
580 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
581 kwargs2 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
582 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
583 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
584 'main_parent': parent}
587 rig.pose.bones[parent]['pole_vector'] = new_pole_vector_value
590 bpy.ops.pose.select_all(action='DESELECT')
592 ##############################
593 ## IK/FK snapping operators ##
594 ##############################
596 class Rigify_Arm_FK2IK(bpy.types.Operator):
597 """ Snaps an FK arm to an IK arm.
599 bl_idname = "pose.rigify_arm_fk2ik_" + rig_id
600 bl_label = "Rigify Snap FK arm to IK"
601 bl_options = {'UNDO'}
603 uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name")
604 farm_fk = bpy.props.StringProperty(name="Forerm FK Name")
605 hand_fk = bpy.props.StringProperty(name="Hand FK Name")
607 uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name")
608 farm_ik = bpy.props.StringProperty(name="Forearm IK Name")
609 hand_ik = bpy.props.StringProperty(name="Hand IK Name")
612 def poll(cls, context):
613 return (context.active_object != None and context.mode == 'POSE')
615 def execute(self, context):
616 use_global_undo = context.user_preferences.edit.use_global_undo
617 context.user_preferences.edit.use_global_undo = False
619 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])
621 context.user_preferences.edit.use_global_undo = use_global_undo
625 class Rigify_Arm_IK2FK(bpy.types.Operator):
626 """ Snaps an IK arm to an FK arm.
628 bl_idname = "pose.rigify_arm_ik2fk_" + rig_id
629 bl_label = "Rigify Snap IK arm to FK"
630 bl_options = {'UNDO'}
632 uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name")
633 farm_fk = bpy.props.StringProperty(name="Forerm FK Name")
634 hand_fk = bpy.props.StringProperty(name="Hand FK Name")
636 uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name")
637 farm_ik = bpy.props.StringProperty(name="Forearm IK Name")
638 hand_ik = bpy.props.StringProperty(name="Hand IK Name")
639 pole = bpy.props.StringProperty(name="Pole IK Name")
641 main_parent = bpy.props.StringProperty(name="Main Parent", default="")
644 def poll(cls, context):
645 return (context.active_object != None and context.mode == 'POSE')
647 def execute(self, context):
648 use_global_undo = context.user_preferences.edit.use_global_undo
649 context.user_preferences.edit.use_global_undo = False
651 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, self.main_parent])
653 context.user_preferences.edit.use_global_undo = use_global_undo
657 class Rigify_Leg_FK2IK(bpy.types.Operator):
658 """ Snaps an FK leg to an IK leg.
660 bl_idname = "pose.rigify_leg_fk2ik_" + rig_id
661 bl_label = "Rigify Snap FK leg to IK"
662 bl_options = {'UNDO'}
664 thigh_fk = bpy.props.StringProperty(name="Thigh FK Name")
665 shin_fk = bpy.props.StringProperty(name="Shin FK Name")
666 foot_fk = bpy.props.StringProperty(name="Foot FK Name")
667 mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name")
669 thigh_ik = bpy.props.StringProperty(name="Thigh IK Name")
670 shin_ik = bpy.props.StringProperty(name="Shin IK Name")
671 foot_ik = bpy.props.StringProperty(name="Foot IK Name")
672 mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name")
675 def poll(cls, context):
676 return (context.active_object != None and context.mode == 'POSE')
678 def execute(self, context):
679 use_global_undo = context.user_preferences.edit.use_global_undo
680 context.user_preferences.edit.use_global_undo = False
682 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])
684 context.user_preferences.edit.use_global_undo = use_global_undo
688 class Rigify_Leg_IK2FK(bpy.types.Operator):
689 """ Snaps an IK leg to an FK leg.
691 bl_idname = "pose.rigify_leg_ik2fk_" + rig_id
692 bl_label = "Rigify Snap IK leg to FK"
693 bl_options = {'UNDO'}
695 thigh_fk = bpy.props.StringProperty(name="Thigh FK Name")
696 shin_fk = bpy.props.StringProperty(name="Shin FK Name")
697 mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name")
698 foot_fk = bpy.props.StringProperty(name="Foot FK Name", default="")
699 thigh_ik = bpy.props.StringProperty(name="Thigh IK Name")
700 shin_ik = bpy.props.StringProperty(name="Shin IK Name")
701 foot_ik = bpy.props.StringProperty(name="Foot IK Name")
702 footroll = bpy.props.StringProperty(name="Foot Roll Name")
703 pole = bpy.props.StringProperty(name="Pole IK Name")
704 mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name")
706 main_parent = bpy.props.StringProperty(name="Main Parent", default="")
709 def poll(cls, context):
710 return (context.active_object != None and context.mode == 'POSE')
712 def execute(self, context):
713 use_global_undo = context.user_preferences.edit.use_global_undo
714 context.user_preferences.edit.use_global_undo = False
716 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.pole, self.mfoot_ik, self.main_parent])
718 context.user_preferences.edit.use_global_undo = use_global_undo
721 ###########################
722 ## IK Rotation Pole Snap ##
723 ###########################
725 class Rigify_Rot2PoleSwitch(bpy.types.Operator):
726 bl_idname = "pose.rigify_rot2pole_" + rig_id
727 bl_label = "Rotation - Pole toggle"
728 bl_description = "Toggles IK chain between rotation and pole target"
729 bone_name = bpy.props.StringProperty(default='')
730 limb_type = bpy.props.StringProperty(name="Limb Type")
731 controls = bpy.props.StringProperty(name="Controls string")
732 ik_ctrl = bpy.props.StringProperty(name="IK Controls string")
733 fk_ctrl = bpy.props.StringProperty(name="FK Controls string")
734 parent = bpy.props.StringProperty(name="Parent name")
735 pole = bpy.props.StringProperty(name="Pole name")
737 def execute(self, context):
741 bpy.ops.pose.select_all(action='DESELECT')
742 rig.pose.bones[self.bone_name].bone.select = True
744 rotPoleToggle(rig, self.limb_type, self.controls, self.ik_ctrl, self.fk_ctrl, self.parent, self.pole)
751 class RigUI(bpy.types.Panel):
752 bl_space_type = 'VIEW_3D'
753 bl_region_type = 'UI'
754 bl_label = "Rig Main Properties"
755 bl_idname = rig_id + "_PT_rig_ui"
758 def poll(self, context):
759 if context.mode != 'POSE':
762 return (context.active_object.data.get("rig_id") == rig_id)
763 except (AttributeError, KeyError, TypeError):
766 def draw(self, context):
768 pose_bones = context.active_object.pose.bones
770 selected_bones = [bone.name for bone in context.selected_pose_bones]
771 selected_bones += [context.active_pose_bone.name]
772 except (AttributeError, TypeError):
775 def is_selected(names):
776 # Returns whether any of the named bones are selected.
777 if type(names) == list:
779 if name in selected_bones:
781 elif names in selected_bones:
789 def layers_ui(layers
, layout
):
790 """ Turn a list of booleans + a list of names into a layer UI.
794 class RigLayers(bpy.types.Panel):
795 bl_space_type = 'VIEW_3D'
796 bl_region_type = 'UI'
797 bl_label = "Rig Layers"
798 bl_idname = rig_id + "_PT_rig_layers"
801 def poll(self, context):
803 return (context.active_object.data.get("rig_id") == rig_id)
804 except (AttributeError, KeyError, TypeError):
807 def draw(self, context):
809 col = layout.column()
814 if layout
[i
][1] not in rows
:
815 rows
[layout
[i
][1]] = []
816 rows
[layout
[i
][1]] += [(layout
[i
][0], i
)]
818 keys
= list(rows
.keys())
822 code
+= "\n row = col.row()\n"
826 code
+= "\n row = col.row()\n"
828 code
+= " row.prop(context.active_object.data, 'layers', index=%s, toggle=True, text='%s')\n" % (str(l
[1]), l
[0])
832 code
+= "\n row = col.row()"
833 code
+= "\n row.separator()"
834 code
+= "\n row = col.row()"
835 code
+= "\n row.separator()\n"
836 code
+= "\n row = col.row()\n"
837 code
+= " row.prop(context.active_object.data, 'layers', index=28, toggle=True, text='Root')\n"
845 bpy.utils.register_class(Rigify_Arm_FK2IK)
846 bpy.utils.register_class(Rigify_Arm_IK2FK)
847 bpy.utils.register_class(Rigify_Leg_FK2IK)
848 bpy.utils.register_class(Rigify_Leg_IK2FK)
849 bpy.utils.register_class(Rigify_Rot2PoleSwitch)
850 bpy.utils.register_class(RigUI)
851 bpy.utils.register_class(RigLayers)
854 bpy.utils.unregister_class(Rigify_Arm_FK2IK)
855 bpy.utils.unregister_class(Rigify_Arm_IK2FK)
856 bpy.utils.unregister_class(Rigify_Leg_FK2IK)
857 bpy.utils.unregister_class(Rigify_Leg_IK2FK)
858 bpy.utils.register_class(Rigify_Rot2PoleSwitch)
859 bpy.utils.unregister_class(RigUI)
860 bpy.utils.unregister_class(RigLayers)