@@ -520,6 +520,112 @@ def ik2fk_leg(obj, fk, ik):
520
520
match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length))
521
521
522
522
523
+ def normalize_finger(obj, fk_master, fk_master_drv, fk_controls, axis):
524
+ """ Absorbs rotation from tweaks to master control bone.
525
+ obj: armature object
526
+ fk_master: master FK control
527
+ fk_master_drv: proxy bones of the master
528
+ fk_controls: list of tweak FK controls
529
+ axis: main transform axis
530
+ """
531
+
532
+ fk_master_drv = parse_bone_names(fk_master_drv)
533
+ fk_controls = parse_bone_names(fk_controls)
534
+
535
+ axis_types = {
536
+ "automatic": [0, 1, 'XYZ'],
537
+ "X": [0, 1, 'XYZ'],
538
+ "-X": [0, -1, 'XYZ'],
539
+ "Y": [1, 1, 'YXZ'],
540
+ "-Y": [1, -1, 'YXZ'],
541
+ "Z": [2, 1, 'ZXY'],
542
+ "-Z": [2, -1, 'ZXY'],
543
+ }
544
+
545
+ axis_id, axis_sign, axis_order = axis_types[axis]
546
+
547
+ # Scan controls to collect data
548
+ ctl_matrix_list = [obj.pose.bones[name].matrix.copy() for name in fk_controls]
549
+
550
+ rotations = []
551
+
552
+ for i, (drv_name, ctl_name, ctl_mat) in enumerate(zip(fk_master_drv, fk_controls, ctl_matrix_list)):
553
+ drv = obj.pose.bones[drv_name]
554
+ ctl = obj.pose.bones[ctl_name]
555
+
556
+ drv_mat = get_pose_matrix_in_other_space(ctl_mat, drv)
557
+
558
+ if i == 0:
559
+ # Absorb first tweak control rotation into the master control
560
+ master = obj.pose.bones[fk_master]
561
+
562
+ master_mat = get_pose_matrix_in_other_space(ctl_mat, master)
563
+
564
+ set_pose_translation(master, master_mat)
565
+ set_pose_rotation(master, drv_mat)
566
+ set_pose_translation(ctl, Matrix())
567
+ set_pose_rotation(ctl, Matrix())
568
+
569
+ bpy.ops.object.mode_set(mode='OBJECT')
570
+ bpy.ops.object.mode_set(mode='POSE')
571
+ else:
572
+ # Collect single axis rotations
573
+ rotations.append(drv_mat.to_quaternion().to_euler(axis_order)[axis_id])
574
+
575
+ # Apply average rotation to the master control scale
576
+ avg_rotation = sum(rotations) / len(rotations)
577
+ obj.pose.bones[fk_master].scale.y = 1 - axis_sign * avg_rotation / pi;
578
+
579
+ # Correct tweak control rotations
580
+ for i, (ctl_name, ctl_mat) in enumerate(zip(fk_controls, ctl_matrix_list)):
581
+ if i > 0:
582
+ ctl = obj.pose.bones[ctl_name]
583
+ mat = get_pose_matrix_in_other_space(ctl_mat, ctl)
584
+ set_pose_translation(ctl, mat)
585
+ set_pose_rotation(ctl, mat)
586
+
587
+ bpy.ops.object.mode_set(mode='OBJECT')
588
+ bpy.ops.object.mode_set(mode='POSE')
589
+
590
+
591
+ def fk2ik_finger(obj, fk_controls, fk_chain, ik_chain):
592
+ """ Matches the fk bones in a finger rig to the ik bones.
593
+ obj: armature object
594
+ fk_controls: list of tweak FK controls
595
+ fk_chain: list of bones with final FK shape
596
+ ik_chain: list of bones with final IK shape
597
+ """
598
+
599
+ fk_controls = parse_bone_names(fk_controls)
600
+ fk_chain = parse_bone_names(fk_chain)
601
+ ik_chain = parse_bone_names(ik_chain)
602
+
603
+ ik_matrix_list = [obj.pose.bones[name].matrix.copy() for name in ik_chain]
604
+
605
+ for i, (ctl_name, fk_name, ik_matrix) in enumerate(zip(fk_controls, fk_chain, ik_matrix_list)):
606
+ fk = obj.pose.bones[fk_name]
607
+ ctl = obj.pose.bones[ctl_name]
608
+
609
+ newmat = ik_matrix * fk.matrix.inverted() * ctl.matrix
610
+ newmat_local = get_pose_matrix_in_other_space(newmat, ctl)
611
+ set_pose_rotation(ctl, newmat_local)
612
+
613
+ bpy.ops.object.mode_set(mode='OBJECT')
614
+ bpy.ops.object.mode_set(mode='POSE')
615
+
616
+
617
+ def ik2fk_finger(obj, fk, ik):
618
+ """ Matches the ik bone in a finger rig to the fk bones.
619
+ obj: armature object
620
+ fk: FK fingertip control
621
+ ik: IK control
622
+ """
623
+ fk_bone = obj.pose.bones[fk]
624
+ ik_bone = obj.pose.bones[ik]
625
+
626
+ match_pose_translation(ik_bone, fk_bone)
627
+
628
+
523
629
################################
524
630
## IK Rotation-Pole functions ##
525
631
################################
@@ -718,6 +824,59 @@ def execute(self, context):
718
824
context.user_preferences.edit.use_global_undo = use_global_undo
719
825
return {'FINISHED'}
720
826
827
+
828
+ class Rigify_Finger_FK2IK(bpy.types.Operator):
829
+ """Snaps an FK finger to the actual shape, and normalizes the master control"""
830
+ bl_idname = "pose.rigify_finger_fk2ik_" + rig_id
831
+ bl_label = "Rigify Snap FK finger to IK"
832
+ bl_options = {'UNDO'}
833
+
834
+ fk_master = bpy.props.StringProperty(name="FK Master Name")
835
+ fk_master_drv = bpy.props.StringProperty(name="FK Master Proxy Name")
836
+ fk_controls = bpy.props.StringProperty(name="FK Control Names")
837
+ fk_chain = bpy.props.StringProperty(name="FK Shape Bone Names")
838
+ ik_chain = bpy.props.StringProperty(name="IK Shape Bone Names")
839
+ axis = bpy.props.StringProperty(name="Main Rotation Axis")
840
+
841
+ @classmethod
842
+ def poll(cls, context):
843
+ return (context.active_object != None and context.mode == 'POSE')
844
+
845
+ def execute(self, context):
846
+ use_global_undo = context.user_preferences.edit.use_global_undo
847
+ context.user_preferences.edit.use_global_undo = False
848
+ try:
849
+ if self.ik_chain != '':
850
+ fk2ik_finger(context.active_object, self.fk_controls, self.fk_chain, self.ik_chain)
851
+ if self.fk_master != '':
852
+ normalize_finger(context.active_object, self.fk_master, self.fk_master_drv, self.fk_controls, self.axis)
853
+ finally:
854
+ context.user_preferences.edit.use_global_undo = use_global_undo
855
+ return {'FINISHED'}
856
+
857
+
858
+ class Rigify_Finger_IK2FK(bpy.types.Operator):
859
+ """Snaps the IK finger control to the FK finger"""
860
+ bl_idname = "pose.rigify_finger_ik2fk_" + rig_id
861
+ bl_label = "Rigify Snap IK finger to FK"
862
+ bl_options = {'UNDO'}
863
+
864
+ fk_ctl = bpy.props.StringProperty(name="FK Name")
865
+ ik_ctl = bpy.props.StringProperty(name="IK Name")
866
+
867
+ @classmethod
868
+ def poll(cls, context):
869
+ return (context.active_object != None and context.mode == 'POSE')
870
+
871
+ def execute(self, context):
872
+ use_global_undo = context.user_preferences.edit.use_global_undo
873
+ context.user_preferences.edit.use_global_undo = False
874
+ try:
875
+ ik2fk_finger(context.active_object, self.fk_ctl, self.ik_ctl)
876
+ finally:
877
+ context.user_preferences.edit.use_global_undo = use_global_undo
878
+ return {'FINISHED'}
879
+
721
880
###########################
722
881
## IK Rotation Pole Snap ##
723
882
###########################
@@ -846,6 +1005,8 @@ def register():
846
1005
bpy.utils.register_class(Rigify_Arm_IK2FK)
847
1006
bpy.utils.register_class(Rigify_Leg_FK2IK)
848
1007
bpy.utils.register_class(Rigify_Leg_IK2FK)
1008
+ bpy.utils.register_class(Rigify_Finger_FK2IK)
1009
+ bpy.utils.register_class(Rigify_Finger_IK2FK)
849
1010
bpy.utils.register_class(Rigify_Rot2PoleSwitch)
850
1011
bpy.utils.register_class(RigUI)
851
1012
bpy.utils.register_class(RigLayers)
@@ -855,7 +1016,9 @@ def unregister():
855
1016
bpy.utils.unregister_class(Rigify_Arm_IK2FK)
856
1017
bpy.utils.unregister_class(Rigify_Leg_FK2IK)
857
1018
bpy.utils.unregister_class(Rigify_Leg_IK2FK)
858
- bpy.utils.register_class(Rigify_Rot2PoleSwitch)
1019
+ bpy.utils.unregister_class(Rigify_Finger_FK2IK)
1020
+ bpy.utils.unregister_class(Rigify_Finger_IK2FK)
1021
+ bpy.utils.unregister_class(Rigify_Rot2PoleSwitch)
859
1022
bpy.utils.unregister_class(RigUI)
860
1023
bpy.utils.unregister_class(RigLayers)
861
1024
0 commit comments