class CLOUDRIG_OT_ikfk_bake(CLOUDRIG_OT_snap_mapped_bake): """Extends CLOUDRIG_OT_snap_mapped_bake with special treatment for the IK elbow.""" bl_idname = "pose.cloudrig_toggle_ikfk_bake" bl_label = "Toggle And Bake IK/FK" bl_description = "Toggle a custom property and snap some bones to some other bones" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} ik_pole: StringProperty() fk_first: StringProperty() fk_last: StringProperty() def init_invoke(self, context): rig = context.object self.pole = rig.pose.bones.get(self.ik_pole) # Can be None. prop_value = get_custom_property_value(rig, self.prop_bone, self.prop_id) self.is_pole = prop_value == 0 and self.pole != None super().init_invoke(context) if self.is_pole: self.bone_names.append(self.pole.name) self.bones = json.dumps(self.bone_names) def save_frame_state(self, context, rig, bone_names=None) -> Tuple[List[Matrix], List[Vector]]: matrices, scales = super().save_frame_state(context, rig) if self.bone_names[0].count("TIP") > 0: # a bone chain with a TIP-bone is one bone longer as the FK-chain # for this have to recalculate the bone matrices[0] = self.get_tip_target_matrix() scales[0] = rig.pose.bones.get(self.bone_names[0]).scale if self.is_pole: matrices.append(self.get_pole_target_matrix()) scales.append(rig.pose.bones.get(self.ik_pole).scale) return matrices, scales def get_tip_target_matrix(self): """ Find the matrix where the IK TIP bone should be. """ rig = self.bake_rig tip_bn = self.bone_names[0] bn = self.bone_map[0][1] print("TIP->FK:", bn) tip = rig.pose.bones.get(tip_bn) assert tip, "TIP-Bone does not exist: " + tip_bn pn = rig.pose.bones.get(bn) assert pn, "FK-Bone does not exist: " + bn tip_mat = tip.matrix.copy() axis = pn.head - pn.tail loc = pn.head - axis tip_mat.translation = loc return tip_mat def get_pole_target_matrix(self): """ Find the matrix where the IK pole should be. """ """ This is only accurate when the bone chain lies perfectly on a plane and the IK Pole Angle is divisible by 90. This should be the case for a correct IK chain! """ rig = self.bake_rig fk_first = rig.pose.bones.get(self.fk_first) fk_last = rig.pose.bones.get(self.fk_last) ik_pole = self.pole assert fk_first and fk_last, f"Can't calculate pole target location due to one of these FK bones missing: {self.fk_first}, {self.fk_last}" # Old part #chain_length = fk_first.vector.length + fk_last.vector.length #pole_distance = chain_length/2 #pole_direction = (fk_first.vector - fk_last.vector).normalized() # New Part # https://blender.stackexchange.com/questions/19754/how-to-set-calculate-pole-angle-of-ik-constraint-so-the-chain-does-not-move pole_distance = (ik_pole.head - fk_first.tail).length ik_axis = fk_last.tail - fk_first.head pole_location = ik_pole.matrix.translation pole_axis = pole_location - fk_first.head pole_normal = ik_axis.cross(pole_axis) pole_direction = (pole_normal.cross(ik_axis)).normalized() pole_loc = fk_first.tail + pole_direction * pole_distance mat = self.pole.matrix.copy() mat.translation = pole_loc return mat