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:
Diffstat (limited to 'rigify/rigs/limbs/limb_rigs.py')
-rw-r--r--rigify/rigs/limbs/limb_rigs.py1047
1 files changed, 1047 insertions, 0 deletions
diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py
new file mode 100644
index 00000000..0b6c0425
--- /dev/null
+++ b/rigify/rigs/limbs/limb_rigs.py
@@ -0,0 +1,1047 @@
+#====================== BEGIN GPL LICENSE BLOCK ======================
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+import json
+
+from ...utils.animation import add_generic_snap_fk_to_ik, add_fk_ik_snap_buttons
+from ...utils.rig import connected_children_names
+from ...utils.bones import BoneDict, put_bone, compute_chain_x_axis, align_bone_orientation
+from ...utils.bones import align_bone_x_axis, align_bone_y_axis, align_bone_z_axis
+from ...utils.naming import strip_org, make_derived_name
+from ...utils.layers import ControlLayersOption
+from ...utils.misc import pairwise_nozip, padnone, map_list
+from ...utils.switch_parent import SwitchParentBuilder
+
+from ...base_rig import stage, BaseRig
+
+from ...utils.widgets_basic import create_circle_widget, create_sphere_widget, create_line_widget, create_limb_widget
+from ..widgets import create_gear_widget, create_ikarrow_widget
+
+from ...rig_ui_template import UTILITIES_FUNC_COMMON_IKFK
+
+from math import pi
+from itertools import count, repeat, chain
+from mathutils import Vector
+from collections import namedtuple
+
+
+SegmentEntry = namedtuple('SegmentEntry', ['org', 'org_idx', 'seg_idx', 'pos'])
+
+
+class BaseLimbRig(BaseRig):
+ """Common base for limb rigs."""
+
+ segmented_orgs = 2 # Number of org bones to segment
+
+ def find_org_bones(self, bone):
+ return BoneDict(
+ main=[bone.name] + connected_children_names(self.obj, bone.name),
+ )
+
+ def initialize(self):
+ orgs = self.bones.org.main
+
+ if len(orgs) < self.segmented_orgs + 1:
+ self.raise_error("Input to rig type must be a chain of at least 3 bones.")
+
+ self.segments = self.params.segments
+ self.bbone_segments = self.params.bbones
+
+ rot_axis = self.params.rotation_axis
+
+ if rot_axis in {'x', 'automatic'}:
+ self.main_axis, self.aux_axis = 'x', 'z'
+ elif rot_axis == 'z':
+ self.main_axis, self.aux_axis = 'z', 'x'
+ else:
+ self.raise_error('Unexpected axis value: {}', rot_axis)
+
+ self.segment_table = [
+ SegmentEntry(org, i, j, self.get_segment_pos(org, j))
+ for i, org in enumerate(orgs[:self.segmented_orgs])
+ for j in range(self.segments)
+ ]
+ self.segment_table_end = [
+ SegmentEntry(org, i + self.segmented_orgs, None, self.get_bone(org).head)
+ for i, org in enumerate(orgs[self.segmented_orgs:])
+ ]
+
+ self.segment_table_full = self.segment_table + self.segment_table_end
+ self.segment_table_tweak = self.segment_table + self.segment_table_end[0:1]
+
+ def generate_bones(self):
+ bones = map_list(self.get_bone, self.bones.org.main[0:2])
+
+ self.elbow_vector = self.compute_elbow_vector(bones)
+ self.pole_angle = self.compute_pole_angle(bones, self.elbow_vector)
+ self.rig_parent_bone = self.get_bone_parent(self.bones.org.main[0])
+
+
+ ####################################################
+ # Utilities
+
+ def compute_elbow_vector(self, bones):
+ lo_vector = bones[1].vector
+ tot_vector = bones[1].tail - bones[0].head
+ return (lo_vector.project(tot_vector) - lo_vector).normalized() * tot_vector.length
+
+ def get_main_axis(self, bone):
+ return getattr(bone, self.main_axis + '_axis')
+
+ def get_aux_axis(self, bone):
+ return getattr(bone, self.aux_axis + '_axis')
+
+ def compute_pole_angle(self, bones, elbow_vector):
+ if self.params.rotation_axis == 'z':
+ return 0
+
+ vector = self.get_aux_axis(bones[0]) + self.get_aux_axis(bones[1])
+
+ if elbow_vector.angle(vector) > pi/2:
+ return -pi/2
+ else:
+ return pi/2
+
+ def get_segment_pos(self, org, seg):
+ bone = self.get_bone(org)
+ return bone.head + bone.vector * (seg / self.segments)
+
+ def align_roll_bone(self, org, name, y_axis):
+ if y_axis:
+ align_bone_y_axis(self.obj, name, y_axis)
+
+ if self.main_axis == 'x':
+ align_bone_x_axis(self.obj, name, self.get_bone(org).x_axis)
+ else:
+ align_bone_z_axis(self.obj, name, self.get_bone(org).z_axis)
+
+ @staticmethod
+ def vector_without_z(vector):
+ return Vector((vector[0], vector[1], 0))
+
+ ####################################################
+ # BONES
+ #
+ # org:
+ # main[]:
+ # Main ORG bone chain
+ # ctrl:
+ # master:
+ # Main property control.
+ # fk[]:
+ # FK control chain.
+ # tweak[]:
+ # Tweak control chain.
+ # ik_base, ik_pole, ik
+ # IK controls
+ # ik_vispole
+ # IK pole visualization.
+ # mch:
+ # master:
+ # Parent of the master control.
+ # follow:
+ # FK follow behavior.
+ # fk[]:
+ # FK chain parents (or None)
+ # ik_stretch
+ # IK stretch switch implementation.
+ # ik_target
+ # Corrected target position.
+ # ik_end
+ # End of the IK chain: [ik_base, ik_end]
+ # deform[]:
+ # DEF bones
+ #
+ ####################################################
+
+ ####################################################
+ # Master control
+
+ @stage.generate_bones
+ def make_master_control(self):
+ org = self.bones.org.main[0]
+ self.bones.mch.master = name = self.copy_bone(org, make_derived_name(org, 'mch', '_parent_socket'), scale=1/12)
+ self.get_bone(name).roll = 0
+ self.bones.ctrl.master = name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_parent'), scale=1/4)
+ self.get_bone(name).roll = 0
+ self.prop_bone = self.bones.ctrl.master
+
+ @stage.parent_bones
+ def parent_master_control(self):
+ self.set_bone_parent(self.bones.ctrl.master, self.bones.mch.master)
+ self.set_bone_parent(self.bones.mch.master, self.bones.mch.follow)
+
+ @stage.configure_bones
+ def configure_master_control(self):
+ bone = self.get_bone(self.bones.ctrl.master)
+ bone.lock_location = (True, True, True)
+ bone.lock_rotation = (True, True, True)
+ bone.lock_scale = (True, True, True)
+ bone.lock_rotation_w = True
+
+ @stage.rig_bones
+ def rig_master_control(self):
+ self.make_constraint(self.bones.mch.master, 'COPY_ROTATION', self.bones.org.main[0])
+
+ @stage.generate_widgets
+ def make_master_control_widget(self):
+ create_gear_widget(self.obj, self.bones.ctrl.master, size=10)
+
+
+ ####################################################
+ # FK follow MCH
+
+ @stage.generate_bones
+ def make_mch_follow_bone(self):
+ org = self.bones.org.main[0]
+ self.bones.mch.follow = self.copy_bone(org, make_derived_name(org, 'mch', '_parent'), scale=1/4)
+
+ @stage.parent_bones
+ def parent_mch_follow_bone(self):
+ mch = self.bones.mch.follow
+ align_bone_orientation(self.obj, mch, 'root')
+ self.set_bone_parent(mch, self.rig_parent_bone)
+
+ @stage.configure_bones
+ def configure_mch_follow_bone(self):
+ ctrl = self.bones.ctrl
+ panel = self.script.panel_with_selected_check(self, [ctrl.master, *ctrl.fk])
+
+ self.make_property(self.prop_bone, 'FK_limb_follow', default=0.0)
+ panel.custom_prop(self.prop_bone, 'FK_limb_follow', text='FK Limb Follow', slider=True)
+
+ @stage.rig_bones
+ def rig_mch_follow_bone(self):
+ mch = self.bones.mch.follow
+
+ con = self.make_constraint(mch, 'COPY_ROTATION', 'root')
+ self.make_constraint(mch, 'COPY_SCALE', 'root')
+
+ self.make_driver(con, 'influence', variables=[(self.prop_bone, 'FK_limb_follow')])
+
+
+ ####################################################
+ # FK control chain
+
+ @stage.generate_bones
+ def make_fk_control_chain(self):
+ self.bones.ctrl.fk = map_list(self.make_fk_control_bone, count(0), self.bones.org.main)
+
+ def get_fk_name(self, i, org, kind):
+ return make_derived_name(org, kind, '_fk' if i <= 2 else '')
+
+ def make_fk_control_bone(self, i, org):
+ return self.copy_bone(org, self.get_fk_name(i, org, 'ctrl'))
+
+ @stage.parent_bones
+ def parent_fk_control_chain(self):
+ fk = self.bones.ctrl.fk
+ for args in zip(count(0), fk, [self.bones.mch.follow]+fk, self.bones.org.main, self.bones.mch.fk):
+ self.parent_fk_control_bone(*args)
+
+ def parent_fk_control_bone(self, i, ctrl, prev, org, parent_mch):
+ if parent_mch:
+ self.set_bone_parent(ctrl, parent_mch)
+ else:
+ self.set_bone_parent(ctrl, prev, use_connect=(i > 0))
+
+ @stage.configure_bones
+ def configure_fk_control_chain(self):
+ for args in zip(count(0), self.bones.ctrl.fk, self.bones.org.main):
+ self.configure_fk_control_bone(*args)
+
+ ControlLayersOption.FK.assign(self.params, self.obj, self.bones.ctrl.fk[0:3])
+
+ def configure_fk_control_bone(self, i, ctrl, org):
+ self.copy_bone_properties(org, ctrl)
+
+ if i == 2:
+ self.get_bone(ctrl).lock_location = True, True, True
+
+ @stage.generate_widgets
+ def make_fk_control_widgets(self):
+ for args in zip(count(0), self.bones.ctrl.fk):
+ self.make_fk_control_widget(*args)
+
+ def make_fk_control_widget(self, i, ctrl):
+ if i < 2:
+ create_limb_widget(self.obj, ctrl)
+ elif i == 2:
+ create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.0)
+ else:
+ create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.5)
+
+
+ ####################################################
+ # FK parents MCH chain
+
+ @stage.generate_bones
+ def make_fk_parent_chain(self):
+ self.bones.mch.fk = map_list(self.make_fk_parent_bone, count(0), self.bones.org.main)
+
+ def make_fk_parent_bone(self, i, org):
+ if 2 <= i <= 3:
+ return self.copy_bone(org, self.get_fk_name(i, org, 'mch'), parent=True, scale=1/4)
+
+ @stage.parent_bones
+ def parent_fk_parent_chain(self):
+ mch = self.bones.mch
+ orgs = self.bones.org.main
+ for args in zip(count(0), mch.fk, [mch.follow]+self.bones.ctrl.fk, orgs, [None]+orgs):
+ self.parent_fk_parent_bone(*args)
+
+ def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org):
+ if i == 2:
+ self.set_bone_parent(parent_mch, prev_ctrl, use_connect=True)
+
+ @stage.rig_bones
+ def rig_fk_parent_chain(self):
+ for args in zip(count(0), self.bones.mch.fk, self.bones.org.main):
+ self.rig_fk_parent_bone(*args)
+
+ def rig_fk_parent_bone(self, i, parent_mch, org):
+ if i == 2:
+ self.make_constraint(parent_mch, 'COPY_SCALE', 'root')
+
+
+ ####################################################
+ # IK controls
+
+ def get_extra_ik_controls(self):
+ return []
+
+ def get_all_ik_controls(self):
+ ctrl = self.bones.ctrl
+ return [ctrl.ik_base, ctrl.ik_pole, ctrl.ik] + self.get_extra_ik_controls()
+
+ @stage.generate_bones
+ def make_ik_controls(self):
+ orgs = self.bones.org.main
+
+ self.bones.ctrl.ik_base = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik'))
+ self.bones.ctrl.ik_pole = self.make_ik_pole_bone(orgs)
+ self.bones.ctrl.ik = self.make_ik_control_bone(orgs)
+
+ self.build_ik_parent_switch(SwitchParentBuilder(self.generator))
+
+ def make_ik_pole_bone(self, orgs):
+ name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik_target'))
+
+ pole = self.get_bone(name)
+ pole.head = self.get_bone(orgs[0]).tail + self.elbow_vector
+ pole.tail = pole.head - self.elbow_vector/8
+ pole.roll = 0
+
+ return name
+
+ def make_ik_control_bone(self, orgs):
+ return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik'))
+
+ def register_switch_parents(self, pbuilder):
+ if self.rig_parent_bone:
+ pbuilder.register_parent(self, self.rig_parent_bone)
+
+ def build_ik_parent_switch(self, pbuilder):
+ ctrl = self.bones.ctrl
+
+ master = lambda: self.bones.ctrl.master
+ pcontrols = lambda: [ ctrl.master ] + self.get_all_ik_controls()
+ pole_parents = lambda: [(self.bones.mch.ik_target, ctrl.ik)]
+
+ self.register_switch_parents(pbuilder)
+
+ pbuilder.build_child(
+ self, ctrl.ik, prop_bone=master, select_parent='root',
+ prop_id='IK_parent', prop_name='IK Parent', controls=pcontrols,
+ )
+
+ pbuilder.build_child(
+ self, ctrl.ik_pole, prop_bone=master, extra_parents=pole_parents,
+ prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols,
+ no_fix_rotation=True, no_fix_scale=True,
+ )
+
+ @stage.parent_bones
+ def parent_ik_controls(self):
+ self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.follow)
+
+ @stage.configure_bones
+ def configure_ik_controls(self):
+ base = self.get_bone(self.bones.ctrl.ik_base)
+ base.rotation_mode = 'ZXY'
+ base.lock_rotation = True, False, True
+ base.ik_stretch = 0.1
+
+ @stage.rig_bones
+ def rig_ik_controls(self):
+ self.rig_hide_pole_control(self.bones.ctrl.ik_pole)
+
+ @stage.generate_widgets
+ def make_ik_control_widgets(self):
+ self.make_ik_base_widget(self.bones.ctrl.ik_base)
+ self.make_ik_pole_widget(self.bones.ctrl.ik_pole)
+ self.make_ik_ctrl_widget(self.bones.ctrl.ik)
+
+ def make_ik_base_widget(self, ctrl):
+ if self.main_axis == 'x':
+ roll = 0
+ else:
+ roll = pi/2
+
+ create_ikarrow_widget(self.obj, ctrl, bone_transform_name=None, roll=roll)
+
+ def make_ik_pole_widget(self, ctrl):
+ create_sphere_widget(self.obj, ctrl, bone_transform_name=None)
+
+ def make_ik_ctrl_widget(self, ctrl):
+ raise NotImplementedError()
+
+
+ ####################################################
+ # IK pole visualization
+
+ @stage.generate_bones
+ def make_ik_vispole_bone(self):
+ orgs = self.bones.org.main
+ name = self.copy_bone(orgs[1], 'VIS_'+make_derived_name(orgs[0], 'ctrl', '_ik_pole'))
+ self.bones.ctrl.ik_vispole = name
+
+ bone = self.get_bone(name)
+ bone.tail = bone.head + Vector((0, 0, bone.length / 10))
+ bone.hide_select = True
+
+ @stage.rig_bones
+ def rig_ik_vispole_bone(self):
+ name = self.bones.ctrl.ik_vispole
+
+ self.make_constraint(name, 'COPY_LOCATION', self.bones.org.main[1])
+ self.make_constraint(
+ name, 'STRETCH_TO', self.bones.ctrl.ik_pole,
+ volume='NO_VOLUME', rest_length=self.get_bone(name).length
+ )
+
+ self.rig_hide_pole_control(name)
+
+ @stage.generate_widgets
+ def make_ik_vispole_widget(self):
+ create_line_widget(self.obj, self.bones.ctrl.ik_vispole)
+
+
+ ####################################################
+ # IK system MCH
+
+ ik_input_head_tail = 0.0
+
+ def get_ik_input_bone(self):
+ return self.bones.ctrl.ik
+
+ def get_ik_output_chain(self):
+ return [self.bones.ctrl.ik_base, self.bones.mch.ik_end, self.bones.mch.ik_target]
+
+ @stage.generate_bones
+ def make_ik_mch_chain(self):
+ orgs = self.bones.org.main
+ self.bones.mch.ik_stretch = self.make_ik_mch_stretch_bone(orgs)
+ self.bones.mch.ik_target = self.make_ik_mch_target_bone(orgs)
+ self.bones.mch.ik_end = self.copy_bone(orgs[1], make_derived_name(orgs[1], 'mch', '_ik'))
+
+ def make_ik_mch_stretch_bone(self, orgs):
+ name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik_stretch'))
+ self.get_bone(name).tail = self.get_bone(orgs[2]).head
+ return name
+
+ def make_ik_mch_target_bone(self, orgs):
+ return self.copy_bone(orgs[2], make_derived_name(orgs[0], 'mch', '_ik_target'))
+
+ @stage.parent_bones
+ def parent_ik_mch_chain(self):
+ self.set_bone_parent(self.bones.mch.ik_stretch, self.bones.mch.follow)
+ self.set_bone_parent(self.bones.mch.ik_target, self.get_ik_input_bone())
+ self.set_bone_parent(self.bones.mch.ik_end, self.bones.ctrl.ik_base)
+
+ @stage.configure_bones
+ def configure_ik_mch_chain(self):
+ ctrl = self.bones.ctrl
+ panel = self.script.panel_with_selected_check(self, ctrl.flatten())
+
+ rig_name = strip_org(self.bones.org.main[2])
+
+ self.make_property(self.prop_bone, 'IK_FK', default=0.0, description='IK/FK Switch')
+ panel.custom_prop(self.prop_bone, 'IK_FK', text='IK-FK ({})'.format(rig_name), slider=True)
+
+ self.add_global_buttons(panel, rig_name)
+
+ panel = self.script.panel_with_selected_check(self, [ctrl.master, *self.get_all_ik_controls()])
+
+ self.make_property(self.prop_bone, 'IK_Stretch', default=1.0, description='IK Stretch')
+ panel.custom_prop(self.prop_bone, 'IK_Stretch', text='IK Stretch', slider=True)
+
+ self.make_property(self.prop_bone, 'pole_vector', default=False, description='Use a pole target control')
+
+ self.add_ik_only_buttons(panel, rig_name)
+
+ def add_global_buttons(self, panel, rig_name):
+ ctrl = self.bones.ctrl
+ ik_chain = self.get_ik_output_chain()
+
+ add_generic_snap_fk_to_ik(
+ panel,
+ fk_bones=self.bones.ctrl.fk[0:len(ik_chain)],
+ ik_bones=ik_chain,
+ ik_ctrl_bones=self.get_all_ik_controls(),
+ rig_name=rig_name
+ )
+
+ add_limb_snap_ik_to_fk(
+ panel,
+ master=ctrl.master,
+ fk_bones=self.bones.ctrl.fk, ik_bones=ik_chain,
+ ik_ctrl_bones=[ctrl.ik_base, ctrl.ik, ctrl.ik_pole],
+ ik_extra_ctrls=self.get_extra_ik_controls(),
+ rig_name=rig_name
+ )
+
+ def add_ik_only_buttons(self, panel, rig_name):
+ ctrl = self.bones.ctrl
+ ik_chain = self.get_ik_output_chain()
+
+ add_limb_toggle_pole(
+ panel, master=ctrl.master,
+ ik_bones=ik_chain,
+ ik_ctrl_bones=[ctrl.ik_base, ctrl.ik, ctrl.ik_pole],
+ ik_extra_ctrls=self.get_extra_ik_controls(),
+ )
+
+ @stage.rig_bones
+ def rig_ik_mch_chain(self):
+ mch = self.bones.mch
+ input_bone = self.get_ik_input_bone()
+
+ self.rig_ik_mch_stretch_bone(mch.ik_stretch, input_bone)
+ self.rig_ik_mch_target_bone(mch.ik_target, mch.ik_stretch, input_bone)
+ self.rig_ik_mch_end_bone(mch.ik_end, mch.ik_target, self.bones.ctrl.ik_pole)
+
+ def rig_ik_mch_stretch_bone(self, mch_stretch, input_bone):
+ self.make_constraint(mch_stretch, 'DAMPED_TRACK', input_bone, head_tail=self.ik_input_head_tail)
+ self.make_constraint(mch_stretch, 'STRETCH_TO', input_bone, head_tail=self.ik_input_head_tail)
+
+ con = self.make_constraint(mch_stretch, 'LIMIT_SCALE', min_y=0.0, max_y=1.05, owner_space='LOCAL')
+
+ self.make_driver(con, "influence", variables=[(self.prop_bone, 'IK_Stretch')], polynomial=[1.0, -1.0])
+
+ def rig_ik_mch_target_bone(self, mch_target, mch_stretch, input_bone):
+ self.make_constraint(mch_target, 'COPY_LOCATION', mch_stretch, head_tail=1.0)
+
+ def rig_ik_mch_end_bone(self, mch_ik, mch_target, ctrl_pole):
+ bone = self.get_bone(mch_ik)
+ bone.ik_stretch = 0.1
+
+ bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
+ setattr(bone, 'lock_ik_' + self.main_axis, False)
+
+ con = self.make_constraint(
+ mch_ik, 'IK', mch_target, chain_count=2,
+ )
+
+ self.make_driver(con, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[0.0, 1.0])
+
+ con_pole = self.make_constraint(
+ mch_ik, 'IK', mch_target, chain_count=2,
+ pole_target=self.obj, pole_subtarget=ctrl_pole, pole_angle=self.pole_angle,
+ )
+
+ self.make_driver(con_pole, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0])
+
+ def rig_hide_pole_control(self, name):
+ self.make_driver(
+ self.get_bone(name).bone, "hide",
+ variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0],
+ )
+
+
+ ####################################################
+ # ORG chain
+
+ @stage.parent_bones
+ def parent_org_chain(self):
+ orgs = self.bones.org.main
+ if len(orgs) > 3:
+ self.get_bone(orgs[3]).use_connect = False
+
+ @stage.rig_bones
+ def rig_org_chain(self):
+ ik = self.get_ik_output_chain()
+ for args in zip(count(0), self.bones.org.main, self.bones.ctrl.fk, padnone(ik)):
+ self.rig_org_bone(*args)
+
+ def rig_org_bone(self, i, org, fk, ik):
+ self.make_constraint(org, 'COPY_TRANSFORMS', fk)
+
+ if ik:
+ con = self.make_constraint(org, 'COPY_TRANSFORMS', ik)
+
+ self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0])
+
+
+ ####################################################
+ # Tweak control chain
+
+ @stage.generate_bones
+ def make_tweak_chain(self):
+ self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), self.segment_table_tweak)
+
+ def make_tweak_bone(self, i, entry):
+ name = make_derived_name(entry.org, 'ctrl', '_tweak')
+ name = self.copy_bone(entry.org, name, scale=1/(2 * self.segments))
+ put_bone(self.obj, name, entry.pos)
+ return name
+
+ @stage.parent_bones
+ def parent_tweak_chain(self):
+ for ctrl, mch in zip(self.bones.ctrl.tweak, self.bones.mch.tweak):
+ self.set_bone_parent(ctrl, mch)
+
+ @stage.configure_bones
+ def configure_tweak_chain(self):
+ for args in zip(count(0), self.bones.ctrl.tweak, self.segment_table_tweak):
+ self.configure_tweak_bone(*args)
+
+ ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.tweak)
+
+ def configure_tweak_bone(self, i, tweak, entry):
+ tweak_pb = self.get_bone(tweak)
+ tweak_pb.lock_rotation = (True, False, True)
+ tweak_pb.lock_scale = (False, True, False)
+ tweak_pb.rotation_mode = 'ZXY'
+
+ if i > 0 and entry.seg_idx is not None:
+ self.make_rubber_tweak_property(i, tweak, entry)
+
+ def make_rubber_tweak_property(self, i, tweak, entry):
+ defval = 1.0 if entry.seg_idx else 0.0
+ text = 'Rubber Tweak ({})'.format(strip_org(entry.org))
+
+ self.make_property(tweak, 'rubber_tweak', defval, max=2.0, soft_max=1.0)
+
+ panel = self.script.panel_with_selected_check(self, [tweak])
+ panel.custom_prop(tweak, 'rubber_tweak', text=text, slider=True)
+
+ @stage.generate_widgets
+ def make_tweak_widgets(self):
+ for tweak in self.bones.ctrl.tweak:
+ self.make_tweak_widget(tweak)
+
+ def make_tweak_widget(self, tweak):
+ create_sphere_widget(self.obj, tweak)
+
+
+ ####################################################
+ # Tweak MCH chain
+
+ @stage.generate_bones
+ def make_tweak_mch_chain(self):
+ self.bones.mch.tweak = map_list(self.make_tweak_mch_bone, count(0), self.segment_table_tweak)
+
+ def make_tweak_mch_bone(self, i, entry):
+ name = make_derived_name(entry.org, 'mch', '_tweak')
+ name = self.copy_bone(entry.org, name, scale=1/(4 * self.segments))
+ put_bone(self.obj, name, entry.pos)
+ return name
+
+ @stage.parent_bones
+ def parent_tweak_mch_chain(self):
+ for mch, entry in zip(self.bones.mch.tweak, self.segment_table_tweak):
+ self.set_bone_parent(mch, entry.org)
+
+ @stage.rig_bones
+ def rig_tweak_mch_chain(self):
+ for args in zip(count(0), self.bones.mch.tweak, self.segment_table_tweak):
+ self.rig_tweak_mch_bone(*args)
+
+ def rig_tweak_mch_bone(self, i, tweak, entry):
+ if entry.seg_idx:
+ tweaks = self.bones.ctrl.tweak
+ prev_tweak = tweaks[i - entry.seg_idx]
+ next_tweak = tweaks[i + self.segments - entry.seg_idx]
+
+ self.make_constraint(tweak, 'COPY_TRANSFORMS', prev_tweak)
+ self.make_constraint(
+ tweak, 'COPY_TRANSFORMS', next_tweak,
+ influence = entry.seg_idx / self.segments
+ )
+ self.make_constraint(tweak, 'DAMPED_TRACK', next_tweak)
+
+ elif entry.seg_idx is not None:
+ self.make_constraint(tweak, 'COPY_SCALE', 'root')
+
+
+ ####################################################
+ # Deform chain
+
+ @stage.generate_bones
+ def make_deform_chain(self):
+ self.bones.deform = map_list(self.make_deform_bone, count(0), self.segment_table_full)
+
+ def make_deform_bone(self, i, entry):
+ name = make_derived_name(entry.org, 'def')
+
+ if entry.seg_idx is None:
+ name = self.copy_bone(entry.org, name)
+ else:
+ name = self.copy_bone(entry.org, name, bbone=True, scale=1/self.segments)
+ put_bone(self.obj, name, entry.pos)
+ self.get_bone(name).bbone_segments = self.bbone_segments
+
+ return name
+
+ @stage.parent_bones
+ def parent_deform_chain(self):
+ self.set_bone_parent(self.bones.deform[0], self.rig_parent_bone)
+ self.parent_bone_chain(self.bones.deform, use_connect=True)
+
+ @stage.rig_bones
+ def rig_deform_chain(self):
+ tweaks = pairwise_nozip(padnone(self.bones.ctrl.tweak))
+ entries = pairwise_nozip(padnone(self.segment_table_full))
+
+ for args in zip(count(0), self.bones.deform, *entries, *tweaks):
+ self.rig_deform_bone(*args)
+
+ def rig_deform_bone(self, i, deform, entry, next_entry, tweak, next_tweak):
+ if tweak:
+ self.make_constraint(deform, 'COPY_TRANSFORMS', tweak)
+
+ if next_tweak:
+ self.make_constraint(deform, 'DAMPED_TRACK', next_tweak)
+ self.make_constraint(deform, 'STRETCH_TO', next_tweak)
+
+ self.rig_deform_easing(i, deform, tweak, next_tweak)
+
+ elif next_entry:
+ self.make_constraint(deform, 'DAMPED_TRACK', next_entry.org)
+ self.make_constraint(deform, 'STRETCH_TO', next_entry.org)
+
+ else:
+ self.make_constraint(deform, 'COPY_TRANSFORMS', entry.org)
+
+ def rig_deform_easing(self, i, deform, tweak, next_tweak):
+ pbone = self.get_bone(deform)
+
+ if 'rubber_tweak' in self.get_bone(tweak):
+ self.make_driver(pbone.bone, 'bbone_easein', variables=[(tweak, 'rubber_tweak')])
+ else:
+ pbone.bone.bbone_easein = 0.0
+
+ if 'rubber_tweak' in self.get_bone(next_tweak):
+ self.make_driver(pbone.bone, 'bbone_easeout', variables=[(next_tweak, 'rubber_tweak')])
+ else:
+ pbone.bone.bbone_easeout = 0.0
+
+
+ ####################################################
+ # Settings
+
+ @classmethod
+ def add_parameters(self, params):
+ """ Add the parameters of this rig type to the
+ RigifyParameters PropertyGroup
+ """
+
+ items = [
+ ('x', 'X manual', ''),
+ ('z', 'Z manual', ''),
+ ('automatic', 'Automatic', '')
+ ]
+
+ params.rotation_axis = bpy.props.EnumProperty(
+ items = items,
+ name = "Rotation Axis",
+ default = 'automatic'
+ )
+
+ params.auto_align_extremity = bpy.props.BoolProperty(
+ name='auto_align_extremity',
+ default=False,
+ description="Auto Align Extremity Bone"
+ )
+
+ params.segments = bpy.props.IntProperty(
+ name = 'limb segments',
+ default = 2,
+ min = 1,
+ description = 'Number of segments'
+ )
+
+ params.bbones = bpy.props.IntProperty(
+ name = 'bbone segments',
+ default = 10,
+ min = 1,
+ description = 'Number of segments'
+ )
+
+ # Setting up extra layers for the FK and tweak
+ ControlLayersOption.FK.add_parameters(params)
+ ControlLayersOption.TWEAK.add_parameters(params)
+
+ @classmethod
+ def parameters_ui(self, layout, params, end='End'):
+ """ Create the ui for the rig parameters."""
+
+ r = layout.row()
+ r.prop(params, "rotation_axis")
+
+ if 'auto' not in params.rotation_axis.lower():
+ r = layout.row()
+ r.prop(params, "auto_align_extremity", text="Auto Align "+end)
+
+ r = layout.row()
+ r.prop(params, "segments")
+
+ r = layout.row()
+ r.prop(params, "bbones")
+
+ ControlLayersOption.FK.parameters_ui(layout, params)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
+
+
+###########################
+# Limb IK to FK operator ##
+###########################
+
+SCRIPT_REGISTER_OP_SNAP_IK_FK = ['POSE_OT_rigify_limb_ik2fk', 'POSE_OT_rigify_limb_ik2fk_bake']
+
+SCRIPT_UTILITIES_OP_SNAP_IK_FK = UTILITIES_FUNC_COMMON_IKFK + ['''
+########################
+## Limb Snap IK to FK ##
+########################
+
+class RigifyLimbIk2FkBase:
+ prop_bone: StringProperty(name="Settings Bone")
+ pole_prop: StringProperty(name="Pole target switch", default="pole_vector")
+ fk_bones: StringProperty(name="FK Bone Chain")
+ ik_bones: StringProperty(name="IK Result Bone Chain")
+ ctrl_bones: StringProperty(name="IK Controls")
+ extra_ctrls: StringProperty(name="Extra IK Controls")
+
+ keyflags = None
+
+ def init_execute(self, context):
+ if self.fk_bones:
+ 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.extra_ctrl_list = json.loads(self.extra_ctrls)
+
+ def get_use_pole(self, obj):
+ bone = obj.pose.bones[self.prop_bone]
+ return self.pole_prop in bone and bone[self.pole_prop]
+
+ def save_frame_state(self, context, obj):
+ return get_chain_transform_matrices(obj, self.fk_bone_list)
+
+ def compute_base_rotation(self, context, ik_bones, ctrl_bones, matrices, use_pole):
+ context.view_layer.update()
+
+ if use_pole:
+ match_pole_target(
+ context.view_layer,
+ ik_bones[0], ik_bones[1], ctrl_bones[2], matrices[0],
+ (ik_bones[0].length + ik_bones[1].length)
+ )
+
+ else:
+ correct_rotation(context.view_layer, ctrl_bones[0], matrices[0])
+
+ def apply_frame_state(self, context, obj, 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 ]
+
+ use_pole = len(ctrl_bones) > 2 and self.get_use_pole(obj)
+
+ # Set the end control position
+ tgt_matrix = ik_bones[2].bone.matrix_local
+ ctrl_matrix = ctrl_bones[1].bone.matrix_local
+ endmat = matrices[2] @ tgt_matrix.inverted() @ ctrl_matrix
+
+ set_transform_from_matrix(
+ obj, self.ctrl_bone_list[1], endmat, keyflags=self.keyflags
+ )
+
+ # Remove foot heel transform, if present
+ for extra in self.extra_ctrl_list:
+ set_transform_from_matrix(
+ obj, extra, Matrix.Identity(4), space='LOCAL', keyflags=self.keyflags
+ )
+
+ # Set the base bone position
+ ctrl_bones[0].matrix_basis = Matrix.Identity(4)
+
+ set_transform_from_matrix(
+ obj, self.ctrl_bone_list[0], matrices[0],
+ no_scale=True, no_rot=use_pole,
+ )
+
+ self.compute_base_rotation(context, ik_bones, ctrl_bones, matrices, use_pole)
+
+ correct_scale(context.view_layer, ctrl_bones[0], matrices[0])
+
+ # Keyframe controls
+ if self.keyflags is not None:
+ if use_pole:
+ keyframe_transform_properties(
+ obj, self.ctrl_bone_list[2], self.keyflags,
+ no_rot=True, no_scale=True,
+ )
+
+ keyframe_transform_properties(
+ obj, self.ctrl_bone_list[0], self.keyflags,
+ no_rot=use_pole,
+ )
+
+class POSE_OT_rigify_limb_ik2fk(RigifyLimbIk2FkBase, RigifySingleUpdateMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_limb_ik2fk_" + rig_id
+ bl_label = "Snap IK->FK"
+ bl_options = {'UNDO', 'INTERNAL'}
+ bl_description = "Snap the IK chain to FK result"
+
+class POSE_OT_rigify_limb_ik2fk_bake(RigifyLimbIk2FkBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_limb_ik2fk_bake_" + rig_id
+ bl_label = "Apply Snap IK->FK To Keyframes"
+ bl_options = {'UNDO', 'INTERNAL'}
+ bl_description = "Snap the IK chain keyframes to FK result"
+
+ def execute_scan_curves(self, context, obj):
+ self.bake_add_bone_frames(self.fk_bone_list, TRANSFORM_PROPS_ALL)
+ 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=''):
+ panel.use_bake_settings()
+ panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_IK_FK)
+ panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_IK_FK)
+
+ op_props = {
+ 'prop_bone': master,
+ 'fk_bones': json.dumps(fk_bones),
+ 'ik_bones': json.dumps(ik_bones),
+ 'ctrl_bones': json.dumps(ik_ctrl_bones),
+ 'extra_ctrls': json.dumps(ik_extra_ctrls),
+ }
+
+ add_fk_ik_snap_buttons(
+ panel, 'pose.rigify_limb_ik2fk_{rig_id}', 'pose.rigify_limb_ik2fk_bake_{rig_id}',
+ label='IK->FK', rig_name=rig_name, properties=op_props,
+ clear_bones=ik_ctrl_bones + ik_extra_ctrls,
+ )
+
+#########################
+# Toggle Pole operator ##
+#########################
+
+SCRIPT_REGISTER_OP_TOGGLE_POLE = ['POSE_OT_rigify_limb_toggle_pole', 'POSE_OT_rigify_limb_toggle_pole_bake']
+
+SCRIPT_UTILITIES_OP_TOGGLE_POLE = SCRIPT_UTILITIES_OP_SNAP_IK_FK + ['''
+####################
+## Toggle IK Pole ##
+####################
+
+class RigifyLimbTogglePoleBase(RigifyLimbIk2FkBase):
+ use_pole: bpy.props.BoolProperty(name="Use Pole Vector")
+
+ keyflags_switch = None
+
+ def save_frame_state(self, context, obj):
+ return get_chain_transform_matrices(obj, self.ik_bone_list)
+
+ def apply_frame_state(self, context, obj, 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 ]
+
+ # Set the pole property
+ set_custom_property_value(
+ obj, self.prop_bone, self.pole_prop, int(self.use_pole),
+ keyflags=self.keyflags_switch
+ )
+
+ # Reset the base bone rotation
+ set_pose_rotation(ctrl_bones[0], Matrix.Identity(4))
+
+ self.compute_base_rotation(context, ik_bones, ctrl_bones, matrices, self.use_pole)
+
+ # Keyframe controls
+ if self.keyflags is not None:
+ if self.use_pole:
+ keyframe_transform_properties(
+ obj, self.ctrl_bone_list[2], self.keyflags,
+ no_rot=True, no_scale=True,
+ )
+ else:
+ keyframe_transform_properties(
+ obj, self.ctrl_bone_list[0], self.keyflags,
+ no_loc=True, no_scale=True,
+ )
+
+ def init_invoke(self, context):
+ self.use_pole = not bool(context.active_object.pose.bones[self.prop_bone][self.pole_prop])
+
+class POSE_OT_rigify_limb_toggle_pole(RigifyLimbTogglePoleBase, RigifySingleUpdateMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_limb_toggle_pole_" + rig_id
+ bl_label = "Toggle Pole"
+ bl_options = {'UNDO', 'INTERNAL'}
+ bl_description = "Switch the IK chain between pole and rotation"
+
+class POSE_OT_rigify_limb_toggle_pole_bake(RigifyLimbTogglePoleBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
+ bl_idname = "pose.rigify_limb_toggle_pole_bake_" + rig_id
+ bl_label = "Apply Toggle Pole To Keyframes"
+ bl_options = {'UNDO', 'INTERNAL'}
+ bl_description = "Switch the IK chain between pole and rotation over a frame range"
+
+ def execute_scan_curves(self, context, obj):
+ self.bake_add_bone_frames(self.ctrl_bone_list, TRANSFORM_PROPS_ALL)
+
+ rot_curves = self.bake_get_all_bone_curves(self.ctrl_bone_list[0], TRANSFORM_PROPS_ROTATION)
+ pole_curves = self.bake_get_all_bone_curves(self.ctrl_bone_list[2], TRANSFORM_PROPS_LOCATION)
+ return rot_curves + pole_curves
+
+ def execute_before_apply(self, context, obj, range, range_raw):
+ self.bake_replace_custom_prop_keys_constant(self.prop_bone, self.pole_prop, int(self.use_pole))
+
+ def draw(self, context):
+ self.layout.prop(self, 'use_pole')
+''']
+
+def add_limb_toggle_pole(panel, *, master=None, ik_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[]):
+ panel.use_bake_settings()
+ panel.script.add_utilities(SCRIPT_UTILITIES_OP_TOGGLE_POLE)
+ panel.script.register_classes(SCRIPT_REGISTER_OP_TOGGLE_POLE)
+
+ op_props = {
+ 'prop_bone': master,
+ 'ik_bones': json.dumps(ik_bones),
+ 'ctrl_bones': json.dumps(ik_ctrl_bones),
+ 'extra_ctrls': json.dumps(ik_extra_ctrls),
+ }
+
+ row = panel.row(align=True)
+ lsplit = row.split(factor=0.75, align=True)
+ lsplit.operator('pose.rigify_limb_toggle_pole_{rig_id}', icon='FORCE_MAGNETIC', properties=op_props)
+ lsplit.custom_prop(master, 'pole_vector', text='')
+ row.operator('pose.rigify_limb_toggle_pole_bake_{rig_id}', text='', icon='ACTION_TWEAK', properties=op_props)