Changeset View
Changeset View
Standalone View
Standalone View
rigify/rigs/limbs/leg.py
| #====================== 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 ======================== | |||||
| import bpy, math | # <pep8 compliant> | ||||
| from ..widgets import create_foot_widget, create_ballsocket_widget, create_gear_widget | |||||
| from .ui import create_script | |||||
| from .limb_utils import * | |||||
| 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 | |||||
| extra_script = """ | |||||
| controls = [%s] | |||||
| ctrl = '%s' | |||||
| if is_selected( controls ): | |||||
| layout.prop( pose_bones[ ctrl ], '["%s"]') | |||||
| if '%s' in pose_bones[ctrl].keys(): | |||||
| layout.prop( pose_bones[ ctrl ], '["%s"]', slider = True ) | |||||
| if '%s' in pose_bones[ctrl].keys(): | |||||
| layout.prop( pose_bones[ ctrl ], '["%s"]', slider = True ) | |||||
| """ | |||||
| 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 | |||||
| class Rig: | |||||
| def __init__(self, obj, bone_name, params): | |||||
| """ Initialize leg rig and key rig properties """ | |||||
| self.obj = obj | |||||
| self.params = params | |||||
| 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 | |||||
| def orient_org_bones(self): | |||||
| bpy.ops.object.mode_set(mode='EDIT') | |||||
| eb = self.obj.data.edit_bones | |||||
| thigh = self.org_bones[0] | |||||
| org_bones = list( | |||||
| [thigh] + connected_children_names(self.obj, thigh) | |||||
| ) # All the provided orgs | |||||
| # 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) | |||||
| 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]] | |||||
| 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() | |||||
| if self.rot_axis != 'automatic': | |||||
| # 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 | import bpy | ||||
| align_bone_x_axis(self.obj, org_foot.name, foot_x) | import math | ||||
| align_bone_x_axis(self.obj, org_toe.name, -foot_x) | |||||
| # Orient heel | from itertools import count | ||||
| align_bone_z_axis(self.obj, org_heel.name, Vector((0, 0, 1))) | from mathutils import Vector | ||||
| def create_parent(self): | 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 | |||||
| org_bones = self.org_bones | from ..widgets import create_foot_widget, create_ballsocket_widget | ||||
| bpy.ops.object.mode_set(mode='EDIT') | from ...base_rig import stage | ||||
| eb = self.obj.data.edit_bones | |||||
| name = get_bone_name( strip_org( org_bones[0] ), 'mch', 'parent' ) | from .limb_rigs import BaseLimbRig | ||||
| 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 | DEG_360 = math.pi * 2 | ||||
| ALL_TRUE = (True, True, True) | |||||
| eb[ mch ].roll = 0.0 | |||||
| # Add non-MCH main limb control | class Rig(BaseLimbRig): | ||||
| name = get_bone_name(strip_org(org_bones[0]), 'ctrl', 'parent') | """Human leg rig.""" | ||||
| 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 | def find_org_bones(self, bone): | ||||
| make_constraint( self, mch, { | bones = super().find_org_bones(bone) | ||||
| 'constraint' : 'COPY_ROTATION', | |||||
| 'subtarget' : 'root' | |||||
| }) | |||||
| make_constraint( self, mch, { | for b in self.get_bone(bones.main[2]).bone.children: | ||||
| 'constraint' : 'COPY_SCALE', | if not b.use_connect and not b.children and not is_rig_base_bone(self.obj, b.name): | ||||
| 'subtarget' : 'root' | bones.heel = b.name | ||||
| }) | break | ||||
| else: | |||||
| self.raise_error("Heel bone not found.") | |||||
| # Limb Follow Driver | return bones | ||||
| pb = self.obj.pose.bones | |||||
| name = 'FK_limb_follow' | def initialize(self): | ||||
| if len(self.bones.org.main) != 4: | |||||
| self.raise_error("Input to rig type must be a chain of 4 bones.") | |||||
| # pb[ mch ][ name ] = 0.0 | super().initialize() | ||||
| # 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)]) | def prepare_bones(self): | ||||
| orgs = self.bones.org.main | |||||
| size = pb[main_parent].bone.y_axis.length * 10 | foot_x = self.vector_without_z(self.get_bone(orgs[2]).y_axis).cross((0, 0, -1)) | ||||
| create_gear_widget(self.obj, main_parent, size=size, bone_transform_name=None) | |||||
| return [mch, main_parent] | if self.params.rotation_axis == 'automatic': | ||||
| align_chain_x_axis(self.obj, orgs[0:2]) | |||||
| def create_tweak(self): | # Orient foot and toe | ||||
| org_bones = self.org_bones | align_bone_x_axis(self.obj, orgs[2], foot_x) | ||||
| align_bone_x_axis(self.obj, orgs[3], -foot_x) | |||||
| bpy.ops.object.mode_set(mode ='EDIT') | align_bone_x_axis(self.obj, self.bones.org.heel, Vector((0, 0, 1))) | ||||
| eb = self.obj.data.edit_bones | |||||
| tweaks = {} | elif self.params.auto_align_extremity: | ||||
| tweaks['ctrl'] = [] | if self.main_axis == 'x': | ||||
| tweaks['mch' ] = [] | align_bone_x_axis(self.obj, orgs[2], foot_x) | ||||
| align_bone_x_axis(self.obj, orgs[3], -foot_x) | |||||
| # 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: | else: | ||||
| targets = [middle,last] | align_bone_z_axis(self.obj, orgs[2], foot_x) | ||||
| factor = self.segments | align_bone_z_axis(self.obj, orgs[3], -foot_x) | ||||
| 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']) | #################################################### | ||||
| # EXTRA BONES | |||||
| # | |||||
| # org: | |||||
| # heel: | |||||
| # Heel location marker bone | |||||
| # ctrl: | |||||
| # heel: | |||||
| # Foot roll control | |||||
| # mch: | |||||
| # heel[]: | |||||
| # Chain of bones implementing foot roll. | |||||
| # | |||||
| #################################################### | |||||
| return tweaks | #################################################### | ||||
| # IK controls | |||||
| def create_def(self, tweaks): | def get_extra_ik_controls(self): | ||||
| org_bones = self.org_bones | return [self.bones.ctrl.heel] | ||||
| bpy.ops.object.mode_set(mode ='EDIT') | def make_ik_control_bone(self, orgs): | ||||
| eb = self.obj.data.edit_bones | name = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik')) | ||||
| def_bones = [] | if self.params.rotation_axis == 'automatic' or self.params.auto_align_extremity: | ||||
| for i, org in enumerate(org_bones): | align_bone_to_axis(self.obj, name, 'y', flip=True) | ||||
| 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: | else: | ||||
| name = get_bone_name(strip_org(org), 'def') | flip_bone(self.obj, name) | ||||
| 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 ): | bone = self.get_bone(name) | ||||
| defval = 0.0 | bone.tail[2] = bone.head[2] | ||||
| else: | bone.roll = 0 | ||||
| defval = 1.0 | |||||
| make_property(pb[t], name, defval, max=2.0, soft_max=1.0) | return name | ||||
| for j,d in enumerate(def_bones[:-1]): | def register_switch_parents(self, pbuilder): | ||||
| if j != 0: | super().register_switch_parents(pbuilder) | ||||
| make_driver(self.obj.data.bones[d], "bbone_easein", variables=[(self.obj, tweaks[j], 'rubber_tweak')]) | |||||
| if j != len( def_bones[:-1] ) - 1: | pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True) | ||||
| make_driver(self.obj.data.bones[d], "bbone_easeout", variables=[(self.obj, tweaks[j+1], 'rubber_tweak')]) | |||||
| return def_bones | def make_ik_ctrl_widget(self, ctrl): | ||||
| create_foot_widget(self.obj, ctrl, bone_transform_name=None) | |||||
| def create_ik(self, parent): | |||||
| org_bones = self.org_bones | |||||
| bpy.ops.object.mode_set(mode='EDIT') | #################################################### | ||||
| eb = self.obj.data.edit_bones | # Heel control | ||||
| ctrl = get_bone_name(org_bones[0], 'ctrl', 'ik') | @stage.generate_bones | ||||
| mch_ik = get_bone_name(org_bones[0], 'mch', 'ik') | def make_heel_control_bone(self): | ||||
| mch_target = get_bone_name(org_bones[0], 'mch', 'ik_target') | org = self.bones.org.main[2] | ||||
| name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_heel_ik'), scale=1/2) | |||||
| for o, ik in zip(org_bones, [ctrl, mch_ik, mch_target]): | self.bones.ctrl.heel = name | ||||
| bone = copy_bone(self.obj, o, ik) | |||||
| if org_bones.index(o) == len(org_bones) - 1: | |||||
| eb[bone].length /= 4 | |||||
| # Create MCH Stretch | |||||
| mch_str = copy_bone( | |||||
| self.obj, | |||||
| org_bones[0], | |||||
| get_bone_name( org_bones[0], 'mch', 'ik_stretch' ) | |||||
| ) | |||||
| eb[ mch_str ].tail = eb[ org_bones[-1] ].head | |||||
| # 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: | self.align_roll_bone(org, name, -self.vector_without_z(self.get_bone(org).vector)) | ||||
| pole_angle = -pi/2 | |||||
| else: | |||||
| pole_angle = pi/2 | |||||
| if self.rot_axis == 'z': | @stage.parent_bones | ||||
| pole_angle = 0 | def parent_heel_control_bone(self): | ||||
| self.set_bone_parent(self.bones.ctrl.heel, self.bones.ctrl.ik) | |||||
| eb[pole_target].head = eb[org_bones[0]].tail + elbow_vector | @stage.configure_bones | ||||
| eb[pole_target].tail = eb[pole_target].head - elbow_vector/8 | def configure_heel_control_bone(self): | ||||
| eb[pole_target].roll = 0.0 | bone = self.get_bone(self.bones.ctrl.heel) | ||||
| bone.lock_location = True, True, True | |||||
| # 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 | if self.main_axis == 'x': | ||||
| pb[ctrl].lock_rotation = True, False, True | bone.lock_rotation = False, False, True | ||||
| if self.rot_axis == 'x' or self.rot_axis == 'automatic': | |||||
| roll = 0 | |||||
| else: | else: | ||||
| roll = pi/2 | bone.lock_rotation = True, False, False | ||||
| 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): | bone.lock_scale = True, True, True | ||||
| org_bones = self.org_bones.copy() | |||||
| bpy.ops.object.mode_set(mode='EDIT') | @stage.generate_widgets | ||||
| eb = self.obj.data.edit_bones | def generate_heel_control_widget(self): | ||||
| create_ballsocket_widget(self.obj, self.bones.ctrl.heel, bone_transform_name=None) | |||||
| ctrls = [] | |||||
| for o in org_bones: | #################################################### | ||||
| bone = copy_bone(self.obj, o, get_bone_name( o, 'ctrl', 'fk')) | # Heel roll MCH | ||||
| ctrls.append(bone) | |||||
| # MCH | @stage.generate_bones | ||||
| mch = copy_bone( | def make_roll_mch_chain(self): | ||||
| self.obj, org_bones[-1], get_bone_name(o, 'mch', 'fk') | orgs = self.bones.org.main | ||||
| ) | self.bones.mch.heel = self.make_roll_mch_bones(orgs[2], orgs[3], self.bones.org.heel) | ||||
| eb[mch].length /= 4 | 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) | |||||
| # Parenting | roll1 = self.copy_bone(foot, make_derived_name(heel, 'mch', '_roll1')) | ||||
| 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 | flip_bone(self.obj, roll1) | ||||
| make_constraint(self, mch, { | self.align_roll_bone(foot, roll1, -foot_bone.vector) | ||||
| 'constraint': 'COPY_SCALE', | |||||
| 'subtarget': 'root' | |||||
| }) | |||||
| # Locks and widgets | roll2 = self.copy_bone(toe, make_derived_name(heel, 'mch', '_roll2'), scale=1/4) | ||||
| pb = self.obj.pose.bones | |||||
| pb[ctrls[2]].lock_location = True, True, True | |||||
| create_limb_widget(self.obj, ctrls[0]) | put_bone(self.obj, roll2, (heel_bone.head + heel_bone.tail) / 2) | ||||
| create_limb_widget(self.obj, ctrls[1]) | self.align_roll_bone(foot, roll2, -axis) | ||||
| create_circle_widget(self.obj, ctrls[2], radius=0.4, head_tail=0.0) | rock1 = self.copy_bone(heel, make_derived_name(heel, 'mch', '_rock1')) | ||||
| ControlLayersOption.FK.assign(self.params, pb, ctrls) | 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) | |||||
| return {'ctrl': ctrls, 'mch': mch} | rock2 = self.copy_bone(heel, make_derived_name(heel, 'mch', '_rock2')) | ||||
| def org_parenting_and_switch(self, org_bones, ik, fk, parent): | align_bone_to_axis(self.obj, rock2, 'y', roll=0, length=heel_bone.length/2) | ||||
| bpy.ops.object.mode_set(mode='EDIT') | align_bone_y_axis(self.obj, rock2, axis) | ||||
| 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') | return [ rock2, rock1, roll2, roll1 ] | ||||
| pb = self.obj.pose.bones | |||||
| pb_parent = pb[parent] | |||||
| # Create ik/fk switch property | @stage.parent_bones | ||||
| prop = make_property(pb_parent, 'IK_FK', 0.0, description='IK/FK Switch') | 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) | |||||
| # Constrain org to IK and FK bones | @stage.rig_bones | ||||
| iks = [ik['ctrl']['limb']] | def rig_roll_mch_chain(self): | ||||
| iks += [ik[k] for k in ['mch_ik', 'mch_target']] | self.rig_roll_mch_bones(self.bones.mch.heel, self.bones.ctrl.heel, self.bones.org.heel) | ||||
| 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'] = [] | def rig_roll_mch_bones(self, chain, heel, org_heel): | ||||
| rock2, rock1, roll2, roll1 = chain | |||||
| bpy.ops.object.mode_set(mode='EDIT') | |||||
| eb = self.obj.data.edit_bones | |||||
| # Create toes def bone | self.make_constraint(roll1, 'COPY_ROTATION', heel, space='LOCAL') | ||||
| toes_def = get_bone_name(org_bones[-1], 'def') | self.make_constraint(roll2, 'COPY_ROTATION', heel, space='LOCAL') | ||||
| 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 for ik control | |||||
| ctrl_socket = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_socket')) | |||||
| eb[ctrl_socket].tail = eb[ctrl_socket].head + 0.8*(eb[ctrl_socket].tail-eb[ctrl_socket].head) | |||||
| eb[ctrl_socket].parent = None | |||||
| eb[ctrl].parent = eb[ctrl_socket] | |||||
| # MCH for pole ik control | |||||
| ctrl_pole_socket = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'pole_ik_socket')) | |||||
| eb[ctrl_pole_socket].tail = eb[ctrl_pole_socket].head + 0.8 * (eb[ctrl_pole_socket].tail - eb[ctrl_pole_socket].head) | |||||
| eb[ctrl_pole_socket].parent = None | |||||
| eb[pole_target].parent = eb[ctrl_pole_socket] | |||||
| ctrl_root = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_root')) | |||||
| eb[ctrl_root].tail = eb[ctrl_root].head + 0.7*(eb[ctrl_root].tail-eb[ctrl_root].head) | |||||
| eb[ctrl_root].use_connect = False | |||||
| eb[ctrl_root].parent = eb['root'] | |||||
| if eb[org_bones[0]].parent: | |||||
| leg_parent = eb[org_bones[0]].parent | |||||
| ctrl_parent = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_parent')) | |||||
| eb[ctrl_parent].tail = eb[ctrl_parent].head + 0.6*(eb[ctrl_parent].tail-eb[ctrl_parent].head) | |||||
| eb[ctrl_parent].use_connect = False | |||||
| if eb[org_bones[0]].parent_recursive: | |||||
| eb[ctrl_parent].parent = eb[org_bones[0]].parent_recursive[-1] | |||||
| else: | |||||
| eb[ctrl_parent].parent = eb[org_bones[0]].parent | |||||
| else: | |||||
| leg_parent = None | |||||
| mch_name = get_bone_name(strip_org(org_bones[0]), 'mch', 'parent_socket') | if self.main_axis == 'x': | ||||
| mch_main_parent = copy_bone(self.obj, org_bones[0], mch_name) | self.make_constraint(roll1, 'LIMIT_ROTATION', use_limit_x=True, max_x=DEG_360, space='LOCAL') | ||||
| eb[mch_main_parent].length = eb[org_bones[0]].length / 12 | self.make_constraint(roll2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_x=-DEG_360, space='LOCAL') | ||||
| 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 | |||||
| # 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: | else: | ||||
| min_y = 0 | self.make_constraint(roll1, 'LIMIT_ROTATION', use_limit_z=True, max_z=DEG_360, space='LOCAL') | ||||
| max_y = math.radians(360) | self.make_constraint(roll2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_z=-DEG_360, space='LOCAL') | ||||
| direction = self.get_main_axis(self.get_bone(heel)).dot(self.get_bone(org_heel).vector) | |||||
| make_constraint( self, b, { | if direction < 0: | ||||
| 'constraint' : 'COPY_ROTATION', | rock2, rock1 = rock1, rock2 | ||||
| '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 ik ctrl to root / parent | |||||
| make_constraint( self, ctrl_socket, { | |||||
| 'constraint' : 'COPY_TRANSFORMS', | |||||
| 'subtarget' : ctrl_root, | |||||
| }) | |||||
| make_constraint(self, ctrl_pole_socket, { | |||||
| 'constraint': 'COPY_TRANSFORMS', | |||||
| 'subtarget': ctrl_root, | |||||
| }) | |||||
| if leg_parent: | |||||
| make_constraint( self, ctrl_socket, { | |||||
| 'constraint' : 'COPY_TRANSFORMS', | |||||
| 'subtarget' : ctrl_parent, | |||||
| 'influence' : 0.0, | |||||
| }) | |||||
| make_constraint(self, ctrl_pole_socket, { | |||||
| 'constraint': 'COPY_TRANSFORMS', | |||||
| 'subtarget': bones['ik']['mch_target'], | |||||
| }) | |||||
| # 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') | self.make_constraint(rock1, 'COPY_ROTATION', heel, space='LOCAL') | ||||
| self.make_constraint(rock2, 'COPY_ROTATION', heel, space='LOCAL') | |||||
| # Add driver to limit scale constraint influence | self.make_constraint(rock1, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, max_y=DEG_360, space='LOCAL') | ||||
| b = bones['ik']['mch_str'] | self.make_constraint(rock2, 'LIMIT_ROTATION', use_limit_xyz=ALL_TRUE, min_y=-DEG_360, space='LOCAL') | ||||
| 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) | # FK parents MCH chain | ||||
| # 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 | |||||
| # Add ballsocket widget to heel | |||||
| create_ballsocket_widget(self.obj, heel, bone_transform_name=None) | |||||
| bpy.ops.object.mode_set(mode='EDIT') | |||||
| eb = self.obj.data.edit_bones | |||||
| 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) | |||||
| eb[toes].use_connect = False | |||||
| eb[toes].parent = eb[toe_mch] | |||||
| # 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 | |||||
| }) | |||||
| # Find IK/FK switch property | |||||
| pb = self.obj.pose.bones | |||||
| prop = rna_idprop_ui_prop_get( pb[bones['fk']['ctrl'][-1]], 'IK_FK' ) | |||||
| # Modify rotation mode for ik and tweak controls | |||||
| pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' | |||||
| for b in bones['tweak']['ctrl']: | |||||
| pb[b].rotation_mode = 'ZXY' | |||||
| # Add driver to limit scale constraint influence | |||||
| b = toe_mch | |||||
| make_driver(pb[b].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)], polynomial=[1.0, -1.0]) | |||||
| # Create toe circle widget | |||||
| create_circle_widget(self.obj, toes, radius=0.4, head_tail=0.5) | |||||
| bones['ik']['ctrl']['terminal'] += [toes] | 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]) | |||||
| bones['ik']['ctrl']['terminal'] += [ heel, ctrl ] | self.set_bone_parent(parent_mch, prev_org, use_connect=True) | ||||
| if leg_parent: | |||||
| bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root, ctrl_parent] | |||||
| else: | else: | ||||
| bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root] | super().parent_fk_parent_bone(i, parent_mch, prev_ctrl, org, prev_org) | ||||
| return bones | |||||
| def create_drivers(self, bones): | |||||
| bpy.ops.object.mode_set(mode='OBJECT') | |||||
| pb = self.obj.pose.bones | |||||
| ctrl = pb[bones['ik']['mch_foot'][0]] | def rig_fk_parent_bone(self, i, parent_mch, org): | ||||
| ctrl_pole = pb[bones['ik']['mch_foot'][1]] | if i == 3: | ||||
| con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.heel[-2]) | |||||
| #owner = pb[bones['ik']['ctrl']['limb']] | self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0]) | ||||
| owner = pb[bones['main_parent']] | |||||
| props = ["IK_follow", "root/parent", "pole_vector", "pole_follow"] | |||||
| for prop in props: | |||||
| if prop == 'pole_vector': | |||||
| make_property(owner, prop, False) | |||||
| mch_ik = pb[bones['ik']['mch_ik']] | |||||
| # ik target hide driver | |||||
| pole_target = pb[bones['ik']['ctrl']['ik_target']] | |||||
| make_driver(pole_target.bone, "hide", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | |||||
| # vis-pole hide driver | |||||
| vispole = pb[bones['ik']['visuals']['vispole']] | |||||
| make_driver(vispole.bone, "hide", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | |||||
| # arrow hide driver | |||||
| # limb = pb[bones['ik']['ctrl']['limb']] | |||||
| # | |||||
| # make_driver(limb.bone, "hide", variables=[(self.obj, owner, prop)], polynomial=[0.0, 1.0]) | |||||
| 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: | else: | ||||
| make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | super().rig_fk_parent_bone(i, parent_mch, org) | ||||
| elif prop == 'IK_follow': | |||||
| make_property(owner, prop, True) | |||||
| make_driver(ctrl.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | |||||
| if len(ctrl.constraints) > 1: | #################################################### | ||||
| make_driver(ctrl.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | # IK system MCH | ||||
| make_driver(ctrl_pole.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | ik_input_head_tail = 1.0 | ||||
| if len(ctrl_pole.constraints) > 1: | def get_ik_input_bone(self): | ||||
| make_driver(ctrl_pole.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) | return self.bones.mch.heel[-1] | ||||
| elif prop == 'root/parent': | @stage.parent_bones | ||||
| if len(ctrl.constraints) > 1: | def parent_ik_mch_chain(self): | ||||
| make_property(owner, prop, 0.0) | super().parent_ik_mch_chain() | ||||
| make_driver(ctrl.constraints[1], "influence", variables=[(self.obj, owner, prop)]) | self.set_bone_parent(self.bones.mch.ik_target, self.bones.ctrl.heel) | ||||
| elif prop == 'pole_follow': | |||||
| if len(ctrl_pole.constraints) > 1: | |||||
| make_property(owner, prop, 0.0) | |||||
| make_driver(ctrl_pole.constraints[1], "influence", variables=[(self.obj, owner, prop)]) | #################################################### | ||||
| # Settings | |||||
| @staticmethod | @classmethod | ||||
| def get_future_names(bones): | def parameters_ui(self, layout, params): | ||||
| super().parameters_ui(layout, params, 'Foot') | |||||
| if len(bones) != 4: | |||||
| return | |||||
| names = dict() | |||||
| 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)) | |||||
| 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' | |||||
| 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 | |||||
| names['limb_type'] = 'leg' | |||||
| 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 | |||||
| def generate(self): | |||||
| bpy.ops.object.mode_set(mode='EDIT') | |||||
| eb = self.obj.data.edit_bones | |||||
| # Adjust org-bones rotation | |||||
| self.orient_org_bones() | |||||
| # Clear parents for org bones | |||||
| for bone in self.org_bones[1:]: | |||||
| eb[bone].use_connect = False | |||||
| eb[bone].parent = None | |||||
| bones = {} | |||||
| # 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.org_parenting_and_switch(self.org_bones, bones['ik'], bones['fk']['ctrl'], bones['main_parent']) | |||||
| bones = self.create_leg(bones) | |||||
| self.create_drivers(bones) | |||||
| controls = [bones['ik']['ctrl']['limb'], bones['ik']['ctrl']['terminal'][-1], bones['ik']['ctrl']['terminal'][-2] ] | |||||
| controls.append(bones['main_parent']) | |||||
| # Create UI | |||||
| controls_string = ", ".join(["'" + x + "'" for x in controls]) | |||||
| script = create_script(bones, 'leg') | |||||
| script += extra_script % (controls_string, bones['main_parent'], 'IK_follow', | |||||
| 'pole_follow', 'pole_follow', 'root/parent', 'root/parent') | |||||
| return { | |||||
| 'script': [script], | |||||
| 'utilities': UTILITIES_RIG_LEG, | |||||
| 'register': REGISTER_RIG_LEG, | |||||
| 'noparent_bones': [bones['ik']['mch_foot'][i] for i in [0,1]], | |||||
| } | |||||
| 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) | |||||
| def parameters_ui(layout, params): | |||||
| """ 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() | |||||
| text = "Auto align Foot" | |||||
| r.prop(params, "auto_align_extremity", text=text) | |||||
| 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) | |||||
| def create_sample(obj): | def create_sample(obj): | ||||
| # generated by rigify.utils.write_metarig | # generated by rigify.utils.write_metarig | ||||
| bpy.ops.object.mode_set(mode='EDIT') | bpy.ops.object.mode_set(mode='EDIT') | ||||
| arm = obj.data | arm = obj.data | ||||
| bones = {} | bones = {} | ||||
| Show All 31 Lines | def create_sample(obj): | ||||
| bone.roll = 0.0000 | bone.roll = 0.0000 | ||||
| bone.use_connect = False | bone.use_connect = False | ||||
| bone.parent = arm.edit_bones[bones['foot.L']] | bone.parent = arm.edit_bones[bones['foot.L']] | ||||
| bones['heel.02.L'] = bone.name | bones['heel.02.L'] = bone.name | ||||
| bpy.ops.object.mode_set(mode='OBJECT') | bpy.ops.object.mode_set(mode='OBJECT') | ||||
| pbone = obj.pose.bones[bones['thigh.L']] | 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_location = (False, False, False) | ||||
| pbone.lock_rotation = (False, False, False) | pbone.lock_rotation = (False, False, False) | ||||
| pbone.lock_rotation_w = False | pbone.lock_rotation_w = False | ||||
| pbone.lock_scale = (False, False, False) | pbone.lock_scale = (False, False, False) | ||||
| pbone.rotation_mode = 'QUATERNION' | pbone.rotation_mode = 'QUATERNION' | ||||
| try: | try: | ||||
| pbone.rigify_parameters.separate_ik_layers = True | pbone.rigify_parameters.separate_ik_layers = True | ||||
| except AttributeError: | except AttributeError: | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | for b in bones: | ||||
| bone.select_tail = True | bone.select_tail = True | ||||
| arm.edit_bones.active = bone | arm.edit_bones.active = bone | ||||
| for eb in arm.edit_bones: | for eb in arm.edit_bones: | ||||
| eb.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) | eb.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) | ||||
| 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) | 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) | ||||
| return bones | |||||
| if __name__ == "__main__": | |||||
| create_sample(bpy.context.active_object) | |||||