import bpy, os from mathutils import * from math import * from bpy.app.handlers import persistent from operator import itemgetter from .utils import * from .define import * fk_leg = [c_prefix+leg_rig_names["thigh_fk"], c_prefix+leg_rig_names["calf_fk"], c_prefix+leg_rig_names["foot_fk"], c_prefix+leg_rig_names["toes_fk"]] ik_leg = [leg_rig_names["thigh_ik"], leg_rig_names["calf_ik"], c_prefix+leg_rig_names["foot_ik"], c_prefix+leg_rig_names["pole_ik"], c_prefix+leg_rig_names["toes_ik"], c_prefix+leg_rig_names["foot_01"], c_prefix+leg_rig_names["foot_roll_cursor"], leg_rig_names["foot_snap"]] fk_arm = [c_prefix+arm_rig_names["arm_fk"], c_prefix+arm_rig_names["forearm_fk"], c_prefix+arm_rig_names["hand_fk"]] ik_arm = [arm_rig_names["arm_ik"], arm_rig_names["forearm_ik"], c_prefix+arm_rig_names["hand_ik"], c_prefix+arm_rig_names["pole_ik"]] ################## OPERATOR CLASSES ################### class MR_OT_arm_bake_fk_to_ik(bpy.types.Operator): """Snaps and bake an FK to an IK arm over a specified frame range""" bl_idname = "pose.mr_bake_arm_fk_to_ik" bl_label = "Snap an FK to IK arm over a specified frame range" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") frame_start : bpy.props.IntProperty(name="Frame start", default=0) frame_end : bpy.props.IntProperty(name="Frame end", default=10) @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def draw(self, context): layout = self.layout layout.prop(self, 'frame_start', text='Frame Start') layout.prop(self, 'frame_end', text='Frame End') def invoke(self, context, event): action = context.active_object.animation_data.action self.frame_start, self.frame_end = action.frame_range[0], action.frame_range[1] wm = context.window_manager return wm.invoke_props_dialog(self, width=400) def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False # save current autokey state auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto # set auto key to True bpy.context.scene.tool_settings.use_keyframe_insert_auto = True try: bname = get_selected_pbone_name() self.side = get_bone_side(bname) bake_fk_to_ik_arm(self) finally: context.preferences.edit.use_global_undo = use_global_undo # restore autokey state bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state return {'FINISHED'} class MR_OT_arm_fk_to_ik(bpy.types.Operator): """Snaps an FK arm to an IK arm""" bl_idname = "pose.mr_arm_fk_to_ik_" bl_label = "Snap FK arm to IK" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False try: bname = get_selected_pbone_name() self.side = get_bone_side(bname) fk_to_ik_arm(self) finally: context.preferences.edit.use_global_undo = use_global_undo return {'FINISHED'} class MR_OT_arm_bake_ik_to_fk(bpy.types.Operator): """Snaps and bake an IK to an FK arm over a specified frame range""" bl_idname = "pose.mr_bake_arm_ik_to_fk" bl_label = "Snap an IK to FK arm over a specified frame range" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") frame_start : bpy.props.IntProperty(name="Frame start", default=0) frame_end : bpy.props.IntProperty(name="Frame end", default=10) @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def draw(self, context): layout = self.layout layout.prop(self, 'frame_start', text='Frame Start') layout.prop(self, 'frame_end', text='Frame End') def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self, width=400) def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False # save current autokey state auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto # set auto key to True bpy.context.scene.tool_settings.use_keyframe_insert_auto = True try: bname = get_selected_pbone_name() self.side = get_bone_side(bname) bake_ik_to_fk_arm(self) finally: context.preferences.edit.use_global_undo = use_global_undo # restore autokey state bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state return {'FINISHED'} class MR_OT_arm_ik_to_fk(bpy.types.Operator): """Snaps an IK arm to an FK arm""" bl_idname = "pose.mr_arm_ik_to_fk_" bl_label = "Snap IK arm to FK" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False try: bname = get_selected_pbone_name() self.side = get_bone_side(bname) ik_to_fk_arm(self) finally: context.preferences.edit.use_global_undo = use_global_undo return {'FINISHED'} class MR_OT_switch_snap_anim(bpy.types.Operator): """Switch and snap IK-FK over multiple frames""" bl_idname = "pose.mr_switch_snap_anim" bl_label = "Switch and Snap IK FK anim" bl_options = {'UNDO'} rig = None side : bpy.props.StringProperty(name="bone side", default="") _side = "" prefix: bpy.props.StringProperty(name="", default="") type : bpy.props.StringProperty(name="type", default="") frame_start : bpy.props.IntProperty(name="Frame start", default=0) frame_end : bpy.props.IntProperty(name="Frame end", default=10) has_action = False @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def draw(self, context): layout = self.layout if self.has_action: layout.prop(self, 'frame_start', text='Frame Start') layout.prop(self, 'frame_end', text='Frame End') else: layout.label(text="This rig is not animated!") def invoke(self, context, event): try: action = context.active_object.animation_data.action if action: self.has_action = True except: pass if self.has_action: self.frame_start, self.frame_end = action.frame_range[0], action.frame_range[1] wm = context.window_manager return wm.invoke_props_dialog(self, width=400) def execute(self, context): if self.has_action == False: return {'FINISHED'} try: scn = context.scene # save current autokey state auto_key_state = scn.tool_settings.use_keyframe_insert_auto # set auto key to True scn.tool_settings.use_keyframe_insert_auto = True # save current frame cur_frame = scn.frame_current self.rig = context.active_object bname = get_selected_pbone_name() self.side = get_bone_side(bname) self._side = '_'+self.side self.prefix = get_mixamo_prefix() if is_selected(fk_leg, bname) or is_selected(ik_leg, bname): self.type = "LEG" elif is_selected(fk_arm, bname) or is_selected(ik_arm, bname): self.type = "ARM" if self.type == "ARM": c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+self._side)#self.prefix+self.side+'Hand') if c_hand_ik['ik_fk_switch'] < 0.5: bake_fk_to_ik_arm(self) else: bake_ik_to_fk_arm(self) elif self.type == "LEG": c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+self._side)#get_pose_bone(self.prefix+self.side+'Foot') if c_foot_ik['ik_fk_switch'] < 0.5: bake_fk_to_ik_leg(self) else: print("Bake IK to FK leg") bake_ik_to_fk_leg(self) finally: # restore autokey state scn.tool_settings.use_keyframe_insert_auto = auto_key_state # restore frame scn.frame_set(cur_frame) return {'FINISHED'} class MR_OT_switch_snap(bpy.types.Operator): """Switch and snap IK-FK for the current frame""" bl_idname = "pose.mr_switch_snap" bl_label = "Switch and Snap IK FK" bl_options = {'UNDO'} rig = None side : bpy.props.StringProperty(name="bone side", default="") _side = "" prefix: bpy.props.StringProperty(name="", default="") type : bpy.props.StringProperty(name="type", default="") @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False try: self.rig = context.active_object bname = get_selected_pbone_name() self.side = get_bone_side(bname) self._side = '_'+self.side self.prefix = get_mixamo_prefix() if is_selected(fk_leg, bname) or is_selected(ik_leg, bname): self.type = "LEG" elif is_selected(fk_arm, bname) or is_selected(ik_arm, bname): self.type = "ARM" if self.type == "ARM": #base_hand = get_pose_bone(self.prefix+self.side+'Hand') c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+self._side)#self.prefix+self.side+'Hand') if c_hand_ik['ik_fk_switch'] < 0.5: fk_to_ik_arm(self) else: ik_to_fk_arm(self) elif self.type == "LEG": #base_foot = get_pose_bone(self.prefix+self.side+'Foot') c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+self._side)#get_pose_bone(self.prefix+self.side+'Foot') if c_foot_ik['ik_fk_switch'] < 0.5: fk_to_ik_leg(self) else: ik_to_fk_leg(self) finally: context.preferences.edit.use_global_undo = use_global_undo return {'FINISHED'} class MR_OT_leg_bake_fk_to_ik(bpy.types.Operator): """Snaps and bake an FK leg to an IK leg over a specified frame range""" bl_idname = "pose.mr_bake_leg_fk_to_ik" bl_label = "Snap an FK to IK leg over a specified frame range" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") _side = "" prefix = "" frame_start : bpy.props.IntProperty(name="Frame start", default=0) frame_end : bpy.props.IntProperty(name="Frame end", default=10) rig = None @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def draw(self, context): layout = self.layout layout.prop(self, 'frame_start', text='Frame Start') layout.prop(self, 'frame_end', text='Frame End') def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self, width=400) def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False # save current autokey state auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto # set auto key to True bpy.context.scene.tool_settings.use_keyframe_insert_auto = True try: self.rig = context.active_object bname = get_selected_pbone_name() self.side = get_bone_side(bname) self._side = '_'+self.side self.prefix = get_mixamo_prefix() bake_fk_to_ik_leg(self) finally: context.preferences.edit.use_global_undo = use_global_undo # restore autokey state bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state return {'FINISHED'} class MR_OT_leg_fk_to_ik(bpy.types.Operator): """Snaps an FK leg to an IK leg""" bl_idname = "pose.mr_leg_fk_to_ik_" bl_label = "Snap FK leg to IK" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") rig = None _side = "" prefix = "" @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False try: self.rig = context.active_object bname = get_selected_pbone_name() self.side = get_bone_side(bname) self._side = '_'+self.side self.prefix = get_mixamo_prefix() fk_to_ik_leg(self) finally: context.preferences.edit.use_global_undo = use_global_undo return {'FINISHED'} class MR_OT_leg_bake_ik_to_fk(bpy.types.Operator): """Snaps and bake an IK leg to an FK leg over a specified frame range""" bl_idname = "pose.mr_bake_leg_ik_to_fk" bl_label = "Snap an IK to FK leg over a specified frame range" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") frame_start : bpy.props.IntProperty(name="Frame start", default=0) frame_end : bpy.props.IntProperty(name="Frame end", default=10) rig = None _side = "" prefix = "" @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def draw(self, context): layout = self.layout layout.prop(self, 'frame_start', text='Frame Start') layout.prop(self, 'frame_end', text='Frame End') def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self, width=400) def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False # save current autokey state auto_key_state = bpy.context.scene.tool_settings.use_keyframe_insert_auto # set auto key to True bpy.context.scene.tool_settings.use_keyframe_insert_auto = True try: self.rig = context.active_object bname = get_selected_pbone_name() self.side = get_bone_side(bname) self._side = '_'+self.side self.prefix = get_mixamo_prefix() bake_ik_to_fk_leg(self) finally: context.preferences.edit.use_global_undo = use_global_undo # restore autokey state bpy.context.scene.tool_settings.use_keyframe_insert_auto = auto_key_state return {'FINISHED'} class MR_OT_leg_ik_to_fk(bpy.types.Operator): """Snaps an IK leg to an FK leg""" bl_idname = "pose.mr_leg_ik_to_fk_" bl_label = "Snap IK leg to FK" bl_options = {'UNDO'} side : bpy.props.StringProperty(name="bone side") rig = None _side = "" prefix = "" @classmethod def poll(cls, context): return (context.active_object != None and context.mode == 'POSE') def execute(self, context): use_global_undo = context.preferences.edit.use_global_undo context.preferences.edit.use_global_undo = False try: self.rig = context.active_object bname = get_selected_pbone_name() self.side = get_bone_side(bname) self._side = '_'+self.side self.prefix = get_mixamo_prefix() ik_to_fk_leg(self) finally: context.preferences.edit.use_global_undo = use_global_undo return {'FINISHED'} ################## FUNCTIONS ################## def set_pose_rotation(pose_bone, mat): 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 snap_pos(pose_bone, target_bone): # Snap a bone to another bone. Supports child of constraints and parent. # if the pose_bone has direct parent if pose_bone.parent: # apply double time because of dependecy lag pose_bone.matrix = target_bone.matrix update_transform() # second apply pose_bone.matrix = target_bone.matrix else: # is there a child of constraint attached? child_of_cns = None if len(pose_bone.constraints) > 0: all_child_of_cns = [i for i in pose_bone.constraints if i.type == "CHILD_OF" and i.influence == 1.0 and i.mute == False and i.target] if len(all_child_of_cns) > 0: child_of_cns = all_child_of_cns[0]# in case of multiple child of constraints enabled, use only the first for now if child_of_cns != None: if child_of_cns.subtarget != "" and get_pose_bone(child_of_cns.subtarget): # apply double time because of dependecy lag pose_bone.matrix = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ target_bone.matrix update_transform() pose_bone.matrix = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ target_bone.matrix else: pose_bone.matrix = target_bone.matrix else: pose_bone.matrix = target_bone.matrix def snap_pos_matrix(pose_bone, target_bone_matrix): # Snap a bone to another bone. Supports child of constraints and parent. # if the pose_bone has direct parent if pose_bone.parent: pose_bone.matrix = target_bone_matrix.copy() update_transform() else: # is there a child of constraint attached? child_of_cns = None if len(pose_bone.constraints) > 0: all_child_of_cns = [i for i in pose_bone.constraints if i.type == "CHILD_OF" and i.influence == 1.0 and i.mute == False and i.target] if len(all_child_of_cns) > 0: child_of_cns = all_child_of_cns[0]# in case of multiple child of constraints enabled, use only the first for now if child_of_cns != None: if child_of_cns.subtarget != "" and get_pose_bone(child_of_cns.subtarget): pose_bone.matrix = get_pose_bone(child_of_cns.subtarget).matrix_channel.inverted() @ target_bone_matrix update_transform() else: pose_bone.matrix = target_bone_matrix.copy() else: pose_bone.matrix = target_bone_matrix.copy() def snap_rot(pose_bone, target_bone): method = 1 if method == 1: 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') bpy.context.view_layer.update() elif method == 2: loc, scale = pose_bone.location.copy(), pose_bone.scale.copy() pose_bone.matrix = target_bone.matrix pose_bone.location, pose_bone.scale = loc, scale bpy.context.view_layer.update() def bake_fk_to_ik_arm(self): for f in range(self.frame_start, self.frame_end +1): bpy.context.scene.frame_set(f) print("baking frame", f) fk_to_ik_arm(self) def fk_to_ik_arm(self): rig = self.rig side = self.side _side = self._side prefix = self.prefix arm_fk = rig.pose.bones[fk_arm[0] + _side] forearm_fk = rig.pose.bones[fk_arm[1] + _side] hand_fk = rig.pose.bones[fk_arm[2] + _side] arm_ik = rig.pose.bones[ik_arm[0] + _side] forearm_ik = rig.pose.bones[ik_arm[1] + _side] hand_ik = rig.pose.bones[ik_arm[2] + _side] pole = rig.pose.bones[ik_arm[3] + _side] #Snap rot snap_rot(arm_fk, arm_ik) snap_rot(forearm_fk, forearm_ik) snap_rot(hand_fk, hand_ik) #Snap scale hand_fk.scale =hand_ik.scale #rot debug forearm_fk.rotation_euler[0]=0 forearm_fk.rotation_euler[1]=0 #switch #base_hand = get_pose_bone(prefix+side+'Hand') c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+_side) c_hand_ik['ik_fk_switch'] = 1.0 #udpate view bpy.context.view_layer.update() #insert key if autokey enable if bpy.context.scene.tool_settings.use_keyframe_insert_auto: #fk chain c_hand_ik.keyframe_insert(data_path='["ik_fk_switch"]') hand_fk.keyframe_insert(data_path="scale") hand_fk.keyframe_insert(data_path="rotation_euler") arm_fk.keyframe_insert(data_path="rotation_euler") forearm_fk.keyframe_insert(data_path="rotation_euler") #ik chain hand_ik.keyframe_insert(data_path="location") hand_ik.keyframe_insert(data_path="rotation_euler") hand_ik.keyframe_insert(data_path="scale") pole.keyframe_insert(data_path="location") # change FK to IK hand selection, if selected if hand_ik.bone.select: hand_fk.bone.select = True hand_ik.bone.select = False def bake_ik_to_fk_arm(self): for f in range(self.frame_start, self.frame_end +1): bpy.context.scene.frame_set(f) print("baking frame", f) ik_to_fk_arm(self) def ik_to_fk_arm(self): rig = self.rig side = self.side _side = self._side prefix = self.prefix arm_fk = rig.pose.bones[fk_arm[0] + _side] forearm_fk = rig.pose.bones[fk_arm[1] + _side] hand_fk = rig.pose.bones[fk_arm[2] + _side] arm_ik = rig.pose.bones[ik_arm[0] + _side] forearm_ik = rig.pose.bones[ik_arm[1] + _side] hand_ik = rig.pose.bones[ik_arm[2] + _side] pole_ik = rig.pose.bones[ik_arm[3] + _side] # Snap # constraint support constraint = None bparent_name = "" parent_type = "" valid_constraint = True # Snap Hand if len(hand_ik.constraints) > 0: for c in hand_ik.constraints: if not c.mute and c.influence > 0.5 and c.type == 'CHILD_OF': if c.target: #if bone if c.target.type == 'ARMATURE': bparent_name = c.subtarget parent_type = "bone" constraint = c #if object else: bparent_name = c.target.name parent_type = "object" constraint = c if constraint != None: if parent_type == "bone": if bparent_name == "": valid_constraint = False if constraint and valid_constraint: if parent_type == "bone": bone_parent = get_pose_bone(bparent_name) hand_ik.matrix = bone_parent.matrix_channel.inverted() @ hand_fk.matrix if parent_type == "object": bone_parent = bpy.data.objects[bparent_name] obj_par = bpy.data.objects[bparent_name] hand_ik.matrix = constraint.inverse_matrix.inverted() @ obj_par.matrix_world.inverted() @ hand_fk.matrix else: hand_ik.matrix = hand_fk.matrix # Snap Pole _axis = forearm_fk.x_axis if side == "Left" else -forearm_fk.x_axis pole_pos = get_ik_pole_pos(arm_fk, forearm_fk, method=2, axis=_axis) pole_mat = Matrix.Translation(pole_pos) snap_pos_matrix(pole_ik, pole_mat) # Switch c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+_side) #base_hand = get_pose_bone(prefix+side+'Hand') c_hand_ik['ik_fk_switch'] = 0.0 # update update_transform() #insert key if autokey enable if bpy.context.scene.tool_settings.use_keyframe_insert_auto: #ik chain c_hand_ik.keyframe_insert(data_path='["ik_fk_switch"]') hand_ik.keyframe_insert(data_path="location") hand_ik.keyframe_insert(data_path="rotation_euler") hand_ik.keyframe_insert(data_path="scale") pole_ik.keyframe_insert(data_path="location") #fk chain hand_fk.keyframe_insert(data_path="location") hand_fk.keyframe_insert(data_path="rotation_euler") hand_fk.keyframe_insert(data_path="scale") arm_fk.keyframe_insert(data_path="rotation_euler") forearm_fk.keyframe_insert(data_path="rotation_euler") # change FK to IK hand selection, if selected if hand_fk.bone.select: hand_fk.bone.select = False hand_ik.bone.select = True def bake_fk_to_ik_leg(self): for f in range(self.frame_start, self.frame_end +1): bpy.context.scene.frame_set(f) print("baking frame", f) fk_to_ik_leg(self) def fk_to_ik_leg(self): rig = self.rig side = self.side _side = self._side prefix = self.prefix thigh_fk = rig.pose.bones[fk_leg[0] + _side] leg_fk = rig.pose.bones[fk_leg[1] + _side] foot_fk = rig.pose.bones[fk_leg[2] + _side] toes_fk = rig.pose.bones[fk_leg[3] + _side] thigh_ik = rig.pose.bones[ik_leg[0] + _side] leg_ik = rig.pose.bones[ik_leg[1] + _side] foot_ik = rig.pose.bones[ik_leg[2] + _side] pole_ik = rig.pose.bones[ik_leg[3] + _side] toes_ik = rig.pose.bones[ik_leg[4] + _side] foot_01_ik = rig.pose.bones[ik_leg[5] + _side] foot_roll_ik = rig.pose.bones[ik_leg[6] + _side] foot_snap_ik = rig.pose.bones[ik_leg[7] + _side] # Thigh snap snap_rot(thigh_fk, thigh_ik) #thigh_fk.matrix = thigh_ik.matrix.copy() # Leg snap snap_rot(leg_fk, leg_ik) # Foot snap snap_rot(foot_fk, foot_snap_ik) foot_fk.scale =foot_ik.scale # Toes snap snap_rot(toes_fk, toes_ik) toes_fk.scale = toes_ik.scale # rotation fix leg_fk.rotation_euler[1] = 0.0 leg_fk.rotation_euler[2] = 0.0 # switch prop value c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+_side) #base_foot = get_pose_bone(prefix+side+'Foot') c_foot_ik['ik_fk_switch'] = 1.0 # udpate hack bpy.context.view_layer.update() #if bpy.context.scene.frame_current == 2: # print(br) #insert key if autokey enable if bpy.context.scene.tool_settings.use_keyframe_insert_auto: #fk chain c_foot_ik.keyframe_insert(data_path='["ik_fk_switch"]') thigh_fk.keyframe_insert(data_path="rotation_euler") leg_fk.keyframe_insert(data_path="rotation_euler") foot_fk.keyframe_insert(data_path="rotation_euler") foot_fk.keyframe_insert(data_path="scale") toes_fk.keyframe_insert(data_path="rotation_euler") toes_fk.keyframe_insert(data_path="scale") #ik chain foot_ik.keyframe_insert(data_path="location") foot_ik.keyframe_insert(data_path="rotation_euler") foot_ik.keyframe_insert(data_path="scale") foot_01_ik.keyframe_insert(data_path="rotation_euler") foot_roll_ik.keyframe_insert(data_path="location") toes_ik.keyframe_insert(data_path="rotation_euler") toes_ik.keyframe_insert(data_path="scale") pole_ik.keyframe_insert(data_path="location") # change IK to FK foot selection, if selected if foot_ik.bone.select: foot_fk.bone.select = True foot_ik.bone.select = False def bake_ik_to_fk_leg(self): for f in range(self.frame_start, self.frame_end +1): bpy.context.scene.frame_set(f) print("baking frame", f) ik_to_fk_leg(self) def ik_to_fk_leg(self): rig = self.rig side = self.side _side = self._side prefix = self.prefix thigh_fk = rig.pose.bones[fk_leg[0] + _side] leg_fk = rig.pose.bones[fk_leg[1] + _side] foot_fk = rig.pose.bones[fk_leg[2] + _side] toes_fk = rig.pose.bones[fk_leg[3] + _side] thigh_ik = rig.pose.bones[ik_leg[0] + _side] calf_ik = rig.pose.bones[ik_leg[1] + _side] foot_ik = rig.pose.bones[ik_leg[2] + _side] pole_ik = rig.pose.bones[ik_leg[3] + _side] toes_ik = rig.pose.bones[ik_leg[4] + _side] foot_01_ik = rig.pose.bones[ik_leg[5] + _side] foot_roll_ik = rig.pose.bones[ik_leg[6] + _side] # reset IK foot_01 and foot_roll foot_01_ik.rotation_euler = [0,0,0] foot_roll_ik.location[0] = 0.0 foot_roll_ik.location[2] = 0.0 # Snap toes toes_ik.rotation_euler = toes_fk.rotation_euler.copy() toes_ik.scale = toes_fk.scale.copy() # Child Of constraint or parent cases constraint = None bparent_name = "" parent_type = "" valid_constraint = True if len(foot_ik.constraints) > 0: for c in foot_ik.constraints: if not c.mute and c.influence > 0.5 and c.type == 'CHILD_OF': if c.target: #if bone if c.target.type == 'ARMATURE': bparent_name = c.subtarget parent_type = "bone" constraint = c #if object else: bparent_name = c.target.name parent_type = "object" constraint = c if constraint != None: if parent_type == "bone": if bparent_name == "": valid_constraint = False # Snap Foot if constraint and valid_constraint: if parent_type == "bone": bone_parent = rig.pose.bones[bparent_name] foot_ik.matrix = bone_parent.matrix_channel.inverted() @ foot_fk.matrix if parent_type == "object": ob = bpy.data.objects[bparent_name] foot_ik.matrix = constraint.inverse_matrix.inverted() @ ob.matrix_world.inverted() @ foot_fk.matrix else: foot_ik.matrix = foot_fk.matrix # update bpy.context.view_layer.update() # Snap Pole pole_pos = get_ik_pole_pos(thigh_fk, leg_fk, method=2, axis=leg_fk.z_axis) pole_mat = Matrix.Translation(pole_pos) snap_pos_matrix(pole_ik, pole_mat) update_transform() # switch c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+_side) #base_foot = get_pose_bone(prefix+side+'Foot') c_foot_ik['ik_fk_switch'] = 0.0 update_transform() #insert key if autokey enable if bpy.context.scene.tool_settings.use_keyframe_insert_auto: #ik chain c_foot_ik.keyframe_insert(data_path='["ik_fk_switch"]') foot_01_ik.keyframe_insert(data_path="rotation_euler") foot_roll_ik.keyframe_insert(data_path="location") foot_ik.keyframe_insert(data_path="location") foot_ik.keyframe_insert(data_path="rotation_euler") foot_ik.keyframe_insert(data_path="scale") toes_ik.keyframe_insert(data_path="rotation_euler") toes_ik.keyframe_insert(data_path="scale") pole_ik.keyframe_insert(data_path="location") #fk chain thigh_fk.keyframe_insert(data_path="rotation_euler") leg_fk.keyframe_insert(data_path="rotation_euler") foot_fk.keyframe_insert(data_path="rotation_euler") foot_fk.keyframe_insert(data_path="scale") toes_fk.keyframe_insert(data_path="rotation_euler") toes_fk.keyframe_insert(data_path="scale") # change IK to FK foot selection, if selected if foot_fk.bone.select: foot_fk.bone.select = False foot_ik.bone.select = True def get_active_child_of_cns(bone): constraint = None bparent_name = "" parent_type = "" valid_constraint = True if len(bone.constraints) > 0: for c in bone.constraints: if not c.mute and c.influence > 0.5 and c.type == 'CHILD_OF': if c.target: if c.target.type == 'ARMATURE':# bone bparent_name = c.subtarget parent_type = "bone" constraint = c else:# object bparent_name = c.target.name parent_type = "object" constraint = c if constraint: if parent_type == "bone": if bparent_name == "": valid_constraint = False return constraint, bparent_name, parent_type, valid_constraint def is_selected(names, selected_bone_name, startswith=False): side = "" if get_bone_side(selected_bone_name) != None: side = get_bone_side(selected_bone_name) _side = "_"+side if startswith == False: if type(names) == list: for name in names: if not "." in name[-2:]: if name + _side == selected_bone_name: return True else: if name[-2:] == ".x": if name[:-2] + _side == selected_bone_name: return True elif names == selected_bone_name: return True else:#startswith if type(names) == list: for name in names: if selected_bone_name.startswith(name): return True else: return selected_bone_name.startswith(names) return False def is_selected_prop(pbone, prop_name): if pbone.bone.keys(): if prop_name in pbone.bone.keys(): return True ################## User Interface ################## class MR_PT_rig_ui(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "Tool" bl_label = "Mixamo Rig Settings" bl_idname = "MR_PT_rig_ui" @classmethod def poll(self, context): if context.mode != 'POSE': return False return True def draw(self, context): layout = self.layout scn = bpy.context.scene rig = context.active_object if rig == None: return if rig.type != "ARMATURE": return # check if a Mixamo ctrl rig is selected if len(rig.data.keys()): if not 'mr_control_rig' in rig.data.keys(): return else: return pose_bones = rig.pose.bones try: active_bone = context.selected_pose_bones[0]#context.active_pose_bone selected_bone_name = active_bone.name except: return side = get_bone_side(selected_bone_name) prefix = get_mixamo_prefix() # Leg if (is_selected(fk_leg, selected_bone_name) or is_selected(ik_leg, selected_bone_name)): # IK-FK Switch col = layout.column(align=True) #foot_base = get_pose_bone(prefix+side.title()+'Foot') c_foot_ik = get_pose_bone(c_prefix+leg_rig_names["foot_ik"]+'_'+side.title()) col.prop(c_foot_ik, '["ik_fk_switch"]', text="IK-FK Switch", slider=True) col.operator(MR_OT_switch_snap.bl_idname, text="Snap Frame IK/FK") col.operator(MR_OT_switch_snap_anim.bl_idname, text="Snap Anim IK-FK") # Arm if is_selected(fk_arm, selected_bone_name) or is_selected(ik_arm, selected_bone_name): # IK-FK Switch col = layout.column(align=True) #hand_base = get_pose_bone(prefix+side.title()+'Hand') c_hand_ik = get_pose_bone(c_prefix+arm_rig_names["hand_ik"]+'_'+side.title()) col.prop(c_hand_ik, '["ik_fk_switch"]', text="IK-FK Switch", slider=True) col.operator(MR_OT_switch_snap.bl_idname, text="Snap Frame IK-FK") col.operator(MR_OT_switch_snap_anim.bl_idname, text="Snap Anim IK-FK") ################## REGISTER ################## classes = ( MR_OT_arm_bake_fk_to_ik, MR_OT_arm_fk_to_ik, MR_OT_arm_bake_ik_to_fk, MR_OT_arm_ik_to_fk, MR_OT_switch_snap, MR_OT_leg_fk_to_ik, MR_OT_leg_bake_fk_to_ik, MR_OT_leg_ik_to_fk, MR_OT_leg_bake_ik_to_fk, MR_PT_rig_ui, MR_OT_switch_snap_anim) def update_mixamo_tab(): try: bpy.utils.unregister_class(MR_PT_rig_ui) except: pass MR_PT_rig_ui.bl_category = bpy.context.preferences.addons[__package__].preferences.mixamo_tab_name bpy.utils.register_class(MR_PT_rig_ui) def register(): from bpy.utils import register_class for cls in classes: register_class(cls) update_mixamo_tab() bpy.types.Scene.mix_show_ik_fk_advanced = bpy.props.BoolProperty(name="Show IK-FK operators", description="Show IK-FK manual operators", default=False) def unregister(): from bpy.utils import unregister_class for cls in classes: unregister_class(cls) del bpy.types.Scene.mix_show_ik_fk_advanced