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.
This commit is contained in:
parent
6afec05c32
commit
0391f865e1
|
@ -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 []
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 []
|
||||
|
|
Loading…
Reference in New Issue