diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-15 20:00:05 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-15 20:00:05 +0300 |
commit | d860b3c7e2fef6d9517cd1a8ca667a838eb869d0 (patch) | |
tree | 50333f3cda920757485f9288df9a2cc83d34469e | |
parent | 9a9923dd278703f05206037a31b4226b337d8448 (diff) |
Rigify: convert the palm rig and support controls for both sides.
-rw-r--r-- | rigify/metarigs/Animals/cat.py | 20 | ||||
-rw-r--r-- | rigify/rigs/limbs/super_palm.py | 307 |
2 files changed, 191 insertions, 136 deletions
diff --git a/rigify/metarigs/Animals/cat.py b/rigify/metarigs/Animals/cat.py index d3de1192..a065c432 100644 --- a/rigify/metarigs/Animals/cat.py +++ b/rigify/metarigs/Animals/cat.py @@ -1748,6 +1748,10 @@ def create(obj): pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + try: + pbone.rigify_parameters.palm_both_sides = True + except AttributeError: + pass pbone = obj.pose.bones[bones['r_palm.002.L']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) @@ -1765,7 +1769,6 @@ def create(obj): pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] pbone = obj.pose.bones[bones['r_palm.004.L']] - pbone.rigify_type = 'limbs.super_palm' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False @@ -1780,6 +1783,10 @@ def create(obj): pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + try: + pbone.rigify_parameters.palm_both_sides = True + except AttributeError: + pass pbone = obj.pose.bones[bones['r_palm.002.R']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) @@ -1797,7 +1804,6 @@ def create(obj): pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] pbone = obj.pose.bones[bones['r_palm.004.R']] - pbone.rigify_type = 'limbs.super_palm' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False @@ -2313,7 +2319,6 @@ def create(obj): pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] pbone = obj.pose.bones[bones['f_palm.004.L']] - pbone.rigify_type = 'limbs.super_palm' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False @@ -2328,6 +2333,10 @@ def create(obj): pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + try: + pbone.rigify_parameters.palm_both_sides = True + except AttributeError: + pass pbone = obj.pose.bones[bones['f_palm.002.L']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) @@ -2345,7 +2354,6 @@ def create(obj): pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] pbone = obj.pose.bones[bones['f_palm.004.R']] - pbone.rigify_type = 'limbs.super_palm' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False @@ -2360,6 +2368,10 @@ def create(obj): pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' pbone.bone.layers = [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False] + try: + pbone.rigify_parameters.palm_both_sides = True + except AttributeError: + pass pbone = obj.pose.bones[bones['f_palm.002.R']] pbone.rigify_type = '' pbone.lock_location = (False, False, False) diff --git a/rigify/rigs/limbs/super_palm.py b/rigify/rigs/limbs/super_palm.py index 8cde6648..a7b322aa 100644 --- a/rigify/rigs/limbs/super_palm.py +++ b/rigify/rigs/limbs/super_palm.py @@ -18,21 +18,22 @@ # <pep8 compliant> +import bpy import re + from math import cos, pi +from itertools import count, repeat -import bpy +from rigify.utils.naming import strip_org, make_derived_name +from rigify.utils.widgets import create_widget +from rigify.utils.misc import map_list -from ...utils import MetarigError -from ...utils import copy_bone -from ...utils import strip_org, deformer -from ...utils import create_widget +from rigify.base_rig import BaseRig, stage def bone_siblings(obj, bone): """ Returns a list of the siblings of the given bone. This requires that the bones has a parent. - """ parent = obj.data.bones[bone].parent @@ -48,133 +49,92 @@ def bone_siblings(obj, bone): return bones -def bone_distance(obj, bone1, bone2): - """ Returns the distance between two bones. - - """ - vec = obj.data.bones[bone1].head - obj.data.bones[bone2].head - return vec.length - - -class Rig: +class Rig(BaseRig): """ A "palm" rig. A set of sibling bones that bend with each other. This is a control and deformation rig. - """ - def __init__(self, obj, bone, params): - """ Gather and validate data about the rig. - """ - self.obj = obj - self.params = params - siblings = bone_siblings(obj, bone) - - if len(siblings) == 0: - raise MetarigError( - "RIGIFY ERROR: Bone '%s': must have a parent and at least one sibling" % - (strip_org(bone))) + def find_org_bones(self, bone): + base_head = bone.bone.head + siblings = bone_siblings(self.obj, bone.name) # Sort list by name and distance siblings.sort() - siblings.sort(key=lambda b: bone_distance(obj, bone, b)) + siblings.sort(key=lambda b: (self.get_bone(b).bone.head - base_head).length) - self.org_bones = [bone] + siblings + return [bone.name] + siblings - # Get rig parameters - self.palm_rotation_axis = params.palm_rotation_axis + def initialize(self): + if len(self.bones.org) <= 1: + self.raise_error('The palm rig must have a parent and at least one sibling') - 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.palm_rotation_axis = self.params.palm_rotation_axis + self.make_secondary = self.params.palm_both_sides - """ - bpy.ops.object.mode_set(mode='EDIT') + self.order = 'YXZ' if 'X' in self.palm_rotation_axis else 'YZX' # Figure out the name for the control bone (remove the last .##) - last_bone = self.org_bones[-1:][0] - ctrl_name = re.sub("([0-9]+\.)", "", strip_org(last_bone)[::-1], count=1)[::-1] - - # Make control bone - ctrl = copy_bone(self.obj, last_bone, ctrl_name) - - # Make deformation bones - def_bones = [] - for bone in self.org_bones: - b = copy_bone(self.obj, bone, deformer(strip_org(bone))) - def_bones += [b] - - # Parenting - eb = self.obj.data.edit_bones - - # turn off inherit scale for all ORG-bones to prevent undesired transformations - - for o in self.org_bones: - eb[o].inherit_scale = 'NONE' - - for d, b in zip(def_bones, self.org_bones): - eb[d].use_connect = False - eb[d].parent = eb[b] - - # Get ORG parent bone - org_parent = eb[self.org_bones[0]].parent.name - - # Get DEF parent from ORG parent - def_parent = deformer(strip_org(org_parent)) - - # Switch parent - if def_parent in eb.keys(): - parent_to = def_parent - else: - parent_to = org_parent - for o in self.org_bones: - eb[o].parent = eb[parent_to] - eb[ctrl].parent = eb[parent_to] - - # Constraints - bpy.ops.object.mode_set(mode='OBJECT') - pb = self.obj.pose.bones - - i = 0 - div = len(self.org_bones) - 1 - for b in self.org_bones: - con = pb[b].constraints.new('COPY_TRANSFORMS') - con.name = "copy_transforms" - con.target = self.obj - con.subtarget = ctrl - con.target_space = 'LOCAL' - con.owner_space = 'LOCAL' - con.influence = i / div - - con = pb[b].constraints.new('COPY_SCALE') - con.name = "copy_scale" - con.target = self.obj - con.subtarget = parent_to - con.target_space = 'WORLD' - con.owner_space = 'WORLD' - con.influence = 1 - - con = pb[b].constraints.new('COPY_ROTATION') - con.name = "copy_rotation" - con.target = self.obj - con.subtarget = ctrl - con.target_space = 'LOCAL' - con.owner_space = 'LOCAL' - if 'X' in self.palm_rotation_axis: - con.invert_x = True - con.use_x = True - con.use_z = False - else: - con.invert_z = True - con.use_x = False - con.use_z = True - con.use_y = False + self.ctrl_name = re.sub("([0-9]+\.)", "", strip_org(self.bones.org[-1])[::-1], count=1)[::-1] + + def parent_bones(self): + self.rig_parent_bone = self.get_bone_parent(self.bones.org[0]) + + # Parent to the deform bone of the parent if exists + def_bone = make_derived_name(self.rig_parent_bone, 'def') + + if def_bone in self.obj.data.bones: + self.rig_parent_bone = def_bone + + #################################################### + # BONES + # + # org[]: + # Original bones in order of distance. + # ctrl: + # master: + # Main control. + # secondary: + # Control for the other side. + # deform[]: + # DEF bones + # + #################################################### + + #################################################### + # Master control + + @stage.generate_bones + def make_master_control(self): + orgs = self.bones.org - con.influence = (i / div) - (1 - cos((i * pi / 2) / div)) + self.bones.ctrl.master = self.copy_bone(orgs[-1], self.ctrl_name, parent=True) - i += 1 + if self.make_secondary: + second_name = make_derived_name(orgs[0], 'ctrl') + self.bones.ctrl.secondary = self.copy_bone(orgs[0], second_name, parent=True) - # Create control widget + @stage.parent_bones + def parent_master_control(self): + self.set_bone_parent(self.bones.ctrl.master, self.rig_parent_bone) + + if self.make_secondary: + self.set_bone_parent(self.bones.ctrl.secondary, self.rig_parent_bone) + + @stage.configure_bones + def configure_master_control(self): + self.copy_bone_properties(self.bones.org[-1], self.bones.ctrl.master) + + if self.make_secondary: + self.copy_bone_properties(self.bones.org[0], self.bones.ctrl.secondary) + + @stage.generate_widgets + def make_master_control_widgets(self): + self.make_control_widget(self.bones.ctrl.master) + + if self.make_secondary: + self.make_control_widget(self.bones.ctrl.secondary) + + def make_control_widget(self, ctrl): w = create_widget(self.obj, ctrl) if w is not None: mesh = w.data @@ -213,27 +173,110 @@ class Rig: mod = w.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 + #################################################### + # ORG bones -def add_parameters(params): - """ Add the parameters of this rig type to the - RigifyParameters PropertyGroup + @stage.parent_bones + def parent_org_chain(self): + for org in self.bones.org: + self.set_bone_parent(org, self.rig_parent_bone) + self.get_bone(org).inherit_scale = 'NONE' - """ - items = [('X', 'X', ''), ('Z', 'Z', '')] - params.palm_rotation_axis = bpy.props.EnumProperty( - items=items, - name="Palm Rotation Axis", - default='X', + @stage.rig_bones + def rig_org_chain(self): + orgs = self.bones.org + ctrl = self.bones.ctrl + + for args in zip(count(0), orgs, repeat(len(orgs))): + self.rig_org_bone(*args) + + def rig_org_bone(self, i, org, num_orgs): + ctrl = self.bones.ctrl + fac = i / (num_orgs - 1) + + if fac > 0: + self.make_constraint( + org, 'COPY_TRANSFORMS', ctrl.master, space='LOCAL', + influence=fac + ) + + if self.make_secondary and fac < 1: + self.make_constraint( + org, 'COPY_LOCATION', ctrl.secondary, space='LOCAL', + use_offset=True, influence=1-fac + ) + self.make_constraint( + org, 'COPY_ROTATION', ctrl.secondary, space='LOCAL', + euler_order=self.order, mix_mode='ADD', influence=1-fac + ) + + elif self.make_secondary: + self.make_constraint( + org, 'COPY_TRANSFORMS', ctrl.secondary, space='LOCAL' ) + self.make_constraint(org, 'COPY_SCALE', self.rig_parent_bone) -def parameters_ui(layout, params): - """ Create the ui for the rig parameters. + self.rig_org_back_rotation(org, ctrl.master, fac) - """ - r = layout.row() - r.label(text="Primary rotation axis:") - r.prop(params, "palm_rotation_axis", text="") + if self.make_secondary: + self.rig_org_back_rotation(org, ctrl.secondary, 1 - fac) + + def rig_org_back_rotation(self, org, ctrl, fac): + if 0 < fac < 1: + inf = (fac + 1) * (fac + cos(fac * pi / 2) - 1) + + if 'X' in self.palm_rotation_axis: + self.make_constraint( + org, 'COPY_ROTATION', ctrl, space='LOCAL', + invert_x=True, use_xyz=(True,False,False), + euler_order=self.order, mix_mode='ADD', influence=inf + ) + else: + self.make_constraint( + org, 'COPY_ROTATION', ctrl, space='LOCAL', + invert_z=True, use_xyz=(False,False,True), + euler_order=self.order, mix_mode='ADD', influence=inf + ) + + #################################################### + # DEF bones + + @stage.generate_bones + def make_def_chain(self): + self.bones.deform = map_list(self.make_deform_bone, self.bones.org) + + def make_deform_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'def')) + + @stage.parent_bones + def parent_deform_chain(self): + for deform, org in zip(self.bones.deform, self.bones.org): + self.set_bone_parent(deform, org, use_connect=False) + + #################################################### + # Settings + + @classmethod + def add_parameters(cls, params): + items = [('X', 'X', ''), ('Z', 'Z', '')] + params.palm_rotation_axis = bpy.props.EnumProperty( + items=items, + name="Palm Rotation Axis", + default='X', + ) + params.palm_both_sides = bpy.props.BoolProperty( + name="Both Sides", + default=False, + description="Create controls for both sides of the palm" + ) + + @classmethod + def parameters_ui(cls, layout, params): + r = layout.row() + r.label(text="Primary rotation axis:") + r.prop(params, "palm_rotation_axis", text="") + layout.prop(params, "palm_both_sides") def create_sample(obj): |