Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/rigify
diff options
context:
space:
mode:
authorNathan Vegdahl <cessen@cessen.com>2011-06-15 05:55:53 +0400
committerNathan Vegdahl <cessen@cessen.com>2011-06-15 05:55:53 +0400
commit6f3d2c4932d9af407fa8bdf9317ccaed5e993dcd (patch)
tree5c881685716a6d242106f9744bfe68cffa36abb2 /rigify
parent2965c00cc3cfc7952641a47970528a34371929a9 (diff)
Rigify: new spine rig!
This rig replaces the old spine rig, and has a super-set of the old spine's features. The main new features are: 1. A separate over-all control for translation. Due to the pivot slide feature of the spine, it didn't really make sense to have one of the spine bones be the control for translation, so I broke that out into a separate control. This control also acts as a root of the spine in general, including for scaling and rotation. If you want to grab the entire spine as one unit, this is how to do it. 2. The spine can now have more than two control bones. The rigger can specify an arbitrary number of the spine bones to be turned into controls upon rig generation. Controls that are not at the end points of the spine are optionally (via an animatable switch) auto-rotated by the the end point controls, so animators can ignore them when they do not require that level of control.
Diffstat (limited to 'rigify')
-rw-r--r--rigify/metarigs/human.py2
-rw-r--r--rigify/rigs/spine.py552
-rw-r--r--rigify/utils.py18
3 files changed, 360 insertions, 212 deletions
diff --git a/rigify/metarigs/human.py b/rigify/metarigs/human.py
index a2524c3f..e4eb9f2b 100644
--- a/rigify/metarigs/human.py
+++ b/rigify/metarigs/human.py
@@ -470,6 +470,8 @@ def create(obj):
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['hips']]
pbone.rigify_type = 'spine'
+ pbone.rigify_parameters.add()
+ pbone.rigify_parameters[0].chain_bone_controls = "1, 2, 3"
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, False, False)
pbone.lock_rotation_w = False
diff --git a/rigify/rigs/spine.py b/rigify/rigs/spine.py
index 37dda1b1..2b4558f4 100644
--- a/rigify/rigs/spine.py
+++ b/rigify/rigs/spine.py
@@ -16,23 +16,31 @@
#
#======================= END GPL LICENSE BLOCK ========================
+""" TODO:
+ - Add parameters for bone transform alphas.
+"""
+
+from math import floor
+
import bpy
from mathutils import Vector
from rigify.utils import MetarigError
-from rigify.utils import copy_bone, flip_bone, put_bone
+from rigify.utils import copy_bone, new_bone, flip_bone, put_bone
from rigify.utils import connected_children_names
from rigify.utils import strip_org, make_mechanism_name, make_deformer_name
-from rigify.utils import obj_to_bone, create_circle_widget
+from rigify.utils import obj_to_bone, create_circle_widget, create_compass_widget
from rna_prop_ui import rna_idprop_ui_prop_get
script = """
-hips = "%s"
-ribs = "%s"
-if is_selected([hips, ribs]):
- layout.prop(pose_bones[ribs], '["pivot_slide"]', text="Pivot Slide (" + ribs + ")", slider=True)
-if is_selected(ribs):
- layout.prop(pose_bones[ribs], '["isolate"]', text="Isolate Rotation (" + ribs + ")", slider=True)
+main = "%s"
+spine = [%s]
+if is_selected([main]+ spine):
+ layout.prop(pose_bones[main], '["pivot_slide"]', text="Pivot Slide (" + main + ")", slider=True)
+
+for name in spine[1:-1]:
+ if is_selected(name):
+ layout.prop(pose_bones[name], '["auto_rotate"]', text="Auto Rotate (" + name + ")", slider=True)
"""
@@ -49,6 +57,23 @@ class Rig:
self.org_bones = [bone_name] + connected_children_names(obj, bone_name)
self.params = params
+ # Collect control bone indices
+ self.control_indices = [0, len(self.org_bones) - 1]
+ temp = self.params.chain_bone_controls.split(",")
+ for i in temp:
+ try:
+ j = int(i) - 1
+ except ValueError:
+ pass
+ else:
+ if (j > 0) and (j < len(self.org_bones)) and (j not in self.control_indices):
+ self.control_indices += [j]
+ self.control_indices.sort()
+
+ self.pivot_rest = self.params.rest_pivot_slide
+ self.pivot_rest = max(self.pivot_rest, 1.0/len(self.org_bones))
+ self.pivot_rest = min(self.pivot_rest, 1.0-(1.0/len(self.org_bones)))
+
if len(self.org_bones) <= 1:
raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones." % (strip_org(bone)))
@@ -83,215 +108,200 @@ class Rig:
""" Generate the control rig.
"""
- #---------------------------------
- # Create the hip and rib controls
bpy.ops.object.mode_set(mode='EDIT')
-
- # Copy org bones
- hip_control = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0]))
- rib_control = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1]))
- rib_mch = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".follow")))
- hinge = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[-1]) + ".hinge"))
-
eb = self.obj.data.edit_bones
+ #-------------------------
+ # Get rest slide position
+ a = self.pivot_rest * len(self.org_bones)
+ i = floor(a)
+ a -= i
+ if i == len(self.org_bones):
+ i -= 1
+ a = 1.0
+
+ pivot_rest_pos = eb[self.org_bones[i]].head.copy()
+ pivot_rest_pos += eb[self.org_bones[i]].vector * a
+
+ #----------------------
+ # Create controls
+
+ # Create control bones
+ controls = []
+ for i in self.control_indices:
+ name = copy_bone(self.obj, self.org_bones[i], strip_org(self.org_bones[i]))
+ controls += [name]
+
+ # Create control parents
+ control_parents = []
+ for i in self.control_indices[1:-1]:
+ name = new_bone(self.obj, make_mechanism_name("par_" + strip_org(self.org_bones[i])))
+ control_parents += [name]
+
+ # Create sub-control bones
+ subcontrols = []
+ for i in self.control_indices:
+ name = new_bone(self.obj, make_mechanism_name("sub_" + strip_org(self.org_bones[i])))
+ subcontrols += [name]
+
+ # Create main control bone
+ main_control = new_bone(self.obj, self.params.spine_main_control_name)
+
+ # Create main control WGT bones
+ main_wgt1 = new_bone(self.obj, make_mechanism_name(self.params.spine_main_control_name + ".01"))
+ main_wgt2 = new_bone(self.obj, make_mechanism_name(self.params.spine_main_control_name + ".02"))
- hip_control_e = eb[hip_control]
- rib_control_e = eb[rib_control]
- rib_mch_e = eb[rib_mch]
- hinge_e = eb[hinge]
-
- # Parenting
- hip_control_e.use_connect = False
- rib_control_e.use_connect = False
- rib_mch_e.use_connect = False
- hinge_e.use_connect = False
-
- hinge_e.parent = None
- rib_control_e.parent = hinge_e
- rib_mch_e.parent = rib_control_e
-
- # Position
- flip_bone(self.obj, hip_control)
- flip_bone(self.obj, hinge)
-
- hinge_e.length /= 2
- rib_mch_e.length /= 2
-
- put_bone(self.obj, rib_control, hip_control_e.head)
- put_bone(self.obj, rib_mch, hip_control_e.head)
-
- bpy.ops.object.mode_set(mode='POSE')
- bpy.ops.object.mode_set(mode='EDIT')
eb = self.obj.data.edit_bones
- # Switch to object mode
+ # Parent the main control
+ eb[main_control].use_connect = False
+ eb[main_control].parent = eb[self.org_bones[0]].parent
+
+ # Parent the main WGTs
+ eb[main_wgt1].use_connect = False
+ eb[main_wgt1].parent = eb[main_control]
+ eb[main_wgt2].use_connect = False
+ eb[main_wgt2].parent = eb[main_wgt1]
+
+ # Parent the controls and sub-controls
+ for name, subname in zip(controls, subcontrols):
+ eb[name].use_connect = False
+ eb[name].parent = eb[main_control]
+ eb[subname].use_connect = False
+ eb[subname].parent = eb[name]
+
+ # Parent the control parents
+ for name, par_name in zip(controls[1:-1], control_parents):
+ eb[par_name].use_connect = False
+ eb[par_name].parent = eb[main_control]
+ eb[name].parent = eb[par_name]
+
+ # Position the main bone
+ put_bone(self.obj, main_control, pivot_rest_pos)
+ eb[main_control].length = sum([eb[b].length for b in self.org_bones]) / 2
+
+ # Position the main WGTs
+ eb[main_wgt1].tail = (0.0, 0.0, sum([eb[b].length for b in self.org_bones]) / 4)
+ eb[main_wgt2].length = sum([eb[b].length for b in self.org_bones]) / 4
+ put_bone(self.obj, main_wgt1, pivot_rest_pos)
+ put_bone(self.obj, main_wgt2, pivot_rest_pos)
+
+ # 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)
+ eb[subname].length = eb[name].length / 3
+
+ # Position the control parents
+ for name, par_name in zip(controls[1:-1], control_parents):
+ put_bone(self.obj, par_name, pivot_rest_pos)
+ eb[par_name].length = eb[name].length / 2
+
+ #-----------------------------------------
+ # Control bone constraints and properties
bpy.ops.object.mode_set(mode='OBJECT')
pb = self.obj.pose.bones
- hip_control_p = pb[hip_control]
- rib_control_p = pb[rib_control]
- hinge_p = pb[hinge]
- # No translation on rib control
- rib_control_p.lock_location = [True, True, True]
-
- # Hip does not use local location
- hip_control_p.bone.use_local_location = False
-
- # Custom hinge property
- prop = rna_idprop_ui_prop_get(rib_control_p, "isolate", create=True)
- rib_control_p["isolate"] = 1.0
- prop["soft_min"] = prop["min"] = 0.0
- prop["soft_max"] = prop["max"] = 1.0
+ # Lock control locations
+ for name in controls:
+ bone = pb[name]
+ bone.lock_location = True, True, True
+
+ # Main control doesn't use local location
+ pb[main_control].bone.use_local_location = False
+
+
+
+ # Intermediate controls follow hips and spine
+ for name, par_name, i in zip(controls[1:-1], control_parents, self.control_indices[1:-1]):
+ bone = pb[par_name]
+
+ # Custom bend_alpha property
+ prop = rna_idprop_ui_prop_get(pb[name], "bend_alpha", create=True)
+ pb[name]["bend_alpha"] = i / (len(self.org_bones) - 1) # set bend alpha
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ # Custom auto_rotate
+ prop = rna_idprop_ui_prop_get(pb[name], "auto_rotate", create=True)
+ pb[name]["auto_rotate"] = 1.0
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ # Constraints
+ con1 = bone.constraints.new('COPY_TRANSFORMS')
+ con1.name = "copy_transforms"
+ con1.target = self.obj
+ con1.subtarget = subcontrols[0]
+
+ con2 = bone.constraints.new('COPY_TRANSFORMS')
+ con2.name = "copy_transforms"
+ con2.target = self.obj
+ con2.subtarget = subcontrols[-1]
+
+ # Drivers
+ fcurve = con1.driver_add("influence")
+ driver = fcurve.driver
+ driver.type = 'AVERAGE'
+ var = driver.variables.new()
+ var.name = "auto"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = pb[name].path_from_id() + '["auto_rotate"]'
- # Constraints
- con = hinge_p.constraints.new('COPY_LOCATION')
- con.name = "copy_location"
- con.target = self.obj
- con.subtarget = hip_control
-
- con1 = hinge_p.constraints.new('COPY_ROTATION')
- con1.name = "isolate_off.01"
- con1.target = self.obj
- con1.subtarget = hip_control
-
- con2 = rib_control_p.constraints.new('COPY_SCALE')
- con2.name = "isolate_off.02"
- con2.target = self.obj
- con2.subtarget = hip_control
- con2.use_offset = True
- con2.target_space = 'LOCAL'
- con2.owner_space = 'LOCAL'
-
- # Drivers for "isolate_off"
- fcurve = con1.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 = rib_control_p.path_from_id() + '["isolate"]'
- mod = fcurve.modifiers[0]
- mod.poly_order = 1
- mod.coefficients[0] = 1.0
- mod.coefficients[1] = -1.0
-
- fcurve = con2.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 = rib_control_p.path_from_id() + '["isolate"]'
- mod = fcurve.modifiers[0]
- mod.poly_order = 1
- mod.coefficients[0] = 1.0
- mod.coefficients[1] = -1.0
-
- # Appearence
- hip_control_p.custom_shape_transform = pb[self.org_bones[0]]
- rib_control_p.custom_shape_transform = pb[self.org_bones[-1]]
+ fcurve = con2.driver_add("influence")
+ driver = fcurve.driver
+ driver.type = 'SCRIPTED'
+ driver.expression = "alpha * auto"
+ var = driver.variables.new()
+ var.name = "alpha"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = pb[name].path_from_id() + '["bend_alpha"]'
+ var = driver.variables.new()
+ var.name = "auto"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = pb[name].path_from_id() + '["auto_rotate"]'
#-------------------------
# Create flex spine chain
-
- # Create bones/parenting/positiong
bpy.ops.object.mode_set(mode='EDIT')
flex_bones = []
- flex_helpers = []
+ flex_subs = []
prev_bone = None
for b in self.org_bones:
# Create bones
bone = copy_bone(self.obj, b, make_mechanism_name(strip_org(b) + ".flex"))
- helper = copy_bone(self.obj, rib_mch, make_mechanism_name(strip_org(b) + ".flex_h"))
+ sub = new_bone(self.obj, make_mechanism_name(strip_org(b) + ".flex_s"))
flex_bones += [bone]
- flex_helpers += [helper]
+ flex_subs += [sub]
eb = self.obj.data.edit_bones
bone_e = eb[bone]
- helper_e = eb[helper]
+ sub_e = eb[sub]
# Parenting
bone_e.use_connect = False
- helper_e.use_connect = False
+ sub_e.use_connect = False
if prev_bone is None:
- helper_e.parent = eb[hip_control]
- bone_e.parent = helper_e
+ sub_e.parent = eb[controls[0]]
+ else:
+ sub_e.parent = eb[prev_bone]
+ bone_e.parent = sub_e
# Position
- put_bone(self.obj, helper, bone_e.head)
- helper_e.length /= 4
+ put_bone(self.obj, sub, bone_e.head)
+ sub_e.length /= 4
+ if prev_bone is not None:
+ sub_e.use_connect = True
prev_bone = bone
- # Constraints
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
- rib_control_p = pb[rib_control]
- rib_mch_p = pb[rib_mch]
-
- inc = 1.0 / (len(flex_helpers) - 1)
- inf = 1.0 / (len(flex_helpers) - 1)
- for b in zip(flex_helpers[1:], flex_bones[:-1], self.org_bones[1:]):
- bone_p = pb[b[0]]
-
- # Scale constraints
- con = bone_p.constraints.new('COPY_SCALE')
- con.name = "copy_scale1"
- con.target = self.obj
- con.subtarget = flex_helpers[0]
- con.influence = 1.0
-
- con = bone_p.constraints.new('COPY_SCALE')
- con.name = "copy_scale2"
- con.target = self.obj
- con.subtarget = rib_mch
- con.influence = inf
-
- # Bend constraints
- con = bone_p.constraints.new('COPY_ROTATION')
- con.name = "bend1"
- con.target = self.obj
- con.subtarget = flex_helpers[0]
- con.influence = 1.0
-
- con = bone_p.constraints.new('COPY_ROTATION')
- con.name = "bend2"
- con.target = self.obj
- con.subtarget = rib_mch
- con.influence = inf
-
- # If not the rib control
- if b[0] != flex_helpers[-1]:
- # Custom bend property
- prop_name = "bend_" + strip_org(b[2])
- prop = rna_idprop_ui_prop_get(rib_control_p, prop_name, create=True)
- rib_control_p[prop_name] = inf
- prop["min"] = 0.0
- prop["max"] = 1.0
- prop["soft_min"] = 0.0
- prop["soft_max"] = 1.0
-
- # Bend driver
- fcurve = con.driver_add("influence")
- driver = fcurve.driver
- var = driver.variables.new()
- driver.type = 'AVERAGE'
- var.name = prop_name
- var.targets[0].id_type = 'OBJECT'
- var.targets[0].id = self.obj
- var.targets[0].data_path = rib_control_p.path_from_id() + '["' + prop_name + '"]'
-
- # Location constraint
- con = bone_p.constraints.new('COPY_LOCATION')
- con.name = "copy_location"
- con.target = self.obj
- con.subtarget = b[1]
- con.head_tail = 1.0
-
- inf += inc
-
#----------------------------
# Create reverse spine chain
@@ -315,7 +325,7 @@ class Rig:
bone_e.tail = Vector(eb[b[0]].head)
#bone_e.head = Vector(eb[b[0]].tail)
if prev_bone is None:
- pass # Position base bone wherever you want, for now do nothing (i.e. position at hips)
+ put_bone(self.obj, bone, pivot_rest_pos)
else:
put_bone(self.obj, bone, eb[prev_bone].tail)
@@ -332,41 +342,48 @@ class Rig:
con.name = "copy_location"
con.target = self.obj
if prev_bone is None:
- con.subtarget = hip_control # Position base bone wherever you want, for now hips
+ con.subtarget = main_control
else:
con.subtarget = prev_bone
con.head_tail = 1.0
prev_bone = bone
- #---------------------------------------------
- # Constrain org bones to flex bone's rotation
+ #----------------------------------------
+ # Constrain original bones to flex spine
+ bpy.ops.object.mode_set(mode='OBJECT')
pb = self.obj.pose.bones
- for b in zip(self.org_bones, flex_bones):
- con = pb[b[0]].constraints.new('COPY_TRANSFORMS')
- con.name = "copy_rotation"
+
+ for obone, fbone in zip(self.org_bones, flex_bones):
+ con = pb[obone].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
con.target = self.obj
- con.subtarget = b[1]
+ con.subtarget = fbone
#---------------------------
# Create pivot slide system
pb = self.obj.pose.bones
bone_p = pb[self.org_bones[0]]
- rib_control_p = pb[rib_control]
+ main_control_p = pb[main_control]
# Custom pivot_slide property
- prop = rna_idprop_ui_prop_get(rib_control_p, "pivot_slide", create=True)
- rib_control_p["pivot_slide"] = 1.0 / len(self.org_bones)
+ prop = rna_idprop_ui_prop_get(main_control_p, "pivot_slide", create=True)
+ main_control_p["pivot_slide"] = self.pivot_rest
prop["min"] = 0.0
prop["max"] = 1.0
prop["soft_min"] = 1.0 / len(self.org_bones)
prop["soft_max"] = 1.0 - (1.0 / len(self.org_bones))
- # Anchor constraint
+ # Anchor constraints
con = bone_p.constraints.new('COPY_LOCATION')
con.name = "copy_location"
con.target = self.obj
con.subtarget = rev_bones[0]
+ con = pb[main_wgt1].constraints.new('COPY_ROTATION')
+ con.name = "copy_rotation"
+ con.target = self.obj
+ con.subtarget = rev_bones[0]
+
# Slide constraints
i = 1
tot = len(rev_bones)
@@ -385,25 +402,113 @@ class Rig:
var.name = "slide"
var.targets[0].id_type = 'OBJECT'
var.targets[0].id = self.obj
- var.targets[0].data_path = rib_control_p.path_from_id() + '["pivot_slide"]'
+ var.targets[0].data_path = main_control_p.path_from_id() + '["pivot_slide"]'
mod = fcurve.modifiers[0]
mod.poly_order = 1
mod.coefficients[0] = 1 - i
mod.coefficients[1] = tot
+ # Main WGT
+ con = pb[main_wgt1].constraints.new('COPY_ROTATION')
+ con.name = "slide." + str(i)
+ con.target = self.obj
+ con.subtarget = rb
+
+ # Driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "slide"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = main_control_p.path_from_id() + '["pivot_slide"]'
+ mod = fcurve.modifiers[0]
+ mod.poly_order = 1
+ mod.coefficients[0] = 1.5 - i
+ mod.coefficients[1] = tot
+
i += 1
+ #----------------------------------
+ # Constrain flex spine to controls
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ # Constrain the bones that correspond exactly to the controls
+ for i, name in zip(self.control_indices, subcontrols):
+ con = pb[flex_subs[i]].constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = name
+
+ # Constrain the bones in-between the controls
+ for i, j, name1, name2 in zip(self.control_indices, self.control_indices[1:], subcontrols, subcontrols[1:]):
+ if (i + 1) < j:
+ for n in range(i + 1, j):
+ bone = pb[flex_subs[n]]
+ # Custom bend_alpha property
+ prop = rna_idprop_ui_prop_get(bone, "bend_alpha", create=True)
+ bone["bend_alpha"] = (n - i) / (j - i) # set bend alpha
+ prop["min"] = 0.0
+ prop["max"] = 1.0
+ prop["soft_min"] = 0.0
+ prop["soft_max"] = 1.0
+
+ con = bone.constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = name1
+
+ con = bone.constraints.new('COPY_TRANSFORMS')
+ con.name = "copy_transforms"
+ con.target = self.obj
+ con.subtarget = name2
+
+ # Driver
+ fcurve = con.driver_add("influence")
+ driver = fcurve.driver
+ var = driver.variables.new()
+ driver.type = 'AVERAGE'
+ var.name = "alpha"
+ var.targets[0].id_type = 'OBJECT'
+ var.targets[0].id = self.obj
+ var.targets[0].data_path = bone.path_from_id() + '["bend_alpha"]'
+
+ #-------------
+ # Final stuff
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = self.obj.pose.bones
+
+ # Control appearance
+ # Main
+ pb[main_control].custom_shape_transform = pb[main_wgt2]
+ w = create_compass_widget(self.obj, main_control)
+ if w != None:
+ obj_to_bone(w, self.obj, main_wgt2)
+
+ # 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)
+ if w != None:
+ obj_to_bone(w, self.obj, self.org_bones[i])
+ # Hips
+ pb[controls[0]].custom_shape_transform = pb[self.org_bones[0]]
# Create control widgets
- w1 = create_circle_widget(self.obj, hip_control, radius=1.0, head_tail=1.0)
- w2 = create_circle_widget(self.obj, rib_control, radius=1.0, head_tail=0.0)
+ w = create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True)
+ if w != None:
+ obj_to_bone(w, self.obj, self.org_bones[0])
- if w1 != None:
- obj_to_bone(w1, self.obj, self.org_bones[0])
- if w2 != None:
- obj_to_bone(w2, self.obj, self.org_bones[-1])
+ # 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)
+ if w != None:
+ obj_to_bone(w, self.obj, self.org_bones[-1])
- # Return control names
- return hip_control, rib_control
+ return [main_control] + controls
def generate(self):
""" Generate the rig.
@@ -412,9 +517,35 @@ class Rig:
"""
self.gen_deform()
- hips, ribs = self.gen_control()
+ controls = self.gen_control()
+
+ controls_string = ", ".join(["'" + x + "'" for x in controls[1:]])
+ return [script % (controls[0], controls_string)]
+
+ @classmethod
+ def add_parameters(self, group):
+ """ Add the parameters of this rig type to the
+ RigifyParameters PropertyGroup
+ """
+ group.spine_main_control_name = bpy.props.StringProperty(name="Main control name", default="torso", description="Name that the main control bone should be given.")
+ group.rest_pivot_slide = bpy.props.FloatProperty(name="Rest Pivot Slide", default=0.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, description="The pivot slide value in the rest pose.")
+ group.chain_bone_controls = bpy.props.StringProperty(name="Control bone list", default="", description="Define which bones have controls.")
+
- return [script % (hips, ribs)]
+ @classmethod
+ def parameters_ui(self, layout, obj, bone):
+ """ Create the ui for the rig parameters.
+ """
+ params = obj.pose.bones[bone].rigify_parameters[0]
+
+ r = layout.row()
+ r.prop(params, "spine_main_control_name")
+
+ r = layout.row()
+ r.prop(params, "rest_pivot_slide", slider=True)
+
+ r = layout.row()
+ r.prop(params, "chain_bone_controls")
@classmethod
def create_sample(self, obj):
@@ -469,6 +600,8 @@ class Rig:
pbone.rotation_mode = 'QUATERNION'
pbone = obj.pose.bones[bones['hips']]
pbone['rigify_type'] = 'spine'
+ pbone.rigify_parameters.add()
+ pbone.rigify_parameters[0].chain_bone_controls = "1, 2, 3"
bpy.ops.object.mode_set(mode='EDIT')
for bone in arm.edit_bones:
@@ -481,4 +614,3 @@ class Rig:
bone.select_head = True
bone.select_tail = True
arm.edit_bones.active = bone
-
diff --git a/rigify/utils.py b/rigify/utils.py
index 193a2779..c9771222 100644
--- a/rigify/utils.py
+++ b/rigify/utils.py
@@ -279,7 +279,7 @@ def create_line_widget(rig, bone_name):
mesh.update()
-def create_circle_widget(rig, bone_name, radius=1.0, head_tail=0.0):
+def create_circle_widget(rig, bone_name, radius=1.0, head_tail=0.0, with_line=False):
""" Creates a basic circle widget, a circle around the y-axis.
radius: the radius of the circle
head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail)
@@ -288,7 +288,10 @@ def create_circle_widget(rig, bone_name, radius=1.0, head_tail=0.0):
if obj != None:
v = [(0.7071068286895752, 2.980232238769531e-07, -0.7071065306663513), (0.8314696550369263, 2.980232238769531e-07, -0.5555699467658997), (0.9238795042037964, 2.682209014892578e-07, -0.3826831877231598), (0.9807852506637573, 2.5331974029541016e-07, -0.19509011507034302), (1.0, 2.365559055306221e-07, 1.6105803979371558e-07), (0.9807853698730469, 2.2351741790771484e-07, 0.19509044289588928), (0.9238796234130859, 2.086162567138672e-07, 0.38268351554870605), (0.8314696550369263, 1.7881393432617188e-07, 0.5555704236030579), (0.7071068286895752, 1.7881393432617188e-07, 0.7071070075035095), (0.5555702447891235, 1.7881393432617188e-07, 0.8314698934555054), (0.38268327713012695, 1.7881393432617188e-07, 0.923879861831665), (0.19509008526802063, 1.7881393432617188e-07, 0.9807855486869812), (-3.2584136988589307e-07, 1.1920928955078125e-07, 1.000000238418579), (-0.19509072601795197, 1.7881393432617188e-07, 0.9807854294776917), (-0.3826838731765747, 1.7881393432617188e-07, 0.9238795638084412), (-0.5555707216262817, 1.7881393432617188e-07, 0.8314695358276367), (-0.7071071863174438, 1.7881393432617188e-07, 0.7071065902709961), (-0.8314700126647949, 1.7881393432617188e-07, 0.5555698871612549), (-0.923879861831665, 2.086162567138672e-07, 0.3826829195022583), (-0.9807853698730469, 2.2351741790771484e-07, 0.1950896978378296), (-1.0, 2.365559907957504e-07, -7.290432222362142e-07), (-0.9807850122451782, 2.5331974029541016e-07, -0.195091113448143), (-0.9238790273666382, 2.682209014892578e-07, -0.38268423080444336), (-0.831468939781189, 2.980232238769531e-07, -0.5555710196495056), (-0.7071058750152588, 2.980232238769531e-07, -0.707107424736023), (-0.555569052696228, 2.980232238769531e-07, -0.8314701318740845), (-0.38268208503723145, 2.980232238769531e-07, -0.923879861831665), (-0.19508881866931915, 2.980232238769531e-07, -0.9807853102684021), (1.6053570561780361e-06, 2.980232238769531e-07, -0.9999997615814209), (0.19509197771549225, 2.980232238769531e-07, -0.9807847142219543), (0.3826850652694702, 2.980232238769531e-07, -0.9238786101341248), (0.5555717945098877, 2.980232238769531e-07, -0.8314683437347412)]
verts = [(a[0] * radius, head_tail, a[2] * radius) for a in v]
- edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
+ if with_line:
+ edges = [(28, 12), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
+ else:
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
mesh = obj.data
mesh.from_pydata(verts, edges, [])
mesh.update()
@@ -334,6 +337,17 @@ def create_bone_widget(rig, bone_name):
mesh.update()
+def create_compass_widget(rig, bone_name):
+ """ Creates a compass-shaped widget.
+ """
+ obj = create_widget(rig, bone_name)
+ if obj != None:
+ verts = [(0.0, 1.2000000476837158, 0.0), (0.19509032368659973, 0.9807852506637573, 0.0), (0.3826834559440613, 0.9238795042037964, 0.0), (0.5555702447891235, 0.8314695954322815, 0.0), (0.7071067690849304, 0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (1.2000000476837158, 7.549790126404332e-08, 0.0), (0.9807853102684021, -0.19509020447731018, 0.0), (0.9238795638084412, -0.38268327713012695, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (0.5555701851844788, -0.8314696550369263, 0.0), (0.38268327713012695, -0.9238796234130859, 0.0), (0.19509008526802063, -0.9807853102684021, 0.0), (-3.2584136988589307e-07, -1.2999999523162842, 0.0), (-0.19509072601795197, -0.9807851910591125, 0.0), (-0.3826838731765747, -0.9238793253898621, 0.0), (-0.5555707216262817, -0.8314692974090576, 0.0), (-0.7071072459220886, -0.707106351852417, 0.0), (-0.8314700126647949, -0.5555696487426758, 0.0), (-0.923879861831665, -0.3826826810836792, 0.0), (-0.9807854294776917, -0.1950894594192505, 0.0), (-1.2000000476837158, 9.655991561885457e-07, 0.0), (-0.980785071849823, 0.1950913518667221, 0.0), (-0.923879086971283, 0.38268446922302246, 0.0), (-0.831468939781189, 0.5555712580680847, 0.0), (-0.7071058750152588, 0.707107663154602, 0.0), (-0.5555691123008728, 0.8314703702926636, 0.0), (-0.38268208503723145, 0.9238801002502441, 0.0), (-0.19508881866931915, 0.9807855486869812, 0.0)]
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
def create_root_widget(rig, bone_name):
""" Creates a widget for the root bone.
"""