diff options
Diffstat (limited to 'rigify/rigs/limbs/leg.py')
-rw-r--r-- | rigify/rigs/limbs/leg.py | 1248 |
1 files changed, 196 insertions, 1052 deletions
diff --git a/rigify/rigs/limbs/leg.py b/rigify/rigs/limbs/leg.py index 59e6f799..098d303a 100644 --- a/rigify/rigs/limbs/leg.py +++ b/rigify/rigs/limbs/leg.py @@ -1,1134 +1,280 @@ - -import bpy, math -from ..widgets import create_foot_widget, create_ballsocket_widget, create_gear_widget -from .ui import create_script -from .limb_utils import * +#====================== 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 math + +from itertools import count from mathutils import Vector -from ...utils import copy_bone, flip_bone, put_bone -from ...utils import strip_org, strip_mch -from ...utils import create_circle_widget, create_sphere_widget, create_line_widget -from ...utils import MetarigError, make_mechanism_name -from ...utils import create_limb_widget, connected_children_names -from ...utils import align_bone_y_axis, align_bone_x_axis, align_bone_z_axis -from ...rig_ui_template import UTILITIES_RIG_LEG, REGISTER_RIG_LEG -from ...utils import ControlLayersOption -from rna_prop_ui import rna_idprop_ui_prop_get -from ...utils.mechanism import make_property, make_driver -from ..widgets import create_ikarrow_widget -from math import trunc, pi -from ...utils.switch_parent import SwitchParentBuilder +from ...utils.rig import is_rig_base_bone +from ...utils.bones import align_chain_x_axis, align_bone_x_axis, align_bone_y_axis, align_bone_z_axis +from ...utils.bones import align_bone_to_axis, flip_bone, put_bone, align_bone_orientation +from ...utils.naming import make_derived_name +from ...utils.misc import map_list +from ..widgets import create_foot_widget, create_ballsocket_widget -IMPLEMENTATION = True # Include and set True if Rig is just an implementation for a wrapper class - # add_parameters and parameters_ui are unused for implementation classes +from ...base_rig import stage +from .limb_rigs import BaseLimbRig -class Rig: - def __init__(self, obj, bone_name, params): - """ Initialize leg rig and key rig properties """ - self.obj = obj - self.params = params +DEG_360 = math.pi * 2 +ALL_TRUE = (True, True, True) - self.org_bones = list( - [bone_name] + connected_children_names(obj, bone_name) - )[:3] # The basic limb is the first 3 bones - self.segments = params.segments - self.bbones = params.bbones - self.limb_type = params.limb_type - self.rot_axis = params.rotation_axis - self.auto_align_extremity = params.auto_align_extremity +class Rig(BaseLimbRig): + """Human leg rig.""" + def find_org_bones(self, bone): + bones = super().find_org_bones(bone) - def orient_org_bones(self): + for b in self.get_bone(bones.main[2]).bone.children: + if not b.use_connect and not b.children and not is_rig_base_bone(self.obj, b.name): + bones.heel = b.name + break + else: + self.raise_error("Heel bone not found.") - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + return bones - thigh = self.org_bones[0] - org_bones = list( - [thigh] + connected_children_names(self.obj, thigh) - ) # All the provided orgs + def initialize(self): + if len(self.bones.org.main) != 4: + self.raise_error("Input to rig type must be a chain of 4 bones.") - # Get heel bone - heel = '' - for b in self.obj.data.bones[org_bones[2]].children: - if not b.use_connect and not b.children: - heel = b.name - if heel: - org_bones.append(heel) + super().initialize() - org_thigh = eb[org_bones[0]] - org_shin = eb[org_bones[1]] - org_foot = eb[org_bones[2]] - org_toe = eb[org_bones[3]] - org_heel = eb[org_bones[4]] + def prepare_bones(self): + orgs = self.bones.org.main - foot_projection_on_xy = Vector((org_foot.y_axis[0], org_foot.y_axis[1], 0)) - foot_x = foot_projection_on_xy.cross(Vector((0, 0, -1))).normalized() + foot_x = self.vector_without_z(self.get_bone(orgs[2]).y_axis).cross((0, 0, -1)) - if self.rot_axis != 'automatic': + if self.params.rotation_axis == 'automatic': + align_chain_x_axis(self.obj, orgs[0:2]) # Orient foot and toe - if self.auto_align_extremity: - if self.rot_axis == 'x': - align_bone_x_axis(self.obj, org_foot.name, foot_x) - align_bone_x_axis(self.obj, org_toe.name, -foot_x) - elif self.rot_axis == 'z': - align_bone_z_axis(self.obj, org_foot.name, foot_x) - align_bone_z_axis(self.obj, org_toe.name, -foot_x) - else: - raise MetarigError(message='IK on %s has forbidden rotation axis (Y)' % self.org_bones[0]) - - return - - # Orient thigh and shin bones - chain_y_axis = org_thigh.y_axis + org_shin.y_axis - chain_rot_axis = org_thigh.y_axis.cross(chain_y_axis).normalized() # ik-plane normal axis (rotation) - - align_bone_x_axis(self.obj, org_thigh.name, chain_rot_axis) - align_bone_x_axis(self.obj, org_shin.name, chain_rot_axis) - - # Orient foot and toe - align_bone_x_axis(self.obj, org_foot.name, foot_x) - align_bone_x_axis(self.obj, org_toe.name, -foot_x) - - # Orient heel - align_bone_z_axis(self.obj, org_heel.name, Vector((0, 0, 1))) - - def create_parent(self): - - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + align_bone_x_axis(self.obj, orgs[2], foot_x) + align_bone_x_axis(self.obj, orgs[3], -foot_x) - name = get_bone_name( strip_org( org_bones[0] ), 'mch', 'parent' ) + align_bone_x_axis(self.obj, self.bones.org.heel, Vector((0, 0, 1))) - mch = copy_bone( self.obj, org_bones[0], name ) - orient_bone( self, eb[mch], 'y' ) - eb[ mch ].length = eb[ org_bones[0] ].length / 4 - - eb[ mch ].parent = eb[ org_bones[0] ].parent - - eb[ mch ].roll = 0.0 - - # Add non-MCH main limb control - name = get_bone_name(strip_org(org_bones[0]), 'ctrl', 'parent') - main_parent = copy_bone(self.obj, org_bones[0], name) - eb[main_parent].length = eb[org_bones[0]].length / 4 - eb[main_parent].parent = eb[org_bones[0]] - eb[main_parent].roll = 0.0 - - # Constraints - make_constraint( self, mch, { - 'constraint' : 'COPY_ROTATION', - 'subtarget' : 'root' - }) - - make_constraint( self, mch, { - 'constraint' : 'COPY_SCALE', - 'subtarget' : 'root' - }) - - # Limb Follow Driver - pb = self.obj.pose.bones - - name = 'FK_limb_follow' - - # pb[ mch ][ name ] = 0.0 - # prop = rna_idprop_ui_prop_get( pb[ mch ], name, create = True ) - make_property(pb[main_parent], name, 0.0) - - make_driver(pb[mch].constraints[0], "influence", variables=[(self.obj, main_parent, name)]) - - size = pb[main_parent].bone.y_axis.length * 10 - create_gear_widget(self.obj, main_parent, size=size, bone_transform_name=None) - - return [mch, main_parent] - - def create_tweak(self): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode ='EDIT') - eb = self.obj.data.edit_bones - - tweaks = {} - tweaks['ctrl'] = [] - tweaks['mch' ] = [] - - # Create and parent mch and ctrl tweaks - for i,org in enumerate(org_bones): - if i < len(org_bones) - 1: - # Create segments if specified - for j in range( self.segments ): - # MCH - name = get_bone_name( strip_org(org), 'mch', 'tweak' ) - mch = copy_bone( self.obj, org, name ) - - # CTRL - name = get_bone_name( strip_org(org), 'ctrl', 'tweak' ) - ctrl = copy_bone( self.obj, org, name ) - - eb[ mch ].length /= self.segments - eb[ ctrl ].length /= self.segments - - # If we have more than one segments, place the head of the - # 2nd and onwards at the correct position - if j > 0: - put_bone(self.obj, mch, eb[ tweaks['mch' ][-1] ].tail) - put_bone(self.obj, ctrl, eb[ tweaks['ctrl'][-1] ].tail) - - tweaks['ctrl'] += [ ctrl ] - tweaks['mch' ] += [ mch ] - - # Parenting the tweak ctrls to mchs - eb[ mch ].parent = eb[ org ] - eb[ ctrl ].parent = eb[ mch ] - - else: # Last limb bone - is not subdivided - name = get_bone_name( strip_org(org), 'mch', 'tweak' ) - mch = copy_bone( self.obj, org_bones[i-1], name ) - eb[ mch ].length = eb[org].length / 4 - put_bone( - self.obj, - mch, - eb[org_bones[i-1]].tail - ) - - ctrl = get_bone_name( strip_org(org), 'ctrl', 'tweak' ) - ctrl = copy_bone( self.obj, org, ctrl ) - eb[ ctrl ].length = eb[org].length / 2 - - tweaks['mch'] += [ mch ] - tweaks['ctrl'] += [ ctrl ] - - # Parenting the tweak ctrls to mchs - eb[ mch ].parent = eb[ org ] - eb[ ctrl ].parent = eb[ mch ] - - # Scale to reduce widget size and maintain conventions! - for mch, ctrl in zip( tweaks['mch'], tweaks['ctrl'] ): - eb[ mch ].length /= 4 - eb[ ctrl ].length /= 2 - - # Constraints - - for i,b in enumerate( tweaks['mch'] ): - first = 0 - middle = trunc( len( tweaks['mch'] ) / 2 ) - last = len( tweaks['mch'] ) - 1 - - if i == first or i == middle: - make_constraint( self, b, { - 'constraint' : 'COPY_SCALE', - 'subtarget' : 'root' - }) - elif i != last: - targets = [] - dt_target_idx = middle - factor = 0 - if i < middle: - targets = [first,middle] - else: - targets = [middle,last] - factor = self.segments - dt_target_idx = last - - # Use copy transforms constraints to position each bone - # exactly in the location respective to its index (between - # the two edges) - make_constraint( self, b, { - 'constraint' : 'COPY_TRANSFORMS', - 'subtarget' : tweaks['ctrl'][targets[0]] - }) - make_constraint( self, b, { - 'constraint' : 'COPY_TRANSFORMS', - 'subtarget' : tweaks['ctrl'][targets[1]], - 'influence' : (i - factor) / self.segments - }) - make_constraint( self, b, { - 'constraint' : 'DAMPED_TRACK', - 'subtarget' : tweaks['ctrl'][ dt_target_idx ], - }) - - # Ctrl bones Locks and Widgets - pb = self.obj.pose.bones - for t in tweaks['ctrl']: - pb[t].lock_rotation = True, False, True - pb[t].lock_scale = False, True, False - - create_sphere_widget(self.obj, t, bone_transform_name=None) - - ControlLayersOption.TWEAK.assign(self.params, pb, tweaks['ctrl']) - - return tweaks - - def create_def(self, tweaks): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode ='EDIT') - eb = self.obj.data.edit_bones - - def_bones = [] - for i, org in enumerate(org_bones): - if i < len(org_bones) - 1: - # Create segments if specified - for j in range(self.segments): - name = get_bone_name(strip_org(org), 'def') - def_name = copy_bone(self.obj, org, name) - - eb[def_name].length /= self.segments - - # If we have more than one segments, place the 2nd and - # onwards on the tail of the previous bone - if j > 0: - put_bone(self.obj, def_name, eb[ def_bones[-1] ].tail) - - def_bones += [def_name] - else: - name = get_bone_name(strip_org(org), 'def') - def_name = copy_bone(self.obj, org, name) - def_bones.append(def_name) - - # Parent deform bones - for i,b in enumerate( def_bones ): - if i > 0: # For all bones but the first (which has no parent) - eb[b].parent = eb[ def_bones[i-1] ] # to previous - eb[b].use_connect = True - - # Constraint def to tweaks - for d,t in zip(def_bones, tweaks): - tidx = tweaks.index(t) - - make_constraint( self, d, { - 'constraint' : 'COPY_TRANSFORMS', - 'subtarget' : t - }) - - if tidx != len(tweaks) - 1: - make_constraint( self, d, { - 'constraint' : 'DAMPED_TRACK', - 'subtarget' : tweaks[ tidx + 1 ], - }) - - make_constraint( self, d, { - 'constraint' : 'STRETCH_TO', - 'subtarget' : tweaks[ tidx + 1 ], - }) - - # Create bbone segments - for bone in def_bones[:-1]: - self.obj.data.bones[bone].bbone_segments = self.bbones - - self.obj.data.bones[ def_bones[0] ].bbone_easein = 0.0 - self.obj.data.bones[ def_bones[-2] ].bbone_easeout = 0.0 - self.obj.data.bones[ def_bones[-1] ].bbone_easein = 0.0 - self.obj.data.bones[ def_bones[-1] ].bbone_easeout = 0.0 - - - # Rubber hose drivers - pb = self.obj.pose.bones - for i, t in enumerate(tweaks[1:-1]): - # Create custom property on tweak bone to control rubber hose - name = 'rubber_tweak' - - if i == trunc( len( tweaks[1:-1] ) / 2 ): - defval = 0.0 + elif self.params.auto_align_extremity: + if self.main_axis == 'x': + align_bone_x_axis(self.obj, orgs[2], foot_x) + align_bone_x_axis(self.obj, orgs[3], -foot_x) else: - defval = 1.0 - - make_property(pb[t], name, defval, max=2.0, soft_max=1.0) - - for j,d in enumerate(def_bones[:-1]): - if j != 0: - make_driver(self.obj.data.bones[d], "bbone_easein", variables=[(self.obj, tweaks[j], 'rubber_tweak')]) - - if j != len( def_bones[:-1] ) - 1: - make_driver(self.obj.data.bones[d], "bbone_easeout", variables=[(self.obj, tweaks[j+1], 'rubber_tweak')]) - - return def_bones - - def create_ik(self, parent): - org_bones = self.org_bones + align_bone_z_axis(self.obj, orgs[2], foot_x) + align_bone_z_axis(self.obj, orgs[3], -foot_x) - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - ctrl = get_bone_name(org_bones[0], 'ctrl', 'ik') - mch_ik = get_bone_name(org_bones[0], 'mch', 'ik') - mch_target = get_bone_name(org_bones[0], 'mch', 'ik_target') + #################################################### + # EXTRA BONES + # + # org: + # heel: + # Heel location marker bone + # ctrl: + # heel: + # Foot roll control + # mch: + # heel[]: + # Chain of bones implementing foot roll. + # + #################################################### - for o, ik in zip(org_bones, [ctrl, mch_ik, mch_target]): - bone = copy_bone(self.obj, o, ik) + #################################################### + # IK controls - if org_bones.index(o) == len(org_bones) - 1: - eb[bone].length /= 4 + def get_extra_ik_controls(self): + return [self.bones.ctrl.heel] - # Create MCH Stretch - mch_str = copy_bone( - self.obj, - org_bones[0], - get_bone_name( org_bones[0], 'mch', 'ik_stretch' ) - ) + def make_ik_control_bone(self, orgs): + name = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik')) - eb[ mch_str ].tail = eb[ org_bones[-1] ].head + if self.params.rotation_axis == 'automatic' or self.params.auto_align_extremity: + align_bone_to_axis(self.obj, name, 'y', flip=True) - # Parenting - eb[ctrl].parent = eb[parent] - eb[mch_str].parent = eb[parent] - eb[mch_ik].parent = eb[ctrl] - - # Make standard pole target bone - pole_name = get_bone_name(org_bones[0], 'ctrl', 'ik_target') - pole_target = copy_bone(self.obj, org_bones[0], pole_name) - - lo_vector = eb[org_bones[1]].tail - eb[org_bones[1]].head - tot_vector = eb[org_bones[0]].head - eb[org_bones[1]].tail - tot_vector.normalize() - elbow_vector = lo_vector.dot(tot_vector)*tot_vector - lo_vector # elbow_vec as rejection of lo on tot - elbow_vector.normalize() - elbow_vector *= (eb[org_bones[1]].tail - eb[org_bones[0]].head).length - - if self.rot_axis == 'x' or self.rot_axis == 'automatic': - z_vector = eb[org_bones[0]].z_axis + eb[org_bones[1]].z_axis - alfa = elbow_vector.angle(z_vector) - elif self.rot_axis == 'z': - x_vector = eb[org_bones[0]].x_axis + eb[org_bones[1]].x_axis - alfa = elbow_vector.angle(x_vector) - - if alfa > pi/2: - pole_angle = -pi/2 - else: - pole_angle = pi/2 - - if self.rot_axis == 'z': - pole_angle = 0 - - eb[pole_target].head = eb[org_bones[0]].tail + elbow_vector - eb[pole_target].tail = eb[pole_target].head - elbow_vector/8 - eb[pole_target].roll = 0.0 - - # Make visual pole - vispole_name = 'VIS_' + get_bone_name(org_bones[0], 'ctrl', 'ik_pole') - vispole = copy_bone(self.obj, org_bones[1], vispole_name) - eb[vispole].tail = eb[vispole].head + Vector((0.0, 0.0, eb[org_bones[1]].length/10)) - eb[vispole].use_connect = False - eb[vispole].hide_select = True - eb[vispole].parent = None - - make_constraint(self, mch_ik, { - 'constraint': 'IK', - 'subtarget': mch_target, - 'chain_count': 2, - }) - - make_constraint(self, mch_ik, { # 2_nd IK for pole targeted chain - 'constraint': 'IK', - 'subtarget': mch_target, - 'chain_count': 2, - }) - - # VIS pole constraints - make_constraint(self, vispole, { - 'constraint': 'COPY_LOCATION', - 'name': 'copy_loc', - 'subtarget': org_bones[1], - }) - - pb = self.obj.pose.bones - - make_constraint(self, vispole, { - 'constraint': 'STRETCH_TO', - 'name': 'stretch_to', - 'subtarget': pole_target, - 'volume': 'NO_VOLUME', - 'rest_length': pb[vispole].length - }) - - pb[mch_ik].constraints[-1].pole_target = self.obj - pb[mch_ik].constraints[-1].pole_subtarget = pole_target - pb[mch_ik].constraints[-1].pole_angle = pole_angle - - pb[ mch_ik ].ik_stretch = 0.1 - pb[ ctrl ].ik_stretch = 0.1 - - # IK constraint Rotation locks - if self.rot_axis == 'z': - pb[mch_ik].lock_ik_x = True - pb[mch_ik].lock_ik_y = True - else: - pb[mch_ik].lock_ik_y = True - pb[mch_ik].lock_ik_z = True - - # Locks and Widget - pb[ctrl].lock_rotation = True, False, True - if self.rot_axis == 'x' or self.rot_axis == 'automatic': - roll = 0 - else: - roll = pi/2 - create_ikarrow_widget(self.obj, ctrl, bone_transform_name=None, roll=roll) - create_sphere_widget(self.obj, pole_target, bone_transform_name=None) - create_line_widget(self.obj, vispole) - - return {'ctrl': {'limb': ctrl, 'ik_target': pole_target}, - 'mch_ik': mch_ik, - 'mch_target': mch_target, - 'mch_str': mch_str, - 'visuals': {'vispole': vispole} - } - - def create_fk(self, parent): - org_bones = self.org_bones.copy() - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - ctrls = [] - - for o in org_bones: - bone = copy_bone(self.obj, o, get_bone_name( o, 'ctrl', 'fk')) - ctrls.append(bone) - - # MCH - mch = copy_bone( - self.obj, org_bones[-1], get_bone_name(o, 'mch', 'fk') - ) - - eb[mch].length /= 4 - - # Parenting - eb[ctrls[0]].parent = eb[parent] - eb[ctrls[1]].parent = eb[ctrls[0]] - eb[ctrls[1]].use_connect = True - eb[ctrls[2]].parent = eb[mch] - eb[mch].parent = eb[ctrls[1]] - eb[mch].use_connect = True - - # Constrain MCH's scale to root - make_constraint(self, mch, { - 'constraint': 'COPY_SCALE', - 'subtarget': 'root' - }) - - # Locks and widgets - pb = self.obj.pose.bones - pb[ctrls[2]].lock_location = True, True, True - - create_limb_widget(self.obj, ctrls[0]) - create_limb_widget(self.obj, ctrls[1]) - - create_circle_widget(self.obj, ctrls[2], radius=0.4, head_tail=0.0) - - ControlLayersOption.FK.assign(self.params, pb, ctrls) - - return {'ctrl': ctrls, 'mch': mch} - - def org_parenting_and_switch(self, org_bones, ik, fk, parent): - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - # re-parent ORGs in a connected chain - for i, o in enumerate(org_bones): - if i > 0: - eb[o].parent = eb[org_bones[i-1]] - if i <= len(org_bones)-1: - eb[o].use_connect = True - - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - pb_parent = pb[parent] - - # Create ik/fk switch property - prop = make_property(pb_parent, 'IK_FK', 0.0, description='IK/FK Switch') - - # Constrain org to IK and FK bones - iks = [ik['ctrl']['limb']] - iks += [ik[k] for k in ['mch_ik', 'mch_target']] - - for o, i, f in zip(org_bones, iks, fk): - make_constraint( self, o, { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': i - }) - make_constraint(self, o, { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': f - }) - - # Add driver to relevant constraint - make_driver(pb[o].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)]) - - def create_leg(self, bones): - org_bones = list( - [self.org_bones[0]] + connected_children_names(self.obj, self.org_bones[0]) - ) - - bones['ik']['ctrl']['terminal'] = [] - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - # Create toes def bone - toes_def = get_bone_name(org_bones[-1], 'def') - toes_def = copy_bone( self.obj, org_bones[-1], toes_def ) - - eb[ toes_def ].use_connect = False - eb[ toes_def ].parent = eb[ bones['def'][-1] ] - eb[ toes_def ].use_connect = True - - bones['def'] += [ toes_def ] - - pole_target = get_bone_name(org_bones[0], 'ctrl', 'ik_target') - - # Create IK leg control - ctrl = get_bone_name(org_bones[2], 'ctrl', 'ik') - ctrl = copy_bone(self.obj, org_bones[2], ctrl) - - # clear parent (so that rigify will parent to root) - eb[ctrl].parent = None - eb[ctrl].use_connect = False - - mch_name = get_bone_name(strip_org(org_bones[0]), 'mch', 'parent_socket') - mch_main_parent = copy_bone(self.obj, org_bones[0], mch_name) - eb[mch_main_parent].length = eb[org_bones[0]].length / 12 - eb[mch_main_parent].parent = eb[bones['parent']] - eb[mch_main_parent].roll = 0.0 - eb[bones['main_parent']].parent = eb[mch_main_parent] - - # Create heel ctrl bone - heel = get_bone_name(org_bones[2], 'ctrl', 'heel_ik') - heel = copy_bone(self.obj, org_bones[2], heel) - - ax = eb[org_bones[2]].head - eb[org_bones[2]].tail - ax[2] = 0 - align_bone_y_axis(self.obj, heel, ax) - if self.rot_axis == 'x' or self.rot_axis == 'automatic': - align_bone_x_axis(self.obj, heel, eb[org_bones[2]].x_axis) - elif self.rot_axis == 'z': - align_bone_z_axis(self.obj, heel, eb[org_bones[2]].z_axis) - eb[heel].length = eb[org_bones[2]].length / 2 - - # Reset control position and orientation - if self.rot_axis == 'automatic' or self.auto_align_extremity: - l = eb[ctrl].length - orient_bone(self, eb[ctrl], 'y', reverse=True) - eb[ctrl].length = l else: - flip_bone(self.obj, ctrl) - eb[ctrl].tail[2] = eb[ctrl].head[2] - eb[ctrl].roll = 0 - - # Switchable parent - pbuilder = SwitchParentBuilder(self.rigify_generator) + flip_bone(self.obj, name) - if eb[org_bones[0]].parent: - pbuilder.register_parent(self.rigify_wrapper, eb[org_bones[0]].parent.name) - - pbuilder.register_parent(self.rigify_wrapper, org_bones[2], exclude_self=True) - - pcontrols = [ bones['main_parent'], bones['ik']['ctrl']['limb'], heel, ctrl, pole_target ] - - pbuilder.build_child( - self.rigify_wrapper, ctrl, - prop_bone=bones['main_parent'], prop_id='IK_parent', prop_name='IK Parent', controls=pcontrols, - ) - - pbuilder.build_child( - self.rigify_wrapper, pole_target, extra_parents=[(bones['ik']['mch_target'], ctrl)], - prop_bone=bones['main_parent'], prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols, - no_fix_rotation=True, no_fix_scale=True - ) - - # Parent - eb[ heel ].use_connect = False - eb[ heel ].parent = eb[ ctrl ] - - eb[ bones['ik']['mch_target'] ].parent = eb[ heel ] - eb[ bones['ik']['mch_target'] ].use_connect = False - - # Create foot mch rock and roll bones - - # Get the tmp heel (floating unconnected without children) - tmp_heel = "" - for b in self.obj.data.bones[org_bones[2]].children: - if not b.use_connect and not b.children: - tmp_heel = b.name - - # roll1 MCH bone - roll1_mch = get_bone_name(tmp_heel, 'mch', 'roll') - roll1_mch = copy_bone(self.obj, org_bones[2], roll1_mch) - - # clear parent - eb[roll1_mch].use_connect = False - eb[roll1_mch].parent = None - - flip_bone(self.obj, roll1_mch) - if self.rot_axis == 'x' or self.rot_axis == 'automatic': - align_bone_x_axis(self.obj, roll1_mch, eb[org_bones[2]].x_axis) - elif self.rot_axis == 'z': - align_bone_z_axis(self.obj, roll1_mch, eb[org_bones[2]].z_axis) - - # Create 2nd roll mch, and two rock mch bones - roll2_mch = get_bone_name(tmp_heel, 'mch', 'roll') - roll2_mch = copy_bone(self.obj, org_bones[3], roll2_mch) - - eb[roll2_mch].use_connect = False - eb[roll2_mch].parent = None - - put_bone( - self.obj, - roll2_mch, - (eb[tmp_heel].head + eb[tmp_heel].tail) / 2 - ) - - eb[ roll2_mch ].length /= 4 - - # Rock MCH bones - rock1_mch = get_bone_name( tmp_heel, 'mch', 'rock' ) - rock1_mch = copy_bone( self.obj, tmp_heel, rock1_mch ) - - eb[ rock1_mch ].use_connect = False - eb[ rock1_mch ].parent = None - - orient_bone( self, eb[ rock1_mch ], 'y', 1.0, reverse = True ) - align_bone_y_axis(self.obj, rock1_mch, ax) - eb[ rock1_mch ].length = eb[ tmp_heel ].length / 2 - - rock2_mch = get_bone_name( tmp_heel, 'mch', 'rock' ) - rock2_mch = copy_bone( self.obj, tmp_heel, rock2_mch ) - - eb[ rock2_mch ].use_connect = False - eb[ rock2_mch ].parent = None - - #orient_bone( self, eb[ rock2_mch ], 'y', 1.0 ) - align_bone_y_axis(self.obj, rock2_mch, ax) - eb[ rock2_mch ].length = eb[ tmp_heel ].length / 2 - - # Parent rock and roll MCH bones - eb[ roll1_mch ].parent = eb[ roll2_mch ] - eb[ roll2_mch ].parent = eb[ rock1_mch ] - eb[ rock1_mch ].parent = eb[ rock2_mch ] - eb[ rock2_mch ].parent = eb[ ctrl ] - - # make mch toe bone - toe = '' - foot = eb[self.org_bones[-1]] - for c in foot.children: - if 'org' in c.name.lower() and c.head == foot.tail: - toe = c.name - if not toe: - raise MetarigError.message("Wrong metarig: can't find ORG-<toe>") - - toe_mch = get_bone_name(toe, 'mch') - toe_mch = copy_bone(self.obj, toe, toe_mch) - eb[toe_mch].length /= 3 - eb[toe_mch].parent = eb[self.org_bones[2]] - eb[toe].use_connect = False - eb[toe].parent = eb[toe_mch] - - # Constrain rock and roll MCH bones - make_constraint( self, roll1_mch, { - 'constraint' : 'COPY_ROTATION', - 'subtarget' : heel, - 'owner_space' : 'LOCAL', - 'target_space' : 'LOCAL' - }) - - if self.rot_axis == 'x'or self.rot_axis == 'automatic': - make_constraint(self, roll1_mch, { - 'constraint': 'LIMIT_ROTATION', - 'use_limit_x': True, - 'max_x': math.radians(360), - 'owner_space': 'LOCAL' - }) - make_constraint(self, roll2_mch, { - 'constraint': 'COPY_ROTATION', - 'subtarget': heel, - 'use_y': False, - 'use_z': False, - 'invert_x': True, - 'owner_space': 'LOCAL', - 'target_space': 'LOCAL' - }) - make_constraint(self, roll2_mch, { - 'constraint': 'LIMIT_ROTATION', - 'use_limit_x': True, - 'max_x': math.radians(360), - 'owner_space': 'LOCAL' - }) - - elif self.rot_axis == 'z': - make_constraint(self, roll1_mch, { - 'constraint': 'LIMIT_ROTATION', - 'use_limit_z': True, - 'max_z': math.radians(360), - 'owner_space': 'LOCAL' - }) - make_constraint(self, roll2_mch, { - 'constraint': 'COPY_ROTATION', - 'subtarget': heel, - 'use_y': False, - 'use_x': False, - 'invert_z': True, - 'owner_space': 'LOCAL', - 'target_space': 'LOCAL' - }) - make_constraint(self, roll2_mch, { - 'constraint': 'LIMIT_ROTATION', - 'use_limit_z': True, - 'max_z': math.radians(360), - 'owner_space': 'LOCAL' - }) - - pb = self.obj.pose.bones - if self.rot_axis == 'x'or self.rot_axis == 'automatic': - ik_rot_axis = pb[org_bones[0]].x_axis - elif self.rot_axis == 'z': - ik_rot_axis = pb[org_bones[0]].z_axis - heel_x_orientation = pb[tmp_heel].y_axis.dot(ik_rot_axis) - for i, b in enumerate([rock1_mch, rock2_mch]): - if heel_x_orientation > 0: - if not i: - min_y = 0 - max_y = math.radians(360) - else: - min_y = math.radians(-360) - max_y = 0 - else: - if not i: - min_y = math.radians(-360) - max_y = 0 - else: - min_y = 0 - max_y = math.radians(360) - - - make_constraint( self, b, { - 'constraint' : 'COPY_ROTATION', - 'subtarget' : heel, - 'use_x' : False, - 'use_z' : False, - 'owner_space' : 'LOCAL', - 'target_space' : 'LOCAL' - }) - make_constraint( self, b, { - 'constraint' : 'LIMIT_ROTATION', - 'use_limit_y' : True, - 'min_y' : min_y, - 'max_y' : max_y, - 'owner_space' : 'LOCAL' - }) - - # Cns toe_mch to MCH roll2 - make_constraint( self, toe_mch, { - 'constraint' : 'COPY_TRANSFORMS', - 'subtarget' : roll2_mch - }) - - # Set up constraints - - # Constrain mch target bone to the ik control and mch stretch - make_constraint( self, bones['ik']['mch_target'], { - 'constraint' : 'COPY_LOCATION', - 'subtarget' : bones['ik']['mch_str'], - 'head_tail' : 1.0 - }) - - # Constrain mch ik stretch bone to the ik control - make_constraint( self, bones['ik']['mch_str'], { - 'constraint' : 'DAMPED_TRACK', - 'subtarget' : roll1_mch, - 'head_tail' : 1.0 - }) - make_constraint( self, bones['ik']['mch_str'], { - 'constraint' : 'STRETCH_TO', - 'subtarget' : roll1_mch, - 'head_tail' : 1.0 - }) - make_constraint( self, bones['ik']['mch_str'], { - 'constraint' : 'LIMIT_SCALE', - 'use_min_y' : True, - 'use_max_y' : True, - 'max_y' : 1.05, - 'owner_space' : 'LOCAL' - }) - make_constraint(self, mch_main_parent, { - 'constraint': 'COPY_ROTATION', - 'subtarget': org_bones[0] - }) - - # Create ik/fk switch property - pb_parent = pb[bones['main_parent']] - pb_parent.lock_location = True, True, True - pb_parent.lock_rotation = True, True, True - pb_parent.lock_scale = True, True, True - - prop = make_property(pb_parent, 'IK_Stretch', 1.0, description='IK Stretch') - - # Add driver to limit scale constraint influence - b = bones['ik']['mch_str'] - - make_driver(pb[b].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)], polynomial=[1.0, -1.0]) - - # Create leg widget - create_foot_widget(self.obj, ctrl, bone_transform_name=None) - - # Create heel ctrl locks - pb[heel].lock_location = True, True, True - if self.rot_axis == 'x'or self.rot_axis == 'automatic': - pb[heel].lock_rotation = False, False, True - elif self.rot_axis == 'z': - pb[heel].lock_rotation = True, False, False - pb[heel].lock_scale = True, True, True + bone = self.get_bone(name) + bone.tail[2] = bone.head[2] + bone.roll = 0 - # Add ballsocket widget to heel - create_ballsocket_widget(self.obj, heel, bone_transform_name=None) + return name - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + def register_switch_parents(self, pbuilder): + super().register_switch_parents(pbuilder) - if len(org_bones) >= 4: - # Create toes control bone - toes = get_bone_name(org_bones[3], 'ctrl') - toes = copy_bone(self.obj, org_bones[3], toes) + pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True) - eb[toes].use_connect = False - eb[toes].parent = eb[toe_mch] + def make_ik_ctrl_widget(self, ctrl): + create_foot_widget(self.obj, ctrl, bone_transform_name=None) - # Constrain 4th ORG to toes - make_constraint(self, org_bones[3], { - 'constraint': 'COPY_TRANSFORMS', - # 'subtarget' : roll2_mch - 'subtarget': toes - }) - # Constrain toes def bones - make_constraint(self, bones['def'][-2], { - 'constraint': 'DAMPED_TRACK', - 'subtarget': toes - }) - make_constraint(self, bones['def'][-2], { - 'constraint': 'STRETCH_TO', - 'subtarget': toes - }) - make_constraint(self, bones['def'][-1], { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': toes - }) + #################################################### + # Heel control - # Find IK/FK switch property - pb = self.obj.pose.bones - prop = rna_idprop_ui_prop_get( pb[bones['fk']['ctrl'][-1]], 'IK_FK' ) + @stage.generate_bones + def make_heel_control_bone(self): + org = self.bones.org.main[2] + name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_heel_ik'), scale=1/2) + self.bones.ctrl.heel = name - # Modify rotation mode for ik and tweak controls - pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' + self.align_roll_bone(org, name, -self.vector_without_z(self.get_bone(org).vector)) - for b in bones['tweak']['ctrl']: - pb[b].rotation_mode = 'ZXY' + @stage.parent_bones + def parent_heel_control_bone(self): + self.set_bone_parent(self.bones.ctrl.heel, self.bones.ctrl.ik) - # Add driver to limit scale constraint influence - b = toe_mch + @stage.configure_bones + def configure_heel_control_bone(self): + bone = self.get_bone(self.bones.ctrl.heel) + bone.lock_location = True, True, True - make_driver(pb[b].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)], polynomial=[1.0, -1.0]) + if self.main_axis == 'x': + bone.lock_rotation = False, False, True + else: + bone.lock_rotation = True, False, False - # Create toe circle widget - create_circle_widget(self.obj, toes, radius=0.4, head_tail=0.5) + bone.lock_scale = True, True, True - bones['ik']['ctrl']['terminal'] += [toes] + @stage.generate_widgets + def generate_heel_control_widget(self): + create_ballsocket_widget(self.obj, self.bones.ctrl.heel, bone_transform_name=None) - bones['ik']['ctrl']['terminal'] += [ heel, ctrl ] - return bones + #################################################### + # Heel roll MCH - def create_drivers(self, bones): + @stage.generate_bones + def make_roll_mch_chain(self): + orgs = self.bones.org.main + self.bones.mch.heel = self.make_roll_mch_bones(orgs[2], orgs[3], self.bones.org.heel) - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones + def make_roll_mch_bones(self, foot, toe, heel): + foot_bone = self.get_bone(foot) + heel_bone = self.get_bone(heel) + axis = -self.vector_without_z(foot_bone.vector) - #owner = pb[bones['ik']['ctrl']['limb']] - owner = pb[bones['main_parent']] + roll1 = self.copy_bone(foot, make_derived_name(heel, 'mch', '_roll1')) - props = ["pole_vector"] + flip_bone(self.obj, roll1) + self.align_roll_bone(foot, roll1, -foot_bone.vector) - for prop in props: + roll2 = self.copy_bone(toe, make_derived_name(heel, 'mch', '_roll2'), scale=1/4) - if prop == 'pole_vector': - make_property(owner, prop, False) - mch_ik = pb[bones['ik']['mch_ik']] + put_bone(self.obj, roll2, (heel_bone.head + heel_bone.tail) / 2) + self.align_roll_bone(foot, roll2, -axis) - # ik target hide driver - pole_target = pb[bones['ik']['ctrl']['ik_target']] + rock1 = self.copy_bone(heel, make_derived_name(heel, 'mch', '_rock1')) - make_driver(pole_target.bone, "hide", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) + align_bone_to_axis(self.obj, rock1, 'y', roll=0, length=heel_bone.length/2, flip=True) + align_bone_y_axis(self.obj, rock1, axis) - # vis-pole hide driver - vispole = pb[bones['ik']['visuals']['vispole']] + rock2 = self.copy_bone(heel, make_derived_name(heel, 'mch', '_rock2')) - make_driver(vispole.bone, "hide", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) + align_bone_to_axis(self.obj, rock2, 'y', roll=0, length=heel_bone.length/2) + align_bone_y_axis(self.obj, rock2, axis) - # arrow hide driver - # limb = pb[bones['ik']['ctrl']['limb']] - # - # make_driver(limb.bone, "hide", variables=[(self.obj, owner, prop)], polynomial=[0.0, 1.0]) + return [ rock2, rock1, roll2, roll1 ] - for cns in mch_ik.constraints: - if 'IK' in cns.type: - if not cns.pole_subtarget: - make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[0.0, 1.0]) - else: - make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) + @stage.parent_bones + def parent_roll_mch_chain(self): + chain = self.bones.mch.heel + self.set_bone_parent(chain[0], self.bones.ctrl.ik) + self.parent_bone_chain(chain) + @stage.rig_bones + def rig_roll_mch_chain(self): + self.rig_roll_mch_bones(self.bones.mch.heel, self.bones.ctrl.heel, self.bones.org.heel) - @staticmethod - def get_future_names(bones): + def rig_roll_mch_bones(self, chain, heel, org_heel): + rock2, rock1, roll2, roll1 = chain - if len(bones) != 4: - return + self.make_constraint(roll1, 'COPY_ROTATION', heel, space='LOCAL') + self.make_constraint(roll2, 'COPY_ROTATION', heel, space='LOCAL') - names = dict() + if self.main_axis == 'x': + self.make_constraint(roll1, 'LIMIT_ROTATION', use_limit_x=True, max_x=DEG_360, space='LOCAL') + self.make_constraint(roll2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_x=-DEG_360, space='LOCAL') + else: + self.make_constraint(roll1, 'LIMIT_ROTATION', use_limit_z=True, max_z=DEG_360, space='LOCAL') + self.make_constraint(roll2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_z=-DEG_360, space='LOCAL') - thigh = strip_mch(strip_org(bones[0].name)) - shin = strip_mch(strip_org(bones[1].name)) - foot = strip_mch(strip_org(bones[2].name)) - toe = strip_mch(strip_org(bones[3].name)) + direction = self.get_main_axis(self.get_bone(heel)).dot(self.get_bone(org_heel).vector) - suffix = '' - if thigh[-2:] == '.L' or thigh[-2:] == '.R': - suffix = thigh[-2:] - thigh = thigh.rstrip(suffix) - shin = shin.rstrip(suffix) - foot = foot.rstrip(suffix) - toe = toe.rstrip(suffix) - - # the following is declared in rig_ui - # controls = ['thigh_ik.R', 'thigh_fk.R', 'shin_fk.R', 'foot_fk.R', 'toe.R', 'foot_heel_ik.R', 'foot_ik.R', - # 'MCH-foot_fk.R', 'thigh_parent.R'] - # tweaks = ['thigh_tweak.R.001', 'shin_tweak.R', 'shin_tweak.R.001'] - # ik_ctrl = ['foot_ik.R', 'MCH-thigh_ik.R', 'MCH-thigh_ik_target.R'] - # fk_ctrl = 'thigh_fk.R' - # parent = 'thigh_parent.R' - # foot_fk = 'foot_fk.R' - # pole = 'thigh_ik_target.R' + if direction < 0: + rock2, rock1 = rock1, rock2 - names['controls'] = [thigh + '_ik', thigh + '_fk', shin + '_fk', foot + '_fk', toe, foot + '_heel_ik', - foot + '_ik', make_mechanism_name(foot + '_fk'), thigh + '_parent'] - names['ik_ctrl'] = [foot + '_ik', make_mechanism_name(thigh) + '_ik', make_mechanism_name(thigh) + '_ik_target'] - names['fk_ctrl'] = thigh + '_fk' + suffix - names['parent'] = thigh + '_parent' + suffix - names['foot_fk'] = foot + '_fk' + suffix - names['pole'] = thigh + '_ik_target' + suffix + self.make_constraint(rock1, 'COPY_ROTATION', heel, space='LOCAL') + self.make_constraint(rock2, 'COPY_ROTATION', heel, space='LOCAL') - names['limb_type'] = 'leg' + self.make_constraint(rock1, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, max_y=DEG_360, space='LOCAL') + self.make_constraint(rock2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_y=-DEG_360, space='LOCAL') - if suffix: - for i, name in enumerate(names['controls']): - names['controls'][i] = name + suffix - for i, name in enumerate(names['ik_ctrl']): - names['ik_ctrl'][i] = name + suffix - return names + #################################################### + # FK parents MCH chain - def generate(self): - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + 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]) - # Adjust org-bones rotation - self.orient_org_bones() + self.set_bone_parent(parent_mch, prev_org, use_connect=True) - # Clear parents for org bones - for bone in self.org_bones[1:]: - eb[bone].use_connect = False - eb[bone].parent = None + else: + super().parent_fk_parent_bone(i, parent_mch, prev_ctrl, org, prev_org) - bones = {} + 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]) - # Create mch limb parent - mch_parent, main_parent = self.create_parent() - bones['parent'] = mch_parent - bones['main_parent'] = main_parent - bones['tweak'] = self.create_tweak() - bones['def'] = self.create_def(bones['tweak']['ctrl']) - bones['ik'] = self.create_ik(bones['parent']) - bones['fk'] = self.create_fk(bones['parent']) + self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0]) - self.org_parenting_and_switch(self.org_bones, bones['ik'], bones['fk']['ctrl'], bones['main_parent']) - - bones = self.create_leg(bones) - self.create_drivers(bones) + else: + super().rig_fk_parent_bone(i, parent_mch, org) - # Create UI - script = create_script(bones, 'leg') - return { - 'script': [script], - 'utilities': UTILITIES_RIG_LEG, - 'register': REGISTER_RIG_LEG, - } - - -def add_parameters(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) + #################################################### + # IK system MCH + ik_input_head_tail = 1.0 -def parameters_ui(layout, params): - """ Create the ui for the rig parameters.""" + def get_ik_input_bone(self): + return self.bones.mch.heel[-1] - r = layout.row() - r.prop(params, "rotation_axis") + @stage.parent_bones + def parent_ik_mch_chain(self): + super().parent_ik_mch_chain() - if 'auto' not in params.rotation_axis.lower(): - r = layout.row() - text = "Auto align Foot" - r.prop(params, "auto_align_extremity", text=text) + self.set_bone_parent(self.bones.mch.ik_target, self.bones.ctrl.heel) - r = layout.row() - r.prop(params, "segments") - r = layout.row() - r.prop(params, "bbones") + #################################################### + # Settings - ControlLayersOption.FK.parameters_ui(layout, params) - ControlLayersOption.TWEAK.parameters_ui(layout, params) + @classmethod + def parameters_ui(self, layout, params): + super().parameters_ui(layout, params, 'Foot') def create_sample(obj): @@ -1176,7 +322,7 @@ def create_sample(obj): bpy.ops.object.mode_set(mode='OBJECT') pbone = obj.pose.bones[bones['thigh.L']] - pbone.rigify_type = 'limbs.super_limb' + pbone.rigify_type = 'limbs.leg' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False @@ -1256,6 +402,4 @@ def create_sample(obj): arm.layers = (False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) - -if __name__ == "__main__": - create_sample(bpy.context.active_object) + return bones |