Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rigify/rigs/limbs/super_finger.py310
1 files changed, 298 insertions, 12 deletions
diff --git a/rigify/rigs/limbs/super_finger.py b/rigify/rigs/limbs/super_finger.py
index 0b3fcd8a..978c0e09 100644
--- a/rigify/rigs/limbs/super_finger.py
+++ b/rigify/rigs/limbs/super_finger.py
@@ -20,6 +20,7 @@
import bpy
import re
+import json
from itertools import count
@@ -27,9 +28,11 @@ from ...utils.errors import MetarigError
from ...utils.bones import put_bone, flip_bone, align_chain_x_axis, set_bone_widget_transform
from ...utils.naming import make_derived_name
from ...utils.widgets import create_widget
-from ...utils.widgets_basic import create_circle_widget
+from ...utils.widgets_basic import create_circle_widget, create_sphere_widget
from ...utils.misc import map_list
from ...utils.layers import ControlLayersOption
+from ...utils.switch_parent import SwitchParentBuilder
+from ...utils.animation import add_generic_snap, add_fk_ik_snap_buttons
from ...base_rig import stage
@@ -42,12 +45,15 @@ class Rig(SimpleChainRig):
super().initialize()
self.bbone_segments = self.params.bbones
- self.first_parent = self.get_bone_parent(self.bones.org[0])
+ self.make_ik = self.params.make_extra_ik_control
def prepare_bones(self):
if self.params.primary_rotation_axis == 'automatic':
align_chain_x_axis(self.obj, self.bones.org)
+ def parent_bones(self):
+ self.rig_parent_bone = self.get_bone_parent(self.bones.org[0])
+
##############################
# Master Control
@@ -138,6 +144,76 @@ class Rig(SimpleChainRig):
create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5)
##############################
+ # IK Control
+
+ @stage.generate_bones
+ def make_ik_control(self):
+ if self.make_ik:
+ self.bones.ctrl.ik = self.make_ik_control_bone(self.bones.org)
+
+ self.build_ik_parent_switch(SwitchParentBuilder(self.generator))
+
+ def make_ik_control_bone(self, orgs):
+ name = self.copy_bone(orgs[-1], make_derived_name(orgs[0], 'ctrl', '_ik'), scale=0.7)
+ put_bone(self.obj, name, self.get_bone(orgs[-1]).tail)
+ return name
+
+ def build_ik_parent_switch(self, pbuilder):
+ ctrl = self.bones.ctrl
+
+ pbuilder.build_child(
+ self, ctrl.ik, prop_bone=ctrl.ik,
+ select_tags=['held_object', 'limb_ik', {'child', 'limb_end'}], only_selected=True,
+ prop_id='IK_parent', prop_name='IK Parent', controls=[ ctrl.ik ],
+ no_fix_rotation=True, no_fix_scale=True,
+ )
+
+ @stage.configure_bones
+ def configure_ik_control(self):
+ if self.make_ik:
+ bone = self.get_bone(self.bones.ctrl.ik)
+ bone.lock_rotation = True, True, True
+ bone.lock_rotation_w = True
+ bone.lock_scale = True, True, True
+
+ @stage.configure_bones
+ def configure_ik_control_properties(self):
+ if self.make_ik:
+ ctrl = self.bones.ctrl
+ rig_name = ctrl.fk[0]
+
+ panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten())
+
+ self.make_property(
+ ctrl.ik, 'FK_IK', 0.0,
+ description="Enable simple IK correction on top of FK posing"
+ )
+ panel.custom_prop(ctrl.ik, 'FK_IK', text="Finger IK ({})".format(rig_name), slider=True)
+
+ axis = self.params.primary_rotation_axis
+
+ add_finger_snap_fk_to_ik(
+ panel, master=ctrl.master, fk_bones=ctrl.fk,
+ ik_bones=self.bones.org, ik_control=ctrl.ik,
+ ik_constraint_bone=self.bones.org[-1],
+ axis=self.axis_options[axis]['id'],
+ rig_name=rig_name, compact=True,
+ )
+
+ add_generic_snap(
+ panel, output_bones=[ctrl.ik], input_bones=ctrl.fk[-1:],
+ input_ctrl_bones=[ctrl.master, *ctrl.fk],
+ label='IK->FK', rig_name=rig_name, tooltip='IK to FK',
+ compact=True, locks=(False,True,True),
+ )
+
+
+ @stage.generate_widgets
+ def make_ik_control_widget(self):
+ if self.make_ik:
+ create_sphere_widget(self.obj, self.bones.ctrl.ik)
+
+ ##############################
# MCH bend chain
@stage.generate_bones
@@ -145,29 +221,29 @@ class Rig(SimpleChainRig):
self.bones.mch.bend = map_list(self.make_mch_bend_bone, self.bones.org)
def make_mch_bend_bone(self, org):
- return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), parent=False)
+ return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), parent=False, scale=0.3)
@stage.parent_bones
def parent_mch_bend_chain(self):
ctrls = self.bones.ctrl.fk
- for args in zip(self.bones.mch.bend, [self.first_parent] + ctrls):
+ for args in zip(self.bones.mch.bend, [self.rig_parent_bone] + ctrls):
self.set_bone_parent(*args)
# Match axis to expression
axis_options = {
- "automatic": {"axis": 0,
+ "automatic": {"axis": 0, "id": '+X',
"expr": '(1-sy)*pi'},
- "X": {"axis": 0,
+ "X": {"axis": 0, "id": '+X',
"expr": '(1-sy)*pi'},
- "-X": {"axis": 0,
+ "-X": {"axis": 0, "id": '-X',
"expr": '-((1-sy)*pi)'},
- "Y": {"axis": 1,
+ "Y": {"axis": 1, "id": '+Y',
"expr": '(1-sy)*pi'},
- "-Y": {"axis": 1,
+ "-Y": {"axis": 1, "id": '-Y',
"expr": '-((1-sy)*pi)'},
- "Z": {"axis": 2,
+ "Z": {"axis": 2, "id": '+Z',
"expr": '(1-sy)*pi'},
- "-Z": {"axis": 2,
+ "-Z": {"axis": 2, "id": '-Z',
"expr": '-((1-sy)*pi)'}
}
@@ -207,7 +283,7 @@ class Rig(SimpleChainRig):
@stage.parent_bones
def parent_mch_stretch_chain(self):
ctrls = self.bones.ctrl.fk
- for args in zip(self.bones.mch.stretch, [self.first_parent] + ctrls[1:]):
+ for args in zip(self.bones.mch.stretch, [self.rig_parent_bone] + ctrls[1:]):
self.set_bone_parent(*args)
@stage.rig_bones
@@ -232,6 +308,30 @@ class Rig(SimpleChainRig):
for args in zip(count(0), self.bones.org, self.bones.mch.stretch):
self.rig_org_bone(*args)
+ if self.make_ik:
+ self.rig_org_ik(self.bones.org, self.bones.ctrl.ik)
+
+ def rig_org_ik(self, orgs, ik_ctrl):
+ axis = self.params.primary_rotation_axis
+ options = self.axis_options[axis]
+
+ # Lock IK axis on child bones, using stiffness to preserve
+ # original rotation rather than zeroing it out.
+ stiffness = [1.0, 1.0, 1.0]
+ stiffness[options['axis']] = 0.0
+
+ for org in orgs[1:]:
+ bone = self.get_bone(org)
+ bone.ik_stiffness_x, bone.ik_stiffness_y, bone.ik_stiffness_z = stiffness
+
+ # Add the constraint
+ con = self.make_constraint(
+ orgs[-1], 'IK', ik_ctrl, name='FingerIK',
+ chain_count=len(orgs), use_stretch=False,
+ )
+
+ self.make_driver(con, "influence", variables=[(ik_ctrl, 'FK_IK')])
+
##############################
# Deform chain
@@ -275,6 +375,12 @@ class Rig(SimpleChainRig):
description = 'Number of B-Bone segments'
)
+ params.make_extra_ik_control = bpy.props.BoolProperty(
+ name = "Extra IK Control",
+ default = False,
+ description = "Create an optional IK control"
+ )
+
ControlLayersOption.TWEAK.add_parameters(params)
@classmethod
@@ -286,9 +392,189 @@ class Rig(SimpleChainRig):
r.prop(params, "primary_rotation_axis", text="")
layout.prop(params, 'bbones')
+ layout.prop(params, 'make_extra_ik_control', text='IK Control')
ControlLayersOption.TWEAK.parameters_ui(layout, params)
+#############################
+# Finger FK to IK operator ##
+#############################
+
+SCRIPT_REGISTER_OP_SNAP_FK_IK = ['POSE_OT_rigify_finger_fk2ik', 'POSE_OT_rigify_finger_fk2ik_bake']
+
+SCRIPT_UTILITIES_OP_SNAP_FK_IK = ['''
+########################
+## Limb Snap IK to FK ##
+########################
+
+class RigifyFingerFk2IkBase:
+ ik_control: StringProperty(name="IK Control")
+ ik_chain: StringProperty(name="IK output chain")
+ constraint_bone: StringProperty(name="Bone With the IK Constraint")
+ fk_master: StringProperty(name="FK Master Control")
+ fk_chain: StringProperty(name="FK Bone Chain")
+ axis: StringProperty(name="Main Rotation Axis", default="+X")
+
+ def init_execute(self, context):
+ self.ik_chain_list = json.loads(self.ik_chain)
+ self.fk_chain_list = json.loads(self.fk_chain)
+
+ # Extracting the IK state - requires forcing IK on temporarily
+ def find_constraint_drivers(self, obj):
+ self.driver_fcurves = {}
+ self.ik_constraint = None
+
+ if self.constraint_bone:
+ bone = obj.pose.bones[self.constraint_bone]
+ self.ik_constraint = con = bone.constraints['FingerIK']
+ self.driver_fcurves = DriverCurveTable(obj).get_prop_curves(con, 'influence')
+
+ def before_save_state(self, context, obj):
+ self.find_constraint_drivers(obj)
+
+ if self.ik_constraint:
+ for fcu in self.driver_fcurves.values():
+ fcu.mute = True
+
+ self.ik_constraint.influence = 1
+
+ context.view_layer.update()
+
+ def get_fk_axis_angles(self, obj):
+ options = self.axis_options[self.axis]
+ angles = []
+
+ for bone in self.fk_chain_list[1:-1]:
+ matrix = obj.pose.bones[bone].matrix_basis
+ eulers = matrix.to_euler(options['order'])
+ angles.append(eulers[options['axis']])
+
+ return angles
+
+ def get_ik_original_matrix(self, obj):
+ bone = obj.pose.bones[self.ik_chain_list[0]]
+
+ if len(bone.constraints) == 1 and bone.constraints[0].type == 'COPY_TRANSFORMS':
+ target = bone.constraints[0].subtarget
+ return obj.pose.bones[target].matrix
+
+ def save_frame_state(self, context, obj):
+ matrices = get_chain_transform_matrices(obj, self.ik_chain_list)
+ fk_matrices = get_chain_transform_matrices(obj, self.fk_chain_list)
+ angles = self.get_fk_axis_angles(obj)
+ ik_original = self.get_ik_original_matrix(obj)
+ return (matrices, fk_matrices, angles, ik_original)
+
+ def after_save_state(self, context, obj):
+ if self.ik_constraint:
+ for fcu in self.driver_fcurves.values():
+ fcu.mute = False
+
+ context.view_layer.update()
+
+ # Applying the state
+ axis_options = {
+ "+X": {"axis": 0, "sign": 1, "order": 'ZYX'},
+ "-X": {"axis": 0, "sign": -1, "order": 'ZYX'},
+ "+Y": {"axis": 1, "sign": 1, "order": 'ZXY'},
+ "-Y": {"axis": 1, "sign": -1, "order": 'ZXY'},
+ "+Z": {"axis": 2, "sign": 1, "order": 'XYZ'},
+ "-Z": {"axis": 2, "sign": -1, "order": 'XYZ'},
+ }
+
+ def apply_frame_state(self, context, obj, state):
+ matrices, fk_matrices, old_angles, ik_original = state
+
+ fk_master = obj.pose.bones[self.fk_master]
+ fk_chain = [ obj.pose.bones[k] for k in self.fk_chain_list ]
+
+ # Set the master control position and rotation.
+ master_mat = matrices[0]
+
+ if ik_original:
+ master_mat = master_mat @ ik_original.inverted() @ fk_matrices[0]
+
+ master_mat = obj.convert_space(
+ pose_bone=fk_chain[0].parent, matrix=master_mat,
+ from_space='POSE', to_space='LOCAL'
+ )
+ master_mat = obj.convert_space(
+ pose_bone=fk_master, matrix=master_mat,
+ from_space='LOCAL', to_space='POSE'
+ )
+ master_mat.translation = matrices[0].translation
+
+ set_transform_from_matrix(obj, self.fk_master, master_mat)
+
+ fk_master.scale = (1, 1, 1)
+
+ if self.keyflags is not None:
+ keyframe_transform_properties(obj, self.fk_master, self.keyflags)
+
+ context.view_layer.update()
+
+ # Apply the detail controls
+ set_chain_transforms_from_matrices(
+ context, obj, self.fk_chain_list[:-1], matrices, keyflags=self.keyflags,
+ )
+
+ set_transform_from_matrix(
+ obj, self.fk_chain_list[-1], Matrix.Identity(4), space='LOCAL', keyflags=self.keyflags
+ )
+
+ # Compute the master scale from average control angle, biased by original
+ options = self.axis_options[self.axis]
+
+ angles = self.get_fk_axis_angles(obj)
+ avg_angle = sum(a - b for a, b in zip(angles, old_angles)) / len(angles)
+
+ fk_master.scale[1] = 1 - avg_angle * options['sign'] / pi
+
+ if self.keyflags is not None:
+ keyframe_transform_properties(obj, self.fk_master, self.keyflags)
+
+ context.view_layer.update()
+
+ # Re-apply the rest of the detail controls
+ set_chain_transforms_from_matrices(
+ context, obj, self.fk_chain_list[1:-1], matrices[1:], keyflags=self.keyflags,
+ )
+
+class POSE_OT_rigify_finger_fk2ik(RigifyFingerFk2IkBase, RigifySingleUpdateMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_finger_fk2ik_" + rig_id
+ bl_label = "Snap FK->IK"
+ bl_description = "Snap the FK chain to IK result"
+
+class POSE_OT_rigify_finger_fk2ik_bake(RigifyFingerFk2IkBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_finger_fk2ik_bake_" + rig_id
+ bl_label = "Apply Snap FK->IK To Keyframes"
+ bl_description = "Snap the FK chain keyframes to IK result"
+
+ def execute_scan_curves(self, context, obj):
+ fk_bones = [self.fk_master, *self.fk_chain_list]
+ self.bake_add_bone_frames(fk_bones + [self.ik_control], TRANSFORM_PROPS_ALL)
+ return self.bake_get_all_bone_curves(fk_bones, TRANSFORM_PROPS_ALL)
+''']
+
+def add_finger_snap_fk_to_ik(panel, *, master=None, fk_bones=[], ik_bones=[], ik_control=None, ik_constraint_bone=None, axis='+X', rig_name='', compact=None):
+ panel.use_bake_settings()
+ panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_FK_IK)
+ panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_FK_IK)
+
+ op_props = {
+ 'fk_master': master,
+ 'fk_chain': json.dumps(fk_bones),
+ 'ik_chain': json.dumps(ik_bones),
+ 'ik_control': ik_control,
+ 'constraint_bone': ik_constraint_bone,
+ 'axis': axis,
+ }
+
+ add_fk_ik_snap_buttons(
+ panel, 'pose.rigify_finger_fk2ik_{rig_id}', 'pose.rigify_finger_fk2ik_bake_{rig_id}',
+ label='FK->IK', rig_name=rig_name, properties=op_props,
+ clear_bones=[master, *fk_bones], compact=compact,
+ )
def create_sample(obj):
# generated by rigify.utils.write_metarig