diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-14 09:17:30 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-14 09:30:00 +0300 |
commit | 8b1df843703fdb51ffa5758625c117c4f10bc6dd (patch) | |
tree | 949e84f80116132eab3ad5f3bac46e745c3a9195 /rigify/rigs/spines/super_head.py | |
parent | 9b693a6be0aa2b0b4825d30ac5034655dce9c0dd (diff) |
Rigify: replace rigs with new implementations using the new base rig.
Spine is split into parts. Limbs and tentacles simply converted.
Differential Revision: https://developer.blender.org/D4624
Diffstat (limited to 'rigify/rigs/spines/super_head.py')
-rw-r--r-- | rigify/rigs/spines/super_head.py | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py new file mode 100644 index 00000000..79ec2396 --- /dev/null +++ b/rigify/rigs/spines/super_head.py @@ -0,0 +1,406 @@ +#====================== 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 + +from itertools import count + +from ...utils.naming import make_derived_name +from ...utils.bones import align_bone_orientation +from ...utils.widgets_basic import create_circle_widget, create_cube_widget +from ...utils.widgets_special import create_neck_bend_widget, create_neck_tweak_widget +from ...utils.misc import map_list + +from ...base_rig import stage + +from .spine_rigs import BaseHeadTailRig + + +class Rig(BaseHeadTailRig): + """ + Head rig with long neck support and connect option. + """ + + use_connect_reverse = False + + def initialize(self): + super().initialize() + + self.long_neck = len(self.bones.org) > 3 + + #################################################### + # BONES + # + # org[]: + # ORG bones + # ctrl: + # neck, head, neck_bend: + # Main controls. + # tweak[]: + # Tweak control chain. + # mch: + # rot_neck, rot_head: + # Main control parents, implement FK follow. + # stretch + # Long neck stretch behavior. + # ik[] + # Long neck IK behavior. + # chain[] + # Tweak parents. + # deform[]: + # DEF bones + # + #################################################### + + #################################################### + # Main control bones + + @stage.generate_bones + def make_control_chain(self): + orgs = self.bones.org + ctrl = self.bones.ctrl + + ctrl.neck = self.make_neck_control_bone(orgs[0], 'neck', orgs[-1]) + ctrl.head = self.make_head_control_bone(orgs[-1], 'head') + if self.long_neck: + ctrl.neck_bend = self.make_neck_bend_control_bone(orgs[0], 'neck_bend', ctrl.neck) + + self.default_prop_bone = ctrl.head + + def make_neck_control_bone(self, org, name, org_head): + name = self.copy_bone(org, name, parent=False) + + # Neck spans all neck bones (except head) + self.get_bone(name).tail = self.get_bone(org_head).head + + return name + + def make_neck_bend_control_bone(self, org, name, neck): + name = self.copy_bone(org, name, parent=False) + neck_bend_eb = self.get_bone(name) + + # Neck pivot position + neck_bones = self.bones.org + if (len(neck_bones)-1) % 2: # odd num of neck bones (head excluded) + center_bone = self.get_bone(neck_bones[int((len(neck_bones))/2) - 1]) + neck_bend_eb.head = (center_bone.head + center_bone.tail)/2 + else: + center_bone = self.get_bone(neck_bones[int((len(neck_bones)-1)/2) - 1]) + neck_bend_eb.head = center_bone.tail + + align_bone_orientation(self.obj, name, neck) + neck_bend_eb.length = self.get_bone(neck).length / 2 + + return name + + def make_head_control_bone(self, org, name): + return self.copy_bone(org, name, parent=False) + + @stage.parent_bones + def parent_control_chain(self): + ctrl = self.bones.ctrl + mch = self.bones.mch + self.set_bone_parent(ctrl.neck, mch.rot_neck) + self.set_bone_parent(ctrl.head, mch.rot_head) + if self.long_neck: + self.set_bone_parent(ctrl.neck_bend, mch.stretch) + + @stage.configure_bones + def configure_control_chain(self): + self.configure_control_bone(0, self.bones.ctrl.neck, self.bones.org[0]) + self.configure_control_bone(2, self.bones.ctrl.head, self.bones.org[-1]) + if self.long_neck: + self.configure_control_bone(1, self.bones.ctrl.neck_bend, self.bones.org[0]) + + @stage.generate_widgets + def make_control_widgets(self): + ctrl = self.bones.ctrl + self.make_neck_widget(ctrl.neck) + self.make_head_widget(ctrl.head) + if self.long_neck: + self.make_neck_bend_widget(ctrl.neck_bend) + + def make_neck_widget(self, ctrl): + radius = 1/max(1, len(self.bones.mch.chain)) + + create_circle_widget( + self.obj, ctrl, + radius=radius, + head_tail=0.5, + bone_transform_name=None + ) + + def make_neck_bend_widget(self, ctrl): + radius = 1/max(1, len(self.bones.mch.chain)) + + create_neck_bend_widget( + self.obj, ctrl, + radius=radius/2, + head_tail=0.0, + bone_transform_name=None + ) + + def make_head_widget(self, ctrl): + # place wgt @ middle of head bone for long necks + if self.long_neck: + head_tail = 0.5 + else: + head_tail = 1.0 + + create_circle_widget( + self.obj, ctrl, + radius = 0.5, + head_tail = head_tail, + with_line = False, + bone_transform_name = None + ) + + #################################################### + # MCH bones associated with main controls + + @stage.generate_bones + def make_mch_control_bones(self): + orgs = self.bones.org + mch = self.bones.mch + + mch.rot_neck = self.make_mch_follow_bone(orgs[0], 'neck', 0.5, copy_scale=True) + mch.rot_head = self.make_mch_follow_bone(orgs[-1], 'head', 0.0, copy_scale=True) + mch.stretch = self.make_mch_stretch_bone(orgs[0], 'STR-neck', orgs[-1]) + + def make_mch_stretch_bone(self, org, name, org_head): + name = self.copy_bone(org, make_derived_name(name, 'mch'), parent=False) + self.get_bone(name).tail = self.get_bone(org_head).head + return name + + @stage.parent_bones + def parent_mch_control_bones(self): + self.set_bone_parent(self.bones.mch.rot_neck, self.rig_parent_bone) + self.set_bone_parent(self.bones.mch.rot_head, self.bones.ctrl.neck) + self.set_bone_parent(self.bones.mch.stretch, self.bones.ctrl.neck) + + @stage.rig_bones + def rig_mch_control_bones(self): + self.rig_mch_stretch_bone(self.bones.mch.stretch, self.bones.ctrl.head) + + def rig_mch_stretch_bone(self, mch, head): + self.make_constraint(mch, 'DAMPED_TRACK', head) + self.make_constraint(mch, 'STRETCH_TO', head) + + #################################################### + # MCH IK chain for the long neck + + @stage.generate_bones + def make_mch_ik_chain(self): + orgs = self.bones.org + if self.long_neck: + self.bones.mch.ik = map_list(self.make_mch_ik_bone, orgs[0:-1]) + + def make_mch_ik_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch', '_ik'), parent=False) + + @stage.parent_bones + def parent_mch_ik_chain(self): + if self.long_neck: + ik = self.bones.mch.ik + self.set_bone_parent(ik[0], self.bones.ctrl.tweak[0]) + self.parent_bone_chain(ik, use_connect=True) + + @stage.rig_bones + def rig_mch_ik_chain(self): + if self.long_neck: + ik = self.bones.mch.ik + head = self.bones.ctrl.head + for args in zip(count(0), ik): + self.rig_mch_ik_bone(*args, len(ik), head) + + def rig_mch_ik_bone(self, i, mch, ik_len, head): + if i == ik_len - 1: + self.make_constraint(mch, 'IK', head, chain_count=ik_len) + + self.get_bone(mch).ik_stretch = 0.1 + + #################################################### + # MCH chain for the middle of the neck + + @stage.generate_bones + def make_mch_chain(self): + orgs = self.bones.org + self.bones.mch.chain = map_list(self.make_mch_bone, orgs[1:-1]) + + def make_mch_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch'), parent=False, scale=1/4) + + @stage.parent_bones + def align_mch_chain(self): + for mch in self.bones.mch.chain: + align_bone_orientation(self.obj, mch, self.bones.ctrl.neck) + + @stage.parent_bones + def parent_mch_chain(self): + mch = self.bones.mch + for bone in mch.chain: + self.set_bone_parent(bone, mch.stretch) + self.get_bone(bone).use_inherit_scale = False + + @stage.rig_bones + def rig_mch_chain(self): + chain = self.bones.mch.chain + if self.long_neck: + ik = self.bones.mch.ik + for args in zip(count(0), chain, ik[1:]): + self.rig_mch_bone_long(*args, len(chain)) + else: + for args in zip(count(0), chain): + self.rig_mch_bone(*args, len(chain)) + + def rig_mch_bone_long(self, i, mch, ik, len_mch): + ctrl = self.bones.ctrl + + self.make_constraint(mch, 'COPY_LOCATION', ik) + + step = 2/(len_mch+1) + xval = (i+1)*step + influence = 2*xval - xval**2 #parabolic influence of pivot + + self.make_constraint( + mch, 'COPY_LOCATION', ctrl.neck_bend, + influence=influence, use_offset=True, space='LOCAL' + ) + + self.make_constraint(mch, 'COPY_SCALE', ctrl.neck) + + def rig_mch_bone(self, i, mch, len_mch): + ctrl = self.bones.ctrl + + nfactor = float((i + 1) / (len_mch + 1)) + self.make_constraint( + mch, 'COPY_ROTATION', ctrl.head, + influence=nfactor, space='LOCAL' + ) + + #################################################### + # Tweak bones + + @stage.generate_bones + def make_tweak_chain(self): + orgs = self.bones.org + self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), orgs[0:-1]) + + @stage.parent_bones + def parent_tweak_chain(self): + ctrl = self.bones.ctrl + mch = self.bones.mch + + for args in zip(ctrl.tweak, [ctrl.neck, *mch.chain]): + self.set_bone_parent(*args) + + @stage.rig_bones + def generate_neck_tweak_widget(self): + # Generate the widget early to override connected parent + if self.long_neck: + bone = self.bones.ctrl.tweak[0] + create_neck_tweak_widget(self.obj, bone, size=1.0) + + #################################################### + # ORG and DEF bones + + @stage.configure_bones + def configure_bbone_chain(self): + self.get_bone(self.bones.deform[-1]).bone.bbone_segments = 1 + + @stage.rig_bones + def rig_org_chain(self): + tweaks = self.bones.ctrl.tweak + [self.bones.ctrl.head] + for args in zip(count(0), self.bones.org, tweaks, tweaks[1:] + [None]): + self.rig_org_bone(*args) + + +def create_sample(obj, *, parent=None): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('neck') + bone.head[:] = 0.0000, 0.0114, 1.6582 + bone.tail[:] = 0.0000, -0.0130, 1.7197 + bone.roll = 0.0000 + bone.use_connect = False + if parent: + bone.parent = arm.edit_bones[parent] + bones['neck'] = bone.name + bone = arm.edit_bones.new('neck.001') + bone.head[:] = 0.0000, -0.0130, 1.7197 + bone.tail[:] = 0.0000, -0.0247, 1.7813 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck']] + bones['neck.001'] = bone.name + bone = arm.edit_bones.new('head') + bone.head[:] = 0.0000, -0.0247, 1.7813 + bone.tail[:] = 0.0000, -0.0247, 1.9796 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck.001']] + bones['head'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['neck']] + pbone.rigify_type = 'spines.super_head' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + try: + pbone.rigify_parameters.connect_chain = bool(parent) + except AttributeError: + pass + try: + pbone.rigify_parameters.tweak_layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['neck.001']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone = obj.pose.bones[bones['head']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone |