diff options
author | Nathan Vegdahl <cessen@cessen.com> | 2013-03-01 04:17:14 +0400 |
---|---|---|
committer | Nathan Vegdahl <cessen@cessen.com> | 2013-03-01 04:17:14 +0400 |
commit | 3e316aace81f6305f61e9ac1d87ebc1717079429 (patch) | |
tree | 1ca9d980b3ed359d67534f7bd7baa9f376d2b3ad /rigify/rigs | |
parent | 38cc068b90491ddd4bb2d8dc5fefa251cfd64ba4 (diff) |
Rigify: significant upgrade to arm and leg rigs, and misc changes/bugfixes.
Arm/leg rig upgrades:
- Arms and legs no longer scale with their parents, which was
problematic when e.g. the torso of a character did
squash-and-stretch causing the arms/legs to distort and shear.
- Squash-and-stretch for both FK and IK rigs.
- Rubber hose controls.
Misc changes/bugfixes:
- Rigify now locks all pose transforms for non-control bones
automatically.
- The README file now correctly reflects the new rig-type API.
- Scrubbed the code for unused variables and imports.
- PEP8 cleanups.
Diffstat (limited to 'rigify/rigs')
-rw-r--r-- | rigify/rigs/biped/arm/__init__.py | 116 | ||||
-rw-r--r-- | rigify/rigs/biped/arm/deform.py | 203 | ||||
-rw-r--r-- | rigify/rigs/biped/arm/fk.py | 166 | ||||
-rw-r--r-- | rigify/rigs/biped/arm/ik.py | 290 | ||||
-rw-r--r-- | rigify/rigs/biped/leg/__init__.py | 119 | ||||
-rw-r--r-- | rigify/rigs/biped/leg/deform.py | 218 | ||||
-rw-r--r-- | rigify/rigs/biped/leg/fk.py | 162 | ||||
-rw-r--r-- | rigify/rigs/biped/leg/ik.py | 325 | ||||
-rw-r--r-- | rigify/rigs/biped/limb_common.py | 1139 | ||||
-rw-r--r-- | rigify/rigs/misc/delta.py | 2 | ||||
-rw-r--r-- | rigify/rigs/neck_short.py | 6 | ||||
-rw-r--r-- | rigify/rigs/spine.py | 11 |
12 files changed, 1454 insertions, 1303 deletions
diff --git a/rigify/rigs/biped/arm/__init__.py b/rigify/rigs/biped/arm/__init__.py index 81b5535c..3235645e 100644 --- a/rigify/rigs/biped/arm/__init__.py +++ b/rigify/rigs/biped/arm/__init__.py @@ -31,13 +31,6 @@ fk_arm = ["%s", "%s", "%s"] ik_arm = ["%s", "%s", "%s", "%s"] if is_selected(fk_arm+ik_arm): layout.prop(pose_bones[ik_arm[2]], '["ikfk_switch"]', text="FK / IK (" + ik_arm[2] + ")", slider=True) -if is_selected(fk_arm): - try: - pose_bones[fk_arm[0]]["isolate"] - layout.prop(pose_bones[fk_arm[0]], '["isolate"]', text="Isolate Rotation (" + fk_arm[0] + ")", slider=True) - except KeyError: - pass -if is_selected(fk_arm+ik_arm): props = layout.operator("pose.rigify_arm_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_arm[0] + ")") props.uarm_fk = fk_arm[0] props.farm_fk = fk_arm[1] @@ -53,7 +46,27 @@ if is_selected(fk_arm+ik_arm): props.farm_ik = ik_arm[1] props.hand_ik = ik_arm[2] props.pole = ik_arm[3] +if is_selected(fk_arm): + try: + pose_bones[fk_arm[0]]["isolate"] + layout.prop(pose_bones[fk_arm[0]], '["isolate"]', text="Isolate Rotation (" + fk_arm[0] + ")", slider=True) + except KeyError: + pass + layout.prop(pose_bones[fk_arm[0]], '["stretch_length"]', text="Length FK (" + fk_arm[0] + ")", slider=True) +if is_selected(ik_arm): + layout.prop(pose_bones[ik_arm[2]], '["stretch_length"]', text="Length IK (" + ik_arm[2] + ")", slider=True) + layout.prop(pose_bones[ik_arm[2]], '["auto_stretch"]', text="Auto-Stretch IK (" + ik_arm[2] + ")", slider=True) +""" +hose_script = """ +hose_arm = ["%s", "%s", "%s"] +if is_selected(hose_arm): + layout.prop(pose_bones[hose_arm[1]], '["smooth_bend"]', text="Smooth Elbow (" + hose_arm[1] + ")", slider=True) +""" + +end_script = """ +if is_selected(fk_arm+ik_arm): + layout.separator() """ @@ -68,6 +81,9 @@ class Rig: Do NOT change any data in the scene. This is a gathering phase only. """ + self.obj = obj + self.params = params + # Gather deform rig self.deform_rig = deform.Rig(obj, bone, params) @@ -83,10 +99,14 @@ class Rig: The main armature should be selected and active before this is called. """ - self.deform_rig.generate() + hose_controls = self.deform_rig.generate() fk_controls = self.fk_rig.generate() ik_controls = self.ik_rig.generate() - return [script % (fk_controls[0], fk_controls[1], fk_controls[2], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3])] + ui_script = script % (fk_controls[0], fk_controls[1], fk_controls[2], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3]) + if self.params.use_complex_arm: + ui_script += hose_script % (hose_controls[0], hose_controls[1], hose_controls[2]) + ui_script += end_script + return [ui_script] def add_parameters(params): @@ -94,25 +114,37 @@ def add_parameters(params): RigifyParameters PropertyGroup """ + params.use_complex_arm = bpy.props.BoolProperty(name="Complex Arm Rig", default=True, description="Generate the full, complex arm rig with twist bones and rubber-hose controls") + params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains") + items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')] params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X') - params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains") - params.elbow_target_base_name = bpy.props.StringProperty(name="Elbow Target Name", default="elbow_target", description="Base name for the generated elbow target") + params.elbow_base_name = bpy.props.StringProperty(name="Elbow Name", default="elbow", description="Base name for the generated elbow-related controls") params.separate_ik_layers = bpy.props.BoolProperty(name="Separate IK Control Layers:", default=False, description="Enable putting the ik controls on a separate layer from the fk controls") params.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on") - params.use_upper_arm_twist = bpy.props.BoolProperty(name="Upper Arm Twist", default=True, description="Generate the dual-bone twist setup for the upper arm") - params.use_forearm_twist = bpy.props.BoolProperty(name="Forearm Twist", default=True, description="Generate the dual-bone twist setup for the forearm") + params.separate_hose_layers = bpy.props.BoolProperty(name="Separate Rubber-hose Control Layers:", default=False, description="Enable putting the rubber-hose controls on a separate layer from the other controls") + params.hose_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the rubber-hose controls to be on") def parameters_ui(layout, params): """ Create the ui for the rig parameters. """ + col = layout.column() + col.prop(params, "use_complex_arm") + + r = layout.row() + r.label(text="Elbow rotation axis:") + r.prop(params, "primary_rotation_axis", text="") + r = layout.row() - r.prop(params, "elbow_target_base_name") + r.prop(params, "elbow_base_name") + + r = layout.row() + r.prop(params, "bend_hint") r = layout.row() r.prop(params, "separate_ik_layers") @@ -160,16 +192,52 @@ def parameters_ui(layout, params): row.prop(params, "ik_layers", index=30, toggle=True, text="") row.prop(params, "ik_layers", index=31, toggle=True, text="") - r = layout.row() - r.label(text="Elbow rotation axis:") - r.prop(params, "primary_rotation_axis", text="") - - r = layout.row() - r.prop(params, "bend_hint") - - col = layout.column() - col.prop(params, "use_upper_arm_twist") - col.prop(params, "use_forearm_twist") + if params.use_complex_arm: + r = layout.row() + r.prop(params, "separate_hose_layers") + + r = layout.row() + r.active = params.separate_hose_layers + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "hose_layers", index=0, toggle=True, text="") + row.prop(params, "hose_layers", index=1, toggle=True, text="") + row.prop(params, "hose_layers", index=2, toggle=True, text="") + row.prop(params, "hose_layers", index=3, toggle=True, text="") + row.prop(params, "hose_layers", index=4, toggle=True, text="") + row.prop(params, "hose_layers", index=5, toggle=True, text="") + row.prop(params, "hose_layers", index=6, toggle=True, text="") + row.prop(params, "hose_layers", index=7, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "hose_layers", index=16, toggle=True, text="") + row.prop(params, "hose_layers", index=17, toggle=True, text="") + row.prop(params, "hose_layers", index=18, toggle=True, text="") + row.prop(params, "hose_layers", index=19, toggle=True, text="") + row.prop(params, "hose_layers", index=20, toggle=True, text="") + row.prop(params, "hose_layers", index=21, toggle=True, text="") + row.prop(params, "hose_layers", index=22, toggle=True, text="") + row.prop(params, "hose_layers", index=23, toggle=True, text="") + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "hose_layers", index=8, toggle=True, text="") + row.prop(params, "hose_layers", index=9, toggle=True, text="") + row.prop(params, "hose_layers", index=10, toggle=True, text="") + row.prop(params, "hose_layers", index=11, toggle=True, text="") + row.prop(params, "hose_layers", index=12, toggle=True, text="") + row.prop(params, "hose_layers", index=13, toggle=True, text="") + row.prop(params, "hose_layers", index=14, toggle=True, text="") + row.prop(params, "hose_layers", index=15, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "hose_layers", index=24, toggle=True, text="") + row.prop(params, "hose_layers", index=25, toggle=True, text="") + row.prop(params, "hose_layers", index=26, toggle=True, text="") + row.prop(params, "hose_layers", index=27, toggle=True, text="") + row.prop(params, "hose_layers", index=28, toggle=True, text="") + row.prop(params, "hose_layers", index=29, toggle=True, text="") + row.prop(params, "hose_layers", index=30, toggle=True, text="") + row.prop(params, "hose_layers", index=31, toggle=True, text="") def create_sample(obj): diff --git a/rigify/rigs/biped/arm/deform.py b/rigify/rigs/biped/arm/deform.py index ad6d8634..a5444207 100644 --- a/rigify/rigs/biped/arm/deform.py +++ b/rigify/rigs/biped/arm/deform.py @@ -18,58 +18,13 @@ # <pep8 compliant> -from math import acos - import bpy -from mathutils import Vector, Matrix + +from .. import limb_common from ....utils import MetarigError -from ....utils import copy_bone, put_bone from ....utils import connected_children_names -from ....utils import strip_org, make_mechanism_name, make_deformer_name - - -def align_roll(obj, bone1, bone2): - bone1_e = obj.data.edit_bones[bone1] - bone2_e = obj.data.edit_bones[bone2] - - bone1_e.roll = 0.0 - - # Get the directions the bones are pointing in, as vectors - y1 = bone1_e.y_axis - x1 = bone1_e.x_axis - y2 = bone2_e.y_axis - x2 = bone2_e.x_axis - - # Get the shortest axis to rotate bone1 on to point in the same direction as bone2 - axis = y1.cross(y2) - axis.normalize() - - # Angle to rotate on that shortest axis - angle = y1.angle(y2) - - # Create rotation matrix to make bone1 point in the same direction as bone2 - rot_mat = Matrix.Rotation(angle, 3, axis) - - # Roll factor - x3 = rot_mat * x1 - dot = x2 * x3 - if dot > 1.0: - dot = 1.0 - elif dot < -1.0: - dot = -1.0 - roll = acos(dot) - - # Set the roll - bone1_e.roll = roll - - # Check if we rolled in the right direction - x3 = rot_mat * bone1_e.x_axis - check = x2 * x3 - - # If not, reverse - if check < 0.9999: - bone1_e.roll = -roll +from ....utils import strip_org class Rig: @@ -77,13 +32,6 @@ class Rig: """ def __init__(self, obj, bone, params): - """ Gather and validate data about the rig. - Store any data or references to data that will be needed later on. - In particular, store references to bones that will be needed, and - store names of bones that will be needed. - Do NOT change any data in the scene. This is a gathering phase only. - - """ self.obj = obj self.params = params @@ -94,140 +42,17 @@ class Rig: raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones" % (strip_org(bone))) # Get rig parameters - self.use_upper_arm_twist = params.use_upper_arm_twist - self.use_forearm_twist = params.use_forearm_twist - - def generate(self): - """ Generate the rig. - Do NOT modify any of the original bones, except for adding constraints. - The main armature should be selected and active before this is called. - - """ - bpy.ops.object.mode_set(mode='EDIT') - - # Create upper arm bones - if self.use_upper_arm_twist: - uarm1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01"))) - uarm2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02"))) - utip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip"))) + if params.separate_hose_layers: + layers = list(params.hose_layers) else: - uarm = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) + layers = None + use_complex_rig = params.use_complex_arm + elbow_base_name = params.elbow_base_name + primary_rotation_axis = params.primary_rotation_axis - # Create forearm bones - if self.use_forearm_twist: - farm1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".01"))) - farm2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".02"))) - ftip = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(self.org_bones[1] + ".tip"))) - else: - farm = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1]))) - - # Create hand bone - hand = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) - - # Get edit bones - eb = self.obj.data.edit_bones - - org_uarm_e = eb[self.org_bones[0]] - if self.use_upper_arm_twist: - uarm1_e = eb[uarm1] - uarm2_e = eb[uarm2] - utip_e = eb[utip] - else: - uarm_e = eb[uarm] + # Based on common limb + self.rubber_hose_limb = limb_common.RubberHoseLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], use_complex_rig, elbow_base_name, primary_rotation_axis, layers) - org_farm_e = eb[self.org_bones[1]] - if self.use_forearm_twist: - farm1_e = eb[farm1] - farm2_e = eb[farm2] - ftip_e = eb[ftip] - else: - farm_e = eb[farm] - - org_hand_e = eb[self.org_bones[2]] - hand_e = eb[hand] - - # Parent and position upper arm bones - if self.use_upper_arm_twist: - uarm1_e.use_connect = False - uarm2_e.use_connect = False - utip_e.use_connect = False - - uarm1_e.parent = org_uarm_e.parent - uarm2_e.parent = org_uarm_e - utip_e.parent = org_uarm_e - - center = Vector((org_uarm_e.head + org_uarm_e.tail) / 2) - - uarm1_e.tail = center - uarm2_e.head = center - put_bone(self.obj, utip, org_uarm_e.tail) - utip_e.length = org_uarm_e.length / 8 - else: - uarm_e.use_connect = False - uarm_e.parent = org_uarm_e - - # Parent and position forearm bones - if self.use_forearm_twist: - farm1_e.use_connect = False - farm2_e.use_connect = False - ftip_e.use_connect = False - - farm1_e.parent = org_farm_e - farm2_e.parent = org_farm_e - ftip_e.parent = org_farm_e - - center = Vector((org_farm_e.head + org_farm_e.tail) / 2) - - farm1_e.tail = center - farm2_e.head = center - put_bone(self.obj, ftip, org_farm_e.tail) - ftip_e.length = org_farm_e.length / 8 - - # Align roll of farm2 with hand - align_roll(self.obj, farm2, hand) - else: - farm_e.use_connect = False - farm_e.parent = org_farm_e - - # Parent hand - hand_e.use_connect = False - hand_e.parent = org_hand_e - - # Object mode, get pose bones - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - if self.use_upper_arm_twist: - uarm1_p = pb[uarm1] - if self.use_forearm_twist: - farm2_p = pb[farm2] - # hand_p = pb[hand] # UNUSED - - # Upper arm constraints - if self.use_upper_arm_twist: - con = uarm1_p.constraints.new('COPY_LOCATION') - con.name = "copy_location" - con.target = self.obj - con.subtarget = self.org_bones[0] - - con = uarm1_p.constraints.new('COPY_SCALE') - con.name = "copy_scale" - con.target = self.obj - con.subtarget = self.org_bones[0] - - con = uarm1_p.constraints.new('DAMPED_TRACK') - con.name = "track_to" - con.target = self.obj - con.subtarget = utip - - # Forearm constraints - if self.use_forearm_twist: - con = farm2_p.constraints.new('COPY_ROTATION') - con.name = "copy_rotation" - con.target = self.obj - con.subtarget = hand - - con = farm2_p.constraints.new('DAMPED_TRACK') - con.name = "track_to" - con.target = self.obj - con.subtarget = ftip + def generate(self): + bone_list = self.rubber_hose_limb.generate() + return bone_list diff --git a/rigify/rigs/biped/arm/fk.py b/rigify/rigs/biped/arm/fk.py index 54b1a459..740b3277 100644 --- a/rigify/rigs/biped/arm/fk.py +++ b/rigify/rigs/biped/arm/fk.py @@ -19,14 +19,14 @@ # <pep8 compliant> import bpy -from rna_prop_ui import rna_idprop_ui_prop_get + +from .. import limb_common from ....utils import MetarigError -from ....utils import copy_bone from ....utils import connected_children_names -from ....utils import strip_org, make_mechanism_name, insert_before_lr +from ....utils import create_widget +from ....utils import strip_org from ....utils import get_layers -from ....utils import create_widget, create_limb_widget class Rig: @@ -42,27 +42,23 @@ class Rig: """ self.obj = obj - self.params = params # Get the chain of 3 connected bones self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2] if len(self.org_bones) != 3: - raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones" % (strip_org(bone))) - - # Get (optional) parent - if self.obj.data.bones[bone].parent is None: - self.org_parent = None - else: - self.org_parent = self.obj.data.bones[bone].parent.name + raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of at least 3 bones" % (strip_org(bone))) - # Get the rig parameters + # Get params if "layers" in params: - self.layers = get_layers(params["layers"]) + layers = get_layers(params["layers"]) else: - self.layers = None + layers = None - self.primary_rotation_axis = params.primary_rotation_axis + primary_rotation_axis = params.primary_rotation_axis + + # Arm is based on common limb + self.fk_limb = limb_common.FKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], primary_rotation_axis, layers) def generate(self): """ Generate the rig. @@ -70,140 +66,12 @@ class Rig: The main armature should be selected and active before this is called. """ - bpy.ops.object.mode_set(mode='EDIT') - - # Create the control bones - uarm = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk"))) - farm = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk"))) - hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk"))) - - # Create the hinge bones - if self.org_parent != None: - hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(uarm + ".hinge")) - socket1 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket1")) - socket2 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket2")) - - # Get edit bones - eb = self.obj.data.edit_bones - - uarm_e = eb[uarm] - farm_e = eb[farm] - hand_e = eb[hand] - - if self.org_parent != None: - hinge_e = eb[hinge] - socket1_e = eb[socket1] - socket2_e = eb[socket2] - - # Parenting - farm_e.parent = uarm_e - hand_e.parent = farm_e - - if self.org_parent != None: - hinge_e.use_connect = False - socket1_e.use_connect = False - socket2_e.use_connect = False - - uarm_e.parent = hinge_e - hinge_e.parent = socket2_e - socket2_e.parent = None - - # Positioning - if self.org_parent != None: - center = (hinge_e.head + hinge_e.tail) / 2 - hinge_e.head = center - socket1_e.length /= 4 - socket2_e.length /= 3 - - # Object mode, get pose bones - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - uarm_p = pb[uarm] - farm_p = pb[farm] - hand_p = pb[hand] - if self.org_parent != None: - hinge_p = pb[hinge] - - if self.org_parent != None: - # socket1_p = pb[socket1] # UNUSED - socket2_p = pb[socket2] - - # Set the elbow to only bend on the x-axis. - farm_p.rotation_mode = 'XYZ' - if 'X' in self.primary_rotation_axis: - farm_p.lock_rotation = (False, True, True) - elif 'Y' in self.primary_rotation_axis: - farm_p.lock_rotation = (True, False, True) - else: - farm_p.lock_rotation = (True, True, False) - - # Hinge transforms are locked, for auto-ik - if self.org_parent != None: - hinge_p.lock_location = True, True, True - hinge_p.lock_rotation = True, True, True - hinge_p.lock_rotation_w = True - hinge_p.lock_scale = True, True, True - - # Set up custom properties - if self.org_parent != None: - prop = rna_idprop_ui_prop_get(uarm_p, "isolate", create=True) - uarm_p["isolate"] = 0.0 - prop["soft_min"] = prop["min"] = 0.0 - prop["soft_max"] = prop["max"] = 1.0 - - # Hinge constraints / drivers - if self.org_parent != None: - con = socket2_p.constraints.new('COPY_LOCATION') - con.name = "copy_location" - con.target = self.obj - con.subtarget = socket1 - - con = socket2_p.constraints.new('COPY_TRANSFORMS') - con.name = "isolate_off" - con.target = self.obj - con.subtarget = socket1 - - # Driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = uarm_p.path_from_id() + '["isolate"]' - mod = fcurve.modifiers[0] - mod.poly_order = 1 - mod.coefficients[0] = 1.0 - mod.coefficients[1] = -1.0 - - # Constrain org bones to controls - con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') - con.name = "fk" - con.target = self.obj - con.subtarget = uarm - - con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') - con.name = "fk" - con.target = self.obj - con.subtarget = farm - - con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') - con.name = "fk" - con.target = self.obj - con.subtarget = hand - - # Set layers if specified - if self.layers: - uarm_p.bone.layers = self.layers - farm_p.bone.layers = self.layers - hand_p.bone.layers = self.layers - - # Create control widgets - create_limb_widget(self.obj, uarm) - create_limb_widget(self.obj, farm) + bone_list = self.fk_limb.generate() + uarm = bone_list[0] + farm = bone_list[1] + hand = bone_list[2] + # Create hand widget ob = create_widget(self.obj, hand) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] diff --git a/rigify/rigs/biped/arm/ik.py b/rigify/rigs/biped/arm/ik.py index 8f288601..d5c9509f 100644 --- a/rigify/rigs/biped/arm/ik.py +++ b/rigify/rigs/biped/arm/ik.py @@ -18,44 +18,14 @@ # <pep8 compliant> -from math import pi, acos - import bpy -from rna_prop_ui import rna_idprop_ui_prop_get -from mathutils import Vector + +from .. import limb_common from ....utils import MetarigError -from ....utils import copy_bone from ....utils import connected_children_names -from ....utils import strip_org, make_mechanism_name, insert_before_lr -from ....utils import create_widget, create_line_widget, create_sphere_widget - - -def angle_on_plane(plane, vec1, vec2): - """ Return the angle between two vectors projected onto a plane. - """ - plane.normalize() - vec1 = vec1 - (plane * (vec1.dot(plane))) - vec2 = vec2 - (plane * (vec2.dot(plane))) - vec1.normalize() - vec2.normalize() - - # Determine the angle - angle = acos(max(-1.0, min(1.0, vec1.dot(vec2)))) - - if angle < 0.00001: # close enough to zero that sign doesn't matter - return angle - - # Determine the sign of the angle - vec3 = vec2.cross(vec1) - vec3.normalize() - sign = vec3.dot(plane) - if sign >= 0: - sign = 1 - else: - sign = -1 - - return angle * sign +from ....utils import strip_org +from ....utils import create_widget class Rig: @@ -73,23 +43,23 @@ class Rig: """ self.obj = obj self.params = params - self.switch = ikfk_switch # Get the chain of 3 connected bones self.org_bones = [bone] + connected_children_names(self.obj, bone)[:2] - if len(self.org_bones) != 3: raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 3 bones" % (strip_org(bone))) # Get the rig parameters if params.separate_ik_layers: - self.layers = list(params.ik_layers) + layers = list(params.ik_layers) else: - self.layers = None - - self.bend_hint = params.bend_hint + layers = None + bend_hint = params.bend_hint + primary_rotation_axis = params.primary_rotation_axis + pole_target_base_name = self.params.elbow_base_name + "_target" - self.primary_rotation_axis = params.primary_rotation_axis + # Arm is based on common limb + self.ik_limb = limb_common.IKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], pole_target_base_name, primary_rotation_axis, bend_hint, layers, ikfk_switch) def generate(self): """ Generate the rig. @@ -97,236 +67,14 @@ class Rig: The main armature should be selected and active before this is called. """ - bpy.ops.object.mode_set(mode='EDIT') - - # Create the bones - uarm = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik")))) - farm = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik")))) - - hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik"))) - pole_target_name = self.params.elbow_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1] - pole = copy_bone(self.obj, self.org_bones[0], pole_target_name) - - vishand = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik"))) - vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik"))) - - # Get edit bones - eb = self.obj.data.edit_bones - - uarm_e = eb[uarm] - farm_e = eb[farm] - hand_e = eb[hand] - pole_e = eb[pole] - vishand_e = eb[vishand] - vispole_e = eb[vispole] - - # Parenting - farm_e.parent = uarm_e - - hand_e.use_connect = False - hand_e.parent = None - - pole_e.use_connect = False - - vishand_e.use_connect = False - vishand_e.parent = None - - vispole_e.use_connect = False - vispole_e.parent = None - - # Misc - hand_e.use_local_location = False - - vishand_e.hide_select = True - vispole_e.hide_select = True - - # Positioning - v1 = farm_e.tail - uarm_e.head - if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: - v2 = v1.cross(farm_e.x_axis) - if (v2 * farm_e.z_axis) > 0.0: - v2 *= -1.0 - else: - v2 = v1.cross(farm_e.z_axis) - if (v2 * farm_e.x_axis) < 0.0: - v2 *= -1.0 - v2.normalize() - v2 *= v1.length - - if '-' in self.primary_rotation_axis: - v2 *= -1 - - pole_e.head = farm_e.head + v2 - pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) - pole_e.roll = 0.0 - - vishand_e.tail = vishand_e.head + Vector((0, 0, v1.length / 32)) - vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) - - # Determine the pole offset value - plane = (farm_e.tail - uarm_e.head).normalized() - vec1 = uarm_e.x_axis.normalized() - vec2 = (pole_e.head - uarm_e.head).normalized() - pole_offset = angle_on_plane(plane, vec1, vec2) - - # Object mode, get pose bones - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - # uarm_p = pb[uarm] # UNUSED - farm_p = pb[farm] - hand_p = pb[hand] - pole_p = pb[pole] - vishand_p = pb[vishand] - vispole_p = pb[vispole] - - # Set the elbow to only bend on the primary axis - if 'X' in self.primary_rotation_axis: - farm_p.lock_ik_y = True - farm_p.lock_ik_z = True - elif 'Y' in self.primary_rotation_axis: - farm_p.lock_ik_x = True - farm_p.lock_ik_z = True - else: - farm_p.lock_ik_x = True - farm_p.lock_ik_y = True - - # Pole target only translates - pole_p.lock_location = False, False, False - pole_p.lock_rotation = True, True, True - pole_p.lock_rotation_w = True - pole_p.lock_scale = True, True, True - - # Set up custom properties - if self.switch is True: - prop = rna_idprop_ui_prop_get(hand_p, "ikfk_switch", create=True) - hand_p["ikfk_switch"] = 0.0 - prop["soft_min"] = prop["min"] = 0.0 - prop["soft_max"] = prop["max"] = 1.0 - - # Bend direction hint - if self.bend_hint: - con = farm_p.constraints.new('LIMIT_ROTATION') - con.name = "bend_hint" - con.owner_space = 'LOCAL' - if self.primary_rotation_axis == 'X': - con.use_limit_x = True - con.min_x = pi / 10 - con.max_x = pi / 10 - elif self.primary_rotation_axis == '-X': - con.use_limit_x = True - con.min_x = -pi / 10 - con.max_x = -pi / 10 - elif self.primary_rotation_axis == 'Y': - con.use_limit_y = True - con.min_y = pi / 10 - con.max_y = pi / 10 - elif self.primary_rotation_axis == '-Y': - con.use_limit_y = True - con.min_y = -pi / 10 - con.max_y = -pi / 10 - elif self.primary_rotation_axis == 'Z': - con.use_limit_z = True - con.min_z = pi / 10 - con.max_z = pi / 10 - elif self.primary_rotation_axis == '-Z': - con.use_limit_z = True - con.min_z = -pi / 10 - con.max_z = -pi / 10 - - # IK Constraint - con = farm_p.constraints.new('IK') - con.name = "ik" - con.target = self.obj - con.subtarget = hand - con.pole_target = self.obj - con.pole_subtarget = pole - con.pole_angle = pole_offset - con.chain_count = 2 - - # Constrain org bones to controls - con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') - con.name = "ik" - con.target = self.obj - con.subtarget = uarm - if self.switch is True: - # IK/FK switch driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' - - con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') - con.name = "ik" - con.target = self.obj - con.subtarget = farm - if self.switch is True: - # IK/FK switch driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' - - con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') - con.name = "ik" - con.target = self.obj - con.subtarget = hand - if self.switch is True: - # IK/FK switch driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' - - # VIS hand constraints - con = vishand_p.constraints.new('COPY_LOCATION') - con.name = "copy_loc" - con.target = self.obj - con.subtarget = self.org_bones[2] - - con = vishand_p.constraints.new('STRETCH_TO') - con.name = "stretch_to" - con.target = self.obj - con.subtarget = hand - con.volume = 'NO_VOLUME' - con.rest_length = vishand_p.length - - # VIS pole constraints - con = vispole_p.constraints.new('COPY_LOCATION') - con.name = "copy_loc" - con.target = self.obj - con.subtarget = self.org_bones[1] - - con = vispole_p.constraints.new('STRETCH_TO') - con.name = "stretch_to" - con.target = self.obj - con.subtarget = pole - con.volume = 'NO_VOLUME' - con.rest_length = vispole_p.length - - # Set layers if specified - if self.layers: - hand_p.bone.layers = self.layers - pole_p.bone.layers = self.layers - vishand_p.bone.layers = self.layers - vispole_p.bone.layers = self.layers - - # Create widgets - create_line_widget(self.obj, vispole) - create_line_widget(self.obj, vishand) - create_sphere_widget(self.obj, pole) + bone_list = self.ik_limb.generate() + uarm = bone_list[0] + farm = bone_list[1] + hand = bone_list[2] + # hand_mch = bone_list[3] + pole = bone_list[4] + # vispole = bone_list[5] + # vishand = bone_list[6] ob = create_widget(self.obj, hand) if ob != None: diff --git a/rigify/rigs/biped/leg/__init__.py b/rigify/rigs/biped/leg/__init__.py index 36619c98..0a9f0ac7 100644 --- a/rigify/rigs/biped/leg/__init__.py +++ b/rigify/rigs/biped/leg/__init__.py @@ -31,13 +31,6 @@ fk_leg = ["%s", "%s", "%s", "%s"] ik_leg = ["%s", "%s", "%s", "%s", "%s", "%s"] if is_selected(fk_leg+ik_leg): layout.prop(pose_bones[ik_leg[2]], '["ikfk_switch"]', text="FK / IK (" + ik_leg[2] + ")", slider=True) -if is_selected(fk_leg): - try: - pose_bones[fk_leg[0]]["isolate"] - layout.prop(pose_bones[fk_leg[0]], '["isolate"]', text="Isolate Rotation (" + fk_leg[0] + ")", slider=True) - except KeyError: - pass -if is_selected(fk_leg+ik_leg): p = layout.operator("pose.rigify_leg_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_leg[0] + ")") p.thigh_fk = fk_leg[0] p.shin_fk = fk_leg[1] @@ -45,6 +38,7 @@ if is_selected(fk_leg+ik_leg): p.mfoot_fk = fk_leg[3] p.thigh_ik = ik_leg[0] p.shin_ik = ik_leg[1] + p.foot_ik = ik_leg[2] p.mfoot_ik = ik_leg[5] p = layout.operator("pose.rigify_leg_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_leg[0] + ")") p.thigh_fk = fk_leg[0] @@ -56,6 +50,27 @@ if is_selected(fk_leg+ik_leg): p.pole = ik_leg[3] p.footroll = ik_leg[4] p.mfoot_ik = ik_leg[5] +if is_selected(fk_leg): + try: + pose_bones[fk_leg[0]]["isolate"] + layout.prop(pose_bones[fk_leg[0]], '["isolate"]', text="Isolate Rotation (" + fk_leg[0] + ")", slider=True) + except KeyError: + pass + layout.prop(pose_bones[fk_leg[0]], '["stretch_length"]', text="Length FK (" + fk_leg[0] + ")", slider=True) +if is_selected(ik_leg): + layout.prop(pose_bones[ik_leg[2]], '["stretch_length"]', text="Length IK (" + ik_leg[2] + ")", slider=True) + layout.prop(pose_bones[ik_leg[2]], '["auto_stretch"]', text="Auto-Stretch IK (" + ik_leg[2] + ")", slider=True) +""" + +hose_script = """ +hose_leg = ["%s", "%s", "%s"] +if is_selected(hose_leg): + layout.prop(pose_bones[hose_leg[1]], '["smooth_bend"]', text="Smooth Knee (" + hose_leg[1] + ")", slider=True) +""" + +end_script = """ +if is_selected(fk_leg+ik_leg): + layout.separator() """ @@ -70,6 +85,9 @@ class Rig: Do NOT change any data in the scene. This is a gathering phase only. """ + self.obj = obj + self.params = params + # Gather deform rig self.deform_rig = deform.Rig(obj, bone, params) @@ -85,10 +103,14 @@ class Rig: The main armature should be selected and active before this is called. """ - self.deform_rig.generate() + hose_controls = self.deform_rig.generate() fk_controls = self.fk_rig.generate() ik_controls = self.ik_rig.generate() - return [script % (fk_controls[0], fk_controls[1], fk_controls[2], fk_controls[3], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3], ik_controls[4], ik_controls[5])] + ui_script = script % (fk_controls[0], fk_controls[1], fk_controls[2], fk_controls[3], ik_controls[0], ik_controls[1], ik_controls[2], ik_controls[3], ik_controls[4], ik_controls[5]) + if self.params.use_complex_leg: + ui_script += hose_script % (hose_controls[0], hose_controls[1], hose_controls[2]) + ui_script += end_script + return [ui_script] def add_parameters(params): @@ -96,26 +118,37 @@ def add_parameters(params): RigifyParameters PropertyGroup """ + params.use_complex_leg = bpy.props.BoolProperty(name="Complex Leg Rig", default=True, description="Generate the full, complex leg rig with twist bones and rubber-hose controls") + params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend (useful for perfectly straight chains)") + items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')] params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X') - params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend (useful for perfectly straight chains)") - params.knee_target_base_name = bpy.props.StringProperty(name="Knee Target Name", default="knee_target", description="Base name for the generated knee target") - + params.knee_base_name = bpy.props.StringProperty(name="Knee Name", default="knee", description="Base name for the generated knee-related controls") params.separate_ik_layers = bpy.props.BoolProperty(name="Separate IK Control Layers:", default=False, description="Enable putting the ik controls on a separate layer from the fk controls") params.ik_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the ik controls to be on") - params.use_thigh_twist = bpy.props.BoolProperty(name="Thigh Twist", default=True, description="Generate the dual-bone twist setup for the thigh") - params.use_shin_twist = bpy.props.BoolProperty(name="Shin Twist", default=True, description="Generate the dual-bone twist setup for the shin") + params.separate_hose_layers = bpy.props.BoolProperty(name="Separate Rubber-hose Control Layers:", default=False, description="Enable putting the rubber-hose controls on a separate layer from the other controls") + params.hose_layers = bpy.props.BoolVectorProperty(size=32, description="Layers for the rubber-hose controls to be on") def parameters_ui(layout, params): """ Create the ui for the rig parameters. """ + col = layout.column() + col.prop(params, "use_complex_leg") + + r = layout.row() + r.label(text="Knee rotation axis:") + r.prop(params, "primary_rotation_axis", text="") + + r = layout.row() + r.prop(params, "knee_base_name") + r = layout.row() - r.prop(params, "knee_target_base_name") + r.prop(params, "bend_hint") r = layout.row() r.prop(params, "separate_ik_layers") @@ -163,16 +196,52 @@ def parameters_ui(layout, params): row.prop(params, "ik_layers", index=30, toggle=True, text="") row.prop(params, "ik_layers", index=31, toggle=True, text="") - r = layout.row() - r.label(text="Knee rotation axis:") - r.prop(params, "primary_rotation_axis", text="") - - r = layout.row() - r.prop(params, "bend_hint") - - col = layout.column() - col.prop(params, "use_thigh_twist") - col.prop(params, "use_shin_twist") + if params.use_complex_leg: + r = layout.row() + r.prop(params, "separate_hose_layers") + + r = layout.row() + r.active = params.separate_hose_layers + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "hose_layers", index=0, toggle=True, text="") + row.prop(params, "hose_layers", index=1, toggle=True, text="") + row.prop(params, "hose_layers", index=2, toggle=True, text="") + row.prop(params, "hose_layers", index=3, toggle=True, text="") + row.prop(params, "hose_layers", index=4, toggle=True, text="") + row.prop(params, "hose_layers", index=5, toggle=True, text="") + row.prop(params, "hose_layers", index=6, toggle=True, text="") + row.prop(params, "hose_layers", index=7, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "hose_layers", index=16, toggle=True, text="") + row.prop(params, "hose_layers", index=17, toggle=True, text="") + row.prop(params, "hose_layers", index=18, toggle=True, text="") + row.prop(params, "hose_layers", index=19, toggle=True, text="") + row.prop(params, "hose_layers", index=20, toggle=True, text="") + row.prop(params, "hose_layers", index=21, toggle=True, text="") + row.prop(params, "hose_layers", index=22, toggle=True, text="") + row.prop(params, "hose_layers", index=23, toggle=True, text="") + + col = r.column(align=True) + row = col.row(align=True) + row.prop(params, "hose_layers", index=8, toggle=True, text="") + row.prop(params, "hose_layers", index=9, toggle=True, text="") + row.prop(params, "hose_layers", index=10, toggle=True, text="") + row.prop(params, "hose_layers", index=11, toggle=True, text="") + row.prop(params, "hose_layers", index=12, toggle=True, text="") + row.prop(params, "hose_layers", index=13, toggle=True, text="") + row.prop(params, "hose_layers", index=14, toggle=True, text="") + row.prop(params, "hose_layers", index=15, toggle=True, text="") + row = col.row(align=True) + row.prop(params, "hose_layers", index=24, toggle=True, text="") + row.prop(params, "hose_layers", index=25, toggle=True, text="") + row.prop(params, "hose_layers", index=26, toggle=True, text="") + row.prop(params, "hose_layers", index=27, toggle=True, text="") + row.prop(params, "hose_layers", index=28, toggle=True, text="") + row.prop(params, "hose_layers", index=29, toggle=True, text="") + row.prop(params, "hose_layers", index=30, toggle=True, text="") + row.prop(params, "hose_layers", index=31, toggle=True, text="") def create_sample(obj): diff --git a/rigify/rigs/biped/leg/deform.py b/rigify/rigs/biped/leg/deform.py index 0d0bb2a7..cde73250 100644 --- a/rigify/rigs/biped/leg/deform.py +++ b/rigify/rigs/biped/leg/deform.py @@ -18,58 +18,14 @@ # <pep8 compliant> -from math import acos - import bpy -from mathutils import Vector, Matrix + +from .. import limb_common from ....utils import MetarigError -from ....utils import copy_bone, put_bone +from ....utils import copy_bone from ....utils import connected_children_names, has_connected_children -from ....utils import strip_org, make_mechanism_name, make_deformer_name - - -def align_roll(obj, bone1, bone2): - bone1_e = obj.data.edit_bones[bone1] - bone2_e = obj.data.edit_bones[bone2] - - bone1_e.roll = 0.0 - - # Get the directions the bones are pointing in, as vectors - y1 = bone1_e.y_axis - x1 = bone1_e.x_axis - y2 = bone2_e.y_axis - x2 = bone2_e.x_axis - - # Get the shortest axis to rotate bone1 on to point in the same direction as bone2 - axis = y1.cross(y2) - axis.normalize() - - # Angle to rotate on that shortest axis - angle = y1.angle(y2) - - # Create rotation matrix to make bone1 point in the same direction as bone2 - rot_mat = Matrix.Rotation(angle, 3, axis) - - # Roll factor - x3 = rot_mat * x1 - dot = x2 * x3 - if dot > 1.0: - dot = 1.0 - elif dot < -1.0: - dot = -1.0 - roll = acos(dot) - - # Set the roll - bone1_e.roll = roll - - # Check if we rolled in the right direction - x3 = rot_mat * bone1_e.x_axis - check = x2 * x3 - - # If not, reverse - if check < 0.9999: - bone1_e.roll = -roll +from ....utils import strip_org, make_deformer_name class Rig: @@ -77,13 +33,6 @@ class Rig: """ def __init__(self, obj, bone, params): - """ Gather and validate data about the rig. - Store any data or references to data that will be needed later on. - In particular, store references to bones that will be needed, and - store names of bones that will be needed. - Do NOT change any data in the scene. This is a gathering phase only. - - """ self.obj = obj self.params = params @@ -104,9 +53,9 @@ class Rig: heel = b.name if foot is None: - raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find foot bone (that is, a bone with >1 children connected) attached to bone '%s'" % (strip_org(bone), strip_org(shin))) + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find foot bone (that is, a bone with >1 children connected) attached to bone '%s'" % (strip_org(bone), strip_org(leg_bones[1]))) if heel is None: - raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find heel bone (that is, a bone with no childrenconnected) attached to bone '%s'" % (strip_org(bone), strip_org(shin))) + raise MetarigError("RIGIFY ERROR: Bone '%s': incorrect bone configuration for rig type -- could not find heel bone (that is, a bone with no children connected) attached to bone '%s'" % (strip_org(bone), strip_org(leg_bones[1]))) # Get the toe toe = None for b in self.obj.data.bones[foot].children: @@ -119,150 +68,25 @@ class Rig: self.org_bones = leg_bones + [foot, toe, heel] # Get rig parameters - self.use_thigh_twist = params.use_thigh_twist - self.use_shin_twist = params.use_shin_twist - - def generate(self): - """ Generate the rig. - Do NOT modify any of the original bones, except for adding constraints. - The main armature should be selected and active before this is called. - - """ - bpy.ops.object.mode_set(mode='EDIT') - - # Create upper arm bones - if self.use_thigh_twist: - thigh1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".01"))) - thigh2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0] + ".02"))) - utip = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[0] + ".tip"))) + if params.separate_hose_layers: + layers = list(params.hose_layers) else: - thigh = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) + layers = None + use_complex_rig = params.use_complex_leg + knee_base_name = params.knee_base_name + primary_rotation_axis = params.primary_rotation_axis - # Create forearm bones - if self.use_shin_twist: - shin1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".01"))) - shin2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1] + ".02"))) - stip = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(self.org_bones[1] + ".tip"))) - else: - shin = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1]))) + # Based on common limb + self.rubber_hose_limb = limb_common.RubberHoseLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], use_complex_rig, knee_base_name, primary_rotation_axis, layers) - # Create foot bone - foot = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) + def generate(self): + bone_list = self.rubber_hose_limb.generate() - # Create toe bone + # Set up toe + bpy.ops.object.mode_set(mode='EDIT') toe = copy_bone(self.obj, self.org_bones[3], make_deformer_name(strip_org(self.org_bones[3]))) - - # Get edit bones eb = self.obj.data.edit_bones + eb[toe].use_connect = False + eb[toe].parent = eb[self.org_bones[3]] - org_thigh_e = eb[self.org_bones[0]] - if self.use_thigh_twist: - thigh1_e = eb[thigh1] - thigh2_e = eb[thigh2] - utip_e = eb[utip] - else: - thigh_e = eb[thigh] - - org_shin_e = eb[self.org_bones[1]] - if self.use_shin_twist: - shin1_e = eb[shin1] - shin2_e = eb[shin2] - stip_e = eb[stip] - else: - shin_e = eb[shin] - - org_foot_e = eb[self.org_bones[2]] - foot_e = eb[foot] - - org_toe_e = eb[self.org_bones[3]] - toe_e = eb[toe] - - # Parent and position thigh bones - if self.use_thigh_twist: - thigh1_e.use_connect = False - thigh2_e.use_connect = False - utip_e.use_connect = False - - thigh1_e.parent = org_thigh_e.parent - thigh2_e.parent = org_thigh_e - utip_e.parent = org_thigh_e - - center = Vector((org_thigh_e.head + org_thigh_e.tail) / 2) - - thigh1_e.tail = center - thigh2_e.head = center - put_bone(self.obj, utip, org_thigh_e.tail) - utip_e.length = org_thigh_e.length / 8 - else: - thigh_e.use_connect = False - thigh_e.parent = org_thigh_e - - # Parent and position shin bones - if self.use_shin_twist: - shin1_e.use_connect = False - shin2_e.use_connect = False - stip_e.use_connect = False - - shin1_e.parent = org_shin_e - shin2_e.parent = org_shin_e - stip_e.parent = org_shin_e - - center = Vector((org_shin_e.head + org_shin_e.tail) / 2) - - shin1_e.tail = center - shin2_e.head = center - put_bone(self.obj, stip, org_shin_e.tail) - stip_e.length = org_shin_e.length / 8 - - # Align roll of shin2 with foot - align_roll(self.obj, shin2, foot) - else: - shin_e.use_connect = False - shin_e.parent = org_shin_e - - # Parent foot - foot_e.use_connect = False - foot_e.parent = org_foot_e - - # Parent toe - toe_e.use_connect = False - toe_e.parent = org_toe_e - - # Object mode, get pose bones - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - if self.use_thigh_twist: - thigh1_p = pb[thigh1] - if self.use_shin_twist: - shin2_p = pb[shin2] - # foot_p = pb[foot] # UNUSED - - # Thigh constraints - if self.use_thigh_twist: - con = thigh1_p.constraints.new('COPY_LOCATION') - con.name = "copy_location" - con.target = self.obj - con.subtarget = self.org_bones[0] - - con = thigh1_p.constraints.new('COPY_SCALE') - con.name = "copy_scale" - con.target = self.obj - con.subtarget = self.org_bones[0] - - con = thigh1_p.constraints.new('DAMPED_TRACK') - con.name = "track_to" - con.target = self.obj - con.subtarget = utip - - # Shin constraints - if self.use_shin_twist: - con = shin2_p.constraints.new('COPY_ROTATION') - con.name = "copy_rotation" - con.target = self.obj - con.subtarget = foot - - con = shin2_p.constraints.new('DAMPED_TRACK') - con.name = "track_to" - con.target = self.obj - con.subtarget = stip + return bone_list diff --git a/rigify/rigs/biped/leg/fk.py b/rigify/rigs/biped/leg/fk.py index 4e224ceb..743e3a19 100644 --- a/rigify/rigs/biped/leg/fk.py +++ b/rigify/rigs/biped/leg/fk.py @@ -19,15 +19,15 @@ # <pep8 compliant> import bpy -from rna_prop_ui import rna_idprop_ui_prop_get from mathutils import Vector +from .. import limb_common + from ....utils import MetarigError -from ....utils import copy_bone from ....utils import connected_children_names, has_connected_children -from ....utils import strip_org, make_mechanism_name, insert_before_lr +from ....utils import strip_org from ....utils import get_layers -from ....utils import create_widget, create_limb_widget +from ....utils import create_widget class Rig: @@ -84,11 +84,14 @@ class Rig: # Get rig parameters if "layers" in params: - self.layers = get_layers(params["layers"]) + layers = get_layers(params["layers"]) else: - self.layers = None + layers = None + + primary_rotation_axis = params.primary_rotation_axis - self.primary_rotation_axis = params.primary_rotation_axis + # Leg is based on common limb + self.fk_limb = limb_common.FKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], primary_rotation_axis, layers) def generate(self): """ Generate the rig. @@ -96,152 +99,23 @@ class Rig: The main armature should be selected and active before this is called. """ - bpy.ops.object.mode_set(mode='EDIT') - - # Create the control bones - thigh = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk"))) - shin = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk"))) - foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk"))) + ctrl_bones = self.fk_limb.generate() + thigh = ctrl_bones[0] + shin = ctrl_bones[1] + foot = ctrl_bones[2] + foot_mch = ctrl_bones[3] - # Create the foot mechanism bone - foot_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2]))) - - # Create the hinge bones - if self.org_parent != None: - hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(thigh + ".hinge")) - socket1 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket1")) - socket2 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket2")) - - # Get edit bones + # Position foot control + bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones - - thigh_e = eb[thigh] - shin_e = eb[shin] foot_e = eb[foot] - foot_mch_e = eb[foot_mch] - - if self.org_parent != None: - hinge_e = eb[hinge] - socket1_e = eb[socket1] - socket2_e = eb[socket2] - - # Parenting - shin_e.parent = thigh_e - foot_e.parent = shin_e - - foot_mch_e.use_connect = False - foot_mch_e.parent = foot_e - - if self.org_parent != None: - hinge_e.use_connect = False - socket1_e.use_connect = False - socket2_e.use_connect = False - - thigh_e.parent = hinge_e - hinge_e.parent = socket2_e - socket2_e.parent = None - - # Positioning vec = Vector(eb[self.org_bones[3]].vector) vec.normalize() foot_e.tail = foot_e.head + (vec * foot_e.length) foot_e.roll = eb[self.org_bones[3]].roll - - if self.org_parent != None: - center = (hinge_e.head + hinge_e.tail) / 2 - hinge_e.head = center - socket1_e.length /= 4 - socket2_e.length /= 3 - - # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - thigh_p = pb[thigh] - shin_p = pb[shin] - foot_p = pb[foot] - if self.org_parent != None: - hinge_p = pb[hinge] - - if self.org_parent != None: - # socket1_p = pb[socket1] # UNUSED - socket2_p = pb[socket2] - - # Set the knee to only bend on the x-axis. - shin_p.rotation_mode = 'XYZ' - if 'X' in self.primary_rotation_axis: - shin_p.lock_rotation = (False, True, True) - elif 'Y' in self.primary_rotation_axis: - shin_p.lock_rotation = (True, False, True) - else: - shin_p.lock_rotation = (True, True, False) - - # Hinge transforms are locked, for auto-ik - if self.org_parent != None: - hinge_p.lock_location = True, True, True - hinge_p.lock_rotation = True, True, True - hinge_p.lock_rotation_w = True - hinge_p.lock_scale = True, True, True - - # Set up custom properties - if self.org_parent != None: - prop = rna_idprop_ui_prop_get(thigh_p, "isolate", create=True) - thigh_p["isolate"] = 0.0 - prop["soft_min"] = prop["min"] = 0.0 - prop["soft_max"] = prop["max"] = 1.0 - - # Hinge constraints / drivers - if self.org_parent != None: - con = socket2_p.constraints.new('COPY_LOCATION') - con.name = "copy_location" - con.target = self.obj - con.subtarget = socket1 - - con = socket2_p.constraints.new('COPY_TRANSFORMS') - con.name = "isolate_off" - con.target = self.obj - con.subtarget = socket1 - - # Driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = thigh_p.path_from_id() + '["isolate"]' - mod = fcurve.modifiers[0] - mod.poly_order = 1 - mod.coefficients[0] = 1.0 - mod.coefficients[1] = -1.0 - - # Constrain org bones to controls - con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') - con.name = "fk" - con.target = self.obj - con.subtarget = thigh - - con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') - con.name = "fk" - con.target = self.obj - con.subtarget = shin - - con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') - con.name = "fk" - con.target = self.obj - con.subtarget = foot_mch - - # Set layers if specified - if self.layers: - thigh_p.bone.layers = self.layers - shin_p.bone.layers = self.layers - foot_p.bone.layers = self.layers - - # Create control widgets - create_limb_widget(self.obj, thigh) - create_limb_widget(self.obj, shin) + # Create foot widget ob = create_widget(self.obj, foot) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] diff --git a/rigify/rigs/biped/leg/ik.py b/rigify/rigs/biped/leg/ik.py index 52eae332..aea8502b 100644 --- a/rigify/rigs/biped/leg/ik.py +++ b/rigify/rigs/biped/leg/ik.py @@ -18,67 +18,17 @@ # <pep8 compliant> -from math import pi, acos - import bpy -from rna_prop_ui import rna_idprop_ui_prop_get from mathutils import Vector +from .. import limb_common + from ....utils import MetarigError +from ....utils import align_bone_x_axis from ....utils import copy_bone, flip_bone, put_bone from ....utils import connected_children_names, has_connected_children from ....utils import strip_org, make_mechanism_name, insert_before_lr -from ....utils import create_widget, create_line_widget, create_sphere_widget, create_circle_widget - - -def align_x_axis(obj, bone, vec): - """ Aligns the x-axis of a bone to the given vector. This only works if it - can be an exact match. - Must be in edit mode. - - """ - vec.normalize() - bone_e = obj.data.edit_bones[bone] - dot = max(-1.0, min(1.0, bone_e.x_axis.dot(vec))) - angle = acos(dot) - - bone_e.roll += angle - - dot1 = bone_e.x_axis.dot(vec) - - bone_e.roll -= angle * 2 - - dot2 = bone_e.x_axis.dot(vec) - - if dot1 > dot2: - bone_e.roll += angle * 2 - - -def angle_on_plane(plane, vec1, vec2): - """ Return the angle between two vectors projected onto a plane. - """ - plane.normalize() - vec1 = vec1 - (plane * (vec1.dot(plane))) - vec2 = vec2 - (plane * (vec2.dot(plane))) - vec1.normalize() - vec2.normalize() - - # Determine the angle - angle = acos(max(-1.0, min(1.0, vec1.dot(vec2)))) - - if angle < 0.00001: # close enough to zero that sign doesn't matter - return angle - - # Determine the sign of the angle - vec3 = vec2.cross(vec1) - vec3.normalize() - sign = vec3.dot(plane) - if sign >= 0: - sign = 1 - else: - sign = -1 - - return angle * sign +from ....utils import create_widget, create_circle_widget class Rig: @@ -136,32 +86,35 @@ class Rig: self.layers = list(params.ik_layers) else: self.layers = None + bend_hint = params.bend_hint + primary_rotation_axis = params.primary_rotation_axis + pole_target_base_name = self.params.knee_base_name + "_target" - self.bend_hint = params.bend_hint - - self.primary_rotation_axis = params.primary_rotation_axis + # Leg is based on common limb + self.ik_limb = limb_common.IKLimb(obj, self.org_bones[0], self.org_bones[1], self.org_bones[2], pole_target_base_name, primary_rotation_axis, bend_hint, self.layers, ikfk_switch) def generate(self): """ Generate the rig. Do NOT modify any of the original bones, except for adding constraints. The main armature should be selected and active before this is called. - """ + # Generate base IK limb + bone_list = self.ik_limb.generate() + thigh = bone_list[0] + shin = bone_list[1] + foot = bone_list[2] + foot_mch = bone_list[3] + pole = bone_list[4] + # vispole = bone_list[5] + # visfoot = bone_list[6] + + # Build IK foot rig bpy.ops.object.mode_set(mode='EDIT') - make_rocker = False if self.org_bones[5] is not None: make_rocker = True # Create the bones - thigh = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik")))) - shin = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik")))) - - foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik"))) - foot_ik_target = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[2], "_ik_target")))) - pole_target_name = self.params.knee_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1] - pole = copy_bone(self.obj, self.org_bones[0], pole_target_name) - toe = copy_bone(self.obj, self.org_bones[3], strip_org(self.org_bones[3])) toe_parent = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".parent"))) toe_parent_socket1 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket1"))) @@ -175,18 +128,12 @@ class Rig: rocker1 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.01"))) rocker2 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.02"))) - visfoot = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik"))) - vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik"))) - # Get edit bones eb = self.obj.data.edit_bones org_foot_e = eb[self.org_bones[2]] - thigh_e = eb[thigh] - shin_e = eb[shin] foot_e = eb[foot] - foot_ik_target_e = eb[foot_ik_target] - pole_e = eb[pole] + foot_ik_target_e = eb[foot_mch] toe_e = eb[toe] toe_parent_e = eb[toe_parent] toe_parent_socket1_e = eb[toe_parent_socket1] @@ -197,20 +144,11 @@ class Rig: if make_rocker: rocker1_e = eb[rocker1] rocker2_e = eb[rocker2] - visfoot_e = eb[visfoot] - vispole_e = eb[vispole] # Parenting - shin_e.parent = thigh_e - - foot_e.use_connect = False - foot_e.parent = None foot_ik_target_e.use_connect = False foot_ik_target_e.parent = roll2_e - pole_e.use_connect = False - pole_e.parent = foot_e - toe_e.parent = toe_parent_e toe_parent_e.use_connect = False toe_parent_e.parent = toe_parent_socket1_e @@ -228,12 +166,6 @@ class Rig: roll2_e.use_connect = False roll2_e.parent = roll1_e - visfoot_e.use_connect = False - visfoot_e.parent = None - - vispole_e.use_connect = False - vispole_e.parent = None - if make_rocker: rocker1_e.use_connect = False rocker2_e.use_connect = False @@ -242,38 +174,12 @@ class Rig: rocker2_e.parent = rocker1_e rocker1_e.parent = foot_e - # Misc - foot_e.use_local_location = False - - visfoot_e.hide_select = True - vispole_e.hide_select = True - # Positioning vec = Vector(toe_e.vector) vec.normalize() foot_e.tail = foot_e.head + (vec * foot_e.length) foot_e.roll = toe_e.roll - v1 = shin_e.tail - thigh_e.head - - if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: - v2 = v1.cross(shin_e.x_axis) - if (v2 * shin_e.z_axis) > 0.0: - v2 *= -1.0 - else: - v2 = v1.cross(shin_e.z_axis) - if (v2 * shin_e.x_axis) < 0.0: - v2 *= -1.0 - v2.normalize() - v2 *= v1.length - - if '-' in self.primary_rotation_axis: - v2 *= -1 - - pole_e.head = shin_e.head + v2 - pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) - pole_e.roll = 0.0 - flip_bone(self.obj, toe_parent_socket1) flip_bone(self.obj, toe_parent_socket2) toe_parent_socket1_e.head = Vector(org_foot_e.tail) @@ -292,13 +198,10 @@ class Rig: foot_roll_e.length /= 2 roll_axis = roll1_e.vector.cross(org_foot_e.vector) - align_x_axis(self.obj, roll1, roll_axis) - align_x_axis(self.obj, roll2, roll_axis) + align_bone_x_axis(self.obj, roll1, roll_axis) + align_bone_x_axis(self.obj, roll2, roll_axis) foot_roll_e.roll = roll2_e.roll - visfoot_e.tail = visfoot_e.head + Vector((0, 0, v1.length / 32)) - vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) - if make_rocker: d = toe_e.y_axis.dot(rocker1_e.x_axis) if d >= 0.0: @@ -306,30 +209,11 @@ class Rig: else: flip_bone(self.obj, rocker1) - # Weird alignment issues. Fix. - toe_parent_e.head = Vector(org_foot_e.head) - toe_parent_e.tail = Vector(org_foot_e.tail) - toe_parent_e.roll = org_foot_e.roll - - foot_e.head = Vector(org_foot_e.head) - - foot_ik_target_e.head = Vector(org_foot_e.head) - foot_ik_target_e.tail = Vector(org_foot_e.tail) - - # Determine the pole offset value - plane = (shin_e.tail - thigh_e.head).normalized() - vec1 = thigh_e.x_axis.normalized() - vec2 = (pole_e.head - thigh_e.head).normalized() - pole_offset = angle_on_plane(plane, vec1, vec2) - # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones - # thigh_p = pb[thigh] # UNUSED - shin_p = pb[shin] foot_p = pb[foot] - pole_p = pb[pole] foot_roll_p = pb[foot_roll] roll1_p = pb[roll1] roll2_p = pb[roll2] @@ -337,21 +221,8 @@ class Rig: rocker1_p = pb[rocker1] rocker2_p = pb[rocker2] toe_p = pb[toe] - toe_parent_p = pb[toe_parent] + # toe_parent_p = pb[toe_parent] toe_parent_socket1_p = pb[toe_parent_socket1] - visfoot_p = pb[visfoot] - vispole_p = pb[vispole] - - # Set the knee to only bend on the primary axis. - if 'X' in self.primary_rotation_axis: - shin_p.lock_ik_y = True - shin_p.lock_ik_z = True - elif 'Y' in self.primary_rotation_axis: - shin_p.lock_ik_x = True - shin_p.lock_ik_z = True - else: - shin_p.lock_ik_x = True - shin_p.lock_ik_y = True # Foot roll control only rotates on x-axis, or x and y if rocker. foot_roll_p.rotation_mode = 'XYZ' @@ -369,59 +240,6 @@ class Rig: rocker1_p.rotation_mode = 'XYZ' rocker2_p.rotation_mode = 'XYZ' - # Pole target only translates - pole_p.lock_location = False, False, False - pole_p.lock_rotation = True, True, True - pole_p.lock_rotation_w = True - pole_p.lock_scale = True, True, True - - # Set up custom properties - if self.switch is True: - prop = rna_idprop_ui_prop_get(foot_p, "ikfk_switch", create=True) - foot_p["ikfk_switch"] = 0.0 - prop["soft_min"] = prop["min"] = 0.0 - prop["soft_max"] = prop["max"] = 1.0 - - # Bend direction hint - if self.bend_hint: - con = shin_p.constraints.new('LIMIT_ROTATION') - con.name = "bend_hint" - con.owner_space = 'LOCAL' - if self.primary_rotation_axis == 'X': - con.use_limit_x = True - con.min_x = pi / 10 - con.max_x = pi / 10 - elif self.primary_rotation_axis == '-X': - con.use_limit_x = True - con.min_x = -pi / 10 - con.max_x = -pi / 10 - elif self.primary_rotation_axis == 'Y': - con.use_limit_y = True - con.min_y = pi / 10 - con.max_y = pi / 10 - elif self.primary_rotation_axis == '-Y': - con.use_limit_y = True - con.min_y = -pi / 10 - con.max_y = -pi / 10 - elif self.primary_rotation_axis == 'Z': - con.use_limit_z = True - con.min_z = pi / 10 - con.max_z = pi / 10 - elif self.primary_rotation_axis == '-Z': - con.use_limit_z = True - con.min_z = -pi / 10 - con.max_z = -pi / 10 - - # IK Constraint - con = shin_p.constraints.new('IK') - con.name = "ik" - con.target = self.obj - con.subtarget = foot_ik_target - con.pole_target = self.obj - con.pole_subtarget = pole - con.pole_angle = pole_offset - con.chain_count = 2 - # toe_parent constraint con = toe_parent_socket1_p.constraints.new('COPY_LOCATION') con.name = "copy_location" @@ -493,103 +311,24 @@ class Rig: var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]' - # Constrain org bones to controls - con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') - con.name = "ik" - con.target = self.obj - con.subtarget = thigh - if self.switch is True: - # IK/FK switch driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = foot_p.path_from_id() + '["ikfk_switch"]' - - con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') - con.name = "ik" - con.target = self.obj - con.subtarget = shin - if self.switch is True: - # IK/FK switch driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = foot_p.path_from_id() + '["ikfk_switch"]' - - con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') - con.name = "ik" - con.target = self.obj - con.subtarget = foot_ik_target - if self.switch is True: - # IK/FK switch driver - fcurve = con.driver_add("influence") - driver = fcurve.driver - var = driver.variables.new() - driver.type = 'AVERAGE' - var.name = "var" - var.targets[0].id_type = 'OBJECT' - var.targets[0].id = self.obj - var.targets[0].data_path = foot_p.path_from_id() + '["ikfk_switch"]' - + # Constrain toe bone to toe control con = pb[self.org_bones[3]].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = toe - # VIS foot constraints - con = visfoot_p.constraints.new('COPY_LOCATION') - con.name = "copy_loc" - con.target = self.obj - con.subtarget = self.org_bones[2] - - con = visfoot_p.constraints.new('STRETCH_TO') - con.name = "stretch_to" - con.target = self.obj - con.subtarget = foot - con.volume = 'NO_VOLUME' - con.rest_length = visfoot_p.length - - # VIS pole constraints - con = vispole_p.constraints.new('COPY_LOCATION') - con.name = "copy_loc" - con.target = self.obj - con.subtarget = self.org_bones[1] - - con = vispole_p.constraints.new('STRETCH_TO') - con.name = "stretch_to" - con.target = self.obj - con.subtarget = pole - con.volume = 'NO_VOLUME' - con.rest_length = vispole_p.length - # Set layers if specified if self.layers: - foot_p.bone.layers = self.layers - pole_p.bone.layers = self.layers foot_roll_p.bone.layers = self.layers - visfoot_p.bone.layers = self.layers - vispole_p.bone.layers = self.layers - toe_p.bone.layers = [(i[0] or i[1]) for i in zip(toe_p.bone.layers, self.layers)] # Both FK and IK layers # Create widgets - create_line_widget(self.obj, vispole) - create_line_widget(self.obj, visfoot) - create_sphere_widget(self.obj, pole) create_circle_widget(self.obj, toe, radius=0.7, head_tail=0.5) - ob = create_widget(self.obj, foot) + ob = create_widget(self.obj, foot_roll) if ob != None: - verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] - edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] + verts = [(0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247)] + edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() @@ -597,10 +336,10 @@ class Rig: mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 - ob = create_widget(self.obj, foot_roll) + ob = create_widget(self.obj, foot) if ob != None: - verts = [(0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247)] - edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)] + verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] + edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() @@ -608,4 +347,4 @@ class Rig: mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 - return [thigh, shin, foot, pole, foot_roll, foot_ik_target] + return [thigh, shin, foot, pole, foot_roll, foot_mch] diff --git a/rigify/rigs/biped/limb_common.py b/rigify/rigs/biped/limb_common.py new file mode 100644 index 00000000..ec29a510 --- /dev/null +++ b/rigify/rigs/biped/limb_common.py @@ -0,0 +1,1139 @@ +#====================== 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 math import pi + +import bpy +from rna_prop_ui import rna_idprop_ui_prop_get +from mathutils import Vector + +from ...utils import angle_on_plane, align_bone_roll, align_bone_z_axis +from ...utils import new_bone, copy_bone, put_bone, make_nonscaling_child +from ...utils import strip_org, make_mechanism_name, make_deformer_name, insert_before_lr +from ...utils import create_widget, create_limb_widget, create_line_widget, create_sphere_widget + + +class FKLimb: + def __init__(self, obj, bone1, bone2, bone3, primary_rotation_axis, layers): + self.obj = obj + + self.org_bones = [bone1, bone2, bone3] + + # Get (optional) parent + if self.obj.data.bones[bone1].parent is None: + self.org_parent = None + else: + self.org_parent = self.obj.data.bones[bone1].parent.name + + # Get the rig parameters + self.layers = layers + self.primary_rotation_axis = primary_rotation_axis + + def generate(self): + bpy.ops.object.mode_set(mode='EDIT') + + # Create non-scaling parent bone + if self.org_parent != None: + loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head) + parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_fk") + else: + parent = None + + # Create the control bones + ulimb = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk"))) + flimb = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk"))) + elimb = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk"))) + + # Create the anti-stretch bones + fantistr = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_antistr.fk")))) + eantistr = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_antistr.fk")))) + + # Create the end-limb mechanism bone + elimb_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2]))) + + # Create the hinge bones + if parent != None: + socket1 = copy_bone(self.obj, ulimb, make_mechanism_name(ulimb + ".socket1")) + socket2 = copy_bone(self.obj, ulimb, make_mechanism_name(ulimb + ".socket2")) + + # Get edit bones + eb = self.obj.data.edit_bones + + ulimb_e = eb[ulimb] + flimb_e = eb[flimb] + elimb_e = eb[elimb] + + fantistr_e = eb[fantistr] + eantistr_e = eb[eantistr] + + elimb_mch_e = eb[elimb_mch] + + if parent != None: + socket1_e = eb[socket1] + socket2_e = eb[socket2] + + # Parenting + elimb_mch_e.use_connect = False + elimb_mch_e.parent = elimb_e + + elimb_e.use_connect = False + elimb_e.parent = eantistr_e + + eantistr_e.use_connect = False + eantistr_e.parent = flimb_e + + flimb_e.use_connect = False + flimb_e.parent = fantistr_e + + fantistr_e.use_connect = False + fantistr_e.parent = ulimb_e + + if parent != None: + socket1_e.use_connect = False + socket1_e.parent = eb[parent] + + socket2_e.use_connect = False + socket2_e.parent = None + + ulimb_e.use_connect = False + ulimb_e.parent = socket2_e + + # Positioning + fantistr_e.length /= 8 + put_bone(self.obj, fantistr, Vector(ulimb_e.tail)) + eantistr_e.length /= 8 + put_bone(self.obj, eantistr, Vector(flimb_e.tail)) + + if parent != None: + socket1_e.length /= 4 + socket2_e.length /= 3 + + # Object mode, get pose bones + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + ulimb_p = pb[ulimb] + flimb_p = pb[flimb] + elimb_p = pb[elimb] + + fantistr_p = pb[fantistr] + eantistr_p = pb[eantistr] + + if parent != None: + socket2_p = pb[socket2] + + # Lock axes + ulimb_p.lock_location = (True, True, True) + flimb_p.lock_location = (True, True, True) + elimb_p.lock_location = (True, True, True) + + # Set the elbow to only bend on the x-axis. + flimb_p.rotation_mode = 'XYZ' + if 'X' in self.primary_rotation_axis: + flimb_p.lock_rotation = (False, True, True) + elif 'Y' in self.primary_rotation_axis: + flimb_p.lock_rotation = (True, False, True) + else: + flimb_p.lock_rotation = (True, True, False) + + # Set up custom properties + if parent != None: + prop = rna_idprop_ui_prop_get(ulimb_p, "isolate", create=True) + ulimb_p["isolate"] = 0.0 + prop["soft_min"] = prop["min"] = 0.0 + prop["soft_max"] = prop["max"] = 1.0 + + prop = rna_idprop_ui_prop_get(ulimb_p, "stretch_length", create=True) + ulimb_p["stretch_length"] = 1.0 + prop["min"] = 0.05 + prop["max"] = 20.0 + prop["soft_min"] = 0.25 + prop["soft_max"] = 4.0 + + # Stretch drivers + def add_stretch_drivers(pose_bone): + driver = pose_bone.driver_add("scale", 1).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "stretch_length" + + driver = pose_bone.driver_add("scale", 0).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "1/sqrt(stretch_length)" + + driver = pose_bone.driver_add("scale", 2).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "1/sqrt(stretch_length)" + + def add_antistretch_drivers(pose_bone): + driver = pose_bone.driver_add("scale", 1).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "1/stretch_length" + + driver = pose_bone.driver_add("scale", 0).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "sqrt(stretch_length)" + + driver = pose_bone.driver_add("scale", 2).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "sqrt(stretch_length)" + + add_stretch_drivers(ulimb_p) + add_stretch_drivers(flimb_p) + add_antistretch_drivers(fantistr_p) + add_antistretch_drivers(eantistr_p) + + # Hinge constraints / drivers + if parent != None: + con = socket2_p.constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + con.subtarget = socket1 + + con = socket2_p.constraints.new('COPY_TRANSFORMS') + con.name = "isolate_off" + con.target = self.obj + con.subtarget = socket1 + + # Driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = ulimb_p.path_from_id() + '["isolate"]' + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + # Constrain org bones to controls + con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') + con.name = "fk" + con.target = self.obj + con.subtarget = ulimb + + con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') + con.name = "fk" + con.target = self.obj + con.subtarget = flimb + + con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') + con.name = "fk" + con.target = self.obj + con.subtarget = elimb_mch + + # Set layers if specified + if self.layers: + ulimb_p.bone.layers = self.layers + flimb_p.bone.layers = self.layers + elimb_p.bone.layers = self.layers + + # Create control widgets + create_limb_widget(self.obj, ulimb) + create_limb_widget(self.obj, flimb) + + ob = create_widget(self.obj, elimb) + if ob != None: + verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] + edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] + mesh = ob.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + mod = ob.modifiers.new("subsurf", 'SUBSURF') + mod.levels = 2 + + return [ulimb, flimb, elimb, elimb_mch] + + +class IKLimb: + """ An IK limb rig, with an optional ik/fk switch. + + """ + def __init__(self, obj, bone1, bone2, bone3, pole_target_base_name, primary_rotation_axis, bend_hint, layers, ikfk_switch=False): + self.obj = obj + self.switch = ikfk_switch + + # Get the chain of 3 connected bones + self.org_bones = [bone1, bone2, bone3] + + # Get (optional) parent + if self.obj.data.bones[bone1].parent is None: + self.org_parent = None + else: + self.org_parent = self.obj.data.bones[bone1].parent.name + + # Get the rig parameters + self.pole_target_base_name = pole_target_base_name + self.layers = layers + self.bend_hint = bend_hint + self.primary_rotation_axis = primary_rotation_axis + + def generate(self): + bpy.ops.object.mode_set(mode='EDIT') + + # Create non-scaling parent bone + if self.org_parent != None: + loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head) + parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_ik") + else: + parent = None + + # Create the bones + ulimb = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik")))) + flimb = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik")))) + elimb = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik"))) + elimb_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2]))) + + ulimb_nostr = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".nostr.ik")))) + flimb_nostr = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".nostr.ik")))) + + ulimb_str = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".stretch.ik")))) + flimb_str = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".stretch.ik")))) + + pole_target_name = self.pole_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1] + pole = copy_bone(self.obj, self.org_bones[0], pole_target_name) + + viselimb = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik"))) + vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik"))) + + # Get edit bones + eb = self.obj.data.edit_bones + + if parent != None: + parent_e = eb[parent] + ulimb_e = eb[ulimb] + flimb_e = eb[flimb] + elimb_e = eb[elimb] + elimb_mch_e = eb[elimb_mch] + ulimb_nostr_e = eb[ulimb_nostr] + flimb_nostr_e = eb[flimb_nostr] + ulimb_str_e = eb[ulimb_str] + flimb_str_e = eb[flimb_str] + pole_e = eb[pole] + viselimb_e = eb[viselimb] + vispole_e = eb[vispole] + + # Parenting + ulimb_e.use_connect = False + ulimb_nostr_e.use_connect = False + if parent != None: + ulimb_e.parent = parent_e + ulimb_nostr_e.parent = parent_e + + flimb_e.parent = ulimb_e + flimb_nostr_e.parent = ulimb_nostr_e + + elimb_e.use_connect = False + elimb_e.parent = None + + elimb_mch_e.use_connect = False + elimb_mch_e.parent = elimb_e + + ulimb_str_e.use_connect = False + ulimb_str_e.parent = ulimb_e.parent + + flimb_str_e.use_connect = False + flimb_str_e.parent = ulimb_e.parent + + pole_e.use_connect = False + if parent != None: + pole_e.parent = parent_e + + viselimb_e.use_connect = False + viselimb_e.parent = None + + vispole_e.use_connect = False + vispole_e.parent = None + + # Misc + elimb_e.use_local_location = False + + viselimb_e.hide_select = True + vispole_e.hide_select = True + + # Positioning + v1 = flimb_e.tail - ulimb_e.head + if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: + v2 = v1.cross(flimb_e.x_axis) + if (v2 * flimb_e.z_axis) > 0.0: + v2 *= -1.0 + else: + v2 = v1.cross(flimb_e.z_axis) + if (v2 * flimb_e.x_axis) < 0.0: + v2 *= -1.0 + v2.normalize() + v2 *= v1.length + + if '-' in self.primary_rotation_axis: + v2 *= -1 + + pole_e.head = flimb_e.head + v2 + pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) + pole_e.roll = 0.0 + + viselimb_e.tail = viselimb_e.head + Vector((0, 0, v1.length / 32)) + vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) + + # Determine the pole offset value + plane = (flimb_e.tail - ulimb_e.head).normalized() + vec1 = ulimb_e.x_axis.normalized() + vec2 = (pole_e.head - ulimb_e.head).normalized() + pole_offset = angle_on_plane(plane, vec1, vec2) + + # Object mode, get pose bones + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + ulimb_p = pb[ulimb] + flimb_p = pb[flimb] + elimb_p = pb[elimb] + ulimb_nostr_p = pb[ulimb_nostr] + flimb_nostr_p = pb[flimb_nostr] + ulimb_str_p = pb[ulimb_str] + flimb_str_p = pb[flimb_str] + pole_p = pb[pole] + viselimb_p = pb[viselimb] + vispole_p = pb[vispole] + + # Set the elbow to only bend on the primary axis + if 'X' in self.primary_rotation_axis: + flimb_p.lock_ik_y = True + flimb_p.lock_ik_z = True + flimb_nostr_p.lock_ik_y = True + flimb_nostr_p.lock_ik_z = True + elif 'Y' in self.primary_rotation_axis: + flimb_p.lock_ik_x = True + flimb_p.lock_ik_z = True + flimb_nostr_p.lock_ik_x = True + flimb_nostr_p.lock_ik_z = True + else: + flimb_p.lock_ik_x = True + flimb_p.lock_ik_y = True + flimb_nostr_p.lock_ik_x = True + flimb_nostr_p.lock_ik_y = True + + # Limb stretches + ulimb_nostr_p.ik_stretch = 0.0 + flimb_nostr_p.ik_stretch = 0.0 + + # This next bit is weird. The values calculated cause + # ulimb and flimb to preserve their relative lengths + # while stretching. + l1 = ulimb_p.length + l2 = flimb_p.length + if l1 < l2: + ulimb_p.ik_stretch = (l1 ** (1 / 3)) / (l2 ** (1 / 3)) + flimb_p.ik_stretch = 1.0 + else: + ulimb_p.ik_stretch = 1.0 + flimb_p.ik_stretch = (l2 ** (1 / 3)) / (l1 ** (1 / 3)) + + # Pole target only translates + pole_p.lock_location = False, False, False + pole_p.lock_rotation = True, True, True + pole_p.lock_rotation_w = True + pole_p.lock_scale = True, True, True + + # Set up custom properties + if self.switch is True: + prop = rna_idprop_ui_prop_get(elimb_p, "ikfk_switch", create=True) + elimb_p["ikfk_switch"] = 0.0 + prop["soft_min"] = prop["min"] = 0.0 + prop["soft_max"] = prop["max"] = 1.0 + + prop = rna_idprop_ui_prop_get(elimb_p, "stretch_length", create=True) + elimb_p["stretch_length"] = 1.0 + prop["min"] = 0.05 + prop["max"] = 20.0 + prop["soft_min"] = 0.25 + prop["soft_max"] = 4.0 + + prop = rna_idprop_ui_prop_get(elimb_p, "auto_stretch", create=True) + elimb_p["auto_stretch"] = 1.0 + prop["soft_min"] = prop["min"] = 0.0 + prop["soft_max"] = prop["max"] = 1.0 + + # Stretch parameter drivers + def add_stretch_drivers(pose_bone): + driver = pose_bone.driver_add("scale", 1).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "stretch_length" + + driver = pose_bone.driver_add("scale", 0).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "stretch_length" + + driver = pose_bone.driver_add("scale", 2).driver + var = driver.variables.new() + var.name = "stretch_length" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]' + driver.type = 'SCRIPTED' + driver.expression = "stretch_length" + add_stretch_drivers(ulimb_nostr_p) + + # Bend direction hint + def add_bend_hint(pose_bone, axis): + con = pose_bone.constraints.new('LIMIT_ROTATION') + con.name = "bend_hint" + con.owner_space = 'LOCAL' + if axis == 'X': + con.use_limit_x = True + con.min_x = pi / 10 + con.max_x = pi / 10 + elif axis == '-X': + con.use_limit_x = True + con.min_x = -pi / 10 + con.max_x = -pi / 10 + elif axis == 'Y': + con.use_limit_y = True + con.min_y = pi / 10 + con.max_y = pi / 10 + elif axis == '-Y': + con.use_limit_y = True + con.min_y = -pi / 10 + con.max_y = -pi / 10 + elif axis == 'Z': + con.use_limit_z = True + con.min_z = pi / 10 + con.max_z = pi / 10 + elif axis == '-Z': + con.use_limit_z = True + con.min_z = -pi / 10 + con.max_z = -pi / 10 + if self.bend_hint: + add_bend_hint(flimb_p, self.primary_rotation_axis) + add_bend_hint(flimb_nostr_p, self.primary_rotation_axis) + + # Constrain normal IK chain to no-stretch IK chain + con = ulimb_p.constraints.new('COPY_TRANSFORMS') + con.name = "pre_stretch" + con.target = self.obj + con.subtarget = ulimb_nostr + + con = flimb_p.constraints.new('COPY_TRANSFORMS') + con.name = "pre_stretch" + con.target = self.obj + con.subtarget = flimb_nostr + + # IK Constraints + con = flimb_nostr_p.constraints.new('IK') + con.name = "ik" + con.target = self.obj + con.subtarget = elimb_mch + con.pole_target = self.obj + con.pole_subtarget = pole + con.pole_angle = pole_offset + con.chain_count = 2 + + con = flimb_p.constraints.new('IK') + con.name = "ik" + con.target = self.obj + con.subtarget = elimb_mch + con.chain_count = 2 + + # Driver to enable/disable auto stretching IK chain + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["auto_stretch"]' + + # Stretch bone constraints + con = ulimb_str_p.constraints.new('COPY_TRANSFORMS') + con.name = "anchor" + con.target = self.obj + con.subtarget = ulimb + con = ulimb_str_p.constraints.new('MAINTAIN_VOLUME') + con.name = "stretch" + con.owner_space = 'LOCAL' + + con = flimb_str_p.constraints.new('COPY_TRANSFORMS') + con.name = "anchor" + con.target = self.obj + con.subtarget = flimb + con = flimb_str_p.constraints.new('MAINTAIN_VOLUME') + con.name = "stretch" + con.owner_space = 'LOCAL' + + # Constrain org bones + con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') + con.name = "ik" + con.target = self.obj + con.subtarget = ulimb_str + if self.switch is True: + # IK/FK switch driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]' + + con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') + con.name = "ik" + con.target = self.obj + con.subtarget = flimb_str + if self.switch is True: + # IK/FK switch driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]' + + con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') + con.name = "ik" + con.target = self.obj + con.subtarget = elimb_mch + if self.switch is True: + # IK/FK switch driver + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]' + + # VIS limb-end constraints + con = viselimb_p.constraints.new('COPY_LOCATION') + con.name = "copy_loc" + con.target = self.obj + con.subtarget = self.org_bones[2] + + con = viselimb_p.constraints.new('STRETCH_TO') + con.name = "stretch_to" + con.target = self.obj + con.subtarget = elimb + con.volume = 'NO_VOLUME' + con.rest_length = viselimb_p.length + + # VIS pole constraints + con = vispole_p.constraints.new('COPY_LOCATION') + con.name = "copy_loc" + con.target = self.obj + con.subtarget = self.org_bones[1] + + con = vispole_p.constraints.new('STRETCH_TO') + con.name = "stretch_to" + con.target = self.obj + con.subtarget = pole + con.volume = 'NO_VOLUME' + con.rest_length = vispole_p.length + + # Set layers if specified + if self.layers: + elimb_p.bone.layers = self.layers + pole_p.bone.layers = self.layers + viselimb_p.bone.layers = self.layers + vispole_p.bone.layers = self.layers + + # Create widgets + create_line_widget(self.obj, vispole) + create_line_widget(self.obj, viselimb) + create_sphere_widget(self.obj, pole) + + ob = create_widget(self.obj, elimb) + if ob != None: + verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] + edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] + mesh = ob.data + mesh.from_pydata(verts, edges, []) + mesh.update() + + mod = ob.modifiers.new("subsurf", 'SUBSURF') + mod.levels = 2 + + return [ulimb, flimb, elimb, elimb_mch, pole, vispole, viselimb] + + +class RubberHoseLimb: + def __init__(self, obj, bone1, bone2, bone3, use_complex_limb, junc_base_name, primary_rotation_axis, layers): + self.obj = obj + + # Get the chain of 3 connected bones + self.org_bones = [bone1, bone2, bone3] + + # Get (optional) parent + if self.obj.data.bones[bone1].parent is None: + self.org_parent = None + else: + self.org_parent = self.obj.data.bones[bone1].parent.name + + # Get rig parameters + self.layers = layers + self.primary_rotation_axis = primary_rotation_axis + self.use_complex_limb = use_complex_limb + self.junc_base_name = junc_base_name + + def generate(self): + bpy.ops.object.mode_set(mode='EDIT') + + # Create non-scaling parent bone + if self.org_parent != None: + loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head) + parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_rh") + else: + parent = None + + if not self.use_complex_limb: + # Simple rig + + # Create bones + ulimb = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) + flimb = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1]))) + elimb = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) + + # Get edit bones + eb = self.obj.data.edit_bones + + ulimb_e = eb[ulimb] + flimb_e = eb[flimb] + elimb_e = eb[elimb] + + # Parenting + elimb_e.parent = flimb_e + elimb_e.use_connect = True + + flimb_e.parent = ulimb_e + flimb_e.use_connect = True + + if parent != None: + elimb_e.use_connect = False + ulimb_e.parent = eb[parent] + + # Object mode, get pose bones + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + ulimb_p = pb[ulimb] + flimb_p = pb[flimb] + elimb_p = pb[elimb] + + # Constrain def bones to org bones + con = ulimb_p.constraints.new('COPY_TRANSFORMS') + con.name = "def" + con.target = self.obj + con.subtarget = self.org_bones[0] + + con = flimb_p.constraints.new('COPY_TRANSFORMS') + con.name = "def" + con.target = self.obj + con.subtarget = self.org_bones[1] + + con = elimb_p.constraints.new('COPY_TRANSFORMS') + con.name = "def" + con.target = self.obj + con.subtarget = self.org_bones[2] + + return [] + else: + # Complex rig + + # Get the .R or .L off the end of the upper limb name if it exists + lr = self.org_bones[0].split(".", 1) + if len(lr) == 1: + lr = "" + else: + lr = lr[1] + + # Create bones + ulimb1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(insert_before_lr(self.org_bones[0], ".01")))) + ulimb2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(insert_before_lr(self.org_bones[0], ".02")))) + flimb1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(insert_before_lr(self.org_bones[1], ".01")))) + flimb2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(insert_before_lr(self.org_bones[1], ".02")))) + elimb = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) + + junc = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".junc")))) + + uhose = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[0], "_hose"))) + jhose = new_bone(self.obj, self.junc_base_name + "_hose." + lr) + fhose = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[1], "_hose"))) + + uhose_par = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(uhose, "_parent")))) + jhose_par = copy_bone(self.obj, junc, make_mechanism_name(strip_org(insert_before_lr(jhose, "_parent")))) + fhose_par = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(fhose, "_parent")))) + + # Get edit bones + eb = self.obj.data.edit_bones + + if parent != None: + parent_e = eb[parent] + else: + parent_e = None + + ulimb1_e = eb[ulimb1] + ulimb2_e = eb[ulimb2] + flimb1_e = eb[flimb1] + flimb2_e = eb[flimb2] + elimb_e = eb[elimb] + + junc_e = eb[junc] + + uhose_e = eb[uhose] + jhose_e = eb[jhose] + fhose_e = eb[fhose] + + uhose_par_e = eb[uhose_par] + jhose_par_e = eb[jhose_par] + fhose_par_e = eb[fhose_par] + + # Parenting + if parent != None: + ulimb1_e.use_connect = False + ulimb1_e.parent = parent_e + + ulimb2_e.use_connect = False + ulimb2_e.parent = eb[self.org_bones[0]] + + flimb1_e.use_connect = True + flimb1_e.parent = ulimb2_e + + flimb2_e.use_connect = False + flimb2_e.parent = eb[self.org_bones[1]] + + elimb_e.use_connect = False + elimb_e.parent = eb[self.org_bones[2]] + + junc_e.use_connect = False + junc_e.parent = eb[self.org_bones[0]] + + uhose_e.use_connect = False + uhose_e.parent = uhose_par_e + + jhose_e.use_connect = False + jhose_e.parent = jhose_par_e + + fhose_e.use_connect = False + fhose_e.parent = fhose_par_e + + uhose_par_e.use_connect = False + uhose_par_e.parent = parent_e + + jhose_par_e.use_connect = False + jhose_par_e.parent = parent_e + + fhose_par_e.use_connect = False + fhose_par_e.parent = parent_e + + # Positioning + ulimb1_e.length *= 0.5 + ulimb2_e.head = Vector(ulimb1_e.tail) + flimb1_e.length *= 0.5 + flimb2_e.head = Vector(flimb1_e.tail) + align_bone_roll(self.obj, flimb2, elimb) + + junc_e.length *= 0.2 + + uhose_par_e.length *= 0.25 + jhose_par_e.length *= 0.15 + fhose_par_e.length *= 0.25 + put_bone(self.obj, uhose_par, Vector(ulimb1_e.tail)) + put_bone(self.obj, jhose_par, Vector(ulimb2_e.tail)) + put_bone(self.obj, fhose_par, Vector(flimb1_e.tail)) + + put_bone(self.obj, uhose, Vector(ulimb1_e.tail)) + put_bone(self.obj, jhose, Vector(ulimb2_e.tail)) + put_bone(self.obj, fhose, Vector(flimb1_e.tail)) + + if 'X' in self.primary_rotation_axis: + upoint = Vector(ulimb1_e.z_axis) + fpoint = Vector(flimb1_e.z_axis) + elif 'Z' in self.primary_rotation_axis: + upoint = Vector(ulimb1_e.x_axis) + fpoint = Vector(flimb1_e.x_axis) + else: # Y + upoint = Vector(ulimb1_e.z_axis) + fpoint = Vector(flimb1_e.z_axis) + + if '-' not in self.primary_rotation_axis: + upoint *= -1 + fpoint *= -1 + + if 'Y' in self.primary_rotation_axis: + uside = Vector(ulimb1_e.x_axis) + fside = Vector(flimb1_e.x_axis) + else: + uside = Vector(ulimb1_e.y_axis) * -1 + fside = Vector(flimb1_e.y_axis) * -1 + + uhose_e.tail = uhose_e.head + upoint + jhose_e.tail = fhose_e.head + upoint + fpoint + fhose_e.tail = fhose_e.head + fpoint + + align_bone_z_axis(self.obj, uhose, uside) + align_bone_z_axis(self.obj, jhose, uside + fside) + align_bone_z_axis(self.obj, fhose, fside) + + l = 0.125 * (ulimb1_e.length + ulimb2_e.length + flimb1_e.length + flimb2_e.length) + uhose_e.length = l + jhose_e.length = l + fhose_e.length = l + + # Object mode, get pose bones + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + + ulimb1_p = pb[ulimb1] + ulimb2_p = pb[ulimb2] + flimb1_p = pb[flimb1] + flimb2_p = pb[flimb2] + elimb_p = pb[elimb] + + junc_p = pb[junc] + + uhose_p = pb[uhose] + jhose_p = pb[jhose] + fhose_p = pb[fhose] + + uhose_par_p = pb[uhose_par] + jhose_par_p = pb[jhose_par] + fhose_par_p = pb[fhose_par] + + # Lock axes + uhose_p.lock_rotation = (True, True, True) + uhose_p.lock_rotation_w = True + uhose_p.lock_scale = (True, True, True) + + jhose_p.lock_rotation = (True, True, True) + jhose_p.lock_rotation_w = True + jhose_p.lock_scale = (True, True, True) + + fhose_p.lock_rotation = (True, True, True) + fhose_p.lock_rotation_w = True + fhose_p.lock_scale = (True, True, True) + + # B-bone settings + ulimb2_p.bone.bbone_segments = 16 + ulimb2_p.bone.bbone_in = 0.0 + ulimb2_p.bone.bbone_out = 1.0 + + flimb1_p.bone.bbone_segments = 16 + flimb1_p.bone.bbone_in = 1.0 + flimb1_p.bone.bbone_out = 0.0 + + # Custom properties + prop = rna_idprop_ui_prop_get(jhose_p, "smooth_bend", create=True) + jhose_p["smooth_bend"] = 0.0 + prop["soft_min"] = prop["min"] = 0.0 + prop["soft_max"] = prop["max"] = 1.0 + + # Drivers + fcurve = ulimb2_p.bone.driver_add("bbone_out") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = jhose_p.path_from_id() + '["smooth_bend"]' + + fcurve = flimb1_p.bone.driver_add("bbone_in") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = jhose_p.path_from_id() + '["smooth_bend"]' + + # Constraints + con = ulimb1_p.constraints.new('COPY_SCALE') + con.name = "anchor" + con.target = self.obj + con.subtarget = self.org_bones[0] + con = ulimb1_p.constraints.new('DAMPED_TRACK') + con.name = "track" + con.target = self.obj + con.subtarget = uhose + con = ulimb1_p.constraints.new('STRETCH_TO') + con.name = "track" + con.target = self.obj + con.subtarget = uhose + con.volume = 'NO_VOLUME' + + con = ulimb2_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = uhose + con = ulimb2_p.constraints.new('DAMPED_TRACK') + con.name = "track" + con.target = self.obj + con.subtarget = jhose + con = ulimb2_p.constraints.new('STRETCH_TO') + con.name = "track" + con.target = self.obj + con.subtarget = jhose + con.volume = 'NO_VOLUME' + + con = flimb1_p.constraints.new('COPY_TRANSFORMS') + con.name = "anchor" + con.target = self.obj + con.subtarget = self.org_bones[1] + con = flimb1_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = jhose + con = flimb1_p.constraints.new('DAMPED_TRACK') + con.name = "track" + con.target = self.obj + con.subtarget = fhose + con = flimb1_p.constraints.new('STRETCH_TO') + con.name = "track" + con.target = self.obj + con.subtarget = fhose + con.volume = 'NO_VOLUME' + + con = flimb2_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = fhose + con = flimb2_p.constraints.new('COPY_ROTATION') + con.name = "twist" + con.target = self.obj + con.subtarget = elimb + con = flimb2_p.constraints.new('DAMPED_TRACK') + con.name = "track" + con.target = self.obj + con.subtarget = self.org_bones[2] + con = flimb2_p.constraints.new('STRETCH_TO') + con.name = "track" + con.target = self.obj + con.subtarget = self.org_bones[2] + con.volume = 'NO_VOLUME' + + con = junc_p.constraints.new('COPY_TRANSFORMS') + con.name = "bend" + con.target = self.obj + con.subtarget = self.org_bones[1] + con.influence = 0.5 + + con = uhose_par_p.constraints.new('COPY_ROTATION') + con.name = "follow" + con.target = self.obj + con.subtarget = self.org_bones[0] + con.influence = 1.0 + con = uhose_par_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = self.org_bones[0] + con.influence = 1.0 + con = uhose_par_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = jhose + con.influence = 0.5 + + con = jhose_par_p.constraints.new('COPY_ROTATION') + con.name = "follow" + con.target = self.obj + con.subtarget = junc + con.influence = 1.0 + con = jhose_par_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = junc + con.influence = 1.0 + + con = fhose_par_p.constraints.new('COPY_ROTATION') + con.name = "follow" + con.target = self.obj + con.subtarget = self.org_bones[1] + con.influence = 1.0 + con = fhose_par_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = jhose + con.influence = 1.0 + con = fhose_par_p.constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + con.subtarget = self.org_bones[2] + con.influence = 0.5 + + # Layers + if self.layers: + uhose_p.bone.layers = self.layers + jhose_p.bone.layers = self.layers + fhose_p.bone.layers = self.layers + else: + layers = list(pb[self.org_bones[0]].bone.layers) + uhose_p.bone.layers = layers + jhose_p.bone.layers = layers + fhose_p.bone.layers = layers + + # Create widgets + create_sphere_widget(self.obj, uhose) + create_sphere_widget(self.obj, jhose) + create_sphere_widget(self.obj, fhose) + + return [uhose, jhose, fhose] diff --git a/rigify/rigs/misc/delta.py b/rigify/rigs/misc/delta.py index a005fb61..84f3612b 100644 --- a/rigify/rigs/misc/delta.py +++ b/rigify/rigs/misc/delta.py @@ -90,7 +90,6 @@ if False: con.target = self.obj con.subtarget = delta - def create_sample(obj): # generated by rigify.utils.write_metarig bpy.ops.object.mode_set(mode='EDIT') @@ -140,7 +139,6 @@ if False: bone.select_tail = True arm.edit_bones.active = bone - def set_mat(obj, bone_name, matrix): """ Sets the bone to have the given transform matrix. """ diff --git a/rigify/rigs/neck_short.py b/rigify/rigs/neck_short.py index 9c24832a..63174d40 100644 --- a/rigify/rigs/neck_short.py +++ b/rigify/rigs/neck_short.py @@ -25,7 +25,7 @@ from ..utils import MetarigError from ..utils import copy_bone, new_bone, put_bone from ..utils import connected_children_names from ..utils import strip_org, make_mechanism_name, make_deformer_name -from ..utils import obj_to_bone, create_circle_widget +from ..utils import create_circle_widget script1 = """ @@ -315,8 +315,8 @@ class Rig: i += 1 # Create control widgets - w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[(len(self.org_bones) - 1) // 2]) - w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[-1]) + create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[(len(self.org_bones) - 1) // 2]) + create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[-1]) # Return control bones return (head_ctrl, neck_ctrl) diff --git a/rigify/rigs/spine.py b/rigify/rigs/spine.py index 0983e83e..680a4ceb 100644 --- a/rigify/rigs/spine.py +++ b/rigify/rigs/spine.py @@ -33,7 +33,7 @@ from ..utils import MetarigError from ..utils import copy_bone, new_bone, flip_bone, put_bone from ..utils import connected_children_names from ..utils import strip_org, make_mechanism_name, make_deformer_name -from ..utils import obj_to_bone, create_circle_widget, create_cube_widget +from ..utils import create_circle_widget, create_cube_widget script = """ main = "%s" @@ -174,7 +174,6 @@ class Rig: eb[main_control].length = sum([eb[b].length for b in self.org_bones]) / 2 # Position the controls and sub-controls - pos = eb[controls[0]].head.copy() for name, subname in zip(controls, subcontrols): put_bone(self.obj, name, pivot_rest_pos) put_bone(self.obj, subname, pivot_rest_pos) @@ -443,23 +442,23 @@ class Rig: # Control appearance # Main - w = create_cube_widget(self.obj, main_control) + create_cube_widget(self.obj, main_control) # Spines for name, i in zip(controls[1:-1], self.control_indices[1:-1]): pb[name].custom_shape_transform = pb[self.org_bones[i]] # Create control widgets - w = create_circle_widget(self.obj, name, radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[i]) + create_circle_widget(self.obj, name, radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[i]) # Hips pb[controls[0]].custom_shape_transform = pb[self.org_bones[0]] # Create control widgets - w = create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[0]) + create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[0]) # Ribs pb[controls[-1]].custom_shape_transform = pb[self.org_bones[-1]] # Create control widgets - w = create_circle_widget(self.obj, controls[-1], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[-1]) + create_circle_widget(self.obj, controls[-1], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[-1]) # Layers pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers |