diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-14 09:17:30 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-14 09:30:00 +0300 |
commit | 8b1df843703fdb51ffa5758625c117c4f10bc6dd (patch) | |
tree | 949e84f80116132eab3ad5f3bac46e745c3a9195 /rigify/rigs | |
parent | 9b693a6be0aa2b0b4825d30ac5034655dce9c0dd (diff) |
Rigify: replace rigs with new implementations using the new base rig.
Spine is split into parts. Limbs and tentacles simply converted.
Differential Revision: https://developer.blender.org/D4624
Diffstat (limited to 'rigify/rigs')
-rw-r--r-- | rigify/rigs/limbs/arm.py | 1223 | ||||
-rw-r--r-- | rigify/rigs/limbs/leg.py | 1248 | ||||
-rw-r--r-- | rigify/rigs/limbs/limb_rigs.py | 1047 | ||||
-rw-r--r-- | rigify/rigs/limbs/paw.py | 1383 | ||||
-rw-r--r-- | rigify/rigs/limbs/rear_paw.py | 9 | ||||
-rw-r--r-- | rigify/rigs/limbs/simple_tentacle.py | 335 | ||||
-rw-r--r-- | rigify/rigs/limbs/super_finger.py | 538 | ||||
-rw-r--r-- | rigify/rigs/limbs/super_limb.py | 221 | ||||
-rw-r--r-- | rigify/rigs/limbs/ui.py | 182 | ||||
-rw-r--r-- | rigify/rigs/spines/basic_spine.py | 321 | ||||
-rw-r--r-- | rigify/rigs/spines/basic_tail.py | 246 | ||||
-rw-r--r-- | rigify/rigs/spines/spine_rigs.py | 208 | ||||
-rw-r--r-- | rigify/rigs/spines/super_head.py | 406 | ||||
-rw-r--r-- | rigify/rigs/spines/super_spine.py | 1205 | ||||
-rw-r--r-- | rigify/rigs/utils.py | 154 |
15 files changed, 3247 insertions, 5479 deletions
diff --git a/rigify/rigs/limbs/arm.py b/rigify/rigs/limbs/arm.py index aacc1e86..4e9cf299 100644 --- a/rigify/rigs/limbs/arm.py +++ b/rigify/rigs/limbs/arm.py @@ -1,1015 +1,116 @@ -import bpy, re -from ..widgets import create_hand_widget, create_gear_widget -from .ui import create_script -from .limb_utils import * -from mathutils import Vector -from ...utils import copy_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 make_mechanism_name -from ...utils import create_limb_widget, connected_children_names -from ...utils import align_bone_x_axis, align_bone_z_axis -from ...rig_ui_template import UTILITIES_RIG_ARM, REGISTER_RIG_ARM -from ...utils import ControlLayersOption -from ...utils.mechanism import make_property, make_driver -from ..widgets import create_ikarrow_widget -from math import trunc, pi +#====================== 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 ======================== -from ...utils.switch_parent import SwitchParentBuilder +# <pep8 compliant> +import bpy -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 itertools import count +from ...utils.bones import BoneDict, compute_chain_x_axis, align_bone_x_axis, align_bone_z_axis +from ...utils.naming import make_derived_name +from ...utils.misc import map_list -class Rig: +from ..widgets import create_hand_widget - def __init__(self, obj, bone_name, params): - """ Initialize arm rig and key rig properties """ - self.obj = obj - self.params = params +from ...base_rig import stage - self.org_bones = list( - [bone_name] + connected_children_names(obj, bone_name) - )[:3] # The basic limb is the first 3 bones +from .limb_rigs import BaseLimbRig - 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 arm rig.""" - def orient_org_bones(self): + def initialize(self): + if len(self.bones.org.main) != 3: + self.raise_error("Input to rig type must be a chain of 3 bones.") - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + super().initialize() - thigh = self.org_bones[0] - org_bones = list( - [thigh] + connected_children_names(self.obj, thigh) - ) # All the provided orgs + def prepare_bones(self): + orgs = self.bones.org.main - org_uarm = eb[org_bones[0]] - org_farm = eb[org_bones[1]] - org_hand = eb[org_bones[2]] + if self.params.rotation_axis == 'automatic': + axis = compute_chain_x_axis(self.obj, orgs[0:2]) - if self.rot_axis != 'automatic': - if self.auto_align_extremity: - z_ground_projection = Vector((org_hand.z_axis[0], org_hand.z_axis[1], 0)) - align_bone_z_axis(self.obj, org_hand.name, z_ground_projection.normalized()) - return + for bone in orgs: + align_bone_x_axis(self.obj, bone, axis) - # Orient uarm farm bones - chain_y_axis = org_uarm.y_axis + org_farm.y_axis - chain_rot_axis = org_uarm.y_axis.cross(chain_y_axis).normalized() # ik-plane normal axis (rotation) + elif self.params.auto_align_extremity: + axis = self.vector_without_z(self.get_bone(orgs[2]).z_axis) - align_bone_x_axis(self.obj, org_uarm.name, chain_rot_axis) - align_bone_x_axis(self.obj, org_farm.name, chain_rot_axis) + align_bone_z_axis(self.obj, orgs[2], axis) - # Orient hand - align_bone_x_axis(self.obj, org_hand.name, chain_rot_axis) + #################################################### + # Overrides - def create_parent(self): + def register_switch_parents(self, pbuilder): + super().register_switch_parents(pbuilder) - org_bones = self.org_bones + pbuilder.register_parent(self, self.bones.org.main[2], exclude_self=True) - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - name = get_bone_name( strip_org( org_bones[0] ), 'mch', 'parent' ) - - 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 = None - 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 - 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 - - 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') - - for o, ik in zip( org_bones, [ ctrl, mch_ik, mch_target ] ): - 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: - 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_arm(self, bones): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - pole_target = get_bone_name(org_bones[0], 'ctrl', 'ik_target') - - # Create IK arm 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 - - # Parent - eb[ bones['ik']['mch_target'] ].parent = eb[ ctrl ] - eb[ bones['ik']['mch_target'] ].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] - - # Switchable parent - pbuilder = SwitchParentBuilder(self.rigify_generator) - - 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'], 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=[ctrl], - prop_bone=bones['main_parent'], prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols, - no_fix_rotation=True, no_fix_scale=True - ) - - # 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': ctrl, - }) - make_constraint(self, bones['ik']['mch_str'], { - 'constraint': 'STRETCH_TO', - 'subtarget': ctrl, - }) - 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] - }) - - pb = self.obj.pose.bones - - # 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 - - # 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' - - 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 hand widget + def make_ik_ctrl_widget(self, ctrl): create_hand_widget(self.obj, ctrl, bone_transform_name=None) - bones['ik']['ctrl']['terminal'] = [ctrl] - - return bones - - def create_drivers(self, bones): - - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - #owner = pb[bones['ik']['ctrl']['limb']] - owner = pb[bones['main_parent']] - - props = ["pole_vector"] - - 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]) + #################################################### + # Settings - # vis-pole hide driver - vispole = pb[bones['ik']['visuals']['vispole']] + @classmethod + def parameters_ui(self, layout, params): + super().parameters_ui(layout, params, 'Hand') - 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: - make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) - - - @staticmethod - def get_future_names(bones): - - if len(bones) != 3: - return - - names = dict() - - uarm = strip_mch(strip_org(bones[0].name)) - farm = strip_mch(strip_org(bones[1].name)) - hand = strip_mch(strip_org(bones[2].name)) - - suffix='' - if uarm[-2:] == '.L' or uarm[-2:] == '.R': - suffix = uarm[-2:] - uarm = uarm.rstrip(suffix) - farm = farm.rstrip(suffix) - hand = hand.rstrip(suffix) - - # the following is declared in rig_ui - # controls = ['upper_arm_ik.L', 'upper_arm_fk.L', 'forearm_fk.L', 'hand_fk.L', 'hand_ik.L', 'MCH-hand_fk.L', - # 'upper_arm_parent.L'] - # tweaks = ['upper_arm_tweak.L.001', 'forearm_tweak.L', 'forearm_tweak.L.001'] - # ik_ctrl = ['hand_ik.L', 'MCH-upper_arm_ik.L', 'MCH-upper_arm_ik_target.L'] - # fk_ctrl = 'upper_arm_fk.L' - # parent = 'upper_arm_parent.L' - # hand_fk = 'hand_fk.L' - # pole = 'upper_arm_ik_target.L' - - names['controls'] = [uarm + '_ik', uarm + '_fk', farm + '_fk', hand + '_fk', hand + '_ik', - make_mechanism_name(hand + '_fk'), uarm + '_parent'] - names['ik_ctrl'] = [hand + '_ik', make_mechanism_name(uarm) + '_ik', make_mechanism_name(uarm) + '_ik_target'] - names['fk_ctrl'] = uarm + '_fk' + suffix - names['parent'] = uarm + '_parent' + suffix - names['hand_fk'] = hand + '_fk' + suffix - names['pole'] = uarm + '_ik_target' + suffix - names['limb_type'] = 'arm' - - 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_arm(bones) - self.create_drivers(bones) - - # Create UI - script = create_script(bones, 'arm') - - return { - 'script': [script], - 'utilities': UTILITIES_RIG_ARM, - 'register': REGISTER_RIG_ARM, - } - - -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 Hand" - 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, limb=False): # generated by rigify.utils.write_metarig bpy.ops.object.mode_set(mode='EDIT') arm = obj.data bones = {} - bone = arm.edit_bones.new('upper_arm.L') - bone.head[:] = 0.1953, 0.0267, 1.5846 - bone.tail[:] = 0.4424, 0.0885, 1.4491 + bone.head[:] = -0.0016, 0.0060, -0.0012 + bone.tail[:] = 0.2455, 0.0678, -0.1367 bone.roll = 2.0724 bone.use_connect = False bones['upper_arm.L'] = bone.name bone = arm.edit_bones.new('forearm.L') - bone.head[:] = 0.4424, 0.0885, 1.4491 - bone.tail[:] = 0.6594, 0.0492, 1.3061 + bone.head[:] = 0.2455, 0.0678, -0.1367 + bone.tail[:] = 0.4625, 0.0285, -0.2797 bone.roll = 2.1535 bone.use_connect = True bone.parent = arm.edit_bones[bones['upper_arm.L']] bones['forearm.L'] = bone.name bone = arm.edit_bones.new('hand.L') - bone.head[:] = 0.6594, 0.0492, 1.3061 - bone.tail[:] = 0.7234, 0.0412, 1.2585 + bone.head[:] = 0.4625, 0.0285, -0.2797 + bone.tail[:] = 0.5265, 0.0205, -0.3273 bone.roll = 2.2103 bone.use_connect = True bone.parent = arm.edit_bones[bones['forearm.L']] bones['hand.L'] = bone.name - bone = arm.edit_bones.new('palm.01.L') - bone.head[:] = 0.6921, 0.0224, 1.2882 - bone.tail[:] = 0.7464, 0.0051, 1.2482 - bone.roll = -2.4928 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['hand.L']] - bones['palm.01.L'] = bone.name - bone = arm.edit_bones.new('palm.02.L') - bone.head[:] = 0.6970, 0.0389, 1.2877 - bone.tail[:] = 0.7518, 0.0277, 1.2487 - bone.roll = -2.5274 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['hand.L']] - bones['palm.02.L'] = bone.name - bone = arm.edit_bones.new('palm.03.L') - bone.head[:] = 0.6963, 0.0545, 1.2874 - bone.tail[:] = 0.7540, 0.0521, 1.2482 - bone.roll = -2.5843 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['hand.L']] - bones['palm.03.L'] = bone.name - bone = arm.edit_bones.new('palm.04.L') - bone.head[:] = 0.6929, 0.0696, 1.2871 - bone.tail[:] = 0.7528, 0.0763, 1.2428 - bone.roll = -2.5155 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['hand.L']] - bones['palm.04.L'] = bone.name - bone = arm.edit_bones.new('f_index.01.L') - bone.head[:] = 0.7464, 0.0051, 1.2482 - bone.tail[:] = 0.7718, 0.0013, 1.2112 - bone.roll = -2.0315 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['palm.01.L']] - bones['f_index.01.L'] = bone.name - bone = arm.edit_bones.new('thumb.01.L') - bone.head[:] = 0.6705, 0.0214, 1.2738 - bone.tail[:] = 0.6857, 0.0015, 1.2404 - bone.roll = -0.1587 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['palm.01.L']] - bones['thumb.01.L'] = bone.name - bone = arm.edit_bones.new('f_middle.01.L') - bone.head[:] = 0.7518, 0.0277, 1.2487 - bone.tail[:] = 0.7762, 0.0234, 1.2058 - bone.roll = -2.0067 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['palm.02.L']] - bones['f_middle.01.L'] = bone.name - bone = arm.edit_bones.new('f_ring.01.L') - bone.head[:] = 0.7540, 0.0521, 1.2482 - bone.tail[:] = 0.7715, 0.0499, 1.2070 - bone.roll = -2.0082 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['palm.03.L']] - bones['f_ring.01.L'] = bone.name - bone = arm.edit_bones.new('f_pinky.01.L') - bone.head[:] = 0.7528, 0.0763, 1.2428 - bone.tail[:] = 0.7589, 0.0765, 1.2156 - bone.roll = -1.9749 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['palm.04.L']] - bones['f_pinky.01.L'] = bone.name - bone = arm.edit_bones.new('f_index.02.L') - bone.head[:] = 0.7718, 0.0013, 1.2112 - bone.tail[:] = 0.7840, -0.0003, 1.1858 - bone.roll = -1.8799 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_index.01.L']] - bones['f_index.02.L'] = bone.name - bone = arm.edit_bones.new('thumb.02.L') - bone.head[:] = 0.6857, 0.0015, 1.2404 - bone.tail[:] = 0.7056, -0.0057, 1.2145 - bone.roll = -0.4798 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['thumb.01.L']] - bones['thumb.02.L'] = bone.name - bone = arm.edit_bones.new('f_middle.02.L') - bone.head[:] = 0.7762, 0.0234, 1.2058 - bone.tail[:] = 0.7851, 0.0218, 1.1749 - bone.roll = -1.8283 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_middle.01.L']] - bones['f_middle.02.L'] = bone.name - bone = arm.edit_bones.new('f_ring.02.L') - bone.head[:] = 0.7715, 0.0499, 1.2070 - bone.tail[:] = 0.7794, 0.0494, 1.1762 - bone.roll = -1.8946 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_ring.01.L']] - bones['f_ring.02.L'] = bone.name - bone = arm.edit_bones.new('f_pinky.02.L') - bone.head[:] = 0.7589, 0.0765, 1.2156 - bone.tail[:] = 0.7618, 0.0770, 1.1932 - bone.roll = -1.9059 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_pinky.01.L']] - bones['f_pinky.02.L'] = bone.name - bone = arm.edit_bones.new('f_index.03.L') - bone.head[:] = 0.7840, -0.0003, 1.1858 - bone.tail[:] = 0.7892, 0.0006, 1.1636 - bone.roll = -1.6760 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_index.02.L']] - bones['f_index.03.L'] = bone.name - bone = arm.edit_bones.new('thumb.03.L') - bone.head[:] = 0.7056, -0.0057, 1.2145 - bone.tail[:] = 0.7194, -0.0098, 1.1995 - bone.roll = -0.5826 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['thumb.02.L']] - bones['thumb.03.L'] = bone.name - bone = arm.edit_bones.new('f_middle.03.L') - bone.head[:] = 0.7851, 0.0218, 1.1749 - bone.tail[:] = 0.7888, 0.0216, 1.1525 - bone.roll = -1.7483 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_middle.02.L']] - bones['f_middle.03.L'] = bone.name - bone = arm.edit_bones.new('f_ring.03.L') - bone.head[:] = 0.7794, 0.0494, 1.1762 - bone.tail[:] = 0.7781, 0.0498, 1.1577 - bone.roll = -1.6582 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_ring.02.L']] - bones['f_ring.03.L'] = bone.name - bone = arm.edit_bones.new('f_pinky.03.L') - bone.head[:] = 0.7618, 0.0770, 1.1932 - bone.tail[:] = 0.7611, 0.0772, 1.1782 - bone.roll = -1.7639 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_pinky.02.L']] - bones['f_pinky.03.L'] = bone.name bpy.ops.object.mode_set(mode='OBJECT') pbone = obj.pose.bones[bones['upper_arm.L']] - pbone.rigify_type = 'limbs.super_limb' + pbone.rigify_type = 'limbs.super_limb' if limb else 'limbs.arm' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' try: - pbone.rigify_parameters.separate_ik_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.ik_layers = [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, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.separate_hose_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.hose_layers = [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, False, False, False, False] - except AttributeError: - pass - try: pbone.rigify_parameters.tweak_layers = [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, False, False, False, False] except AttributeError: pass @@ -1031,199 +132,6 @@ def create_sample(obj): pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['palm.01.L']] - pbone.rigify_type = 'limbs.super_palm' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'YXZ' - pbone = obj.pose.bones[bones['palm.02.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'YXZ' - pbone = obj.pose.bones[bones['palm.03.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'YXZ' - pbone = obj.pose.bones[bones['palm.04.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'YXZ' - pbone = obj.pose.bones[bones['f_index.01.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.separate_extra_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.extra_layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_extra_layers = False - except AttributeError: - pass - pbone = obj.pose.bones[bones['thumb.01.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.extra_layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.separate_extra_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_extra_layers = False - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_middle.01.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.separate_extra_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.extra_layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_extra_layers = False - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_ring.01.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.separate_extra_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.extra_layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_extra_layers = False - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_pinky.01.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.separate_extra_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.extra_layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_extra_layers = False - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_index.02.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['thumb.02.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_middle.02.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_ring.02.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_pinky.02.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_index.03.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['thumb.03.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_middle.03.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_ring.03.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_pinky.03.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' bpy.ops.object.mode_set(mode='EDIT') for bone in arm.edit_bones: @@ -1237,13 +145,4 @@ def create_sample(obj): bone.select_tail = True arm.edit_bones.active = bone - for eb in arm.edit_bones: - if ('arm' in eb.name) or ('hand' in eb.name): - eb.layers = (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, False, False, False, False, False, False) - else: - eb.layers = (False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) - arm.layers = (False, False, False, False, False, True, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) - - -if __name__ == "__main__": - create_sample(bpy.context.active_object) + return bones 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 diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py new file mode 100644 index 00000000..0b6c0425 --- /dev/null +++ b/rigify/rigs/limbs/limb_rigs.py @@ -0,0 +1,1047 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + +import bpy +import json + +from ...utils.animation import add_generic_snap_fk_to_ik, add_fk_ik_snap_buttons +from ...utils.rig import connected_children_names +from ...utils.bones import BoneDict, put_bone, compute_chain_x_axis, align_bone_orientation +from ...utils.bones import align_bone_x_axis, align_bone_y_axis, align_bone_z_axis +from ...utils.naming import strip_org, make_derived_name +from ...utils.layers import ControlLayersOption +from ...utils.misc import pairwise_nozip, padnone, map_list +from ...utils.switch_parent import SwitchParentBuilder + +from ...base_rig import stage, BaseRig + +from ...utils.widgets_basic import create_circle_widget, create_sphere_widget, create_line_widget, create_limb_widget +from ..widgets import create_gear_widget, create_ikarrow_widget + +from ...rig_ui_template import UTILITIES_FUNC_COMMON_IKFK + +from math import pi +from itertools import count, repeat, chain +from mathutils import Vector +from collections import namedtuple + + +SegmentEntry = namedtuple('SegmentEntry', ['org', 'org_idx', 'seg_idx', 'pos']) + + +class BaseLimbRig(BaseRig): + """Common base for limb rigs.""" + + segmented_orgs = 2 # Number of org bones to segment + + def find_org_bones(self, bone): + return BoneDict( + main=[bone.name] + connected_children_names(self.obj, bone.name), + ) + + def initialize(self): + orgs = self.bones.org.main + + if len(orgs) < self.segmented_orgs + 1: + self.raise_error("Input to rig type must be a chain of at least 3 bones.") + + self.segments = self.params.segments + self.bbone_segments = self.params.bbones + + rot_axis = self.params.rotation_axis + + if rot_axis in {'x', 'automatic'}: + self.main_axis, self.aux_axis = 'x', 'z' + elif rot_axis == 'z': + self.main_axis, self.aux_axis = 'z', 'x' + else: + self.raise_error('Unexpected axis value: {}', rot_axis) + + self.segment_table = [ + SegmentEntry(org, i, j, self.get_segment_pos(org, j)) + for i, org in enumerate(orgs[:self.segmented_orgs]) + for j in range(self.segments) + ] + self.segment_table_end = [ + SegmentEntry(org, i + self.segmented_orgs, None, self.get_bone(org).head) + for i, org in enumerate(orgs[self.segmented_orgs:]) + ] + + self.segment_table_full = self.segment_table + self.segment_table_end + self.segment_table_tweak = self.segment_table + self.segment_table_end[0:1] + + def generate_bones(self): + bones = map_list(self.get_bone, self.bones.org.main[0:2]) + + self.elbow_vector = self.compute_elbow_vector(bones) + self.pole_angle = self.compute_pole_angle(bones, self.elbow_vector) + self.rig_parent_bone = self.get_bone_parent(self.bones.org.main[0]) + + + #################################################### + # Utilities + + def compute_elbow_vector(self, bones): + lo_vector = bones[1].vector + tot_vector = bones[1].tail - bones[0].head + return (lo_vector.project(tot_vector) - lo_vector).normalized() * tot_vector.length + + def get_main_axis(self, bone): + return getattr(bone, self.main_axis + '_axis') + + def get_aux_axis(self, bone): + return getattr(bone, self.aux_axis + '_axis') + + def compute_pole_angle(self, bones, elbow_vector): + if self.params.rotation_axis == 'z': + return 0 + + vector = self.get_aux_axis(bones[0]) + self.get_aux_axis(bones[1]) + + if elbow_vector.angle(vector) > pi/2: + return -pi/2 + else: + return pi/2 + + def get_segment_pos(self, org, seg): + bone = self.get_bone(org) + return bone.head + bone.vector * (seg / self.segments) + + def align_roll_bone(self, org, name, y_axis): + if y_axis: + align_bone_y_axis(self.obj, name, y_axis) + + if self.main_axis == 'x': + align_bone_x_axis(self.obj, name, self.get_bone(org).x_axis) + else: + align_bone_z_axis(self.obj, name, self.get_bone(org).z_axis) + + @staticmethod + def vector_without_z(vector): + return Vector((vector[0], vector[1], 0)) + + #################################################### + # BONES + # + # org: + # main[]: + # Main ORG bone chain + # ctrl: + # master: + # Main property control. + # fk[]: + # FK control chain. + # tweak[]: + # Tweak control chain. + # ik_base, ik_pole, ik + # IK controls + # ik_vispole + # IK pole visualization. + # mch: + # master: + # Parent of the master control. + # follow: + # FK follow behavior. + # fk[]: + # FK chain parents (or None) + # ik_stretch + # IK stretch switch implementation. + # ik_target + # Corrected target position. + # ik_end + # End of the IK chain: [ik_base, ik_end] + # deform[]: + # DEF bones + # + #################################################### + + #################################################### + # Master control + + @stage.generate_bones + def make_master_control(self): + org = self.bones.org.main[0] + self.bones.mch.master = name = self.copy_bone(org, make_derived_name(org, 'mch', '_parent_socket'), scale=1/12) + self.get_bone(name).roll = 0 + self.bones.ctrl.master = name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_parent'), scale=1/4) + self.get_bone(name).roll = 0 + self.prop_bone = self.bones.ctrl.master + + @stage.parent_bones + def parent_master_control(self): + self.set_bone_parent(self.bones.ctrl.master, self.bones.mch.master) + self.set_bone_parent(self.bones.mch.master, self.bones.mch.follow) + + @stage.configure_bones + def configure_master_control(self): + bone = self.get_bone(self.bones.ctrl.master) + bone.lock_location = (True, True, True) + bone.lock_rotation = (True, True, True) + bone.lock_scale = (True, True, True) + bone.lock_rotation_w = True + + @stage.rig_bones + def rig_master_control(self): + self.make_constraint(self.bones.mch.master, 'COPY_ROTATION', self.bones.org.main[0]) + + @stage.generate_widgets + def make_master_control_widget(self): + create_gear_widget(self.obj, self.bones.ctrl.master, size=10) + + + #################################################### + # FK follow MCH + + @stage.generate_bones + def make_mch_follow_bone(self): + org = self.bones.org.main[0] + self.bones.mch.follow = self.copy_bone(org, make_derived_name(org, 'mch', '_parent'), scale=1/4) + + @stage.parent_bones + def parent_mch_follow_bone(self): + mch = self.bones.mch.follow + align_bone_orientation(self.obj, mch, 'root') + self.set_bone_parent(mch, self.rig_parent_bone) + + @stage.configure_bones + def configure_mch_follow_bone(self): + ctrl = self.bones.ctrl + panel = self.script.panel_with_selected_check(self, [ctrl.master, *ctrl.fk]) + + self.make_property(self.prop_bone, 'FK_limb_follow', default=0.0) + panel.custom_prop(self.prop_bone, 'FK_limb_follow', text='FK Limb Follow', slider=True) + + @stage.rig_bones + def rig_mch_follow_bone(self): + mch = self.bones.mch.follow + + con = self.make_constraint(mch, 'COPY_ROTATION', 'root') + self.make_constraint(mch, 'COPY_SCALE', 'root') + + self.make_driver(con, 'influence', variables=[(self.prop_bone, 'FK_limb_follow')]) + + + #################################################### + # FK control chain + + @stage.generate_bones + def make_fk_control_chain(self): + self.bones.ctrl.fk = map_list(self.make_fk_control_bone, count(0), self.bones.org.main) + + def get_fk_name(self, i, org, kind): + return make_derived_name(org, kind, '_fk' if i <= 2 else '') + + def make_fk_control_bone(self, i, org): + return self.copy_bone(org, self.get_fk_name(i, org, 'ctrl')) + + @stage.parent_bones + def parent_fk_control_chain(self): + fk = self.bones.ctrl.fk + for args in zip(count(0), fk, [self.bones.mch.follow]+fk, self.bones.org.main, self.bones.mch.fk): + self.parent_fk_control_bone(*args) + + def parent_fk_control_bone(self, i, ctrl, prev, org, parent_mch): + if parent_mch: + self.set_bone_parent(ctrl, parent_mch) + else: + self.set_bone_parent(ctrl, prev, use_connect=(i > 0)) + + @stage.configure_bones + def configure_fk_control_chain(self): + for args in zip(count(0), self.bones.ctrl.fk, self.bones.org.main): + self.configure_fk_control_bone(*args) + + ControlLayersOption.FK.assign(self.params, self.obj, self.bones.ctrl.fk[0:3]) + + def configure_fk_control_bone(self, i, ctrl, org): + self.copy_bone_properties(org, ctrl) + + if i == 2: + self.get_bone(ctrl).lock_location = True, True, True + + @stage.generate_widgets + def make_fk_control_widgets(self): + for args in zip(count(0), self.bones.ctrl.fk): + self.make_fk_control_widget(*args) + + def make_fk_control_widget(self, i, ctrl): + if i < 2: + create_limb_widget(self.obj, ctrl) + elif i == 2: + create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.0) + else: + create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.5) + + + #################################################### + # FK parents MCH chain + + @stage.generate_bones + def make_fk_parent_chain(self): + self.bones.mch.fk = map_list(self.make_fk_parent_bone, count(0), self.bones.org.main) + + def make_fk_parent_bone(self, i, org): + if 2 <= i <= 3: + return self.copy_bone(org, self.get_fk_name(i, org, 'mch'), parent=True, scale=1/4) + + @stage.parent_bones + def parent_fk_parent_chain(self): + mch = self.bones.mch + orgs = self.bones.org.main + for args in zip(count(0), mch.fk, [mch.follow]+self.bones.ctrl.fk, orgs, [None]+orgs): + self.parent_fk_parent_bone(*args) + + def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org): + if i == 2: + self.set_bone_parent(parent_mch, prev_ctrl, use_connect=True) + + @stage.rig_bones + def rig_fk_parent_chain(self): + for args in zip(count(0), self.bones.mch.fk, self.bones.org.main): + self.rig_fk_parent_bone(*args) + + def rig_fk_parent_bone(self, i, parent_mch, org): + if i == 2: + self.make_constraint(parent_mch, 'COPY_SCALE', 'root') + + + #################################################### + # IK controls + + def get_extra_ik_controls(self): + return [] + + def get_all_ik_controls(self): + ctrl = self.bones.ctrl + return [ctrl.ik_base, ctrl.ik_pole, ctrl.ik] + self.get_extra_ik_controls() + + @stage.generate_bones + def make_ik_controls(self): + orgs = self.bones.org.main + + self.bones.ctrl.ik_base = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik')) + self.bones.ctrl.ik_pole = self.make_ik_pole_bone(orgs) + self.bones.ctrl.ik = self.make_ik_control_bone(orgs) + + self.build_ik_parent_switch(SwitchParentBuilder(self.generator)) + + def make_ik_pole_bone(self, orgs): + name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik_target')) + + pole = self.get_bone(name) + pole.head = self.get_bone(orgs[0]).tail + self.elbow_vector + pole.tail = pole.head - self.elbow_vector/8 + pole.roll = 0 + + return name + + def make_ik_control_bone(self, orgs): + return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik')) + + def register_switch_parents(self, pbuilder): + if self.rig_parent_bone: + pbuilder.register_parent(self, self.rig_parent_bone) + + def build_ik_parent_switch(self, pbuilder): + ctrl = self.bones.ctrl + + master = lambda: self.bones.ctrl.master + pcontrols = lambda: [ ctrl.master ] + self.get_all_ik_controls() + pole_parents = lambda: [(self.bones.mch.ik_target, ctrl.ik)] + + self.register_switch_parents(pbuilder) + + pbuilder.build_child( + self, ctrl.ik, prop_bone=master, select_parent='root', + prop_id='IK_parent', prop_name='IK Parent', controls=pcontrols, + ) + + pbuilder.build_child( + self, ctrl.ik_pole, prop_bone=master, extra_parents=pole_parents, + prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols, + no_fix_rotation=True, no_fix_scale=True, + ) + + @stage.parent_bones + def parent_ik_controls(self): + self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.follow) + + @stage.configure_bones + def configure_ik_controls(self): + base = self.get_bone(self.bones.ctrl.ik_base) + base.rotation_mode = 'ZXY' + base.lock_rotation = True, False, True + base.ik_stretch = 0.1 + + @stage.rig_bones + def rig_ik_controls(self): + self.rig_hide_pole_control(self.bones.ctrl.ik_pole) + + @stage.generate_widgets + def make_ik_control_widgets(self): + self.make_ik_base_widget(self.bones.ctrl.ik_base) + self.make_ik_pole_widget(self.bones.ctrl.ik_pole) + self.make_ik_ctrl_widget(self.bones.ctrl.ik) + + def make_ik_base_widget(self, ctrl): + if self.main_axis == 'x': + roll = 0 + else: + roll = pi/2 + + create_ikarrow_widget(self.obj, ctrl, bone_transform_name=None, roll=roll) + + def make_ik_pole_widget(self, ctrl): + create_sphere_widget(self.obj, ctrl, bone_transform_name=None) + + def make_ik_ctrl_widget(self, ctrl): + raise NotImplementedError() + + + #################################################### + # IK pole visualization + + @stage.generate_bones + def make_ik_vispole_bone(self): + orgs = self.bones.org.main + name = self.copy_bone(orgs[1], 'VIS_'+make_derived_name(orgs[0], 'ctrl', '_ik_pole')) + self.bones.ctrl.ik_vispole = name + + bone = self.get_bone(name) + bone.tail = bone.head + Vector((0, 0, bone.length / 10)) + bone.hide_select = True + + @stage.rig_bones + def rig_ik_vispole_bone(self): + name = self.bones.ctrl.ik_vispole + + self.make_constraint(name, 'COPY_LOCATION', self.bones.org.main[1]) + self.make_constraint( + name, 'STRETCH_TO', self.bones.ctrl.ik_pole, + volume='NO_VOLUME', rest_length=self.get_bone(name).length + ) + + self.rig_hide_pole_control(name) + + @stage.generate_widgets + def make_ik_vispole_widget(self): + create_line_widget(self.obj, self.bones.ctrl.ik_vispole) + + + #################################################### + # IK system MCH + + ik_input_head_tail = 0.0 + + def get_ik_input_bone(self): + return self.bones.ctrl.ik + + def get_ik_output_chain(self): + return [self.bones.ctrl.ik_base, self.bones.mch.ik_end, self.bones.mch.ik_target] + + @stage.generate_bones + def make_ik_mch_chain(self): + orgs = self.bones.org.main + self.bones.mch.ik_stretch = self.make_ik_mch_stretch_bone(orgs) + self.bones.mch.ik_target = self.make_ik_mch_target_bone(orgs) + self.bones.mch.ik_end = self.copy_bone(orgs[1], make_derived_name(orgs[1], 'mch', '_ik')) + + def make_ik_mch_stretch_bone(self, orgs): + name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik_stretch')) + self.get_bone(name).tail = self.get_bone(orgs[2]).head + return name + + def make_ik_mch_target_bone(self, orgs): + return self.copy_bone(orgs[2], make_derived_name(orgs[0], 'mch', '_ik_target')) + + @stage.parent_bones + def parent_ik_mch_chain(self): + self.set_bone_parent(self.bones.mch.ik_stretch, self.bones.mch.follow) + self.set_bone_parent(self.bones.mch.ik_target, self.get_ik_input_bone()) + self.set_bone_parent(self.bones.mch.ik_end, self.bones.ctrl.ik_base) + + @stage.configure_bones + def configure_ik_mch_chain(self): + ctrl = self.bones.ctrl + panel = self.script.panel_with_selected_check(self, ctrl.flatten()) + + rig_name = strip_org(self.bones.org.main[2]) + + self.make_property(self.prop_bone, 'IK_FK', default=0.0, description='IK/FK Switch') + panel.custom_prop(self.prop_bone, 'IK_FK', text='IK-FK ({})'.format(rig_name), slider=True) + + self.add_global_buttons(panel, rig_name) + + panel = self.script.panel_with_selected_check(self, [ctrl.master, *self.get_all_ik_controls()]) + + self.make_property(self.prop_bone, 'IK_Stretch', default=1.0, description='IK Stretch') + panel.custom_prop(self.prop_bone, 'IK_Stretch', text='IK Stretch', slider=True) + + self.make_property(self.prop_bone, 'pole_vector', default=False, description='Use a pole target control') + + self.add_ik_only_buttons(panel, rig_name) + + def add_global_buttons(self, panel, rig_name): + ctrl = self.bones.ctrl + ik_chain = self.get_ik_output_chain() + + add_generic_snap_fk_to_ik( + panel, + fk_bones=self.bones.ctrl.fk[0:len(ik_chain)], + ik_bones=ik_chain, + ik_ctrl_bones=self.get_all_ik_controls(), + rig_name=rig_name + ) + + add_limb_snap_ik_to_fk( + panel, + master=ctrl.master, + fk_bones=self.bones.ctrl.fk, ik_bones=ik_chain, + ik_ctrl_bones=[ctrl.ik_base, ctrl.ik, ctrl.ik_pole], + ik_extra_ctrls=self.get_extra_ik_controls(), + rig_name=rig_name + ) + + def add_ik_only_buttons(self, panel, rig_name): + ctrl = self.bones.ctrl + ik_chain = self.get_ik_output_chain() + + add_limb_toggle_pole( + panel, master=ctrl.master, + ik_bones=ik_chain, + ik_ctrl_bones=[ctrl.ik_base, ctrl.ik, ctrl.ik_pole], + ik_extra_ctrls=self.get_extra_ik_controls(), + ) + + @stage.rig_bones + def rig_ik_mch_chain(self): + mch = self.bones.mch + input_bone = self.get_ik_input_bone() + + self.rig_ik_mch_stretch_bone(mch.ik_stretch, input_bone) + self.rig_ik_mch_target_bone(mch.ik_target, mch.ik_stretch, input_bone) + self.rig_ik_mch_end_bone(mch.ik_end, mch.ik_target, self.bones.ctrl.ik_pole) + + def rig_ik_mch_stretch_bone(self, mch_stretch, input_bone): + self.make_constraint(mch_stretch, 'DAMPED_TRACK', input_bone, head_tail=self.ik_input_head_tail) + self.make_constraint(mch_stretch, 'STRETCH_TO', input_bone, head_tail=self.ik_input_head_tail) + + con = self.make_constraint(mch_stretch, 'LIMIT_SCALE', min_y=0.0, max_y=1.05, owner_space='LOCAL') + + self.make_driver(con, "influence", variables=[(self.prop_bone, 'IK_Stretch')], polynomial=[1.0, -1.0]) + + def rig_ik_mch_target_bone(self, mch_target, mch_stretch, input_bone): + self.make_constraint(mch_target, 'COPY_LOCATION', mch_stretch, head_tail=1.0) + + def rig_ik_mch_end_bone(self, mch_ik, mch_target, ctrl_pole): + bone = self.get_bone(mch_ik) + bone.ik_stretch = 0.1 + + bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True + setattr(bone, 'lock_ik_' + self.main_axis, False) + + con = self.make_constraint( + mch_ik, 'IK', mch_target, chain_count=2, + ) + + self.make_driver(con, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[0.0, 1.0]) + + con_pole = self.make_constraint( + mch_ik, 'IK', mch_target, chain_count=2, + pole_target=self.obj, pole_subtarget=ctrl_pole, pole_angle=self.pole_angle, + ) + + self.make_driver(con_pole, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0]) + + def rig_hide_pole_control(self, name): + self.make_driver( + self.get_bone(name).bone, "hide", + variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0], + ) + + + #################################################### + # ORG chain + + @stage.parent_bones + def parent_org_chain(self): + orgs = self.bones.org.main + if len(orgs) > 3: + self.get_bone(orgs[3]).use_connect = False + + @stage.rig_bones + def rig_org_chain(self): + ik = self.get_ik_output_chain() + for args in zip(count(0), self.bones.org.main, self.bones.ctrl.fk, padnone(ik)): + self.rig_org_bone(*args) + + def rig_org_bone(self, i, org, fk, ik): + self.make_constraint(org, 'COPY_TRANSFORMS', fk) + + if ik: + con = self.make_constraint(org, 'COPY_TRANSFORMS', ik) + + self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0]) + + + #################################################### + # Tweak control chain + + @stage.generate_bones + def make_tweak_chain(self): + self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), self.segment_table_tweak) + + def make_tweak_bone(self, i, entry): + name = make_derived_name(entry.org, 'ctrl', '_tweak') + name = self.copy_bone(entry.org, name, scale=1/(2 * self.segments)) + put_bone(self.obj, name, entry.pos) + return name + + @stage.parent_bones + def parent_tweak_chain(self): + for ctrl, mch in zip(self.bones.ctrl.tweak, self.bones.mch.tweak): + self.set_bone_parent(ctrl, mch) + + @stage.configure_bones + def configure_tweak_chain(self): + for args in zip(count(0), self.bones.ctrl.tweak, self.segment_table_tweak): + self.configure_tweak_bone(*args) + + ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.tweak) + + def configure_tweak_bone(self, i, tweak, entry): + tweak_pb = self.get_bone(tweak) + tweak_pb.lock_rotation = (True, False, True) + tweak_pb.lock_scale = (False, True, False) + tweak_pb.rotation_mode = 'ZXY' + + if i > 0 and entry.seg_idx is not None: + self.make_rubber_tweak_property(i, tweak, entry) + + def make_rubber_tweak_property(self, i, tweak, entry): + defval = 1.0 if entry.seg_idx else 0.0 + text = 'Rubber Tweak ({})'.format(strip_org(entry.org)) + + self.make_property(tweak, 'rubber_tweak', defval, max=2.0, soft_max=1.0) + + panel = self.script.panel_with_selected_check(self, [tweak]) + panel.custom_prop(tweak, 'rubber_tweak', text=text, slider=True) + + @stage.generate_widgets + def make_tweak_widgets(self): + for tweak in self.bones.ctrl.tweak: + self.make_tweak_widget(tweak) + + def make_tweak_widget(self, tweak): + create_sphere_widget(self.obj, tweak) + + + #################################################### + # Tweak MCH chain + + @stage.generate_bones + def make_tweak_mch_chain(self): + self.bones.mch.tweak = map_list(self.make_tweak_mch_bone, count(0), self.segment_table_tweak) + + def make_tweak_mch_bone(self, i, entry): + name = make_derived_name(entry.org, 'mch', '_tweak') + name = self.copy_bone(entry.org, name, scale=1/(4 * self.segments)) + put_bone(self.obj, name, entry.pos) + return name + + @stage.parent_bones + def parent_tweak_mch_chain(self): + for mch, entry in zip(self.bones.mch.tweak, self.segment_table_tweak): + self.set_bone_parent(mch, entry.org) + + @stage.rig_bones + def rig_tweak_mch_chain(self): + for args in zip(count(0), self.bones.mch.tweak, self.segment_table_tweak): + self.rig_tweak_mch_bone(*args) + + def rig_tweak_mch_bone(self, i, tweak, entry): + if entry.seg_idx: + tweaks = self.bones.ctrl.tweak + prev_tweak = tweaks[i - entry.seg_idx] + next_tweak = tweaks[i + self.segments - entry.seg_idx] + + self.make_constraint(tweak, 'COPY_TRANSFORMS', prev_tweak) + self.make_constraint( + tweak, 'COPY_TRANSFORMS', next_tweak, + influence = entry.seg_idx / self.segments + ) + self.make_constraint(tweak, 'DAMPED_TRACK', next_tweak) + + elif entry.seg_idx is not None: + self.make_constraint(tweak, 'COPY_SCALE', 'root') + + + #################################################### + # Deform chain + + @stage.generate_bones + def make_deform_chain(self): + self.bones.deform = map_list(self.make_deform_bone, count(0), self.segment_table_full) + + def make_deform_bone(self, i, entry): + name = make_derived_name(entry.org, 'def') + + if entry.seg_idx is None: + name = self.copy_bone(entry.org, name) + else: + name = self.copy_bone(entry.org, name, bbone=True, scale=1/self.segments) + put_bone(self.obj, name, entry.pos) + self.get_bone(name).bbone_segments = self.bbone_segments + + return name + + @stage.parent_bones + def parent_deform_chain(self): + self.set_bone_parent(self.bones.deform[0], self.rig_parent_bone) + self.parent_bone_chain(self.bones.deform, use_connect=True) + + @stage.rig_bones + def rig_deform_chain(self): + tweaks = pairwise_nozip(padnone(self.bones.ctrl.tweak)) + entries = pairwise_nozip(padnone(self.segment_table_full)) + + for args in zip(count(0), self.bones.deform, *entries, *tweaks): + self.rig_deform_bone(*args) + + def rig_deform_bone(self, i, deform, entry, next_entry, tweak, next_tweak): + if tweak: + self.make_constraint(deform, 'COPY_TRANSFORMS', tweak) + + if next_tweak: + self.make_constraint(deform, 'DAMPED_TRACK', next_tweak) + self.make_constraint(deform, 'STRETCH_TO', next_tweak) + + self.rig_deform_easing(i, deform, tweak, next_tweak) + + elif next_entry: + self.make_constraint(deform, 'DAMPED_TRACK', next_entry.org) + self.make_constraint(deform, 'STRETCH_TO', next_entry.org) + + else: + self.make_constraint(deform, 'COPY_TRANSFORMS', entry.org) + + def rig_deform_easing(self, i, deform, tweak, next_tweak): + pbone = self.get_bone(deform) + + if 'rubber_tweak' in self.get_bone(tweak): + self.make_driver(pbone.bone, 'bbone_easein', variables=[(tweak, 'rubber_tweak')]) + else: + pbone.bone.bbone_easein = 0.0 + + if 'rubber_tweak' in self.get_bone(next_tweak): + self.make_driver(pbone.bone, 'bbone_easeout', variables=[(next_tweak, 'rubber_tweak')]) + else: + pbone.bone.bbone_easeout = 0.0 + + + #################################################### + # Settings + + @classmethod + def add_parameters(self, params): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + + items = [ + ('x', 'X manual', ''), + ('z', 'Z manual', ''), + ('automatic', 'Automatic', '') + ] + + params.rotation_axis = bpy.props.EnumProperty( + items = items, + name = "Rotation Axis", + default = 'automatic' + ) + + params.auto_align_extremity = bpy.props.BoolProperty( + name='auto_align_extremity', + default=False, + description="Auto Align Extremity Bone" + ) + + params.segments = bpy.props.IntProperty( + name = 'limb segments', + default = 2, + min = 1, + description = 'Number of segments' + ) + + params.bbones = bpy.props.IntProperty( + name = 'bbone segments', + default = 10, + min = 1, + description = 'Number of segments' + ) + + # Setting up extra layers for the FK and tweak + ControlLayersOption.FK.add_parameters(params) + ControlLayersOption.TWEAK.add_parameters(params) + + @classmethod + def parameters_ui(self, layout, params, end='End'): + """ Create the ui for the rig parameters.""" + + r = layout.row() + r.prop(params, "rotation_axis") + + if 'auto' not in params.rotation_axis.lower(): + r = layout.row() + r.prop(params, "auto_align_extremity", text="Auto Align "+end) + + r = layout.row() + r.prop(params, "segments") + + r = layout.row() + r.prop(params, "bbones") + + ControlLayersOption.FK.parameters_ui(layout, params) + ControlLayersOption.TWEAK.parameters_ui(layout, params) + + +########################### +# Limb IK to FK operator ## +########################### + +SCRIPT_REGISTER_OP_SNAP_IK_FK = ['POSE_OT_rigify_limb_ik2fk', 'POSE_OT_rigify_limb_ik2fk_bake'] + +SCRIPT_UTILITIES_OP_SNAP_IK_FK = UTILITIES_FUNC_COMMON_IKFK + [''' +######################## +## Limb Snap IK to FK ## +######################## + +class RigifyLimbIk2FkBase: + prop_bone: StringProperty(name="Settings Bone") + pole_prop: StringProperty(name="Pole target switch", default="pole_vector") + fk_bones: StringProperty(name="FK Bone Chain") + ik_bones: StringProperty(name="IK Result Bone Chain") + ctrl_bones: StringProperty(name="IK Controls") + extra_ctrls: StringProperty(name="Extra IK Controls") + + keyflags = None + + def init_execute(self, context): + if self.fk_bones: + self.fk_bone_list = json.loads(self.fk_bones) + self.ik_bone_list = json.loads(self.ik_bones) + self.ctrl_bone_list = json.loads(self.ctrl_bones) + self.extra_ctrl_list = json.loads(self.extra_ctrls) + + def get_use_pole(self, obj): + bone = obj.pose.bones[self.prop_bone] + return self.pole_prop in bone and bone[self.pole_prop] + + def save_frame_state(self, context, obj): + return get_chain_transform_matrices(obj, self.fk_bone_list) + + def compute_base_rotation(self, context, ik_bones, ctrl_bones, matrices, use_pole): + context.view_layer.update() + + if use_pole: + match_pole_target( + context.view_layer, + ik_bones[0], ik_bones[1], ctrl_bones[2], matrices[0], + (ik_bones[0].length + ik_bones[1].length) + ) + + else: + correct_rotation(context.view_layer, ctrl_bones[0], matrices[0]) + + def apply_frame_state(self, context, obj, matrices): + ik_bones = [ obj.pose.bones[k] for k in self.ik_bone_list ] + ctrl_bones = [ obj.pose.bones[k] for k in self.ctrl_bone_list ] + + use_pole = len(ctrl_bones) > 2 and self.get_use_pole(obj) + + # Set the end control position + tgt_matrix = ik_bones[2].bone.matrix_local + ctrl_matrix = ctrl_bones[1].bone.matrix_local + endmat = matrices[2] @ tgt_matrix.inverted() @ ctrl_matrix + + set_transform_from_matrix( + obj, self.ctrl_bone_list[1], endmat, keyflags=self.keyflags + ) + + # Remove foot heel transform, if present + for extra in self.extra_ctrl_list: + set_transform_from_matrix( + obj, extra, Matrix.Identity(4), space='LOCAL', keyflags=self.keyflags + ) + + # Set the base bone position + ctrl_bones[0].matrix_basis = Matrix.Identity(4) + + set_transform_from_matrix( + obj, self.ctrl_bone_list[0], matrices[0], + no_scale=True, no_rot=use_pole, + ) + + self.compute_base_rotation(context, ik_bones, ctrl_bones, matrices, use_pole) + + correct_scale(context.view_layer, ctrl_bones[0], matrices[0]) + + # Keyframe controls + if self.keyflags is not None: + if use_pole: + keyframe_transform_properties( + obj, self.ctrl_bone_list[2], self.keyflags, + no_rot=True, no_scale=True, + ) + + keyframe_transform_properties( + obj, self.ctrl_bone_list[0], self.keyflags, + no_rot=use_pole, + ) + +class POSE_OT_rigify_limb_ik2fk(RigifyLimbIk2FkBase, RigifySingleUpdateMixin, bpy.types.Operator): + bl_idname = "pose.rigify_limb_ik2fk_" + rig_id + bl_label = "Snap IK->FK" + bl_options = {'UNDO', 'INTERNAL'} + bl_description = "Snap the IK chain to FK result" + +class POSE_OT_rigify_limb_ik2fk_bake(RigifyLimbIk2FkBase, RigifyBakeKeyframesMixin, bpy.types.Operator): + bl_idname = "pose.rigify_limb_ik2fk_bake_" + rig_id + bl_label = "Apply Snap IK->FK To Keyframes" + bl_options = {'UNDO', 'INTERNAL'} + bl_description = "Snap the IK chain keyframes to FK result" + + def execute_scan_curves(self, context, obj): + self.bake_add_bone_frames(self.fk_bone_list, TRANSFORM_PROPS_ALL) + return self.bake_get_all_bone_curves(self.ctrl_bone_list + self.extra_ctrl_list, TRANSFORM_PROPS_ALL) +'''] + +def add_limb_snap_ik_to_fk(panel, *, master=None, fk_bones=[], ik_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[], rig_name=''): + panel.use_bake_settings() + panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_IK_FK) + panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_IK_FK) + + op_props = { + 'prop_bone': master, + 'fk_bones': json.dumps(fk_bones), + 'ik_bones': json.dumps(ik_bones), + 'ctrl_bones': json.dumps(ik_ctrl_bones), + 'extra_ctrls': json.dumps(ik_extra_ctrls), + } + + add_fk_ik_snap_buttons( + panel, 'pose.rigify_limb_ik2fk_{rig_id}', 'pose.rigify_limb_ik2fk_bake_{rig_id}', + label='IK->FK', rig_name=rig_name, properties=op_props, + clear_bones=ik_ctrl_bones + ik_extra_ctrls, + ) + +######################### +# Toggle Pole operator ## +######################### + +SCRIPT_REGISTER_OP_TOGGLE_POLE = ['POSE_OT_rigify_limb_toggle_pole', 'POSE_OT_rigify_limb_toggle_pole_bake'] + +SCRIPT_UTILITIES_OP_TOGGLE_POLE = SCRIPT_UTILITIES_OP_SNAP_IK_FK + [''' +#################### +## Toggle IK Pole ## +#################### + +class RigifyLimbTogglePoleBase(RigifyLimbIk2FkBase): + use_pole: bpy.props.BoolProperty(name="Use Pole Vector") + + keyflags_switch = None + + def save_frame_state(self, context, obj): + return get_chain_transform_matrices(obj, self.ik_bone_list) + + def apply_frame_state(self, context, obj, matrices): + ik_bones = [ obj.pose.bones[k] for k in self.ik_bone_list ] + ctrl_bones = [ obj.pose.bones[k] for k in self.ctrl_bone_list ] + + # Set the pole property + set_custom_property_value( + obj, self.prop_bone, self.pole_prop, int(self.use_pole), + keyflags=self.keyflags_switch + ) + + # Reset the base bone rotation + set_pose_rotation(ctrl_bones[0], Matrix.Identity(4)) + + self.compute_base_rotation(context, ik_bones, ctrl_bones, matrices, self.use_pole) + + # Keyframe controls + if self.keyflags is not None: + if self.use_pole: + keyframe_transform_properties( + obj, self.ctrl_bone_list[2], self.keyflags, + no_rot=True, no_scale=True, + ) + else: + keyframe_transform_properties( + obj, self.ctrl_bone_list[0], self.keyflags, + no_loc=True, no_scale=True, + ) + + def init_invoke(self, context): + self.use_pole = not bool(context.active_object.pose.bones[self.prop_bone][self.pole_prop]) + +class POSE_OT_rigify_limb_toggle_pole(RigifyLimbTogglePoleBase, RigifySingleUpdateMixin, bpy.types.Operator): + bl_idname = "pose.rigify_limb_toggle_pole_" + rig_id + bl_label = "Toggle Pole" + bl_options = {'UNDO', 'INTERNAL'} + bl_description = "Switch the IK chain between pole and rotation" + +class POSE_OT_rigify_limb_toggle_pole_bake(RigifyLimbTogglePoleBase, RigifyBakeKeyframesMixin, bpy.types.Operator): + bl_idname = "pose.rigify_limb_toggle_pole_bake_" + rig_id + bl_label = "Apply Toggle Pole To Keyframes" + bl_options = {'UNDO', 'INTERNAL'} + bl_description = "Switch the IK chain between pole and rotation over a frame range" + + def execute_scan_curves(self, context, obj): + self.bake_add_bone_frames(self.ctrl_bone_list, TRANSFORM_PROPS_ALL) + + rot_curves = self.bake_get_all_bone_curves(self.ctrl_bone_list[0], TRANSFORM_PROPS_ROTATION) + pole_curves = self.bake_get_all_bone_curves(self.ctrl_bone_list[2], TRANSFORM_PROPS_LOCATION) + return rot_curves + pole_curves + + def execute_before_apply(self, context, obj, range, range_raw): + self.bake_replace_custom_prop_keys_constant(self.prop_bone, self.pole_prop, int(self.use_pole)) + + def draw(self, context): + self.layout.prop(self, 'use_pole') +'''] + +def add_limb_toggle_pole(panel, *, master=None, ik_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[]): + panel.use_bake_settings() + panel.script.add_utilities(SCRIPT_UTILITIES_OP_TOGGLE_POLE) + panel.script.register_classes(SCRIPT_REGISTER_OP_TOGGLE_POLE) + + op_props = { + 'prop_bone': master, + 'ik_bones': json.dumps(ik_bones), + 'ctrl_bones': json.dumps(ik_ctrl_bones), + 'extra_ctrls': json.dumps(ik_extra_ctrls), + } + + row = panel.row(align=True) + lsplit = row.split(factor=0.75, align=True) + lsplit.operator('pose.rigify_limb_toggle_pole_{rig_id}', icon='FORCE_MAGNETIC', properties=op_props) + lsplit.custom_prop(master, 'pole_vector', text='') + row.operator('pose.rigify_limb_toggle_pole_bake_{rig_id}', text='', icon='ACTION_TWEAK', properties=op_props) diff --git a/rigify/rigs/limbs/paw.py b/rigify/rigs/limbs/paw.py index b57f07dd..fa590b66 100644 --- a/rigify/rigs/limbs/paw.py +++ b/rigify/rigs/limbs/paw.py @@ -1,1044 +1,202 @@ -import bpy -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_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, create_gear_widget -from ..widgets import create_foot_widget, create_ballsocket_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 paw rig and key rig properties """ - self.obj = obj - self.params = params - - self.org_bones = list( - [bone_name] + connected_children_names(obj, bone_name) - )[:4] # The basic limb is the first 4 bones for a paw - - 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 +#====================== 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> - thigh = self.org_bones[0] - org_bones = list( - [thigh] + connected_children_names(self.obj, thigh) - ) # All the provided orgs - - org_thigh = eb[org_bones[0]] - org_shin = eb[org_bones[1]] - org_foot = eb[org_bones[2]] - org_toe = eb[org_bones[3]] - - 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]) +import bpy +import math - return +from itertools import count +from mathutils import Vector - # 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) +from ...utils.bones import compute_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 - align_bone_x_axis(self.obj, org_thigh.name, chain_rot_axis) - align_bone_x_axis(self.obj, org_shin.name, chain_rot_axis) +from ...utils.widgets_basic import create_circle_widget +from ..widgets import create_foot_widget, create_ballsocket_widget - # # Orient foot and toe - align_bone_x_axis(self.obj, org_foot.name, chain_rot_axis) - align_bone_x_axis(self.obj, org_toe.name, chain_rot_axis) +from ...base_rig import stage - def create_parent(self): +from .limb_rigs import BaseLimbRig - org_bones = self.org_bones - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones +class Rig(BaseLimbRig): + """Paw rig.""" - name = get_bone_name( strip_org( org_bones[0] ), 'mch', 'parent' ) + segmented_orgs = 3 - mch = copy_bone( self.obj, org_bones[0], name ) - orient_bone( self, eb[mch], 'y' ) - eb[ mch ].length = eb[ org_bones[0] ].length / 4 + def initialize(self): + if len(self.bones.org.main) != 4: + self.raise_error("Input to rig type must be a chain of 4 bones.") - eb[ mch ].parent = eb[ org_bones[0] ].parent + super().initialize() - eb[ mch ].roll = 0.0 + def prepare_bones(self): + orgs = self.bones.org.main - # 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 + foot_x = self.vector_without_z(self.get_bone(orgs[2]).y_axis).cross((0, 0, 1)) - # Constraints - make_constraint( self, mch, { - 'constraint' : 'COPY_ROTATION', - 'subtarget' : 'root' - }) + if self.params.rotation_axis == 'automatic': + axis = compute_chain_x_axis(self.obj, orgs[0:2]) - make_constraint( self, mch, { - 'constraint' : 'COPY_SCALE', - 'subtarget' : 'root' - }) + for bone in orgs: + align_bone_x_axis(self.obj, bone, axis) - # 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'] ) / 3 ) - middle1 = middle + self.segments - last = len( tweaks['mch'] ) - 1 - - if i == first or i == middle or i == middle1: - make_constraint( self, b, { - 'constraint' : 'COPY_SCALE', - 'subtarget' : 'root' - }) - elif i != last: - targets = [] - factor = 0 - if i < middle: - dt_target_idx = middle - targets = [first,middle] - elif i > middle and i < middle1: - targets = [middle,middle1] - factor = self.segments - dt_target_idx = middle1 - else: - targets = [middle1,last] - factor = self.segments * 2 - 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 not (i+1) % self.segments: - defvalue = 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: - defvalue = 1.0 - - make_property(pb[t], name, defvalue, 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 + align_bone_z_axis(self.obj, orgs[2], foot_x) + align_bone_z_axis(self.obj, orgs[3], -foot_x) - def create_ik(self, parent): - org_bones = self.org_bones - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + #################################################### + # EXTRA BONES + # + # ctrl: + # heel: + # Foot heel control + # mch: + # toe_socket: + # IK toe orientation bone. + # + #################################################### - 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') + #################################################### + # IK controls - for o, ik in zip(org_bones, [ctrl, mch_ik, mch_target]): - bone = copy_bone(self.obj, o, ik) + def get_extra_ik_controls(self): + return [self.bones.ctrl.heel] - if org_bones.index(o) == len(org_bones) - 1: - eb[bone].length /= 4 + def make_ik_control_bone(self, orgs): + name = self.copy_bone(orgs[3], make_derived_name(orgs[2], 'ctrl', '_ik')) - # Create MCH Stretch - mch_str = copy_bone( - self.obj, - org_bones[0], - get_bone_name( org_bones[0], 'mch', 'ik_stretch' ) - ) + if self.params.rotation_axis == 'automatic' or self.params.auto_align_extremity: + align_bone_to_axis(self.obj, name, 'y', flip=True) - eb[ mch_str ].tail = eb[ org_bones[-2] ].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: - 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 + flip_bone(self.obj, name) - # 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) + bone = self.get_bone(name) + bone.tail[2] = bone.head[2] + bone.roll = 0 - return {'ctrl': {'limb': ctrl, 'ik_target': pole_target}, - 'mch_ik': mch_ik, - 'mch_target': mch_target, - 'mch_str': mch_str, - 'visuals': {'vispole': vispole} - } + vec = self.get_bone(orgs[3]).tail - self.get_bone(orgs[2]).head + self.get_bone(name).length = self.vector_without_z(vec).length - def create_fk(self, parent): - org_bones = self.org_bones.copy() + return name + def register_switch_parents(self, pbuilder): + super().register_switch_parents(pbuilder) - org_bones.pop() - - 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_paw(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 - - pole_target = get_bone_name(org_bones[0], 'ctrl', 'ik_target') - - # Create IK paw control - ctrl = get_bone_name(org_bones[2], 'ctrl', 'ik') - ctrl = copy_bone(self.obj, org_bones[3], 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: - paw_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: - paw_parent = None + pbuilder.register_parent(self, self.bones.org.main[3], exclude_self=True) - 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) - - 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) - - # clear parent - eb[ heel ].parent = None - eb[ heel ].use_connect = False - - # Parent - eb[ heel ].parent = eb[ ctrl ] - eb[ heel ].use_connect = False - - flip_bone( self.obj, heel ) - - eb[ bones['ik']['mch_target'] ].parent = eb[ heel ] - eb[ bones['ik']['mch_target'] ].use_connect = False - - # Reset control position and orientation - if self.rot_axis == 'automatic' or self.auto_align_extremity: - orient_bone(self, eb[ctrl], 'y', reverse=True) - else: - flip_bone(self.obj, ctrl) - eb[ctrl].tail[2] = eb[ctrl].head[2] - eb[ctrl].roll = 0 - v = eb[org_bones[-1]].tail - eb[org_bones[-2]].head - eb[ctrl].length = Vector((v[0], v[1], 0)).length - - # make mch toe bone - toes_mch = get_bone_name(org_bones[3], 'mch') - toes_mch = copy_bone(self.obj, org_bones[3], toes_mch) - - eb[toes_mch].use_connect = False - eb[toes_mch].parent = eb[ctrl] - - eb[toes_mch].length /= 4 - - # make mch toe parent bone - toes_mch_parent = get_bone_name(org_bones[3], 'mch', 'parent') - toes_mch_parent = copy_bone(self.obj, org_bones[3], toes_mch_parent) - - eb[toes_mch_parent].use_connect = True - eb[toes_mch_parent].parent = eb[org_bones[2]] - - eb[toes_mch_parent].length /= 2 - - eb[org_bones[3]].use_connect = False - eb[org_bones[3]].parent = eb[toes_mch_parent] - - # 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 paw_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' : heel, - 'head_tail' : 1.0 - }) - make_constraint( self, bones['ik']['mch_str'], { - 'constraint' : 'STRETCH_TO', - 'subtarget' : heel, - '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] - }) - - pb = self.obj.pose.bones - - # 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 paw widget + def make_ik_ctrl_widget(self, ctrl): create_foot_widget(self.obj, ctrl, bone_transform_name=None) - # Create heel ctrl locks - pb[heel].lock_location = True, True, True - 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[toes_mch_parent] - # Constrain 4th ORG to toes MCH bone - make_constraint( self, toes_mch_parent, { - 'constraint' : 'COPY_TRANSFORMS', - 'subtarget' : toes_mch - }) + #################################################### + # Heel control - # Constrain 4th ORG to toes MCH bone - make_constraint(self, org_bones[3], { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': toes - }) + @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')) + self.bones.ctrl.heel = name - make_constraint( self, bones['def'][-1], { - 'constraint' : 'DAMPED_TRACK', - 'subtarget' : toes, - 'head_tail' : 1 - }) - make_constraint( self, bones['def'][-1], { - 'constraint' : 'STRETCH_TO', - 'subtarget' : toes, - 'head_tail' : 1 - }) + flip_bone(self.obj, name) - # Find IK/FK switch property - pb = self.obj.pose.bones - prop = rna_idprop_ui_prop_get( pb[bones['fk']['ctrl'][-1]], 'IK_FK' ) + @stage.parent_bones + def parent_heel_control_bone(self): + self.set_bone_parent(self.bones.ctrl.heel, self.bones.ctrl.ik) - # Modify rotation mode for ik and tweak controls - pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' + @stage.configure_bones + def configure_heel_control_bone(self): + bone = self.get_bone(self.bones.ctrl.heel) + bone.lock_location = True, True, True + bone.lock_scale = True, True, True - for b in bones['tweak']['ctrl']: - pb[b].rotation_mode = 'ZXY' + @stage.generate_widgets + def generate_heel_control_widget(self): + create_ballsocket_widget(self.obj, self.bones.ctrl.heel, bone_transform_name=None) - # Add driver to limit scale constraint influence - b = toes_mch_parent - make_driver(pb[b].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)], polynomial=[1.0, -1.0]) + #################################################### + # FK parents MCH chain - # Create toe circle widget - create_circle_widget(self.obj, toes, radius=0.4, head_tail=0.5) + @stage.generate_bones + def make_toe_socket_bone(self): + org = self.bones.org.main[3] + self.bones.mch.toe_socket = self.copy_bone(org, make_derived_name(org, 'mch', '_ik_socket')) - bones['ik']['ctrl']['terminal'] += [toes] + def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org): + if i == 3: + self.set_bone_parent(parent_mch, prev_org, use_connect=True) + self.set_bone_parent(self.bones.mch.toe_socket, self.bones.ctrl.ik) - bones['ik']['ctrl']['terminal'] += [ heel, ctrl ] - - if paw_parent: - bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root, ctrl_parent] else: - bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root] - - 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]] - ctrl_pole = pb[bones['ik']['mch_foot'][1]] - - #owner = pb[bones['ik']['ctrl']['limb']] - 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: - make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) - - 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]) - - make_driver(ctrl_pole.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) - - if len(ctrl_pole.constraints) > 1: - make_driver(ctrl_pole.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0]) - - elif prop == 'root/parent': - if len(ctrl.constraints) > 1: - make_property(owner, prop, 0.0) - - make_driver(ctrl.constraints[1], "influence", variables=[(self.obj, owner, prop)]) - - 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)]) - - @staticmethod - def get_future_names(bones): + super().parent_fk_parent_bone(i, parent_mch, prev_ctrl, org, prev_org) - if len(bones) != 4: - return + def rig_fk_parent_bone(self, i, parent_mch, org): + if i == 3: + con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.toe_socket) - names = dict() + self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0]) - 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'] = 'paw' - - 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_paw(bones) - self.create_drivers(bones) - - controls = [bones['ik']['ctrl']['limb'], bones['ik']['ctrl']['terminal'][-1], bones['ik']['ctrl']['terminal'][-2]] + else: + super().rig_fk_parent_bone(i, parent_mch, org) - controls.append(bones['main_parent']) - # Create UI - controls_string = ", ".join(["'" + x + "'" for x in controls]) + #################################################### + # IK system MCH - script = create_script(bones, 'paw') - script += extra_script % (controls_string, bones['main_parent'], 'IK_follow', - 'pole_follow', 'pole_follow', 'root/parent', 'root/parent') + ik_input_head_tail = 1.0 - 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' - ) + def get_ik_input_bone(self): + return self.bones.ctrl.heel - params.bbones = bpy.props.IntProperty( - name = 'bbone segments', - default = 10, - min = 1, - description = 'Number of segments' - ) + @stage.parent_bones + def parent_ik_mch_chain(self): + super().parent_ik_mch_chain() - # Setting up extra layers for the FK and tweak - ControlLayersOption.FK.add_parameters(params) - ControlLayersOption.TWEAK.add_parameters(params) + self.set_bone_parent(self.bones.mch.ik_target, self.bones.ctrl.heel) -def parameters_ui(layout, params): - """ Create the ui for the rig parameters.""" + #################################################### + # Deform chain - r = layout.row() - r.prop(params, "rotation_axis") + def rig_deform_bone(self, i, deform, entry, next_entry, tweak, next_tweak): + super().rig_deform_bone(i, deform, entry, next_entry, tweak, next_tweak) - if 'auto' not in params.rotation_axis.lower(): - r = layout.row() - text = "Auto align Claw" - r.prop(params, "auto_align_extremity", text=text) + if tweak and not (next_tweak or next_entry): + self.make_constraint(deform, 'DAMPED_TRACK', entry.org, head_tail=1.0) + self.make_constraint(deform, 'STRETCH_TO', entry.org, head_tail=1.0) - 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, 'Claw') def create_sample(obj): @@ -1048,275 +206,79 @@ def create_sample(obj): bones = {} - bone = arm.edit_bones.new('upper_arm.L') - bone.head[:] = 0.0313, -0.1149, 0.2257 - bone.tail[:] = 0.0313, -0.0878, 0.1235 - bone.roll = 3.1416 - bone.use_connect = False - bones['upper_arm.L'] = bone.name - bone = arm.edit_bones.new('forearm.L') - bone.head[:] = 0.0313, -0.0878, 0.1235 - bone.tail[:] = 0.0313, -0.1117, 0.0254 - bone.roll = 3.1416 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['upper_arm.L']] - bones['forearm.L'] = bone.name - bone = arm.edit_bones.new('hand.L') - bone.head[:] = 0.0313, -0.1117, 0.0254 - bone.tail[:] = 0.0313, -0.1297, 0.0094 - bone.roll = 3.1416 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['forearm.L']] - bones['hand.L'] = bone.name - bone = arm.edit_bones.new('f_toe.L') - bone.head[:] = 0.0313, -0.1297, 0.0094 - bone.tail[:] = 0.0313, -0.1463, 0.0094 + bone = arm.edit_bones.new('thigh.L') + bone.head[:] = 0.0000, 0.0017, 0.7379 + bone.tail[:] = 0.0000, -0.0690, 0.4731 bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['hand.L']] - bones['f_toe.L'] = bone.name - bone = arm.edit_bones.new('f_palm.004.L') - bone.head[:] = 0.0393, -0.1278, 0.0100 - bone.tail[:] = 0.0406, -0.1304, 0.0100 - bone.roll = -0.0006 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_toe.L']] - bones['f_palm.004.L'] = bone.name - bone = arm.edit_bones.new('f_palm.001.L') - bone.head[:] = 0.0216, -0.1278, 0.0100 - bone.tail[:] = 0.0199, -0.1331, 0.0100 - bone.roll = 0.0004 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_toe.L']] - bones['f_palm.001.L'] = bone.name - bone = arm.edit_bones.new('f_palm.002.L') - bone.head[:] = 0.0273, -0.1278, 0.0100 - bone.tail[:] = 0.0273, -0.1345, 0.0100 - bone.roll = 3.1416 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_toe.L']] - bones['f_palm.002.L'] = bone.name - bone = arm.edit_bones.new('f_palm.003.L') - bone.head[:] = 0.0341, -0.1278, 0.0100 - bone.tail[:] = 0.0340, -0.1345, 0.0100 - bone.roll = 0.0101 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_toe.L']] - bones['f_palm.003.L'] = bone.name - bone = arm.edit_bones.new('f_pinky.001.L') - bone.head[:] = 0.0406, -0.1304, 0.0074 - bone.tail[:] = 0.0408, -0.1337, 0.0065 - bone.roll = -0.6234 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_palm.004.L']] - bones['f_pinky.001.L'] = bone.name - bone = arm.edit_bones.new('f_index.001.L') - bone.head[:] = 0.0199, -0.1331, 0.0077 - bone.tail[:] = 0.0193, -0.1372, 0.0060 - bone.roll = 0.7154 bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_palm.001.L']] - bones['f_index.001.L'] = bone.name - bone = arm.edit_bones.new('f_middle.001.L') - bone.head[:] = 0.0273, -0.1345, 0.0107 - bone.tail[:] = 0.0273, -0.1407, 0.0082 + bones['thigh.L'] = bone.name + bone = arm.edit_bones.new('shin.L') + bone.head[:] = 0.0000, -0.0690, 0.4731 + bone.tail[:] = 0.0000, 0.1364, 0.2473 bone.roll = 0.0000 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_palm.002.L']] - bones['f_middle.001.L'] = bone.name - bone = arm.edit_bones.new('f_ring.001.L') - bone.head[:] = 0.0340, -0.1345, 0.0107 - bone.tail[:] = 0.0340, -0.1407, 0.0082 - bone.roll = 0.0000 - bone.use_connect = False - bone.parent = arm.edit_bones[bones['f_palm.003.L']] - bones['f_ring.001.L'] = bone.name - bone = arm.edit_bones.new('f_pinky.002.L') - bone.head[:] = 0.0408, -0.1337, 0.0065 - bone.tail[:] = 0.0413, -0.1400, 0.0023 - bone.roll = -0.2560 bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_pinky.001.L']] - bones['f_pinky.002.L'] = bone.name - bone = arm.edit_bones.new('f_index.002.L') - bone.head[:] = 0.0193, -0.1372, 0.0060 - bone.tail[:] = 0.0186, -0.1427, 0.0028 - bone.roll = 0.5229 + bone.parent = arm.edit_bones[bones['thigh.L']] + bones['shin.L'] = bone.name + bone = arm.edit_bones.new('foot.L') + bone.head[:] = 0.0000, 0.1364, 0.2473 + bone.tail[:] = 0.0000, 0.0736, 0.0411 + bone.roll = -0.0002 bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_index.001.L']] - bones['f_index.002.L'] = bone.name - bone = arm.edit_bones.new('f_middle.002.L') - bone.head[:] = 0.0273, -0.1407, 0.0082 - bone.tail[:] = 0.0273, -0.1496, 0.0030 - bone.roll = 0.0000 + bone.parent = arm.edit_bones[bones['shin.L']] + bones['foot.L'] = bone.name + bone = arm.edit_bones.new('toe.L') + bone.head[:] = 0.0000, 0.0736, 0.0411 + bone.tail[:] = 0.0000, -0.0594, 0.0000 + bone.roll = -3.1416 bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_middle.001.L']] - bones['f_middle.002.L'] = bone.name - bone = arm.edit_bones.new('f_ring.002.L') - bone.head[:] = 0.0340, -0.1407, 0.0082 - bone.tail[:] = 0.0340, -0.1491, 0.0033 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['f_ring.001.L']] - bones['f_ring.002.L'] = bone.name + bone.parent = arm.edit_bones[bones['foot.L']] + bones['toe.L'] = bone.name bpy.ops.object.mode_set(mode='OBJECT') - pbone = obj.pose.bones[bones['upper_arm.L']] - pbone.rigify_type = 'limbs.super_limb' + pbone = obj.pose.bones[bones['thigh.L']] + pbone.rigify_type = 'limbs.paw' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' try: - pbone.rigify_parameters.separate_ik_layers = True + pbone.rigify_parameters.fk_layers = [False, False, False, False, 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] except AttributeError: pass try: - pbone.rigify_parameters.ik_layers = [False, 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] - except AttributeError: - pass - try: - pbone.rigify_parameters.separate_hose_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.hose_layers = [False, False, 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] + pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, 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] except AttributeError: pass try: pbone.rigify_parameters.limb_type = "paw" except AttributeError: pass - try: - pbone.rigify_parameters.fk_layers = [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, False, False, False, False, False] - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_layers = [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, False, False, False, False] - except AttributeError: - pass - pbone = obj.pose.bones[bones['forearm.L']] + pbone = obj.pose.bones[bones['shin.L']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['hand.L']] + pbone = obj.pose.bones[bones['foot.L']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_toe.L']] + pbone = obj.pose.bones[bones['toe.L']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_palm.004.L']] - pbone.rigify_type = 'limbs.super_palm' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_palm.001.L']] - pbone.rigify_type = 'limbs.super_palm' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_palm.002.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_palm.003.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_pinky.001.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.tweak_layers = [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, False, False, False, False, False, False, False] - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_index.001.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' try: - pbone.rigify_parameters.tweak_layers = [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, False, False, False, False, False, False, False] - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_middle.001.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.tweak_layers = [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, False, False, False, False, False, False, False] - except AttributeError: - pass - pbone = obj.pose.bones[bones['f_ring.001.L']] - pbone.rigify_type = 'limbs.simple_tentacle' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.tweak_layers = [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, False, False, False, False, False, False, False] + pbone.rigify_parameters.limb_type = "paw" except AttributeError: pass - pbone = obj.pose.bones[bones['f_pinky.002.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_index.002.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_middle.002.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['f_ring.002.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' bpy.ops.object.mode_set(mode='EDIT') for bone in arm.edit_bones: @@ -1329,14 +291,3 @@ def create_sample(obj): bone.select_head = True bone.select_tail = True arm.edit_bones.active = bone - - for eb in arm.edit_bones: - if ('arm' in eb.name) or ('hand' in eb.name): - eb.layers = (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, False, False, False, False, False, False) - else: - eb.layers = (False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) - arm.layers = (False, False, False, False, False, True, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) - - -if __name__ == "__main__": - create_sample(bpy.context.active_object) diff --git a/rigify/rigs/limbs/rear_paw.py b/rigify/rigs/limbs/rear_paw.py index 74974bb6..2d6894e1 100644 --- a/rigify/rigs/limbs/rear_paw.py +++ b/rigify/rigs/limbs/rear_paw.py @@ -1,17 +1,14 @@ import bpy from .paw import Rig as pawRig -from .paw import parameters_ui -from .paw import add_parameters + 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(pawRig): - - def __init__(self, obj, bone_name, params): - super(Rig, self).__init__(obj, bone_name, params) + pass def create_sample(obj): @@ -135,7 +132,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.paw' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False diff --git a/rigify/rigs/limbs/simple_tentacle.py b/rigify/rigs/limbs/simple_tentacle.py index 82fcafe8..ea5a2224 100644 --- a/rigify/rigs/limbs/simple_tentacle.py +++ b/rigify/rigs/limbs/simple_tentacle.py @@ -1,267 +1,118 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + import bpy -from ...utils import copy_bone -from ...utils import strip_org, make_deformer_name, connected_children_names -from ...utils import put_bone, create_sphere_widget -from ...utils import create_circle_widget, align_bone_x_axis -from ...utils import MetarigError -from ...utils import ControlLayersOption +from itertools import count -class Rig: +from ...utils.bones import align_chain_x_axis +from ...utils.widgets_basic import create_circle_widget +from ...utils.layers import ControlLayersOption +from ...utils.misc import map_list - def __init__(self, obj, bone_name, params): - self.obj = obj - self.org_bones = [bone_name] + connected_children_names(obj, bone_name) - self.params = params +from ...base_rig import stage - self.copy_rotation_axes = params.copy_rotation_axes +from ..chain_rigs import TweakChainRig - if len(self.org_bones) <= 1: - raise MetarigError( - "RIGIFY ERROR: invalid rig structure on bone: %s" % (strip_org(bone_name)) - ) - def orient_org_bones(self): +class Rig(TweakChainRig): + def initialize(self): + super().initialize() - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + self.copy_rotation_axes = self.params.copy_rotation_axes + # Prepare + def prepare_bones(self): if self.params.roll_alignment == "automatic": - - first_bone = eb[self.org_bones[0]] - last_bone = eb[self.org_bones[-1]] - - # Orient uarm farm bones - chain_y_axis = last_bone.tail - first_bone.head - chain_rot_axis = first_bone.y_axis.cross(chain_y_axis) # ik-plane normal axis (rotation) - if chain_rot_axis.length < first_bone.length/100: - chain_rot_axis = first_bone.x_axis.normalized() - else: - chain_rot_axis = chain_rot_axis.normalized() - - for bone in self.org_bones: - align_bone_x_axis(self.obj, bone, chain_rot_axis) - - def make_controls(self): - - bpy.ops.object.mode_set(mode='EDIT') - org_bones = self.org_bones - - ctrl_chain = [] - for i in range(len(org_bones)): - name = org_bones[i] - - ctrl_bone = copy_bone( - self.obj, - name, - strip_org(name) - ) - - ctrl_chain.append(ctrl_bone) - - # Make widgets - bpy.ops.object.mode_set(mode='OBJECT') - - for ctrl in ctrl_chain: - create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) - - return ctrl_chain - - def make_tweaks(self): - - bpy.ops.object.mode_set(mode ='EDIT') - eb = self.obj.data.edit_bones - org_bones = self.org_bones - - tweak_chain = [] - for i in range(len(org_bones) + 1): - if i == len(org_bones): - # Make final tweak at the tip of the tentacle - name = org_bones[i-1] - else: - name = org_bones[i] - - tweak_bone = copy_bone( - self.obj, - name, - "tweak_" + strip_org(name) + align_chain_x_axis(self.obj, self.bones.org) + + # Parent + @stage.parent_bones + def parent_control_chain(self): + # use_connect=False for backward compatibility + self.parent_bone_chain(self.bones.ctrl.fk, use_connect=False) + + # Configure + @stage.configure_bones + def configure_tweak_chain(self): + super().configure_tweak_chain() + + ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.tweak) + + def configure_tweak_bone(self, i, tweak): + super().configure_tweak_bone(i, tweak) + + # Backward compatibility + self.get_bone(tweak).rotation_mode = 'QUATERNION' + + # Rig + @stage.rig_bones + def rig_control_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(count(0), ctrls, [None] + ctrls): + self.rig_control_bone(*args) + + def rig_control_bone(self, i, ctrl, prev_ctrl): + if prev_ctrl: + self.make_constraint( + ctrl, 'COPY_ROTATION', prev_ctrl, + use_xyz=self.copy_rotation_axes, + space='LOCAL', use_offset=True ) - tweak_e = eb[tweak_bone] - - tweak_e.length /= 2 # Set size to half - - if i == len( org_bones ): - # Position final tweak at the tip - put_bone(self.obj, tweak_bone, eb[org_bones[-1]].tail) - - tweak_chain.append(tweak_bone) - - # Make widgets - bpy.ops.object.mode_set(mode='OBJECT') + # Widgets + def make_control_widget(self, ctrl): + create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) - for tweak in tweak_chain: - create_sphere_widget(self.obj, tweak) - tweak_pb = self.obj.pose.bones[tweak] - - # Set locks - if tweak_chain.index(tweak) != len(tweak_chain) - 1: - tweak_pb.lock_rotation = (True, False, True) - tweak_pb.lock_scale = (False, True, False) - else: - tweak_pb.lock_rotation_w = True - tweak_pb.lock_rotation = (True, True, True) - tweak_pb.lock_scale = (True, True, True) - - ControlLayersOption.TWEAK.assign(self.params, self.obj.pose.bones, tweak_chain) - - return tweak_chain - - def make_deform(self): - - bpy.ops.object.mode_set(mode='EDIT') - org_bones = self.org_bones - - def_chain = [] - for i in range(len(org_bones)): - name = org_bones[i] - - def_bone = copy_bone( - self.obj, - name, - make_deformer_name(strip_org(name)) + @classmethod + def add_parameters(self, params): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + params.copy_rotation_axes = bpy.props.BoolVectorProperty( + size=3, + description="Automation axes", + default=tuple([i == 0 for i in range(0, 3)]) ) - def_chain.append(def_bone) - - return def_chain - - def parent_bones(self, all_bones): - - bpy.ops.object.mode_set(mode='EDIT') - org_bones = self.org_bones - eb = self.obj.data.edit_bones - - # Parent control bones - for bone in all_bones['control'][1:]: - previous_index = all_bones['control'].index(bone) - 1 - eb[bone].parent = eb[all_bones['control'][previous_index]] - - # Parent tweak bones - tweaks = all_bones['tweak'] - for tweak in all_bones['tweak']: - parent = '' - if tweaks.index(tweak) == len(tweaks) - 1: - parent = all_bones['control'][-1] - else: - parent = all_bones['control'][tweaks.index(tweak)] - - eb[tweak].parent = eb[parent] - - # Parent deform bones - for bone in all_bones['deform'][1:]: - previous_index = all_bones['deform'].index(bone) - 1 - - eb[bone].parent = eb[all_bones['deform'][previous_index]] - eb[bone].use_connect = True - - # Parent org bones ( to tweaks by default, or to the controls ) - for org, tweak in zip(org_bones, all_bones['tweak']): - eb[org].parent = eb[tweak] - - def make_constraints(self, all_bones): - - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - # Deform bones' constraints - ctrls = all_bones['control'] - tweaks = all_bones['tweak'] - deforms = all_bones['deform'] - - for deform, tweak, ctrl in zip( deforms, tweaks, ctrls ): - con = pb[deform].constraints.new('COPY_TRANSFORMS') - con.target = self.obj - con.subtarget = tweak - - con = pb[deform].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = tweaks[tweaks.index(tweak) + 1] - - con = pb[deform].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = tweaks[tweaks.index(tweak) + 1] - - # Control bones' constraints - if ctrl != ctrls[0]: - con = pb[ctrl].constraints.new('COPY_ROTATION') - con.target = self.obj - con.subtarget = ctrls[ctrls.index(ctrl) - 1] - for i, prop in enumerate(['use_x', 'use_y', 'use_z']): - if self.copy_rotation_axes[i]: - setattr(con, prop, True) - else: - setattr(con, prop, False) - con.use_offset = True - con.target_space = 'LOCAL' - con.owner_space = 'LOCAL' - - def generate(self): - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - self.orient_org_bones() - - # Clear all initial parenting - for bone in self.org_bones: - # eb[ bone ].parent = None - eb[bone].use_connect = False + # Setting up extra tweak layers + ControlLayersOption.TWEAK.add_parameters(params) - # Creating all bones - ctrl_chain = self.make_controls() - tweak_chain = self.make_tweaks() - def_chain = self.make_deform() + items = [('automatic', 'Automatic', ''), ('manual', 'Manual', '')] + params.roll_alignment = bpy.props.EnumProperty(items=items, name="Bone roll alignment", default='automatic') - all_bones = { - 'control': ctrl_chain, - 'tweak': tweak_chain, - 'deform': def_chain - } - self.make_constraints(all_bones) - self.parent_bones(all_bones) + @classmethod + def parameters_ui(self, layout, params): + """ Create the ui for the rig parameters. + """ + r = layout.row() + r.prop(params, "roll_alignment") -def add_parameters(params): - """ Add the parameters of this rig type to the - RigifyParameters PropertyGroup - """ - params.copy_rotation_axes = bpy.props.BoolVectorProperty( - size=3, - description="Automation axes", - default=tuple([i == 0 for i in range(0, 3)]) - ) - - # Setting up extra tweak layers - ControlLayersOption.TWEAK.add_parameters(params) - - items = [('automatic', 'Automatic', ''), ('manual', 'Manual', '')] - params.roll_alignment = bpy.props.EnumProperty(items=items, name="Bone roll alignment", default='automatic') - - -def parameters_ui(layout, params): - """ Create the ui for the rig parameters. - """ - - r = layout.row() - r.prop(params, "roll_alignment") + row = layout.row(align=True) + for i, axis in enumerate(['x', 'y', 'z']): + row.prop(params, "copy_rotation_axes", index=i, toggle=True, text=axis) - row = layout.row(align=True) - for i, axis in enumerate(['x', 'y', 'z']): - row.prop(params, "copy_rotation_axes", index=i, toggle=True, text=axis) - - ControlLayersOption.TWEAK.parameters_ui(layout, params) + ControlLayersOption.TWEAK.parameters_ui(layout, params) def create_sample(obj): diff --git a/rigify/rigs/limbs/super_finger.py b/rigify/rigs/limbs/super_finger.py index 081dca7a..1a9171a7 100644 --- a/rigify/rigs/limbs/super_finger.py +++ b/rigify/rigs/limbs/super_finger.py @@ -1,298 +1,76 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + import bpy -from ...utils import copy_bone, flip_bone -from ...utils import strip_org, make_deformer_name, connected_children_names, make_mechanism_name -from ...utils import create_circle_widget, create_widget -from ...utils import MetarigError, align_bone_x_axis -from ...utils.mechanism import make_property +import re -script = """ -controls = [%s] -master_name = '%s' -if is_selected(controls): - layout.prop(pose_bones[master_name], '["%s"]', text="Curvature", slider=True) -""" +from itertools import count +from ...utils.errors import MetarigError +from ...utils.bones import flip_bone, align_chain_x_axis +from ...utils.naming import make_derived_name +from ...utils.widgets import create_widget +from ...utils.widgets_basic import create_circle_widget +from ...utils.misc import map_list -class Rig: +from ...base_rig import stage - def __init__(self, obj, bone_name, params): - self.obj = obj - self.org_bones = [bone_name] + connected_children_names(obj, bone_name) - self.params = params +from ..chain_rigs import SimpleChainRig - if len(self.org_bones) <= 1: - raise MetarigError("RIGIFY ERROR: Bone '%s': listen bro, that finger rig jusaint put tugetha rite. A little hint, use more than one bone!!" % (strip_org(bone_name))) - def orient_org_bones(self): +class Rig(SimpleChainRig): + """A finger rig with master control.""" + def initialize(self): + super().initialize() - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + self.bbone_segments = 8 + self.first_parent = self.get_bone_parent(self.bones.org[0]) + def prepare_bones(self): if self.params.primary_rotation_axis == 'automatic': + align_chain_x_axis(self.obj, self.bones.org) - first_bone = eb[self.org_bones[0]] - last_bone = eb[self.org_bones[-1]] - - # Orient uarm farm bones - chain_y_axis = last_bone.tail - first_bone.head - chain_rot_axis = first_bone.y_axis.cross(chain_y_axis) # ik-plane normal axis (rotation) - if chain_rot_axis.length < first_bone.length/100: - chain_rot_axis = first_bone.x_axis.normalized() - else: - chain_rot_axis = chain_rot_axis.normalized() - - for bone in self.org_bones: - align_bone_x_axis(self.obj, bone, chain_rot_axis) + ############################## + # Master Control - def generate(self): - org_bones = self.org_bones + @stage.generate_bones + def make_master_control(self): + orgs = self.bones.org + name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_master'), parent=True) + self.bones.ctrl.master = name - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + first_bone = self.get_bone(orgs[0]) + last_bone = self.get_bone(orgs[-1]) + self.get_bone(name).tail += (last_bone.tail - first_bone.head) * 1.25 - self.orient_org_bones() + @stage.configure_bones + def configure_master_control(self): + master = self.bones.ctrl.master - # Bone name lists - ctrl_chain = [] - def_chain = [] - mch_chain = [] - mch_drv_chain = [] + bone = self.get_bone(master) + bone.lock_scale = True, False, True - # Create ctrl master bone - org_name = self.org_bones[0] - temp_name = strip_org(self.org_bones[0]) + @stage.generate_widgets + def make_master_control_widget(self): + master_name = self.bones.ctrl.master - if temp_name[-2:] == '.L' or temp_name[-2:] == '.R': - suffix = temp_name[-2:] - master_name = temp_name[:-2] + "_master" + suffix - else: - master_name = temp_name + "_master" - master_name = copy_bone(self.obj, org_name, master_name) - ctrl_bone_master = eb[master_name] - - # Parenting bug fix ?? - ctrl_bone_master.use_connect = False - ctrl_bone_master.parent = None - - ctrl_bone_master.tail += (eb[org_bones[-1]].tail - eb[org_name].head) * 1.25 - - for bone in org_bones: - eb[bone].use_connect = False - if org_bones.index(bone) != 0: - eb[bone].parent = None - - # Creating the bone chains - for i in range(len(self.org_bones)): - - name = self.org_bones[i] - ctrl_name = strip_org(name) - - # Create control bones - ctrl_bone = copy_bone(self.obj, name, ctrl_name) - ctrl_bone_e = eb[ctrl_name] - - # Create deformation bones - def_name = make_deformer_name(ctrl_name) - def_bone = copy_bone(self.obj, name, def_name) - - # Create mechanism bones - mch_name = make_mechanism_name(ctrl_name) - mch_bone = copy_bone(self.obj, name, mch_name) - - # Create mechanism driver bones - drv_name = make_mechanism_name(ctrl_name) + "_drv" - mch_bone_drv = copy_bone(self.obj, name, drv_name) - - # Adding to lists - ctrl_chain += [ctrl_bone] - def_chain += [def_bone] - mch_chain += [mch_bone] - mch_drv_chain += [mch_bone_drv] - - # Restoring org chain parenting - for bone in org_bones[1:]: - eb[bone].parent = eb[org_bones[org_bones.index(bone) - 1]] - - # Parenting the master bone to the first org - ctrl_bone_master = eb[master_name] - ctrl_bone_master.parent = eb[org_bones[0]] - - # Parenting chain bones - for i in range(len(self.org_bones)): - # Edit bone references - def_bone_e = eb[def_chain[i]] - ctrl_bone_e = eb[ctrl_chain[i]] - mch_bone_e = eb[mch_chain[i]] - mch_bone_drv_e = eb[mch_drv_chain[i]] - - if i == 0: - # First ctl bone - ctrl_bone_e.parent = mch_bone_drv_e - ctrl_bone_e.use_connect = False - # First def bone - def_bone_e.parent = eb[self.org_bones[i]].parent - def_bone_e.use_connect = False - # First mch bone - mch_bone_e.parent = eb[self.org_bones[i]].parent - mch_bone_e.use_connect = False - # First mch driver bone - mch_bone_drv_e.parent = eb[self.org_bones[i]].parent - mch_bone_drv_e.use_connect = False - else: - # The rest - ctrl_bone_e.parent = mch_bone_drv_e - ctrl_bone_e.use_connect = False - - def_bone_e.parent = eb[def_chain[i-1]] - def_bone_e.use_connect = True - - mch_bone_drv_e.parent = eb[ctrl_chain[i-1]] - mch_bone_drv_e.use_connect = False - - # Parenting mch bone - mch_bone_e.parent = ctrl_bone_e - mch_bone_e.use_connect = False - - # Creating tip control bone - tip_name = copy_bone(self.obj, org_bones[-1], temp_name) - ctrl_bone_tip = eb[tip_name] - flip_bone(self.obj, tip_name) - ctrl_bone_tip.length /= 2 - - ctrl_bone_tip.parent = eb[ctrl_chain[-1]] - - bpy.ops.object.mode_set(mode='OBJECT') - - pb = self.obj.pose.bones - - # Setting pose bones locks - pb_master = pb[master_name] - pb_master.lock_scale = True, False, True - - pb[tip_name].lock_scale = True, True, True - pb[tip_name].lock_rotation = True, True, True - pb[tip_name].lock_rotation_w = True - - make_property(pb_master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect") - - # Pose settings - for org, ctrl, deform, mch, mch_drv in zip(self.org_bones, ctrl_chain, def_chain, mch_chain, mch_drv_chain): - - # Constraining the deform bones - con = pb[deform].constraints.new('COPY_TRANSFORMS') - con.target = self.obj - con.subtarget = mch - - # Constraining the mch bones - if mch_chain.index(mch) == 0: - con = pb[mch].constraints.new('COPY_LOCATION') - con.target = self.obj - con.subtarget = ctrl - - con = pb[mch].constraints.new('COPY_SCALE') - con.target = self.obj - con.subtarget = ctrl - - con = pb[mch].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - - con = pb[mch].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - con.volume = 'NO_VOLUME' - - elif mch_chain.index(mch) == len(mch_chain) - 1: - con = pb[mch].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = tip_name - - con = pb[mch].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = tip_name - con.volume = 'NO_VOLUME' - else: - con = pb[mch].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - - con = pb[mch].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - con.volume = 'NO_VOLUME' - - # Constraining and driving mch driver bones - pb[mch_drv].rotation_mode = 'YZX' - - if mch_drv_chain.index(mch_drv) == 0: - # Constraining to master bone - con = pb[mch_drv].constraints.new('COPY_LOCATION') - con.target = self.obj - con.subtarget = master_name - - con = pb[mch_drv].constraints.new('COPY_ROTATION') - con.target = self.obj - con.subtarget = master_name - con.target_space = 'LOCAL' - con.owner_space = 'LOCAL' - - else: - # Match axis to expression - options = { - "automatic": {"axis": 0, - "expr": '(1-sy)*pi'}, - "X": {"axis": 0, - "expr": '(1-sy)*pi'}, - "-X": {"axis": 0, - "expr": '-((1-sy)*pi)'}, - "Y": {"axis": 1, - "expr": '(1-sy)*pi'}, - "-Y": {"axis": 1, - "expr": '-((1-sy)*pi)'}, - "Z": {"axis": 2, - "expr": '(1-sy)*pi'}, - "-Z": {"axis": 2, - "expr": '-((1-sy)*pi)'} - } - - axis = self.params.primary_rotation_axis - - # Drivers - drv = pb[mch_drv].driver_add("rotation_euler", options[axis]["axis"]).driver - drv.type = 'SCRIPTED' - drv.expression = options[axis]["expr"] - drv_var = drv.variables.new() - drv_var.name = 'sy' - drv_var.type = "SINGLE_PROP" - drv_var.targets[0].id = self.obj - drv_var.targets[0].data_path = pb[master_name].path_from_id() + '.scale.y' - - # Setting bone curvature setting, custom property, and drivers - def_bone = self.obj.data.bones[deform] - - def_bone.bbone_segments = 8 - drv = def_bone.driver_add("bbone_easein").driver # Ease in - - drv.type='SUM' - drv_var = drv.variables.new() - drv_var.name = "curvature" - drv_var.type = "SINGLE_PROP" - drv_var.targets[0].id = self.obj - drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' - - drv = def_bone.driver_add("bbone_easeout").driver # Ease out - - drv.type='SUM' - drv_var = drv.variables.new() - drv_var.name = "curvature" - drv_var.type = "SINGLE_PROP" - drv_var.targets[0].id = self.obj - drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' - - # Assigning shapes to control bones - create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) - - # Create ctrl master widget w = create_widget(self.obj, master_name) if w is not None: mesh = w.data @@ -307,31 +85,189 @@ class Rig: mesh.from_pydata(verts, edges, []) mesh.update() - # Create tip control widget - create_circle_widget(self.obj, tip_name, radius=0.3, head_tail=0.0) + ############################## + # Control chain + + @stage.generate_bones + def make_control_chain(self): + orgs = self.bones.org + self.bones.ctrl.fk = map_list(self.make_control_bone, count(0), orgs) + self.bones.ctrl.fk += [self.make_tip_control_bone(orgs[-1], orgs[0])] + + def make_control_bone(self, i, org): + return self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=False) + + def make_tip_control_bone(self, org, name_org): + name = self.copy_bone(org, make_derived_name(name_org, 'ctrl'), parent=False) + + flip_bone(self.obj, name) + self.get_bone(name).length /= 2 + + return name + + @stage.parent_bones + def parent_control_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(ctrls, self.bones.mch.bend + ctrls[-2:]): + self.set_bone_parent(*args) + + @stage.configure_bones + def configure_control_chain(self): + for args in zip(count(0), self.bones.ctrl.fk, self.bones.org + [None]): + self.configure_control_bone(*args) + + def configure_control_bone(self, i, ctrl, org): + if org: + self.copy_bone_properties(org, ctrl) + else: + bone = self.get_bone(ctrl) + bone.lock_rotation_w = True + bone.lock_rotation = (True, True, True) + bone.lock_scale = (True, True, True) + + def make_control_widget(self, ctrl): + if ctrl == self.bones.ctrl.fk[-1]: + # Tip control + create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.0) + else: + create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) + + ############################## + # MCH bend chain + + @stage.generate_bones + def make_mch_bend_chain(self): + self.bones.mch.bend = map_list(self.make_mch_bend_bone, self.bones.org) + + def make_mch_bend_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), parent=False) + + @stage.parent_bones + def parent_mch_bend_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(self.bones.mch.bend, [self.first_parent] + ctrls): + self.set_bone_parent(*args) + + # Match axis to expression + axis_options = { + "automatic": {"axis": 0, + "expr": '(1-sy)*pi'}, + "X": {"axis": 0, + "expr": '(1-sy)*pi'}, + "-X": {"axis": 0, + "expr": '-((1-sy)*pi)'}, + "Y": {"axis": 1, + "expr": '(1-sy)*pi'}, + "-Y": {"axis": 1, + "expr": '-((1-sy)*pi)'}, + "Z": {"axis": 2, + "expr": '(1-sy)*pi'}, + "-Z": {"axis": 2, + "expr": '-((1-sy)*pi)'} + } + + @stage.rig_bones + def rig_mch_bend_chain(self): + for args in zip(count(0), self.bones.mch.bend): + self.rig_mch_bend_bone(*args) + + def rig_mch_bend_bone(self, i, mch): + master = self.bones.ctrl.master + if i == 0: + self.make_constraint(mch, 'COPY_LOCATION', master) + self.make_constraint(mch, 'COPY_ROTATION', master, space='LOCAL') + else: + axis = self.params.primary_rotation_axis + options = self.axis_options[axis] + + bone = self.get_bone(mch) + bone.rotation_mode = 'YZX' + + self.make_driver( + bone, 'rotation_euler', index=options['axis'], + expression=options['expr'], + variables={'sy': (master, '.scale.y')} + ) + + ############################## + # MCH stretch chain + + @stage.generate_bones + def make_mch_stretch_chain(self): + self.bones.mch.stretch = map_list(self.make_mch_stretch_bone, self.bones.org) + + def make_mch_stretch_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch'), parent=False) + + @stage.parent_bones + def parent_mch_stretch_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(self.bones.mch.stretch, [self.first_parent] + ctrls[1:]): + self.set_bone_parent(*args) + + @stage.rig_bones + def rig_mch_stretch_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(count(0), self.bones.mch.stretch, ctrls, ctrls[1:]): + self.rig_mch_stretch_bone(*args) + + def rig_mch_stretch_bone(self, i, mch, ctrl, ctrl_next): + if i == 0: + self.make_constraint(mch, 'COPY_LOCATION', ctrl) + self.make_constraint(mch, 'COPY_SCALE', ctrl) + + self.make_constraint(mch, 'DAMPED_TRACK', ctrl_next) + self.make_constraint(mch, 'STRETCH_TO', ctrl_next, volume='NO_VOLUME') + + ############################## + # ORG chain + + @stage.rig_bones + def rig_org_chain(self): + for args in zip(count(0), self.bones.org, self.bones.mch.stretch): + self.rig_org_bone(*args) + + ############################## + # Deform chain + + @stage.configure_bones + def configure_master_properties(self): + master = self.bones.ctrl.master + + self.make_property(master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect") # Create UI - controls_string = ", ".join( - ["'" + x + "'" for x in ctrl_chain] - ) + ", " + "'" + master_name + "'" - return [script % (controls_string, master_name, 'finger_curve')] - - -def add_parameters(params): - """ Add the parameters of this rig type to the - RigifyParameters PropertyGroup - """ - items = [('automatic', 'Automatic', ''), ('X', 'X manual', ''), ('Y', 'Y manual', ''), ('Z', 'Z manual', ''), - ('-X', '-X manual', ''), ('-Y', '-Y manual', ''), ('-Z', '-Z manual', '')] - params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='automatic') - - -def parameters_ui(layout, params): - """ Create the ui for the rig parameters. - """ - r = layout.row() - r.label(text="Bend rotation axis:") - r.prop(params, "primary_rotation_axis", text="") + panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten()) + panel.custom_prop(master, 'finger_curve', text="Curvature", slider=True) + + def rig_deform_bone(self, i, deform, org): + master = self.bones.ctrl.master + bone = self.get_bone(deform) + + self.make_constraint(deform, 'COPY_TRANSFORMS', org) + + self.make_driver(bone.bone, 'bbone_easein', variables=[(master, 'finger_curve')]) + self.make_driver(bone.bone, 'bbone_easeout', variables=[(master, 'finger_curve')]) + + ############### + # OPTIONS + + @classmethod + def add_parameters(self, params): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + items = [('automatic', 'Automatic', ''), ('X', 'X manual', ''), ('Y', 'Y manual', ''), ('Z', 'Z manual', ''), + ('-X', '-X manual', ''), ('-Y', '-Y manual', ''), ('-Z', '-Z manual', '')] + params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='automatic') + + @classmethod + def parameters_ui(self, layout, params): + """ Create the ui for the rig parameters. + """ + r = layout.row() + r.label(text="Bend rotation axis:") + r.prop(params, "primary_rotation_axis", text="") def create_sample(obj): diff --git a/rigify/rigs/limbs/super_limb.py b/rigify/rigs/limbs/super_limb.py index 0d557bb7..5020f091 100644 --- a/rigify/rigs/limbs/super_limb.py +++ b/rigify/rigs/limbs/super_limb.py @@ -1,46 +1,41 @@ -import bpy +#====================== 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> -from .arm import Rig as armRig -from .leg import Rig as legRig -from .paw import Rig as pawRig +import bpy -from ...utils import ControlLayersOption +from ...base_generate import SubstitutionRig -class Rig: +from .limb_rigs import BaseLimbRig - def __init__(self, obj, bone_name, params): - """ Initialize super_limb rig wrapper class """ - self.obj = obj - self.params = params +from . import arm, leg, paw - if params.limb_type == 'arm': - self.limb = armRig(obj, bone_name, params) - elif params.limb_type == 'leg': - self.limb = legRig(obj, bone_name, params) - elif params.limb_type == 'paw': - self.limb = pawRig(obj, bone_name, params) - def generate(self): - self.limb.rigify_generator = self.rigify_generator - self.limb.rigify_wrapper = self.rigify_wrapper +RIGS = { 'arm': arm.Rig, 'leg': leg.Rig, 'paw': paw.Rig } - return self.limb.generate() - @staticmethod - def get_future_names(bones): - if bones[0].rigify_parameters.limb_type == 'arm': - return armRig.get_future_names(bones) - elif bones[0].rigify_parameters.limb_type == 'leg': - return legRig.get_future_names(bones) - elif bones[0].rigify_parameters.limb_type == 'paw': - return pawRig.get_future_names(bones) +class Rig(SubstitutionRig): + def substitute(self): + return [ self.instantiate_rig(RIGS[self.params.limb_type], self.base_bone) ] def add_parameters(params): - """ Add the parameters of this rig type to the - RigifyParameters PropertyGroup - """ - items = [ ('arm', 'Arm', ''), ('leg', 'Leg', ''), @@ -53,171 +48,15 @@ def add_parameters(params): default = 'arm' ) - 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) + BaseLimbRig.add_parameters(params) def parameters_ui(layout, params): - """ Create the ui for the rig parameters.""" - r = layout.row() r.prop(params, "limb_type") - r = layout.row() - r.prop(params, "rotation_axis") - - if 'auto' not in params.rotation_axis.lower(): - r = layout.row() - etremities = {'arm': 'Hand', 'leg': 'Foot', 'paw': 'Claw'} - text = "Auto align " + etremities[params.limb_type] - 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) + RIGS[params.limb_type].parameters_ui(layout, params) def create_sample(obj): - # generated by rigify.utils.write_metarig - bpy.ops.object.mode_set(mode='EDIT') - arm = obj.data - - bones = {} - - bone = arm.edit_bones.new('upper_arm.L') - bone.head[:] = -0.0016, 0.0060, -0.0012 - bone.tail[:] = 0.2455, 0.0678, -0.1367 - bone.roll = 2.0724 - bone.use_connect = False - bones['upper_arm.L'] = bone.name - bone = arm.edit_bones.new('forearm.L') - bone.head[:] = 0.2455, 0.0678, -0.1367 - bone.tail[:] = 0.4625, 0.0285, -0.2797 - bone.roll = 2.1535 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['upper_arm.L']] - bones['forearm.L'] = bone.name - bone = arm.edit_bones.new('hand.L') - bone.head[:] = 0.4625, 0.0285, -0.2797 - bone.tail[:] = 0.5265, 0.0205, -0.3273 - bone.roll = 2.2103 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['forearm.L']] - bones['hand.L'] = bone.name - - bpy.ops.object.mode_set(mode='OBJECT') - pbone = obj.pose.bones[bones['upper_arm.L']] - pbone.rigify_type = 'limbs.super_limb' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - try: - pbone.rigify_parameters.separate_ik_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.ik_layers = [ - 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, False, False, False, - False, False - ] - except AttributeError: - pass - try: - pbone.rigify_parameters.separate_hose_layers = True - except AttributeError: - pass - try: - pbone.rigify_parameters.hose_layers = [ - 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, False, False, - False, False - ] - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_layers = [ - 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, False, False, - False, False - ] - except AttributeError: - pass - try: - pbone.rigify_parameters.fk_layers = [ - 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, False, False, False, - False, False - ] - except AttributeError: - pass - pbone = obj.pose.bones[bones['forearm.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['hand.L']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - - bpy.ops.object.mode_set(mode='EDIT') - for bone in arm.edit_bones: - bone.select = False - bone.select_head = False - bone.select_tail = False - for b in bones: - bone = arm.edit_bones[bones[b]] - bone.select = True - bone.select_head = True - bone.select_tail = True - arm.edit_bones.active = bone + arm.create_sample(obj, limb=True) diff --git a/rigify/rigs/limbs/ui.py b/rigify/rigs/limbs/ui.py deleted file mode 100644 index 952fbca4..00000000 --- a/rigify/rigs/limbs/ui.py +++ /dev/null @@ -1,182 +0,0 @@ -script_arm = """ -controls = [%s] -tweaks = [%s] -ik_ctrl = [%s] -fk_ctrl = '%s' -parent = '%s' -hand_fk = '%s' -pole = '%s' - -# IK/FK Switch on all Control Bones -if is_selected( controls ): - layout.prop( pose_bones[parent], '["%s"]', slider = True ) - props = layout.operator("pose.rigify_arm_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_ctrl + ")") - props.uarm_fk = controls[1] - props.farm_fk = controls[2] - props.hand_fk = controls[3] - props.uarm_ik = controls[0] - props.farm_ik = ik_ctrl[1] - props.hand_ik = controls[4] - props = layout.operator("pose.rigify_arm_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_ctrl + ")") - props.uarm_fk = controls[1] - props.farm_fk = controls[2] - props.hand_fk = controls[3] - props.uarm_ik = controls[0] - props.farm_ik = ik_ctrl[1] - props.hand_ik = controls[4] - props.pole = pole - props.main_parent = parent - props = layout.operator("pose.rigify_rot2pole_" + rig_id, text="Switch Rotation-Pole") - props.bone_name = controls[1] - props.limb_type = "arm" - props.controls = str(controls) - props.ik_ctrl = str(ik_ctrl) - props.fk_ctrl = str(fk_ctrl) - props.parent = str(parent) - props.pole = str(pole) - - -# BBone rubber hose on each Respective Tweak -for t in tweaks: - if is_selected( t ): - layout.prop( pose_bones[ t ], '["%s"]', slider = True ) - -# IK Stretch and pole_vector on IK Control bone -if is_selected( ik_ctrl ) or is_selected(parent): - layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) - layout.prop( pose_bones[ parent ], '["%s"]') - -# FK limb follow -if is_selected( fk_ctrl ) or is_selected(parent): - layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) -""" - -script_leg = """ -controls = [%s] -tweaks = [%s] -ik_ctrl = [%s] -fk_ctrl = '%s' -parent = '%s' -foot_fk = '%s' -pole = '%s' - -# IK/FK Switch on all Control Bones -if is_selected( controls ): - layout.prop( pose_bones[parent], '["%s"]', slider = True ) - props = layout.operator("pose.rigify_leg_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_ctrl + ")") - props.thigh_fk = controls[1] - props.shin_fk = controls[2] - props.foot_fk = controls[3] - props.mfoot_fk = controls[7] - props.thigh_ik = controls[0] - props.shin_ik = ik_ctrl[1] - props.foot_ik = ik_ctrl[2] - props.mfoot_ik = ik_ctrl[2] - props = layout.operator("pose.rigify_leg_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_ctrl + ")") - props.thigh_fk = controls[1] - props.shin_fk = controls[2] - props.foot_fk = controls[3] - props.mfoot_fk = controls[7] - props.thigh_ik = controls[0] - props.shin_ik = ik_ctrl[1] - props.foot_ik = controls[6] - props.pole = pole - props.footroll = controls[5] - props.mfoot_ik = ik_ctrl[2] - props.main_parent = parent - props = layout.operator("pose.rigify_rot2pole_" + rig_id, text="Toggle Rotation and Pole") - props.bone_name = controls[1] - props.limb_type = "leg" - props.controls = str(controls) - props.ik_ctrl = str(ik_ctrl) - props.fk_ctrl = str(fk_ctrl) - props.parent = str(parent) - props.pole = str(pole) - -# BBone rubber hose on each Respective Tweak -for t in tweaks: - if is_selected( t ): - layout.prop( pose_bones[ t ], '["%s"]', slider = True ) - -# IK Stretch and pole_vector on IK Control bone -if is_selected( ik_ctrl ) or is_selected(parent): - layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) - layout.prop( pose_bones[ parent ], '["%s"]') - -# FK limb follow -if is_selected( fk_ctrl ) or is_selected(parent): - layout.prop( pose_bones[ parent ], '["%s"]', slider = True ) -""" - -def create_script( bones, limb_type=None): - # All ctrls have IK/FK switch - controls = [bones['ik']['ctrl']['limb']] + bones['fk']['ctrl'] - controls += bones['ik']['ctrl']['terminal'] - controls += [bones['fk']['mch']] - controls += [bones['main_parent']] - - controls_string = ", ".join(["'" + x + "'" for x in controls]) - - # All tweaks have their own bbone prop - tweaks = bones['tweak']['ctrl'][1:-1] - tweaks_string = ", ".join(["'" + x + "'" for x in tweaks]) - - # IK ctrl has IK stretch - ik_ctrl = [ bones['ik']['ctrl']['terminal'][-1] ] - ik_ctrl += [ bones['ik']['mch_ik'] ] - ik_ctrl += [ bones['ik']['mch_target'] ] - - ik_ctrl_string = ", ".join(["'" + x + "'" for x in ik_ctrl]) - - if 'ik_target' in bones['ik']['ctrl'].keys(): - pole = bones['ik']['ctrl']['ik_target'] - else: - pole = '' - - if limb_type == 'arm': - return script_arm % ( - controls_string, - tweaks_string, - ik_ctrl_string, - bones['fk']['ctrl'][0], - bones['main_parent'], - bones['fk']['ctrl'][-1], - pole, - 'IK_FK', - 'rubber_tweak', - 'IK_Stretch', - 'pole_vector', - 'FK_limb_follow' - ) - - elif limb_type == 'leg': - return script_leg % ( - controls_string, - tweaks_string, - ik_ctrl_string, - bones['fk']['ctrl'][0], - bones['main_parent'], - bones['fk']['ctrl'][-1], - pole, - 'IK_FK', - 'rubber_tweak', - 'IK_Stretch', - 'pole_vector', - 'FK_limb_follow' - ) - - elif limb_type == 'paw': - return script_leg % ( - controls_string, - tweaks_string, - ik_ctrl_string, - bones['fk']['ctrl'][0], - bones['main_parent'], - bones['fk']['ctrl'][-1], - pole, - 'IK_FK', - 'rubber_tweak', - 'IK_Stretch', - 'pole_vector', - 'FK_limb_follow' - ) diff --git a/rigify/rigs/spines/basic_spine.py b/rigify/rigs/spines/basic_spine.py new file mode 100644 index 00000000..d90e4c8d --- /dev/null +++ b/rigify/rigs/spines/basic_spine.py @@ -0,0 +1,321 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + +import bpy + +from itertools import count, repeat + +from ...utils.errors import MetarigError +from ...utils.layers import ControlLayersOption +from ...utils.naming import strip_org, make_deformer_name, make_mechanism_name +from ...utils.bones import BoneDict, put_bone, align_bone_to_axis +from ...utils.widgets_basic import create_circle_widget +from ...utils.misc import map_list + +from ...base_rig import stage +from .spine_rigs import BaseSpineRig + + +class Rig(BaseSpineRig): + """ + Spine rig with fixed pivot, hip/chest controls and tweaks. + """ + + def initialize(self): + super().initialize() + + # Check if user provided the pivot position + self.pivot_pos = self.params.pivot_pos + + if not (0 < self.pivot_pos < len(self.bones.org)): + self.raise_error("Please specify a valid pivot bone position.") + + #################################################### + # BONES + # + # org[]: + # ORG bones + # ctrl: + # master, hips, chest: + # Main controls. + # tweak[]: + # Tweak control chain. + # mch: + # pivot: + # Pivot tweak parent. + # chain: + # chest[], hips[]: + # Tweak parents, distributing master deform. + # wgt_hips, wgt_chest: + # Widget position bones. + # deform[]: + # DEF bones + # + #################################################### + + #################################################### + # Master control bone + + @stage.generate_bones + def make_master_control(self): + super().make_master_control() + + # Put the main control in the middle of the hip bone + base_bone = self.get_bone(self.bones.org[0]) + put_bone(self.obj, self.bones.ctrl.master, (base_bone.head + base_bone.tail) / 2) + + #################################################### + # Main control bones + + @stage.generate_bones + def make_control_chain(self): + orgs = self.bones.org + pivot = self.pivot_pos + + self.bones.ctrl.hips = self.make_hips_control_bone(orgs[pivot-1], 'hips') + self.bones.ctrl.chest = self.make_chest_control_bone(orgs[pivot], 'chest') + + def make_hips_control_bone(self, org, name): + name = self.copy_bone(org, name, parent=False) + align_bone_to_axis(self.obj, name, 'y', length=self.length / 4, flip=True) + return name + + def make_chest_control_bone(self, org, name): + name = self.copy_bone(org, name, parent=False) + align_bone_to_axis(self.obj, name, 'y', length=self.length / 3) + return name + + @stage.parent_bones + def parent_control_chain(self): + ctrl = self.bones.ctrl + self.set_bone_parent(ctrl.hips, ctrl.master) + self.set_bone_parent(ctrl.chest, ctrl.master) + + @stage.configure_bones + def configure_control_chain(self): + pass + + @stage.generate_widgets + def make_control_widgets(self): + ctrl = self.bones.ctrl + mch = self.bones.mch + self.make_control_widget(ctrl.hips, mch.wgt_hips) + self.make_control_widget(ctrl.chest, mch.wgt_chest) + + def make_control_widget(self, ctrl, wgt_mch): + self.get_bone(ctrl).custom_shape_transform = self.get_bone(wgt_mch) + + create_circle_widget( + self.obj, ctrl, + radius=1.0, + head_tail=0.75, + with_line=False, + bone_transform_name=wgt_mch + ) + + #################################################### + # MCH bones associated with main controls + + @stage.generate_bones + def make_mch_control_bones(self): + orgs = self.bones.org + mch = self.bones.mch + + mch.pivot = self.make_mch_pivot_bone(orgs[self.pivot_pos], 'pivot') + mch.wgt_hips = self.make_mch_widget_bone(orgs[0], 'WGT-hips') + mch.wgt_chest = self.make_mch_widget_bone(orgs[-1], 'WGT-chest') + + def make_mch_pivot_bone(self, org, name): + name = self.copy_bone(org, make_mechanism_name(name), parent=False) + align_bone_to_axis(self.obj, name, 'y', length=self.length * 0.6 / 4) + return name + + def make_mch_widget_bone(self, org, name): + return self.copy_bone(org, make_mechanism_name(name), parent=False) + + @stage.parent_bones + def parent_mch_control_bones(self): + mch = self.bones.mch + self.set_bone_parent(mch.pivot, mch.chain.chest[0]) + self.set_bone_parent(mch.wgt_hips, mch.chain.hips[0]) + self.set_bone_parent(mch.wgt_chest, mch.chain.chest[-1]) + + @stage.rig_bones + def rig_mch_control_bones(self): + mch = self.bones.mch + # Is it actually intending to compute average of these, or is this really intentional? + # This effectively adds rotations of the hip and chest controls. + self.make_constraint(mch.pivot, 'COPY_TRANSFORMS', mch.chain.hips[-1], space='LOCAL') + + #################################################### + # MCH chain for distributing hip & chest transform + + @stage.generate_bones + def make_mch_chain(self): + orgs = self.bones.org + self.bones.mch.chain = BoneDict( + hips = map_list(self.make_mch_bone, orgs[0:self.pivot_pos], repeat(True)), + chest = map_list(self.make_mch_bone, orgs[self.pivot_pos:], repeat(False)), + ) + + def make_mch_bone(self, org, is_hip): + name = self.copy_bone(org, make_mechanism_name(strip_org(org)), parent=False) + align_bone_to_axis(self.obj, name, 'y', length=self.length / 10, flip=is_hip) + return name + + @stage.parent_bones + def parent_mch_chain(self): + master = self.bones.ctrl.master + chain = self.bones.mch.chain + self.parent_bone_chain([master, *reversed(chain.hips)]) + self.parent_bone_chain([master, *chain.chest]) + + @stage.rig_bones + def rig_mch_chain(self): + ctrl = self.bones.ctrl + chain = self.bones.mch.chain + for mch in chain.hips: + self.rig_mch_bone(mch, ctrl.hips, len(chain.hips)) + for mch in chain.chest: + self.rig_mch_bone(mch, ctrl.chest, len(chain.chest)) + + def rig_mch_bone(self, mch, control, count): + self.make_constraint(mch, 'COPY_TRANSFORMS', control, space='LOCAL', influence=1/count) + + #################################################### + # Tweak bones + + @stage.parent_bones + def parent_tweak_chain(self): + mch = self.bones.mch + chain = mch.chain + parents = [chain.hips[0], *chain.hips[0:-1], mch.pivot, *chain.chest[1:], chain.chest[-1]] + for args in zip(self.bones.ctrl.tweak, parents): + self.set_bone_parent(*args) + + #################################################### + # SETTINGS + + @classmethod + def add_parameters(self, params): + params.pivot_pos = bpy.props.IntProperty( + name='pivot_position', + default=2, + min=0, + description='Position of the torso control and pivot point' + ) + + super().add_parameters(params) + + @classmethod + def parameters_ui(self, layout, params): + r = layout.row() + r.prop(params, "pivot_pos") + + super().parameters_ui(layout, params) + + +def create_sample(obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('spine') + bone.head[:] = 0.0000, 0.0552, 1.0099 + bone.tail[:] = 0.0000, 0.0172, 1.1573 + bone.roll = 0.0000 + bone.use_connect = False + bones['spine'] = bone.name + + bone = arm.edit_bones.new('spine.001') + bone.head[:] = 0.0000, 0.0172, 1.1573 + bone.tail[:] = 0.0000, 0.0004, 1.2929 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['spine']] + bones['spine.001'] = bone.name + + bone = arm.edit_bones.new('spine.002') + bone.head[:] = 0.0000, 0.0004, 1.2929 + bone.tail[:] = 0.0000, 0.0059, 1.4657 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['spine.001']] + bones['spine.002'] = bone.name + + bone = arm.edit_bones.new('spine.003') + bone.head[:] = 0.0000, 0.0059, 1.4657 + bone.tail[:] = 0.0000, 0.0114, 1.6582 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['spine.002']] + bones['spine.003'] = bone.name + + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['spine']] + pbone.rigify_type = 'spines.basic_spine' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + + try: + pbone.rigify_parameters.tweak_layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['spine.001']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone = obj.pose.bones[bones['spine.002']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone = obj.pose.bones[bones['spine.003']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + + return bones diff --git a/rigify/rigs/spines/basic_tail.py b/rigify/rigs/spines/basic_tail.py new file mode 100644 index 00000000..6697858c --- /dev/null +++ b/rigify/rigs/spines/basic_tail.py @@ -0,0 +1,246 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + +import bpy + +from itertools import count + +from ...utils.naming import strip_org, make_derived_name +from ...utils.bones import put_bone, flip_bone, is_same_position, connect_bbone_chain_handles, align_bone_orientation +from ...utils.widgets_basic import create_circle_widget +from ...utils.layers import ControlLayersOption +from ...utils.misc import map_list + +from ...base_rig import stage + +from ..chain_rigs import TweakChainRig, SimpleChainRig +from ..widgets import create_ballsocket_widget + +from .spine_rigs import BaseHeadTailRig + + +class Rig(BaseHeadTailRig): + def initialize(self): + super().initialize() + + self.copy_rotation_axes = self.params.copy_rotation_axes + + def parent_bones(self): + super().parent_bones() + + if self.connected_tweak and self.use_connect_reverse: + self.rig_parent_bone = self.connected_tweak + + #################################################### + # Master control + + @stage.generate_bones + def make_master_control(self): + org = self.bones.org[0] + self.bones.ctrl.master = self.copy_bone(org, make_derived_name(org, 'ctrl', '_master')) + self.default_prop_bone = self.bones.ctrl.master + + @stage.parent_bones + def parent_master_control(self): + self.set_bone_parent(self.bones.ctrl.master, self.rig_parent_bone) + + @stage.configure_bones + def configure_master_control(self): + bone = self.get_bone(self.bones.ctrl.master) + bone.lock_location = True, True, True + + @stage.generate_widgets + def make_master_control_widget(self): + bone = self.bones.ctrl.master + self.get_bone(bone).custom_shape_transform = self.get_bone(self.bones.ctrl.tweak[-1]) + create_ballsocket_widget(self.obj, bone, size=0.7) + + #################################################### + # Control bones + + @stage.parent_bones + def parent_control_chain(self): + self.set_bone_parent(self.bones.ctrl.fk[0], self.bones.mch.rot_tail) + self.parent_bone_chain(self.bones.ctrl.fk, use_connect=False) + + @stage.rig_bones + def rig_control_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(count(0), ctrls, [self.bones.ctrl.master] + ctrls): + self.rig_control_bone(*args) + + def rig_control_bone(self, i, ctrl, prev_ctrl): + self.make_constraint( + ctrl, 'COPY_ROTATION', prev_ctrl, + use_xyz=self.copy_rotation_axes, + space='LOCAL', use_offset=True + ) + + # Widgets + def make_control_widget(self, ctrl): + create_circle_widget(self.obj, ctrl, radius=0.5, head_tail=0.75) + + #################################################### + # MCH bones associated with main controls + + @stage.generate_bones + def make_mch_control_bones(self): + self.bones.mch.rot_tail = self.make_mch_follow_bone(self.bones.org[0], 'tail', 0.0) + + @stage.parent_bones + def parent_mch_control_bones(self): + self.set_bone_parent(self.bones.mch.rot_tail, self.rig_parent_bone) + + #################################################### + # Tweak bones + + @stage.generate_bones + def make_tweak_chain(self): + orgs = self.bones.org + self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), orgs[0:1] + orgs) + + def make_tweak_bone(self, i, org): + if i == 0: + if self.check_connect_tweak(org): + return self.connected_tweak + + else: + name = self.copy_bone(org, 'tweak_base_' + strip_org(org), parent=False, scale=0.5) + + else: + name = self.copy_bone(org, 'tweak_' + strip_org(org), parent=False, scale=0.5) + put_bone(self.obj, name, self.get_bone(org).tail) + + return name + + #################################################### + # Deform chain + + @stage.configure_bones + def configure_deform_chain(self): + if self.use_connect_chain and self.use_connect_reverse: + self.get_bone(self.bones.deform[-1]).bone.bbone_easein = 0.0 + self.get_bone(self.rigify_parent.bones.deform[0]).bone.bbone_easein = 1.0 + else: + self.get_bone(self.bones.deform[-1]).bone.bbone_easeout = 0.0 + + + #################################################### + # SETTINGS + + @classmethod + def add_parameters(self, params): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + + super().add_parameters(params) + + params.copy_rotation_axes = bpy.props.BoolVectorProperty( + size=3, + description="Automation axes", + default=tuple([i == 0 for i in range(0, 3)]) + ) + + + @classmethod + def parameters_ui(self, layout, params): + """ Create the ui for the rig parameters. + """ + + row = layout.row(align=True) + for i, axis in enumerate(['x', 'y', 'z']): + row.prop(params, "copy_rotation_axes", index=i, toggle=True, text=axis) + + super().parameters_ui(layout, params) + + +def create_sample(obj, *, parent=None): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('tail') + bone.head[:] = 0.0000, 0.0552, 1.0099 + bone.tail[:] = -0.0000, 0.0582, 0.8669 + bone.roll = 0.0000 + bone.use_connect = False + if parent: + bone.parent = arm.edit_bones[parent] + bones['tail'] = bone.name + bone = arm.edit_bones.new('tail.001') + bone.head[:] = -0.0000, 0.0582, 0.8669 + bone.tail[:] = -0.0000, 0.0365, 0.7674 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['tail']] + bones['tail.001'] = bone.name + bone = arm.edit_bones.new('tail.002') + bone.head[:] = -0.0000, 0.0365, 0.7674 + bone.tail[:] = -0.0000, 0.0010, 0.6984 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['tail.001']] + bones['tail.002'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['tail']] + pbone.rigify_type = 'spines.basic_tail' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + try: + pbone.rigify_parameters.connect_chain = bool(parent) + except AttributeError: + pass + try: + pbone.rigify_parameters.tweak_layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['tail.001']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone = obj.pose.bones[bones['tail.002']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone diff --git a/rigify/rigs/spines/spine_rigs.py b/rigify/rigs/spines/spine_rigs.py new file mode 100644 index 00000000..47a72287 --- /dev/null +++ b/rigify/rigs/spines/spine_rigs.py @@ -0,0 +1,208 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + +import bpy + +from itertools import count + +from ...utils.layers import ControlLayersOption +from ...utils.naming import make_derived_name +from ...utils.bones import align_bone_orientation, align_bone_to_axis +from ...utils.widgets_basic import create_cube_widget +from ...utils.switch_parent import SwitchParentBuilder + +from ...base_rig import stage + +from ..chain_rigs import TweakChainRig, ConnectingChainRig + + +class BaseSpineRig(TweakChainRig): + """ + Spine rig with tweaks. + """ + + bbone_segments = 8 + + def initialize(self): + if len(self.bones.org) < 3: + self.raise_error("Input to rig type must be a chain of 3 or more bones.") + + self.length = sum([self.get_bone(b).length for b in self.bones.org]) + + #################################################### + # BONES + # + # ctrl: + # master + # Main control. + # + #################################################### + + #################################################### + # Master control bone + + @stage.generate_bones + def make_master_control(self): + self.bones.ctrl.master = name = self.copy_bone(self.bones.org[0], 'torso') + + align_bone_to_axis(self.obj, name, 'y', length=self.length * 0.6) + + SwitchParentBuilder(self.generator).register_parent(self, name) + + @stage.parent_bones + def parent_master_control(self): + self.set_bone_parent(self.bones.ctrl.master, self.rig_parent_bone) + + @stage.configure_bones + def configure_master_control(self): + pass + + @stage.generate_widgets + def make_master_control_widget(self): + create_cube_widget( + self.obj, self.bones.ctrl.master, + radius=0.5, + bone_transform_name=None + ) + + #################################################### + # Tweak bones + + @stage.configure_bones + def configure_tweak_chain(self): + super().configure_tweak_chain() + + ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.tweak) + + #################################################### + # Deform bones + + @stage.configure_bones + def configure_bbone_chain(self): + self.get_bone(self.bones.deform[0]).bone.bbone_easein = 0.0 + + #################################################### + # SETTINGS + + @classmethod + def add_parameters(self, params): + # Setting up extra layers for the FK and tweak + ControlLayersOption.TWEAK.add_parameters(params) + + @classmethod + def parameters_ui(self, layout, params): + ControlLayersOption.TWEAK.parameters_ui(layout, params) + + +class BaseHeadTailRig(ConnectingChainRig): + """ Base for head and tail rigs. """ + + def initialize(self): + super().initialize() + + self.rotation_bones = [] + + #################################################### + # Utilities + + def get_parent_master(self, default_bone): + """ Return the parent's master control bone if connecting and found. """ + + if self.use_connect_chain and 'master' in self.rigify_parent.bones.ctrl: + return self.rigify_parent.bones.ctrl.master + else: + return default_bone + + def get_parent_master_panel(self, default_bone): + """ Return the parent's master control bone if connecting and found, and script panel. """ + + controls = self.bones.ctrl.flatten() + prop_bone = self.get_parent_master(default_bone) + + if prop_bone != default_bone: + owner = self.rigify_parent + controls += self.rigify_parent.bones.ctrl.flatten() + else: + owner = self + + return prop_bone, self.script.panel_with_selected_check(owner, controls) + + #################################################### + # Rotation follow + + def make_mch_follow_bone(self, org, name, defval, *, copy_scale=False): + bone = self.copy_bone(org, make_derived_name('ROT-'+name, 'mch'), parent=True) + self.rotation_bones.append((org, name, bone, defval, copy_scale)) + return bone + + @stage.parent_bones + def align_mch_follow_bones(self): + self.follow_bone = self.get_parent_master('root') + + for org, name, bone, defval, copy_scale in self.rotation_bones: + align_bone_orientation(self.obj, bone, self.follow_bone) + + @stage.configure_bones + def configure_mch_follow_bones(self): + self.prop_bone, panel = self.get_parent_master_panel(self.default_prop_bone) + + for org, name, bone, defval, copy_scale in self.rotation_bones: + textname = name.replace('_',' ').title() + ' Follow' + + self.make_property(self.prop_bone, name+'_follow', default=float(defval)) + panel.custom_prop(self.prop_bone, name+'_follow', text=textname, slider=True) + + @stage.rig_bones + def rig_mch_follow_bones(self): + for org, name, bone, defval, copy_scale in self.rotation_bones: + self.rig_mch_rotation_bone(bone, name+'_follow', copy_scale) + + def rig_mch_rotation_bone(self, mch, prop_name, copy_scale): + con = self.make_constraint(mch, 'COPY_ROTATION', self.follow_bone) + + self.make_driver(con, 'influence', variables=[(self.prop_bone, prop_name)], polynomial=[1,-1]) + + if copy_scale: + self.make_constraint(mch, 'COPY_SCALE', self.follow_bone) + + #################################################### + # Tweak chain + + @stage.configure_bones + def configure_tweak_chain(self): + super().configure_tweak_chain() + + ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.tweak) + + #################################################### + # Settings + + @classmethod + def add_parameters(self, params): + super().add_parameters(params) + + # Setting up extra layers for the FK and tweak + ControlLayersOption.TWEAK.add_parameters(params) + + @classmethod + def parameters_ui(self, layout, params): + super().parameters_ui(layout, params) + + ControlLayersOption.TWEAK.parameters_ui(layout, params) diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py new file mode 100644 index 00000000..79ec2396 --- /dev/null +++ b/rigify/rigs/spines/super_head.py @@ -0,0 +1,406 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +# <pep8 compliant> + +import bpy + +from itertools import count + +from ...utils.naming import make_derived_name +from ...utils.bones import align_bone_orientation +from ...utils.widgets_basic import create_circle_widget, create_cube_widget +from ...utils.widgets_special import create_neck_bend_widget, create_neck_tweak_widget +from ...utils.misc import map_list + +from ...base_rig import stage + +from .spine_rigs import BaseHeadTailRig + + +class Rig(BaseHeadTailRig): + """ + Head rig with long neck support and connect option. + """ + + use_connect_reverse = False + + def initialize(self): + super().initialize() + + self.long_neck = len(self.bones.org) > 3 + + #################################################### + # BONES + # + # org[]: + # ORG bones + # ctrl: + # neck, head, neck_bend: + # Main controls. + # tweak[]: + # Tweak control chain. + # mch: + # rot_neck, rot_head: + # Main control parents, implement FK follow. + # stretch + # Long neck stretch behavior. + # ik[] + # Long neck IK behavior. + # chain[] + # Tweak parents. + # deform[]: + # DEF bones + # + #################################################### + + #################################################### + # Main control bones + + @stage.generate_bones + def make_control_chain(self): + orgs = self.bones.org + ctrl = self.bones.ctrl + + ctrl.neck = self.make_neck_control_bone(orgs[0], 'neck', orgs[-1]) + ctrl.head = self.make_head_control_bone(orgs[-1], 'head') + if self.long_neck: + ctrl.neck_bend = self.make_neck_bend_control_bone(orgs[0], 'neck_bend', ctrl.neck) + + self.default_prop_bone = ctrl.head + + def make_neck_control_bone(self, org, name, org_head): + name = self.copy_bone(org, name, parent=False) + + # Neck spans all neck bones (except head) + self.get_bone(name).tail = self.get_bone(org_head).head + + return name + + def make_neck_bend_control_bone(self, org, name, neck): + name = self.copy_bone(org, name, parent=False) + neck_bend_eb = self.get_bone(name) + + # Neck pivot position + neck_bones = self.bones.org + if (len(neck_bones)-1) % 2: # odd num of neck bones (head excluded) + center_bone = self.get_bone(neck_bones[int((len(neck_bones))/2) - 1]) + neck_bend_eb.head = (center_bone.head + center_bone.tail)/2 + else: + center_bone = self.get_bone(neck_bones[int((len(neck_bones)-1)/2) - 1]) + neck_bend_eb.head = center_bone.tail + + align_bone_orientation(self.obj, name, neck) + neck_bend_eb.length = self.get_bone(neck).length / 2 + + return name + + def make_head_control_bone(self, org, name): + return self.copy_bone(org, name, parent=False) + + @stage.parent_bones + def parent_control_chain(self): + ctrl = self.bones.ctrl + mch = self.bones.mch + self.set_bone_parent(ctrl.neck, mch.rot_neck) + self.set_bone_parent(ctrl.head, mch.rot_head) + if self.long_neck: + self.set_bone_parent(ctrl.neck_bend, mch.stretch) + + @stage.configure_bones + def configure_control_chain(self): + self.configure_control_bone(0, self.bones.ctrl.neck, self.bones.org[0]) + self.configure_control_bone(2, self.bones.ctrl.head, self.bones.org[-1]) + if self.long_neck: + self.configure_control_bone(1, self.bones.ctrl.neck_bend, self.bones.org[0]) + + @stage.generate_widgets + def make_control_widgets(self): + ctrl = self.bones.ctrl + self.make_neck_widget(ctrl.neck) + self.make_head_widget(ctrl.head) + if self.long_neck: + self.make_neck_bend_widget(ctrl.neck_bend) + + def make_neck_widget(self, ctrl): + radius = 1/max(1, len(self.bones.mch.chain)) + + create_circle_widget( + self.obj, ctrl, + radius=radius, + head_tail=0.5, + bone_transform_name=None + ) + + def make_neck_bend_widget(self, ctrl): + radius = 1/max(1, len(self.bones.mch.chain)) + + create_neck_bend_widget( + self.obj, ctrl, + radius=radius/2, + head_tail=0.0, + bone_transform_name=None + ) + + def make_head_widget(self, ctrl): + # place wgt @ middle of head bone for long necks + if self.long_neck: + head_tail = 0.5 + else: + head_tail = 1.0 + + create_circle_widget( + self.obj, ctrl, + radius = 0.5, + head_tail = head_tail, + with_line = False, + bone_transform_name = None + ) + + #################################################### + # MCH bones associated with main controls + + @stage.generate_bones + def make_mch_control_bones(self): + orgs = self.bones.org + mch = self.bones.mch + + mch.rot_neck = self.make_mch_follow_bone(orgs[0], 'neck', 0.5, copy_scale=True) + mch.rot_head = self.make_mch_follow_bone(orgs[-1], 'head', 0.0, copy_scale=True) + mch.stretch = self.make_mch_stretch_bone(orgs[0], 'STR-neck', orgs[-1]) + + def make_mch_stretch_bone(self, org, name, org_head): + name = self.copy_bone(org, make_derived_name(name, 'mch'), parent=False) + self.get_bone(name).tail = self.get_bone(org_head).head + return name + + @stage.parent_bones + def parent_mch_control_bones(self): + self.set_bone_parent(self.bones.mch.rot_neck, self.rig_parent_bone) + self.set_bone_parent(self.bones.mch.rot_head, self.bones.ctrl.neck) + self.set_bone_parent(self.bones.mch.stretch, self.bones.ctrl.neck) + + @stage.rig_bones + def rig_mch_control_bones(self): + self.rig_mch_stretch_bone(self.bones.mch.stretch, self.bones.ctrl.head) + + def rig_mch_stretch_bone(self, mch, head): + self.make_constraint(mch, 'DAMPED_TRACK', head) + self.make_constraint(mch, 'STRETCH_TO', head) + + #################################################### + # MCH IK chain for the long neck + + @stage.generate_bones + def make_mch_ik_chain(self): + orgs = self.bones.org + if self.long_neck: + self.bones.mch.ik = map_list(self.make_mch_ik_bone, orgs[0:-1]) + + def make_mch_ik_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch', '_ik'), parent=False) + + @stage.parent_bones + def parent_mch_ik_chain(self): + if self.long_neck: + ik = self.bones.mch.ik + self.set_bone_parent(ik[0], self.bones.ctrl.tweak[0]) + self.parent_bone_chain(ik, use_connect=True) + + @stage.rig_bones + def rig_mch_ik_chain(self): + if self.long_neck: + ik = self.bones.mch.ik + head = self.bones.ctrl.head + for args in zip(count(0), ik): + self.rig_mch_ik_bone(*args, len(ik), head) + + def rig_mch_ik_bone(self, i, mch, ik_len, head): + if i == ik_len - 1: + self.make_constraint(mch, 'IK', head, chain_count=ik_len) + + self.get_bone(mch).ik_stretch = 0.1 + + #################################################### + # MCH chain for the middle of the neck + + @stage.generate_bones + def make_mch_chain(self): + orgs = self.bones.org + self.bones.mch.chain = map_list(self.make_mch_bone, orgs[1:-1]) + + def make_mch_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch'), parent=False, scale=1/4) + + @stage.parent_bones + def align_mch_chain(self): + for mch in self.bones.mch.chain: + align_bone_orientation(self.obj, mch, self.bones.ctrl.neck) + + @stage.parent_bones + def parent_mch_chain(self): + mch = self.bones.mch + for bone in mch.chain: + self.set_bone_parent(bone, mch.stretch) + self.get_bone(bone).use_inherit_scale = False + + @stage.rig_bones + def rig_mch_chain(self): + chain = self.bones.mch.chain + if self.long_neck: + ik = self.bones.mch.ik + for args in zip(count(0), chain, ik[1:]): + self.rig_mch_bone_long(*args, len(chain)) + else: + for args in zip(count(0), chain): + self.rig_mch_bone(*args, len(chain)) + + def rig_mch_bone_long(self, i, mch, ik, len_mch): + ctrl = self.bones.ctrl + + self.make_constraint(mch, 'COPY_LOCATION', ik) + + step = 2/(len_mch+1) + xval = (i+1)*step + influence = 2*xval - xval**2 #parabolic influence of pivot + + self.make_constraint( + mch, 'COPY_LOCATION', ctrl.neck_bend, + influence=influence, use_offset=True, space='LOCAL' + ) + + self.make_constraint(mch, 'COPY_SCALE', ctrl.neck) + + def rig_mch_bone(self, i, mch, len_mch): + ctrl = self.bones.ctrl + + nfactor = float((i + 1) / (len_mch + 1)) + self.make_constraint( + mch, 'COPY_ROTATION', ctrl.head, + influence=nfactor, space='LOCAL' + ) + + #################################################### + # Tweak bones + + @stage.generate_bones + def make_tweak_chain(self): + orgs = self.bones.org + self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), orgs[0:-1]) + + @stage.parent_bones + def parent_tweak_chain(self): + ctrl = self.bones.ctrl + mch = self.bones.mch + + for args in zip(ctrl.tweak, [ctrl.neck, *mch.chain]): + self.set_bone_parent(*args) + + @stage.rig_bones + def generate_neck_tweak_widget(self): + # Generate the widget early to override connected parent + if self.long_neck: + bone = self.bones.ctrl.tweak[0] + create_neck_tweak_widget(self.obj, bone, size=1.0) + + #################################################### + # ORG and DEF bones + + @stage.configure_bones + def configure_bbone_chain(self): + self.get_bone(self.bones.deform[-1]).bone.bbone_segments = 1 + + @stage.rig_bones + def rig_org_chain(self): + tweaks = self.bones.ctrl.tweak + [self.bones.ctrl.head] + for args in zip(count(0), self.bones.org, tweaks, tweaks[1:] + [None]): + self.rig_org_bone(*args) + + +def create_sample(obj, *, parent=None): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('neck') + bone.head[:] = 0.0000, 0.0114, 1.6582 + bone.tail[:] = 0.0000, -0.0130, 1.7197 + bone.roll = 0.0000 + bone.use_connect = False + if parent: + bone.parent = arm.edit_bones[parent] + bones['neck'] = bone.name + bone = arm.edit_bones.new('neck.001') + bone.head[:] = 0.0000, -0.0130, 1.7197 + bone.tail[:] = 0.0000, -0.0247, 1.7813 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck']] + bones['neck.001'] = bone.name + bone = arm.edit_bones.new('head') + bone.head[:] = 0.0000, -0.0247, 1.7813 + bone.tail[:] = 0.0000, -0.0247, 1.9796 + bone.roll = 0.0000 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck.001']] + bones['head'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['neck']] + pbone.rigify_type = 'spines.super_head' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + try: + pbone.rigify_parameters.connect_chain = bool(parent) + except AttributeError: + pass + try: + pbone.rigify_parameters.tweak_layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + except AttributeError: + pass + pbone = obj.pose.bones[bones['neck.001']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone = obj.pose.bones[bones['head']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone diff --git a/rigify/rigs/spines/super_spine.py b/rigify/rigs/spines/super_spine.py index ebc76fcb..3f7e41e1 100644 --- a/rigify/rigs/spines/super_spine.py +++ b/rigify/rigs/spines/super_spine.py @@ -1,1007 +1,108 @@ -import bpy -from mathutils import Vector -from ...utils import copy_bone, flip_bone, put_bone, org, align_bone_y_axis, align_bone_x_axis -from ...utils import strip_org, make_deformer_name, connected_children_names -from ...utils import create_circle_widget, create_sphere_widget, create_neck_bend_widget, create_neck_tweak_widget -from ..widgets import create_ballsocket_widget -from ...utils import MetarigError, make_mechanism_name, create_cube_widget -from ...utils import ControlLayersOption -from ...utils.mechanism import make_property, make_driver - -from ...utils.switch_parent import SwitchParentBuilder - -script = """ -controls = [%s] -torso = '%s' - -if is_selected( controls ): - if hasattr(pose_bones[torso],'["%s"]'): - layout.prop( pose_bones[ torso ], '["%s"]', slider = True ) - if hasattr(pose_bones[torso],'["%s"]'): - layout.prop( pose_bones[ torso ], '["%s"]', slider = True ) - if hasattr(pose_bones[torso],'["%s"]'): - layout.prop( pose_bones[ torso ], '["%s"]', slider = True ) -""" - - -class Rig: - - def __init__(self, obj, bone_name, params): - """ Initialize torso rig and key rig properties """ - - eb = obj.data.edit_bones - - self.obj = obj - self.org_bones = [bone_name] + connected_children_names(obj, bone_name) - self.params = params - # self.spine_length = sum([eb[b].length for b in self.org_bones]) - self.copy_rotation_axes = params.copy_rotation_axes - self.use_head = params.use_head - self.use_tail = params.use_tail - - # Check if user provided the pivot position - if params.pivot_pos: - self.pivot_pos = params.pivot_pos + 1 - else: - raise MetarigError( - "RIGIFY ERROR: please specify pivot bone position" - ) - - # Check if neck is lower than pivot - if self.use_head and params.neck_pos <= params.pivot_pos and params.neck_pos != 0: - raise MetarigError( - "RIGIFY ERROR: Neck cannot be below or the same as pivot. (use 0 for no neck)" - ) - else: - self.neck_pos = params.neck_pos - - if not self.use_head: - self.neck_pos = len(self.org_bones) - - if self.use_tail and self.pivot_pos - 2 > 0: - self.tail_pos = params.tail_pos - - # Report error of user created less than the minimum of bones for rig - min_bone_number = 3 - if self.use_head: - min_bone_number += 1 - if self.use_tail: - min_bone_number += 2 - - if len(self.org_bones) < min_bone_number: - raise MetarigError( - "RIGIFY ERROR: invalid rig structure on %s" % (strip_org(bone_name)) - ) - - def build_bone_structure(self): - """ Divide meta-rig into lists of bones according to torso rig anatomy: - Neck --> Upper torso --> Lower torso --> Tail (optional) """ - - if self.pivot_pos and (self.neck_pos == 0 or self.neck_pos > self.pivot_pos): - - neck_index = self.neck_pos - 1 - pivot_index = self.pivot_pos - 1 - - tail_index = 0 - if self.use_tail and self.tail_pos > 1: # 2 bones for the tail at least - tail_index = self.tail_pos - 1 - - if self.use_head: - neck_bones = self.org_bones[neck_index::] - upper_torso_bones = self.org_bones[pivot_index :neck_index] - else: - neck_bones = [] - upper_torso_bones = self.org_bones[pivot_index ::] - - tail_bones = [] - if tail_index: - lower_torso_bones = self.org_bones[tail_index + 1:pivot_index ] - tail_bones = self.org_bones[:tail_index+1] - else: - lower_torso_bones = self.org_bones[:pivot_index ] - - torso_bones = upper_torso_bones + lower_torso_bones - eb = self.obj.data.edit_bones - self.spine_length = sum([eb[b].length for b in torso_bones]) - - return { - 'neck': neck_bones, - 'upper': upper_torso_bones, - 'lower': lower_torso_bones, - 'tail': tail_bones - } - - else: - return 'ERROR' - - def orient_bone(self, eb, axis, scale, reverse=False): - v = Vector((0,0,0)) - - setattr(v, axis, scale) - - if reverse: - tail_vec = v @ self.obj.matrix_world - eb.head[:] = eb.tail - eb.tail[:] = eb.head + tail_vec - else: - tail_vec = v @ self.obj.matrix_world - eb.tail[:] = eb.head + tail_vec - - def create_pivot(self, pivot): - """ Create the pivot control and mechanism bones """ - org_bones = self.org_bones - pivot_name = org_bones[pivot-1] - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - # Create torso control bone - torso_name = 'torso' - ctrl_name = copy_bone(self.obj, pivot_name, torso_name) - ctrl_eb = eb[ctrl_name] - - self.orient_bone(ctrl_eb, 'y', self.spine_length * 0.6) - - # Create mch_pivot - mch_name = make_mechanism_name('pivot') - mch_name = copy_bone(self.obj, ctrl_name, mch_name) - mch_eb = eb[mch_name] - - mch_eb.length /= 4 - - # Positioning pivot in a more usable location for animators - # if self.use_tail and self.tail_pos > 0: - # pivot_loc = eb[org_bones[pivot-1]].head - if self.use_tail and self.tail_pos > 0: - first_torso_bone = self.tail_pos - pivot_loc = (eb[org_bones[first_torso_bone]].head + eb[org_bones[first_torso_bone]].tail)/2 - else: - pivot_loc = (eb[org_bones[0]].head + eb[org_bones[0]].tail) / 2 - - put_bone(self.obj, ctrl_name, pivot_loc) - - return { - 'ctrl': ctrl_name, - 'mch': mch_name - } - - def create_deform(self): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - def_bones = [] - for org_b in org_bones: - def_name = make_deformer_name(strip_org(org_b)) - def_name = copy_bone(self.obj, org_b, def_name) - def_bones.append(def_name) - - return def_bones - - def create_neck(self, neck_bones): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - if not self.use_head: - return { - 'ctrl_neck': '', - 'ctrl': '', - 'mch_str': '', - 'mch_neck': '', - 'mch_head': '', - 'mch': [], - 'tweak': [], - 'neck_bend': '', - 'original_names': neck_bones - } - - neck, neck_bend = '', '' - if len(neck_bones) >= 2: - # Create neck control - neck = copy_bone(self.obj, org(neck_bones[0]), 'neck') - neck_eb = eb[neck] - - # Neck spans all neck bones (except head) - neck_eb.tail[:] = eb[org(neck_bones[-1])].head - - if len(neck_bones) > 3: - - # Create neck bend control - neck_bend = copy_bone(self.obj, org(neck_bones[0]), 'neck_bend') - neck_bend_eb = eb[neck_bend] - - # Neck pivot position - if (len(neck_bones)-1) % 2: # odd num of neck bones (head excluded) - center_bone = org(neck_bones[int((len(neck_bones))/2) - 1]) - neck_bend_eb.head = (eb[center_bone].head + eb[center_bone].tail)/2 - else: - center_bone = org(neck_bones[int((len(neck_bones)-1)/2) - 1]) - neck_bend_eb.head = eb[center_bone].tail - - align_bone_y_axis(self.obj, neck_bend, eb[neck].y_axis) - align_bone_x_axis(self.obj, neck_bend, eb[neck].x_axis) - eb[neck_bend].length = eb[neck].length / 2 - - # Create head control - head = copy_bone(self.obj, org(neck_bones[-1]), 'head') - - # MCH bones - mch_str, mch_neck = '', '' - if len(neck_bones) >= 2: - # Neck MCH stretch - mch_str = copy_bone(self.obj, neck, make_mechanism_name('STR-neck')) - - # Neck MCH rotation - mch_neck = copy_bone( - self.obj, neck, make_mechanism_name('ROT-neck') - ) - - self.orient_bone(eb[mch_neck], 'y', self.spine_length / 10) - - # Head MCH rotation - mch_head = copy_bone(self.obj, head, make_mechanism_name('ROT-head')) - self.orient_bone(eb[mch_head], 'y', self.spine_length / 10) - - twk, mch = [], [] - - if len(neck_bones) >= 2: - # Intermediary bones - for b in neck_bones[1:-1]: # All except 1st (neck) and last (head) - mch_name = copy_bone(self.obj, org(b), make_mechanism_name(b)) - eb[mch_name].length /= 4 - align_bone_y_axis(self.obj, mch_name, eb[neck].y_axis) - align_bone_x_axis(self.obj, mch_name, eb[neck].x_axis) - eb[mch_name].use_inherit_scale = False - mch += [mch_name] - - # Tweak bones - for b in neck_bones[:-1]: # All except last bone - twk_name = "tweak_" + b - twk_name = copy_bone(self.obj, org(b), twk_name) - - eb[twk_name].length /= 2 - - twk += [twk_name] - - return { - 'ctrl_neck': neck, - 'ctrl': head, - 'mch_str': mch_str, - 'mch_neck': mch_neck, - 'mch_head': mch_head, - 'mch': mch, - 'tweak': twk, - 'neck_bend': neck_bend, - 'original_names': neck_bones - } - - def create_chest(self, chest_bones): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - # get total spine length - - # Create chest control bone - chest = copy_bone(self.obj, org(chest_bones[0]), 'chest') - self.orient_bone(eb[chest], 'y', self.spine_length / 3) - - # create chest mch_wgt - mch_wgt = copy_bone( - self.obj, org(chest_bones[-1]), - make_mechanism_name('WGT-chest') - ) - - # Create mch and twk bones - twk, mch = [], [] - - for b in chest_bones: - mch_name = copy_bone( self.obj, org(b), make_mechanism_name(b) ) - self.orient_bone( eb[mch_name], 'y', self.spine_length / 10 ) - - twk_name = "tweak_" + b - twk_name = copy_bone( self.obj, org(b), twk_name ) - eb[twk_name].length /= 2 - - mch += [ mch_name ] - twk += [ twk_name ] - - return { - 'ctrl' : chest, - 'mch' : mch, - 'tweak' : twk, - 'mch_wgt' : mch_wgt - } - - def create_hips(self, hip_bones): - org_bones = self.org_bones - - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - # Create hips control bone - hips = copy_bone(self.obj, org(hip_bones[-1]), 'hips') - self.orient_bone( - eb[hips], - 'y', - self.spine_length / 4, - reverse = True - ) - - # create hips mch_wgt - mch_wgt = copy_bone( - self.obj, org(hip_bones[0]), - make_mechanism_name('WGT-hips') - ) - - # Create mch and tweak bones - twk, mch = [], [] - for b in hip_bones: - mch_name = copy_bone( self.obj, org(b), make_mechanism_name(b) ) - self.orient_bone( - eb[mch_name], 'y', self.spine_length / 10, reverse = True - ) - - twk_name = "tweak_" + b - twk_name = copy_bone( self.obj, org(b), twk_name ) - - eb[twk_name].length /= 2 - - mch += [ mch_name ] - twk += [ twk_name ] - - return { - 'ctrl' : hips, - 'mch' : mch, - 'tweak' : twk, - 'mch_wgt' : mch_wgt - } - - def create_tail(self, tail_bones): - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - org_bones = self.org_bones - - ctrl_chain = [] - for i in range(len(tail_bones)): - name = tail_bones[i] - - ctrl_bone = copy_bone( - self.obj, - org(name), - strip_org(name) - ) - - flip_bone(self.obj, ctrl_bone) - ctrl_chain.append(ctrl_bone) - - # Main ctrl - name = tail_bones[-1] - main_ctrl_bone = copy_bone( - self.obj, - org(name), - strip_org(name).split('.')[0] + "_master" - ) - flip_bone(self.obj, main_ctrl_bone) - - mch_tail = '' - tail_first = org_bones[self.tail_pos-1] - mch_rot_tail = copy_bone( - self.obj, - org(tail_first), - make_mechanism_name("ROT-tail") - ) - - self.orient_bone(eb[mch_rot_tail], 'y', eb[tail_first].length) - put_bone(self.obj, mch_rot_tail, eb[tail_first].tail) - mch_tail = mch_rot_tail - - tweak_chain = [] - for i in range(len(tail_bones)): - name = tail_bones[i] - - tweak_bone = copy_bone( - self.obj, - org(name), - "tweak_" + strip_org(name) - ) - - tweak_e = eb[tweak_bone] - tweak_e.length /= 2 # Set size to half - - # Position tweaks - flip_bone(self.obj, tweak_bone) - put_bone(self.obj, tweak_bone, eb[org(name)].head) - - tweak_chain.append(tweak_bone) - - return { - 'ctrl': ctrl_chain, - 'ctrl_tail': main_ctrl_bone, - 'mch_tail': mch_tail, - 'tweak': tweak_chain, - 'original_names': tail_bones - } - - def parent_bones(self, bones): - org_bones = self.org_bones - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones - - # Parent deform bones - for i, b in enumerate(bones['def']): - if i > 0: # For all bones but the first (which has no parent) - eb[b].parent = eb[bones['def'][i-1]] # to previous - eb[b].use_connect = True - - # Parent control bones - # Head control => MCH-rotation_head - if self.use_head: - eb[bones['neck']['ctrl']].parent = eb[bones['neck']['mch_head']] +#====================== 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> - # Tail control chain - if self.use_tail: - tail_ctrl = bones['tail']['ctrl'] - for i, b in enumerate(tail_ctrl[:-1]): - eb[b].parent = eb[tail_ctrl[i+1]] - eb[tail_ctrl[-1]].parent = eb[bones['tail']['mch_tail']] - eb[bones['tail']['ctrl_tail']].parent = eb[org_bones[self.tail_pos]] - - if bones['neck']['ctrl_neck']: - # MCH stretch => neck ctrl - eb[ bones['neck']['mch_str']].parent = eb[ bones['neck']['ctrl_neck']] - - # Neck control => MCH-rotation_neck - eb[bones['neck']['ctrl_neck']].parent = eb[bones['neck']['mch_neck']] - - # Neck pivot => MCH-rotation_neck - if bones['neck']['neck_bend']: - # eb[bones['neck']['neck_bend']].parent = eb[bones['neck']['ctrl_neck']] - eb[bones['neck']['neck_bend']].parent = eb[bones['neck']['mch_str']] - - # Parent hips and chest controls to torso - eb[bones['chest']['ctrl']].parent = eb[bones['pivot']['ctrl']] - eb[bones['hips']['ctrl']].parent = eb[bones['pivot']['ctrl']] - - # Parent mch bones - if bones['neck']['ctrl_neck']: - # Neck mch - eb[bones['neck']['mch_head']].parent = eb[bones['neck']['ctrl_neck']] - elif self.use_head: - eb[bones['neck']['mch_head']].parent = eb[bones['chest']['mch'][-1]] - - for i, b in enumerate([eb[n] for n in bones['neck']['mch']]): - b.parent = eb[bones['neck']['mch_str']] - # for org_b in bones['neck']['original_names']: - # if org_b in b.name: - # b.parent = eb[org(org_b)] - - # Chest mch bones and neck mch - chest_mch = bones['chest']['mch'] + [bones['neck']['mch_neck']] - for i, b in enumerate(chest_mch): - if i == 0: - eb[b].parent = eb[bones['pivot']['ctrl']] - elif b: - eb[b].parent = eb[chest_mch[i-1]] - - # Hips mch bones - for i, b in enumerate(bones['hips']['mch']): - if i == len(bones['hips']['mch']) - 1: - eb[b].parent = eb[bones['pivot']['ctrl']] - else: - eb[b].parent = eb[bones['hips']['mch'][i+1]] - - # mch pivot - eb[bones['pivot']['mch']].parent = eb[bones['chest']['mch'][0]] - - # MCH widgets - eb[bones['chest']['mch_wgt']].parent = eb[bones['chest']['mch'][-1]] - eb[bones['hips']['mch_wgt']].parent = eb[bones['hips']['mch'][0]] - - # Neck Tweaks - if bones['neck']['tweak']: - # Neck tweaks - for i, twk in enumerate( bones['neck']['tweak']): - if i == 0: - eb[twk].parent = eb[ bones['neck']['ctrl_neck']] - else: - eb[twk].parent = eb[ bones['neck']['mch'][i-1]] - - # Chest tweaks - for twk, mch in zip( bones['chest']['tweak'], bones['chest']['mch']): - if bones['chest']['tweak'].index(twk) == 0: - eb[twk].parent = eb[bones['pivot']['mch']] - else: - eb[twk].parent = eb[mch] - - # Hips tweaks - for i, twk in enumerate(bones['hips']['tweak']): - if i == 0: - eb[twk].parent = eb[bones['hips']['mch'][i]] - else: - eb[twk].parent = eb[bones['hips']['mch'][i-1]] - - # Tail mchs - if self.use_tail: - mch_rot_tail = bones['tail']['mch_tail'] - eb[mch_rot_tail].parent = eb[bones['hips']['tweak'][0]] - - # Tail tweaks - if self.use_tail: - for i, twk in enumerate(bones['tail']['tweak']): - if i == 0: - eb[twk].parent = eb[bones['tail']['ctrl'][i]] - else: - eb[twk].parent = eb[bones['tail']['ctrl'][i-1]] - - # Parent orgs to matching tweaks - tweaks = [] - if self.use_tail: - tweaks += bones['tail']['tweak'] - - tweaks += bones['hips']['tweak'] + bones['chest']['tweak'] - if self.use_head: - tweaks += bones['neck']['tweak'] + [bones['neck']['ctrl']] - - original_neck_bones = [org(b) for b in bones['neck']['original_names']] - original_neck_bones = original_neck_bones[1:-1] # exclude first neck bone and head - for b, twk in zip(org_bones[:-1], tweaks): - if b in original_neck_bones and len(bones['neck']['original_names']) > 3: - idx = org_bones.index(b) - org_parent = org_bones[idx-1] - eb[b].parent = eb[org_parent] - else: - eb[b].parent = eb[twk] - - if self.use_head: - eb[org_bones[-1]].parent = eb[bones['neck']['ctrl']] - - def make_constraint(self, bone, constraint): - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - owner_pb = pb[bone] - const = owner_pb.constraints.new(constraint['constraint']) - const.target = self.obj - - # filter constraint props to those that actually exist in the currnet - # type of constraint, then assign values to each - for p in [k for k in constraint.keys() if k in dir(const)]: - setattr(const, p, constraint[p]) - - def constrain_bones(self, bones): - # MCH bones - - # head and neck MCH bones - for b in [bones['neck']['mch_head'], bones['neck']['mch_neck']]: - if b: - self.make_constraint(b, { - 'constraint': 'COPY_ROTATION', - 'subtarget': bones['pivot']['ctrl'], - }) - self.make_constraint(b, { - 'constraint': 'COPY_SCALE', - 'subtarget': bones['pivot']['ctrl'], - }) - - if bones['neck']['mch_str']: - # Neck MCH Stretch - self.make_constraint(bones['neck']['mch_str'], { - 'constraint': 'DAMPED_TRACK', - 'subtarget': bones['neck']['ctrl'], - }) - self.make_constraint(bones['neck']['mch_str'], { - 'constraint': 'STRETCH_TO', - 'subtarget': bones['neck']['ctrl'], - }) - - # Intermediary mch bones - intermediaries = [bones['neck'], bones['chest'], bones['hips']] - - for i, l in enumerate(intermediaries): - mch = l['mch'] - - for j, b in enumerate(mch): - - if i == 0: # Neck mch-s - if len(bones['neck']['original_names']) > 3: - self.make_constraint(b, { - 'constraint': 'COPY_LOCATION', - 'subtarget': org(l['original_names'][j+1]), - 'influence': 1.0 - }) - else: - nfactor = float((j + 1) / len(mch)) - self.make_constraint(b, { - 'constraint': 'COPY_ROTATION', - 'subtarget': l['ctrl'], - 'influence': nfactor - }) - - step = 2/(len(mch)+1) - xval = (j+1)*step - influence = 2*xval - xval**2 #parabolic influence of pivot - - if bones['neck']['neck_bend']: - self.make_constraint(b, { - 'constraint': 'COPY_LOCATION', - 'subtarget': l['neck_bend'], - 'influence': influence, - 'use_offset': True, - 'owner_space': 'LOCAL', - 'target_space': 'LOCAL' - }) - - if len(bones['neck']['original_names']) > 3: - self.make_constraint(b, { - 'constraint': 'COPY_SCALE', - 'subtarget': bones['neck']['ctrl_neck'], - 'influence': 1.0 - }) - - else: - factor = float(1 / len(l['tweak'])) - self.make_constraint(b, { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': l['ctrl'], - 'influence': factor, - 'owner_space': 'LOCAL', - 'target_space': 'LOCAL' - }) - - # Tail ctrls - if self.use_tail: - tail_ctrl = bones['tail']['ctrl'] - tail_ctrl.append(bones['tail']['ctrl_tail']) - - for i, b in enumerate(tail_ctrl[:-1]): - self.make_constraint(b, { - 'constraint': 'COPY_ROTATION', - 'subtarget': tail_ctrl[i+1], - 'influence': 1.0, - 'use_x': self.copy_rotation_axes[0], - 'use_y': self.copy_rotation_axes[1], - 'use_z': self.copy_rotation_axes[2], - 'use_offset': True, - 'owner_space': 'LOCAL', - 'target_space': 'LOCAL' - }) - - b = bones['tail']['mch_tail'] - self.make_constraint(b, { - 'constraint': 'COPY_ROTATION', - 'subtarget': bones['pivot']['ctrl'], - 'influence': 1.0, - }) - - # MCH pivot - self.make_constraint(bones['pivot']['mch'], { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': bones['hips']['mch'][-1], - 'owner_space': 'LOCAL', - 'target_space': 'LOCAL' - }) - - # DEF bones - deform = bones['def'] - tweaks = [] - if self.use_tail: - tweaks += bones['tail']['tweak'] - - tweaks += bones['hips']['tweak'] + bones['chest']['tweak'] - if self.use_head: - tweaks += bones['neck']['tweak'] + [bones['neck']['ctrl']] - - for d, t in zip(deform, tweaks): - tidx = tweaks.index(t) - - self.make_constraint(d, { - 'constraint': 'COPY_TRANSFORMS', - 'subtarget': t - }) - - if tidx != len(tweaks) - 1: - if self.use_tail and t in bones['tail']['tweak']: - self.make_constraint(d, { - 'constraint': 'DAMPED_TRACK', - 'subtarget': tweaks[tidx + 1], - 'track_axis': 'TRACK_NEGATIVE_Y' - }) - else: - self.make_constraint(d, { - 'constraint': 'DAMPED_TRACK', - 'subtarget': tweaks[tidx + 1], - }) - - self.make_constraint(d, { - 'constraint': 'STRETCH_TO', - 'subtarget': tweaks[tidx + 1], - }) - - pb = self.obj.pose.bones - - if bones['neck']['neck_bend']: - pb[bones['neck']['neck_bend']].rotation_mode = 'ZXY' - pb[bones['neck']['neck_bend']].lock_rotation[0] = True - pb[bones['neck']['neck_bend']].lock_rotation[2] = True - - for t in tweaks: - if t != bones['neck']['ctrl']: - pb[t].rotation_mode = 'ZXY' - - original_neck_bones = [org(b) for b in bones['neck']['original_names']] - # make IK on neck ORGs - if len(original_neck_bones) > 3: - last_neck = original_neck_bones[-2] - self.make_constraint(last_neck, { - 'constraint': 'IK', - 'subtarget': bones['neck']['ctrl'], - 'chain_count': len(original_neck_bones) - 1 - }) - - for b in original_neck_bones[:-1]: - pb[b].ik_stretch = 0.1 - - def create_drivers(self, bones): - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - # Setting the torso's props - torso = pb[bones['pivot']['ctrl']] - - props = [] - owners = [] - - if self.use_head: - props += ["head_follow"] - owners += [bones['neck']['mch_head']] - if bones['neck']['mch_neck']: - props += ["neck_follow"] - owners += [bones['neck']['mch_neck']] - if self.use_tail: - props += ["tail_follow"] - owners += [bones['tail']['mch_tail']] - - for prop in props: - if prop == 'neck_follow': - defval = 0.5 - else: - defval = 0.0 - - make_property(torso, prop, defval) - - # driving the follow rotation switches for neck and head - for bone, prop, in zip(owners, props): - # Add driver to copy rotation constraint - make_driver(pb[bone].constraints[0], "influence", variables=[(self.obj, torso, prop)], polynomial=[1.0, -1.0]) - - def locks_and_widgets(self, bones): - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - # deform bones bbone segments - for bone in bones['def'][:-1]: - self.obj.data.bones[bone].bbone_segments = 8 - - self.obj.data.bones[bones['def'][0]].bbone_easein = 0.0 - self.obj.data.bones[bones['def'][-2]].bbone_easeout = 1.0 - - # Locks - tweaks = bones['neck']['tweak'] + bones['chest']['tweak'] - tweaks += bones['hips']['tweak'] - - if self.use_tail: - tweaks += bones['tail']['tweak'] - pb[bones['tail']['ctrl_tail']].lock_location = True, True, True - - # Tweak bones locks - for bone in tweaks: - pb[bone].lock_rotation = True, False, True - pb[bone].lock_scale = False, True, False - - # Widgets - - # Assigning a widget to torso bone - create_cube_widget( - self.obj, - bones['pivot']['ctrl'], - radius=0.5, - bone_transform_name=None - ) - - # Assigning widgets to control bones - gen_ctrls = [ - bones['chest']['ctrl'], - bones['hips']['ctrl'] - ] - - tail_ctrls = [] - if self.use_tail and bones['tail']['ctrl']: - tail_ctrls = bones['tail']['ctrl'] + [bones['tail']['ctrl_tail']] - gen_ctrls.extend(bones['tail']['ctrl']) - - create_ballsocket_widget( - self.obj, - bones['tail']['ctrl_tail'], - size=0.7, - bone_transform_name=None - ) - - for bone in gen_ctrls: - - if bone in tail_ctrls: - radius = 0.5 - else: - radius = 1.0 - - # place chest on neck-base for very long necks - if bone == bones['chest']['ctrl'] and len(bones['neck']['original_names']) > 3: - head_tail = 0.0 - else: - head_tail = 0.75 - - create_circle_widget( - self.obj, - bone, - radius=radius, - head_tail=head_tail, - with_line=False, - bone_transform_name=None - ) - - if bones['neck']['ctrl_neck']: - # Neck ctrl widget - if len(bones['neck']['mch']) == 0: - radius = 1 - else: - radius = 1/(len(bones['neck']['mch'])) - create_circle_widget( - self.obj, - bones['neck']['ctrl_neck'], - radius=radius, - head_tail=0.5, - bone_transform_name=None - ) - - if bones['neck']['neck_bend']: - # Neck pivot widget - if len(bones['neck']['mch']) == 0: - radius = 0.5 - else: - radius = 1/(2*len(bones['neck']['mch'])) - create_neck_bend_widget( - self.obj, - bones['neck']['neck_bend'], - radius=radius, - head_tail=0.0, - bone_transform_name=None - ) +import bpy - # Head widget - # place wgt @ middle of head bone for long necks - if len(bones['neck']['original_names']) > 3: - head_tail = 0.5 - else: - head_tail = 1.0 - if self.use_head: - create_circle_widget( - self.obj, - bones['neck']['ctrl'], - radius = 0.5, - head_tail = head_tail, - with_line = False, - bone_transform_name = None - ) +from ...utils.rig import connected_children_names +from ...utils.layers import ControlLayersOption +from ...utils.bones import BoneUtilityMixin, flip_bone_chain - # place widgets on correct bones - chest_widget_loc = pb[bones['chest']['mch_wgt']] - pb[bones['chest']['ctrl']].custom_shape_transform = chest_widget_loc +from ...base_generate import SubstitutionRig - hips_widget_loc = pb[bones['hips']['mch_wgt']] +from . import basic_spine, basic_tail, super_head - if self.use_tail: - hips_widget_loc = pb[bones['def'][self.tail_pos]] - pb[bones['tail']['ctrl_tail']].custom_shape_transform = pb[bones['tail']['tweak'][0]] - pb[bones['hips']['ctrl']].custom_shape_transform = hips_widget_loc +class Rig(SubstitutionRig, BoneUtilityMixin): + """Compatibility proxy for the monolithic super_spine rig that splits it into parts.""" - # Assigning widgets to tweak bones and layers - for bone in tweaks: + def substitute(self): + params_copy = dict(self.params) + orgs = [self.base_bone] + connected_children_names(self.obj, self.base_bone) - if bones['neck']['tweak'] and bone == bones['neck']['tweak'][0] \ - and len(bones['neck']['original_names']) > 3: - create_neck_tweak_widget(self.obj, bone, size=1.0, bone_transform_name=None) - continue - create_sphere_widget(self.obj, bone, bone_transform_name=None) + # Split the bone list according to the settings + spine_orgs = orgs + head_orgs = None + tail_orgs = None - ControlLayersOption.TWEAK.assign(self.params, pb, tweaks) + pivot_pos = self.params.pivot_pos - def generate(self): - # Torso Rig Anatomy: - # Neck: all bones above neck point, last bone is head - # Upper torso: all bones between pivot and neck start - # Lower torso: all bones below pivot until tail point - # Tail: all bones below tail point + if self.params.use_head: + neck_pos = self.params.neck_pos + if neck_pos <= pivot_pos: + self.raise_error("Neck cannot be below or the same as pivot.") + if neck_pos >= len(orgs): + self.raise_error("Neck is too short.") - bone_chains = self.build_bone_structure() + spine_orgs = orgs[0 : neck_pos-1] + head_orgs = orgs[neck_pos-1 : ] - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + if self.params.use_tail: + tail_pos = self.params.tail_pos + if tail_pos < 2: + self.raise_error("Tail is too short.") + if tail_pos >= pivot_pos: + self.raise_error("Tail cannot be above or the same as pivot.") - # Clear parents for org bones - for bone in self.org_bones: - eb[bone].use_connect = False - eb[bone].parent = None + tail_orgs = list(reversed(spine_orgs[0 : tail_pos])) + spine_orgs = spine_orgs[tail_pos : ] + pivot_pos -= tail_pos - if bone_chains != 'ERROR': + # Split the bone chain and flip the tail + if head_orgs or tail_orgs: + bpy.ops.object.mode_set(mode='EDIT') - # Create lists of bones and strip "ORG" from their names - neck_bones = [strip_org(b) for b in bone_chains['neck']] - upper_torso_bones = [strip_org(b) for b in bone_chains['upper']] - lower_torso_bones = [strip_org(b) for b in bone_chains['lower']] - tail_bones = [strip_org(b) for b in bone_chains['tail']] + if spine_orgs[0] != orgs[0]: + self.set_bone_parent(spine_orgs[0], self.get_bone_parent(orgs[0])) - bones = {} + if head_orgs: + self.get_bone(head_orgs[0]).use_connect = False - bones['def'] = self.create_deform() # Gets org bones from self - bones['pivot'] = self.create_pivot(self.pivot_pos) - bones['neck'] = self.create_neck(neck_bones) - bones['chest'] = self.create_chest(upper_torso_bones) - bones['hips'] = self.create_hips(lower_torso_bones) + if tail_orgs: + flip_bone_chain(self.obj, reversed(tail_orgs)) + self.set_bone_parent(tail_orgs[0], spine_orgs[0]) - # Register viable parent bones - pbuilder = SwitchParentBuilder(self.rigify_generator) - pbuilder.register_parent(self.rigify_wrapper, bones['pivot']['ctrl'], name='Torso') - pbuilder.register_parent(self.rigify_wrapper, bone_chains['lower'][0], name='Hips') - pbuilder.register_parent(self.rigify_wrapper, bone_chains['upper'][-1], name='Chest') - if self.use_head: - pbuilder.register_parent(self.rigify_wrapper, bone_chains['neck'][-1], name='Head') + bpy.ops.object.mode_set(mode='OBJECT') - # TODO: Add create tail - if tail_bones: - bones['tail'] = self.create_tail(tail_bones) + # Create the parts + self.assign_params(spine_orgs[0], params_copy, pivot_pos=pivot_pos) - # TEST - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + result = [ self.instantiate_rig(basic_spine.Rig, spine_orgs[0]) ] - self.parent_bones(bones) - self.constrain_bones(bones) - self.create_drivers(bones) - self.locks_and_widgets(bones) + if tail_orgs: + self.assign_params(tail_orgs[0], params_copy, connect_chain=True) - else: - return + result += [ self.instantiate_rig(basic_tail.Rig, tail_orgs[0]) ] - controls = [bones['neck']['ctrl'], bones['neck']['ctrl_neck']] - controls += [bones['chest']['ctrl'], bones['hips']['ctrl']] - controls += [bones['pivot']['ctrl']] + if head_orgs: + self.assign_params(head_orgs[0], params_copy, connect_chain=True) - if self.use_tail: - controls.extend(bones['tail']['ctrl']) + result += [ self.instantiate_rig(super_head.Rig, head_orgs[0]) ] - # Create UI - controls_string = ", ".join(["'" + x + "'" for x in controls]) - return [script % ( - controls_string, - bones['pivot']['ctrl'], - 'head_follow', - 'head_follow', - 'neck_follow', - 'neck_follow', - 'tail_follow', - 'tail_follow', - )] + return result def add_parameters(params): - """ Add the parameters of this rig type to the - RigifyParameters PropertyGroup - """ + basic_spine.Rig.add_parameters(params) + basic_tail.Rig.add_parameters(params) + super_head.Rig.add_parameters(params) + params.neck_pos = bpy.props.IntProperty( name = 'neck_position', default = 6, @@ -1009,19 +110,6 @@ def add_parameters(params): description = 'Neck start position' ) - params.pivot_pos = bpy.props.IntProperty( - name='pivot_position', - default=2, - min=0, - description='Position of the torso control and pivot point' - ) - - params.copy_rotation_axes = bpy.props.BoolVectorProperty( - size=3, - description="Automation axes", - default=tuple([i == 0 for i in range(0, 3)]) - ) - params.tail_pos = bpy.props.IntProperty( name='tail_position', default=2, @@ -1041,9 +129,6 @@ def add_parameters(params): description='Create head and neck bones' ) - # Setting up extra layers for the FK and tweak - ControlLayersOption.TWEAK.add_parameters(params) - def parameters_ui(layout, params): """ Create the ui for the rig parameters.""" @@ -1074,136 +159,6 @@ def parameters_ui(layout, params): def create_sample(obj): - # generated by rigify.utils.write_metarig - bpy.ops.object.mode_set(mode='EDIT') - arm = obj.data - - bones = {} - - bone = arm.edit_bones.new('spine') - bone.head[:] = 0.0000, 0.0552, 1.0099 - bone.tail[:] = 0.0000, 0.0172, 1.1573 - bone.roll = 0.0000 - bone.use_connect = False - bones['spine'] = bone.name - - bone = arm.edit_bones.new('spine.001') - bone.head[:] = 0.0000, 0.0172, 1.1573 - bone.tail[:] = 0.0000, 0.0004, 1.2929 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['spine']] - bones['spine.001'] = bone.name - - bone = arm.edit_bones.new('spine.002') - bone.head[:] = 0.0000, 0.0004, 1.2929 - bone.tail[:] = 0.0000, 0.0059, 1.4657 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['spine.001']] - bones['spine.002'] = bone.name - - bone = arm.edit_bones.new('spine.003') - bone.head[:] = 0.0000, 0.0059, 1.4657 - bone.tail[:] = 0.0000, 0.0114, 1.6582 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['spine.002']] - bones['spine.003'] = bone.name - - bone = arm.edit_bones.new('spine.004') - bone.head[:] = 0.0000, 0.0114, 1.6582 - bone.tail[:] = 0.0000, -0.013, 1.7197 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['spine.003']] - bones['spine.004'] = bone.name - - bone = arm.edit_bones.new('spine.005') - bone.head[:] = 0.0000, -0.013, 1.7197 - bone.tail[:] = 0.0000, -0.0247, 1.7813 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['spine.004']] - bones['spine.005'] = bone.name - - bone = arm.edit_bones.new('spine.006') - bone.head[:] = 0.0000, -0.0247, 1.7813 - bone.tail[:] = 0.0000, -0.0247, 1.9796 - bone.roll = 0.0000 - bone.use_connect = True - bone.parent = arm.edit_bones[bones['spine.005']] - bones['spine.006'] = bone.name - - - bpy.ops.object.mode_set(mode='OBJECT') - pbone = obj.pose.bones[bones['spine']] - pbone.rigify_type = 'spines.super_spine' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - - try: - pbone.rigify_parameters.neck_pos = 5 - except AttributeError: - pass - try: - pbone.rigify_parameters.tweak_layers = [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] - except AttributeError: - pass - pbone = obj.pose.bones[bones['spine.001']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['spine.002']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['spine.003']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['spine.004']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['spine.005']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - pbone = obj.pose.bones[bones['spine.006']] - pbone.rigify_type = '' - pbone.lock_location = (False, False, False) - pbone.lock_rotation = (False, False, False) - pbone.lock_rotation_w = False - pbone.lock_scale = (False, False, False) - pbone.rotation_mode = 'QUATERNION' - - bpy.ops.object.mode_set(mode='EDIT') - for bone in arm.edit_bones: - bone.select = False - bone.select_head = False - bone.select_tail = False - for b in bones: - bone = arm.edit_bones[bones[b]] - bone.select = True - bone.select_head = True - bone.select_tail = True - arm.edit_bones.active = bone + bones = basic_spine.create_sample(obj) + basic_tail.create_sample(obj, parent=bones['spine']) + super_head.create_sample(obj, parent=bones['spine.003']) diff --git a/rigify/rigs/utils.py b/rigify/rigs/utils.py index c08cb8f9..12dcbb26 100644 --- a/rigify/rigs/utils.py +++ b/rigify/rigs/utils.py @@ -1,8 +1,158 @@ -from .limbs.super_limb import Rig as LimbRig from ..utils import connected_children_names +from ..utils.naming import strip_mch, strip_org, make_mechanism_name import re +def get_future_names_arm(bones): + if len(bones) != 3: + return + + names = dict() + + uarm = strip_mch(strip_org(bones[0].name)) + farm = strip_mch(strip_org(bones[1].name)) + hand = strip_mch(strip_org(bones[2].name)) + + suffix='' + if uarm[-2:] == '.L' or uarm[-2:] == '.R': + suffix = uarm[-2:] + uarm = uarm.rstrip(suffix) + farm = farm.rstrip(suffix) + hand = hand.rstrip(suffix) + + # the following is declared in rig_ui + # controls = ['upper_arm_ik.L', 'upper_arm_fk.L', 'forearm_fk.L', 'hand_fk.L', 'hand_ik.L', 'MCH-hand_fk.L', + # 'upper_arm_parent.L'] + # tweaks = ['upper_arm_tweak.L.001', 'forearm_tweak.L', 'forearm_tweak.L.001'] + # ik_ctrl = ['hand_ik.L', 'MCH-upper_arm_ik.L', 'MCH-upper_arm_ik_target.L'] + # fk_ctrl = 'upper_arm_fk.L' + # parent = 'upper_arm_parent.L' + # hand_fk = 'hand_fk.L' + # pole = 'upper_arm_ik_target.L' + + names['controls'] = [uarm + '_ik', uarm + '_fk', farm + '_fk', hand + '_fk', hand + '_ik', + make_mechanism_name(hand + '_fk'), uarm + '_parent'] + names['ik_ctrl'] = [hand + '_ik', make_mechanism_name(uarm) + '_ik', make_mechanism_name(uarm) + '_ik_target'] + names['fk_ctrl'] = uarm + '_fk' + suffix + names['parent'] = uarm + '_parent' + suffix + names['hand_fk'] = hand + '_fk' + suffix + names['pole'] = uarm + '_ik_target' + suffix + names['limb_type'] = 'arm' + + 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 get_future_names_leg(bones): + 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 get_future_names_paw(bones): + 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'] = 'paw' + + 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 get_future_names(bones): + if bones[0].rigify_parameters.limb_type == 'arm': + return get_future_names_arm(bones) + elif bones[0].rigify_parameters.limb_type == 'leg': + return get_future_names_leg(bones) + elif bones[0].rigify_parameters.limb_type == 'paw': + return get_future_names_paw(bones) + + def get_limb_generated_names(rig): pbones = rig.pose.bones @@ -16,6 +166,6 @@ def get_limb_generated_names(rig): for child in children: if re.match('^ORG', child) or re.match('^MCH', child): super_limb_orgs.append(pbones[child]) - names[b.name] = LimbRig.get_future_names(super_limb_orgs) + names[b.name] = get_future_names(super_limb_orgs) return names |