[Bf-extensions-cvs] [a41ceb3a] master: Rigify: add optional primitive IK in limbs.super_finger.

Alexander Gavrilov noreply at git.blender.org
Mon Dec 16 16:56:30 CET 2019


Commit: a41ceb3ac8d48fcf8f47360376d009ea08dbb894
Author: Alexander Gavrilov
Date:   Sat Nov 2 11:57:25 2019 +0300
Branches: master
https://developer.blender.org/rBAa41ceb3ac8d48fcf8f47360376d009ea08dbb894

Rigify: add optional primitive IK in limbs.super_finger.

Basic IK support in fingers could be useful for easily
avoiding fingertips sliding when animating minor movement
between the hand and an object it is holding.

As there are 10 fingers, to limit the performance impact the
IK itself is implemented using just one extra control, one
constraint, and one driver. The parent switch adds one more
bone, constraint and driver.

This simple implementation requires applying IK as a correction
on top of the FK shape to share the FK controls for precisely
defining the shape, which means that stretch can't be implemented
without giving up on exact IK<->FK snapping. This also means
that unlike limbs this IK is not indended for independent use,
and must always be used as a local anti-slide fix on top of
primarily FK animation.

The parent switch is designed to work with the extra wrist
control and/or a held object pivot, demonstrating the tag
feature of SwitchParentBuilder.

===================================================================

M	rigify/rigs/limbs/super_finger.py

===================================================================

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
 
@@ -137,6 +143,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
 
@@ -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(se

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list