From 0391f865e12d432ad06050f654a04f259361f123 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 4 Jan 2022 18:40:07 +0300 Subject: Rigify: support separate IK and FK controls for the toe. Currently the leg rig tries to share one control between IK and FK modes, which looks as a nice optimization at first, but makes it impossible to IK/FK snap correctly if the IK foot is rolled forward. This commit adds an option to generate separate toe controls. --- rigify/rigs/limbs/front_paw.py | 5 ++-- rigify/rigs/limbs/leg.py | 54 ++++++++++++++++++++++++++++++++++++++---- rigify/rigs/limbs/limb_rigs.py | 50 ++++++++++++++++++++++++++++---------- rigify/rigs/limbs/paw.py | 1 + rigify/rigs/limbs/rear_paw.py | 5 ++-- 5 files changed, 93 insertions(+), 22 deletions(-) (limited to 'rigify') diff --git a/rigify/rigs/limbs/front_paw.py b/rigify/rigs/limbs/front_paw.py index f89ca6c4..83cbbd81 100644 --- a/rigify/rigs/limbs/front_paw.py +++ b/rigify/rigs/limbs/front_paw.py @@ -55,10 +55,11 @@ class Rig(pawRig): return [self.bones.ctrl.heel] def get_ik_fk_position_chains(self): - ik_chain, fk_chain = super().get_ik_fk_position_chains() + ik_chain, tail_chain, fk_chain = super().get_ik_fk_position_chains() + assert not tail_chain if not self.use_heel2: return [*ik_chain, ik_chain[-1]], [*fk_chain, fk_chain[-1]] - return ik_chain, fk_chain + return ik_chain, tail_chain, fk_chain def get_extra_ik_controls(self): extra = [self.bones.ctrl.heel2] if self.use_heel2 else [] diff --git a/rigify/rigs/limbs/leg.py b/rigify/rigs/limbs/leg.py index d1b82c15..051c0ec0 100644 --- a/rigify/rigs/limbs/leg.py +++ b/rigify/rigs/limbs/leg.py @@ -63,6 +63,11 @@ class Rig(BaseLimbRig): self.pivot_type = self.params.foot_pivot_type self.heel_euler_order = 'ZXY' if self.main_axis == 'x' else 'XZY' + self.use_ik_toe = self.params.extra_ik_toe + + if self.use_ik_toe: + self.fk_name_suffix_cutoff = 3 + self.fk_ik_layer_cutoff = 4 assert self.pivot_type in {'ANKLE', 'TOE', 'ANKLE_TOE'} @@ -118,6 +123,9 @@ class Rig(BaseLimbRig): #################################################### # IK controls + def get_tail_ik_controls(self): + return [self.bones.ctrl.ik_toe] if self.use_ik_toe else [] + def get_extra_ik_controls(self): controls = super().get_extra_ik_controls() + [self.bones.ctrl.heel] if self.pivot_type == 'ANKLE_TOE': @@ -210,6 +218,31 @@ class Rig(BaseLimbRig): def generate_heel_control_widget(self): create_ballsocket_widget(self.obj, self.bones.ctrl.heel) + #################################################### + # IK toe control + + @stage.generate_bones + def make_ik_toe_control(self): + if self.use_ik_toe: + self.bones.ctrl.ik_toe = self.make_ik_toe_control_bone(self.bones.org.main[3]) + + def make_ik_toe_control_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'ctrl', '_ik')) + + @stage.parent_bones + def parent_ik_toe_control(self): + if self.use_ik_toe: + self.set_bone_parent(self.bones.ctrl.ik_toe, self.bones.mch.heel[2]) + + @stage.configure_bones + def configure_ik_toe_control(self): + if self.use_ik_toe: + self.copy_bone_properties(self.bones.org.main[3], self.bones.ctrl.ik_toe) + + @stage.generate_widgets + def make_ik_toe_control_widget(self): + if self.use_ik_toe: + self.make_fk_control_widget(3, self.bones.ctrl.ik_toe) #################################################### # Heel roll MCH @@ -288,23 +321,26 @@ class Rig(BaseLimbRig): def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org): if i == 3: - align_bone_orientation(self.obj, parent_mch, self.bones.mch.heel[2]) + if not self.use_ik_toe: + align_bone_orientation(self.obj, parent_mch, self.bones.mch.heel[2]) - self.set_bone_parent(parent_mch, prev_org, use_connect=True) + self.set_bone_parent(parent_mch, prev_org, use_connect=True) + else: + self.set_bone_parent(parent_mch, prev_ctrl, use_connect=True, inherit_scale='ALIGNED') else: super().parent_fk_parent_bone(i, parent_mch, prev_ctrl, org, prev_org) def rig_fk_parent_bone(self, i, parent_mch, org): if i == 3: - con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.heel[2]) + if not self.use_ik_toe: + con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.heel[2]) - self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0]) + self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0]) else: super().rig_fk_parent_bone(i, parent_mch, org) - #################################################### # IK system MCH @@ -340,9 +376,17 @@ class Rig(BaseLimbRig): default = 'ANKLE_TOE' ) + params.extra_ik_toe = bpy.props.BoolProperty( + name='Separate IK Toe', + default=False, + description="Generate a separate IK toe control for better IK/FK snapping" + ) + + @classmethod def parameters_ui(self, layout, params): layout.prop(params, 'foot_pivot_type') + layout.prop(params, 'extra_ik_toe') super().parameters_ui(layout, params, 'Foot') diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py index ae854638..2e6fe538 100644 --- a/rigify/rigs/limbs/limb_rigs.py +++ b/rigify/rigs/limbs/limb_rigs.py @@ -257,6 +257,7 @@ class BaseLimbRig(BaseRig): self.bones.ctrl.fk = map_list(self.make_fk_control_bone, count(0), self.bones.org.main) fk_name_suffix_cutoff = 2 + fk_ik_layer_cutoff = 3 def get_fk_name(self, i, org, kind): return make_derived_name(org, kind, '_fk' if i <= self.fk_name_suffix_cutoff else '') @@ -283,8 +284,9 @@ class BaseLimbRig(BaseRig): for args in zip(count(0), self.bones.ctrl.fk, self.bones.org.main): self.configure_fk_control_bone(*args) - ControlLayersOption.FK.assign_rig(self, self.bones.ctrl.fk[0:3]) - ControlLayersOption.FK.assign_rig(self, self.bones.ctrl.fk[3:], combine=True, priority=1) + cut = self.fk_ik_layer_cutoff + ControlLayersOption.FK.assign_rig(self, self.bones.ctrl.fk[0:cut]) + ControlLayersOption.FK.assign_rig(self, self.bones.ctrl.fk[cut:], combine=True, priority=1) def configure_fk_control_bone(self, i, ctrl, org): self.copy_bone_properties(org, ctrl) @@ -352,16 +354,24 @@ class BaseLimbRig(BaseRig): def get_middle_ik_controls(self): return [] + def get_tail_ik_controls(self): + return [] + def get_ik_fk_position_chains(self): ik_chain = self.get_ik_output_chain() - return ik_chain, self.bones.ctrl.fk[0:len(ik_chain)] + tail_chain = self.get_tail_ik_controls() + return ik_chain, tail_chain, self.bones.ctrl.fk[0:len(ik_chain)+len(tail_chain)] def get_ik_control_chain(self): ctrl = self.bones.ctrl return [ctrl.ik_base, ctrl.ik_pole, *self.get_middle_ik_controls(), ctrl.ik] def get_all_ik_controls(self): - return self.get_ik_control_chain() + self.get_extra_ik_controls() + return [ + *self.get_ik_control_chain(), + *self.get_tail_ik_controls(), + *self.get_extra_ik_controls(), + ] @stage.generate_bones def make_ik_controls(self): @@ -596,22 +606,20 @@ class BaseLimbRig(BaseRig): def add_global_buttons(self, panel, rig_name): ctrl = self.bones.ctrl - ik_chain = self.get_ik_output_chain() - fk_chain = ctrl.fk[0:len(ik_chain)] + ik_chain, tail_chain, fk_chain = self.get_ik_fk_position_chains() add_generic_snap_fk_to_ik( panel, - fk_bones=fk_chain, ik_bones=ik_chain, + fk_bones=fk_chain, ik_bones=ik_chain+tail_chain, ik_ctrl_bones=self.get_all_ik_controls(), rig_name=rig_name ) - ik_chain, fk_chain = self.get_ik_fk_position_chains() add_limb_snap_ik_to_fk( panel, master=ctrl.master, - fk_bones=fk_chain, ik_bones=ik_chain, + fk_bones=fk_chain, ik_bones=ik_chain, tail_bones=tail_chain, ik_ctrl_bones=self.get_ik_control_chain(), ik_extra_ctrls=self.get_extra_ik_controls(), rig_name=rig_name @@ -619,7 +627,7 @@ class BaseLimbRig(BaseRig): def add_ik_only_buttons(self, panel, rig_name): ctrl = self.bones.ctrl - ik_chain, fk_chain = self.get_ik_fk_position_chains() + ik_chain, tail_chain, fk_chain = self.get_ik_fk_position_chains() add_limb_toggle_pole( panel, master=ctrl.master, @@ -688,7 +696,7 @@ class BaseLimbRig(BaseRig): @stage.rig_bones def rig_org_chain(self): - ik = self.get_ik_output_chain() + ik = self.get_ik_output_chain() + self.get_tail_ik_controls() for args in zip(count(0), self.bones.org.main, self.bones.ctrl.fk, padnone(ik)): self.rig_org_bone(*args) @@ -979,6 +987,7 @@ class RigifyLimbIk2FkBase: fk_bones: StringProperty(name="FK Bone Chain") ik_bones: StringProperty(name="IK Result Bone Chain") ctrl_bones: StringProperty(name="IK Controls") + tail_bones: StringProperty(name="Tail IK Controls", default="[]") extra_ctrls: StringProperty(name="Extra IK Controls") def init_execute(self, context): @@ -986,6 +995,7 @@ class RigifyLimbIk2FkBase: self.fk_bone_list = json.loads(self.fk_bones) self.ik_bone_list = json.loads(self.ik_bones) self.ctrl_bone_list = json.loads(self.ctrl_bones) + self.tail_bone_list = json.loads(self.tail_bones) self.extra_ctrl_list = json.loads(self.extra_ctrls) def get_use_pole(self, obj): @@ -1016,9 +1026,15 @@ class RigifyLimbIk2FkBase: mat = convert_pose_matrix_via_rest_delta(mat, ik, ctrl) set_transform_from_matrix(obj, ctrl.name, mat, keyflags=keyflags) - def apply_frame_state(self, context, obj, matrices): + def apply_frame_state(self, context, obj, all_matrices): ik_bones = [ obj.pose.bones[k] for k in self.ik_bone_list ] ctrl_bones = [ obj.pose.bones[k] for k in self.ctrl_bone_list ] + tail_bones = [ obj.pose.bones[k] for k in self.tail_bone_list ] + + assert len(all_matrices) == len(ik_bones) + len(tail_bones) + + matrices = all_matrices[0:len(ik_bones)] + tail_matrices = all_matrices[len(ik_bones):] use_pole = self.get_use_pole(obj) @@ -1055,6 +1071,11 @@ class RigifyLimbIk2FkBase: # Assign middle control transforms (final pass) self.assign_middle_controls(context, obj, matrices, ik_bones, ctrl_bones, keyflags=self.keyflags) + # Assign tail control transforms + for mat, ctrl in zip(tail_matrices, tail_bones): + context.view_layer.update() + set_transform_from_matrix(obj, ctrl.name, mat, keyflags=self.keyflags) + # Keyframe controls if self.keyflags is not None: if use_pole: @@ -1083,16 +1104,19 @@ class POSE_OT_rigify_limb_ik2fk_bake(RigifyLimbIk2FkBase, RigifyBakeKeyframesMix return self.bake_get_all_bone_curves(self.ctrl_bone_list + self.extra_ctrl_list, TRANSFORM_PROPS_ALL) '''] -def add_limb_snap_ik_to_fk(panel, *, master=None, fk_bones=[], ik_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[], rig_name=''): +def add_limb_snap_ik_to_fk(panel, *, master=None, fk_bones=[], ik_bones=[], tail_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[], rig_name=''): panel.use_bake_settings() panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_IK_FK) panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_IK_FK) + assert len(fk_bones) == len(ik_bones) + len(tail_bones) + op_props = { 'prop_bone': master, 'fk_bones': json.dumps(fk_bones), 'ik_bones': json.dumps(ik_bones), 'ctrl_bones': json.dumps(ik_ctrl_bones), + 'tail_bones': json.dumps(tail_bones), 'extra_ctrls': json.dumps(ik_extra_ctrls), } diff --git a/rigify/rigs/limbs/paw.py b/rigify/rigs/limbs/paw.py index 9ea97188..3aa336ac 100644 --- a/rigify/rigs/limbs/paw.py +++ b/rigify/rigs/limbs/paw.py @@ -46,6 +46,7 @@ class Rig(BaseLimbRig): if self.use_heel2: self.toe_bone_index = 4 self.fk_name_suffix_cutoff = 3 + self.fk_ik_layer_cutoff = 4 super().initialize() diff --git a/rigify/rigs/limbs/rear_paw.py b/rigify/rigs/limbs/rear_paw.py index 1292dc2e..cdbbd91e 100644 --- a/rigify/rigs/limbs/rear_paw.py +++ b/rigify/rigs/limbs/rear_paw.py @@ -55,10 +55,11 @@ class Rig(pawRig): return [self.bones.ctrl.heel] def get_ik_fk_position_chains(self): - ik_chain, fk_chain = super().get_ik_fk_position_chains() + ik_chain, tail_chain, fk_chain = super().get_ik_fk_position_chains() + assert not tail_chain if not self.use_heel2: return [*ik_chain, ik_chain[-1]], [*fk_chain, fk_chain[-1]] - return ik_chain, fk_chain + return ik_chain, tail_chain, fk_chain def get_extra_ik_controls(self): extra = [self.bones.ctrl.heel2] if self.use_heel2 else [] -- cgit v1.2.3