From 6f3d2c4932d9af407fa8bdf9317ccaed5e993dcd Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl <cessen@cessen.com> Date: Wed, 15 Jun 2011 01:55:53 +0000 Subject: [PATCH] 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. --- rigify/metarigs/human.py | 2 + rigify/rigs/spine.py | 552 ++++++++++++++++++++++++--------------- rigify/utils.py | 18 +- 3 files changed, 360 insertions(+), 212 deletions(-) diff --git a/rigify/metarigs/human.py b/rigify/metarigs/human.py index a2524c3f4..e4eb9f2bf 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 37dda1b1c..2b4558f4f 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 193a2779a..c97712228 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. """ -- GitLab