diff options
Diffstat (limited to 'rigify/rigs/neck_short.py')
-rw-r--r-- | rigify/rigs/neck_short.py | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/rigify/rigs/neck_short.py b/rigify/rigs/neck_short.py new file mode 100644 index 00000000..3c34fbda --- /dev/null +++ b/rigify/rigs/neck_short.py @@ -0,0 +1,385 @@ +#====================== BEGIN GPL LICENSE BLOCK ====================== +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +#======================= END GPL LICENSE BLOCK ======================== + +import bpy +from mathutils import Vector +from rigify.utils import MetarigError +from rigify.utils import copy_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 rna_prop_ui import rna_idprop_ui_prop_get + +script1 = """ +head_neck = ["%s", "%s"] +""" + +script2 = """ +if is_selected(head_neck[0]): + layout.prop(pose_bones[head_neck[0]], '["isolate"]', text="Isolate (" + head_neck[0] + ")", slider=True) +""" + +script3 = """ +if is_selected(head_neck): + layout.prop(pose_bones[head_neck[0]], '["neck_follow"]', text="Neck Follow Head (" + head_neck[0] + ")", slider=True) +""" + + +class Rig: + """ A "spine" rig. It turns a chain of bones into a rig with two controls: + One for the hips, and one for the rib cage. + + """ + def __init__(self, obj, bone_name, params): + """ Gather and validate data about the rig. + + """ + self.obj = obj + self.org_bones = [bone_name] + connected_children_names(obj, bone_name) + self.params = params + + 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))) + + self.isolate = False + if self.obj.data.bones[bone_name].parent: + self.isolate = True + + def gen_deform(self): + """ Generate the deformation rig. + + """ + for name in self.org_bones: + bpy.ops.object.mode_set(mode='EDIT') + eb = self.obj.data.edit_bones + + # Create deform bone + bone_e = eb[copy_bone(self.obj, name)] + + # Change its name + bone_e.name = make_deformer_name(strip_org(name)) + bone_name = bone_e.name + + # Leave edit mode + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bone + bone = self.obj.pose.bones[bone_name] + + # Constrain to the original bone + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name + + def gen_control(self): + """ Generate the control rig. + + """ + #--------------------------------- + # Create the neck and head controls + bpy.ops.object.mode_set(mode='EDIT') + + # Create bones + neck_ctrl = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) + neck_follow = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[0] + ".follow"))) + + head_ctrl = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1])) + head_mch = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(self.org_bones[-1]))) + if self.isolate: + head_socket1 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket1"))) + head_socket2 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket2"))) + + # Create neck chain bones + neck = [] + helpers = [] + for name in self.org_bones: + neck += [copy_bone(self.obj, name, make_mechanism_name(strip_org(name)))] + helpers += [copy_bone(self.obj, neck_ctrl, make_mechanism_name(strip_org(name + ".02")))] + + # Fetch edit bones + eb = self.obj.data.edit_bones + + neck_ctrl_e = eb[neck_ctrl] + neck_follow_e = eb[neck_follow] + head_ctrl_e = eb[head_ctrl] + head_mch_e = eb[head_mch] + if self.isolate: + head_socket1_e = eb[head_socket1] + head_socket2_e = eb[head_socket2] + + # Parenting + head_ctrl_e.use_connect = False + head_ctrl_e.parent = neck_ctrl_e.parent + head_mch_e.use_connect = False + head_mch_e.parent = head_ctrl_e + + if self.isolate: + head_socket1_e.use_connect = False + head_socket1_e.parent = neck_ctrl_e.parent + + head_socket2_e.use_connect = False + head_socket2_e.parent = None + + head_ctrl_e.parent = head_socket2_e + + for (name1, name2) in zip(neck, helpers): + eb[name1].use_connect = False + eb[name1].parent = eb[name2] + eb[name2].use_connect = False + eb[name2].parent = neck_ctrl_e.parent + + neck_follow_e.use_connect = False + neck_follow_e.parent = neck_ctrl_e.parent + neck_ctrl_e.parent = neck_follow_e + + # Position + put_bone(self.obj, neck_follow, neck_ctrl_e.head) + put_bone(self.obj, head_ctrl, neck_ctrl_e.head) + put_bone(self.obj, head_mch, neck_ctrl_e.head) + head_mch_e.length /= 2 + + if self.isolate: + put_bone(self.obj, head_socket1, neck_ctrl_e.head) + head_mch_e.length /= 2 + + put_bone(self.obj, head_socket2, neck_ctrl_e.head) + head_mch_e.length /= 3 + + for (name1, name2) in zip(neck, helpers): + put_bone(self.obj, name2, eb[name1].head) + eb[name2].length /= 3 + + # Switch to object mode + bpy.ops.object.mode_set(mode='OBJECT') + pb = self.obj.pose.bones + neck_ctrl_p = pb[neck_ctrl] + neck_follow_p = pb[neck_follow] + head_ctrl_p = pb[head_ctrl] + if self.isolate: + head_socket1_p = pb[head_socket1] + head_socket2_p = pb[head_socket2] + + # Custom bone appearance + neck_ctrl_p.custom_shape_transform = pb[self.org_bones[(len(self.org_bones) - 1) // 2]] + head_ctrl_p.custom_shape_transform = pb[self.org_bones[-1]] + + # Custom properties + prop = rna_idprop_ui_prop_get(head_ctrl_p, "inf_extent", create=True) + head_ctrl_p["inf_extent"] = 0.5 + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + prop = rna_idprop_ui_prop_get(head_ctrl_p, "neck_follow", create=True) + head_ctrl_p["neck_follow"] = 1.0 + prop["min"] = 0.0 + prop["max"] = 2.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + if self.isolate: + prop = rna_idprop_ui_prop_get(head_ctrl_p, "isolate", create=True) + head_ctrl_p["isolate"] = 0.0 + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + # Constraints + + # Neck follow + con = neck_follow_p.constraints.new('COPY_ROTATION') + con.name = "copy_rotation" + con.target = self.obj + con.subtarget = head_ctrl + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "follow" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["neck_follow"]' + driver.expression = "follow / 2" + + # Isolate + if self.isolate: + con = head_socket2_p.constraints.new('COPY_LOCATION') + con.name = "copy_location" + con.target = self.obj + con.subtarget = head_socket1 + + con = head_socket2_p.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = head_socket1 + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "isolate" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["isolate"]' + driver.expression = "1.0 - isolate" + + # Neck chain + first = True + prev = None + i = 0 + l = len(neck) + for (name1, name2, org_name) in zip(neck, helpers, self.org_bones): + con = pb[org_name].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = self.obj + con.subtarget = name1 + + n_con = pb[name2].constraints.new('COPY_TRANSFORMS') + n_con.name = "neck" + n_con.target = self.obj + n_con.subtarget = neck_ctrl + + h_con = pb[name2].constraints.new('COPY_TRANSFORMS') + h_con.name = "head" + h_con.target = self.obj + h_con.subtarget = head_mch + + con = pb[name2].constraints.new('COPY_LOCATION') + con.name = "anchor" + con.target = self.obj + if first: + con.subtarget = neck_ctrl + else: + con.subtarget = prev + con.head_tail = 1.0 + + # Drivers + n = (i + 1) / l + + # Neck influence + fcurve = n_con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "ext" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]' + driver.expression = "1.0 if (%.4f > (1.0-ext) or (1.0-ext) == 0.0) else (%.4f / (1.0-ext))" % (n, n) + + # Head influence + if (i + 1) == l: + h_con.influence = 1.0 + else: + fcurve = h_con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'SCRIPTED' + var.name = "ext" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = self.obj + var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]' + driver.expression = "0.0 if (%.4f <= (1.0-ext)) else ((%.4f - (1.0-ext)) / ext)" % (n, n) + + first = False + prev = name1 + i += 1 + + # Create control widgets + w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5) + w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5) + + if w1 != None: + obj_to_bone(w1, self.obj, self.org_bones[(len(self.org_bones) - 1) // 2]) + if w2 != None: + obj_to_bone(w2, self.obj, self.org_bones[-1]) + + # Return control bones + return (head_ctrl, neck_ctrl) + + 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. + + """ + self.gen_deform() + (head, neck) = self.gen_control() + + script = script1 % (head, neck) + if self.isolate: + script += script2 + script += script3 + + return [script] + + @classmethod + def create_sample(self, obj): + # generated by rigify.utils.write_metarig + bpy.ops.object.mode_set(mode='EDIT') + arm = obj.data + + bones = {} + + bone = arm.edit_bones.new('neck') + bone.head[:] = 0.0000, 0.0000, 0.0000 + bone.tail[:] = 0.0000, -0.0500, 0.1500 + bone.roll = 0.0000 + bone.use_connect = False + bones['neck'] = bone.name + bone = arm.edit_bones.new('head') + bone.head[:] = 0.0000, -0.0500, 0.1500 + bone.tail[:] = 0.0000, -0.0500, 0.4000 + bone.roll = 3.1416 + bone.use_connect = True + bone.parent = arm.edit_bones[bones['neck']] + bones['head'] = bone.name + + bpy.ops.object.mode_set(mode='OBJECT') + pbone = obj.pose.bones[bones['neck']] + pbone.rigify_type = 'neck_short' + pbone.lock_location = (True, True, True) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + pbone.rigify_parameters.add() + pbone = obj.pose.bones[bones['head']] + pbone.rigify_type = '' + pbone.lock_location = (False, False, False) + pbone.lock_rotation = (False, False, False) + pbone.lock_rotation_w = False + pbone.lock_scale = (False, False, False) + pbone.rotation_mode = 'QUATERNION' + + bpy.ops.object.mode_set(mode='EDIT') + for bone in arm.edit_bones: + bone.select = False + bone.select_head = False + bone.select_tail = False + for b in bones: + bone = arm.edit_bones[bones[b]] + bone.select = True + bone.select_head = True + bone.select_tail = True + arm.edit_bones.active = bone + |