diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-14 09:17:30 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-14 09:30:00 +0300 |
commit | 8b1df843703fdb51ffa5758625c117c4f10bc6dd (patch) | |
tree | 949e84f80116132eab3ad5f3bac46e745c3a9195 /rigify/rigs/limbs/super_finger.py | |
parent | 9b693a6be0aa2b0b4825d30ac5034655dce9c0dd (diff) |
Rigify: replace rigs with new implementations using the new base rig.
Spine is split into parts. Limbs and tentacles simply converted.
Differential Revision: https://developer.blender.org/D4624
Diffstat (limited to 'rigify/rigs/limbs/super_finger.py')
-rw-r--r-- | rigify/rigs/limbs/super_finger.py | 538 |
1 files changed, 237 insertions, 301 deletions
diff --git a/rigify/rigs/limbs/super_finger.py b/rigify/rigs/limbs/super_finger.py index 081dca7a..1a9171a7 100644 --- a/rigify/rigs/limbs/super_finger.py +++ b/rigify/rigs/limbs/super_finger.py @@ -1,298 +1,76 @@ +#====================== 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 ======================== + +# <pep8 compliant> + import bpy -from ...utils import copy_bone, flip_bone -from ...utils import strip_org, make_deformer_name, connected_children_names, make_mechanism_name -from ...utils import create_circle_widget, create_widget -from ...utils import MetarigError, align_bone_x_axis -from ...utils.mechanism import make_property +import re -script = """ -controls = [%s] -master_name = '%s' -if is_selected(controls): - layout.prop(pose_bones[master_name], '["%s"]', text="Curvature", slider=True) -""" +from itertools import count +from ...utils.errors import MetarigError +from ...utils.bones import flip_bone, align_chain_x_axis +from ...utils.naming import make_derived_name +from ...utils.widgets import create_widget +from ...utils.widgets_basic import create_circle_widget +from ...utils.misc import map_list -class Rig: +from ...base_rig import stage - def __init__(self, obj, bone_name, params): - self.obj = obj - self.org_bones = [bone_name] + connected_children_names(obj, bone_name) - self.params = params +from ..chain_rigs import SimpleChainRig - if len(self.org_bones) <= 1: - raise MetarigError("RIGIFY ERROR: Bone '%s': listen bro, that finger rig jusaint put tugetha rite. A little hint, use more than one bone!!" % (strip_org(bone_name))) - def orient_org_bones(self): +class Rig(SimpleChainRig): + """A finger rig with master control.""" + def initialize(self): + super().initialize() - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + self.bbone_segments = 8 + self.first_parent = self.get_bone_parent(self.bones.org[0]) + def prepare_bones(self): if self.params.primary_rotation_axis == 'automatic': + align_chain_x_axis(self.obj, self.bones.org) - first_bone = eb[self.org_bones[0]] - last_bone = eb[self.org_bones[-1]] - - # Orient uarm farm bones - chain_y_axis = last_bone.tail - first_bone.head - chain_rot_axis = first_bone.y_axis.cross(chain_y_axis) # ik-plane normal axis (rotation) - if chain_rot_axis.length < first_bone.length/100: - chain_rot_axis = first_bone.x_axis.normalized() - else: - chain_rot_axis = chain_rot_axis.normalized() - - for bone in self.org_bones: - align_bone_x_axis(self.obj, bone, chain_rot_axis) + ############################## + # Master Control - def generate(self): - org_bones = self.org_bones + @stage.generate_bones + def make_master_control(self): + orgs = self.bones.org + name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_master'), parent=True) + self.bones.ctrl.master = name - bpy.ops.object.mode_set(mode='EDIT') - eb = self.obj.data.edit_bones + first_bone = self.get_bone(orgs[0]) + last_bone = self.get_bone(orgs[-1]) + self.get_bone(name).tail += (last_bone.tail - first_bone.head) * 1.25 - self.orient_org_bones() + @stage.configure_bones + def configure_master_control(self): + master = self.bones.ctrl.master - # Bone name lists - ctrl_chain = [] - def_chain = [] - mch_chain = [] - mch_drv_chain = [] + bone = self.get_bone(master) + bone.lock_scale = True, False, True - # Create ctrl master bone - org_name = self.org_bones[0] - temp_name = strip_org(self.org_bones[0]) + @stage.generate_widgets + def make_master_control_widget(self): + master_name = self.bones.ctrl.master - if temp_name[-2:] == '.L' or temp_name[-2:] == '.R': - suffix = temp_name[-2:] - master_name = temp_name[:-2] + "_master" + suffix - else: - master_name = temp_name + "_master" - master_name = copy_bone(self.obj, org_name, master_name) - ctrl_bone_master = eb[master_name] - - # Parenting bug fix ?? - ctrl_bone_master.use_connect = False - ctrl_bone_master.parent = None - - ctrl_bone_master.tail += (eb[org_bones[-1]].tail - eb[org_name].head) * 1.25 - - for bone in org_bones: - eb[bone].use_connect = False - if org_bones.index(bone) != 0: - eb[bone].parent = None - - # Creating the bone chains - for i in range(len(self.org_bones)): - - name = self.org_bones[i] - ctrl_name = strip_org(name) - - # Create control bones - ctrl_bone = copy_bone(self.obj, name, ctrl_name) - ctrl_bone_e = eb[ctrl_name] - - # Create deformation bones - def_name = make_deformer_name(ctrl_name) - def_bone = copy_bone(self.obj, name, def_name) - - # Create mechanism bones - mch_name = make_mechanism_name(ctrl_name) - mch_bone = copy_bone(self.obj, name, mch_name) - - # Create mechanism driver bones - drv_name = make_mechanism_name(ctrl_name) + "_drv" - mch_bone_drv = copy_bone(self.obj, name, drv_name) - - # Adding to lists - ctrl_chain += [ctrl_bone] - def_chain += [def_bone] - mch_chain += [mch_bone] - mch_drv_chain += [mch_bone_drv] - - # Restoring org chain parenting - for bone in org_bones[1:]: - eb[bone].parent = eb[org_bones[org_bones.index(bone) - 1]] - - # Parenting the master bone to the first org - ctrl_bone_master = eb[master_name] - ctrl_bone_master.parent = eb[org_bones[0]] - - # Parenting chain bones - for i in range(len(self.org_bones)): - # Edit bone references - def_bone_e = eb[def_chain[i]] - ctrl_bone_e = eb[ctrl_chain[i]] - mch_bone_e = eb[mch_chain[i]] - mch_bone_drv_e = eb[mch_drv_chain[i]] - - if i == 0: - # First ctl bone - ctrl_bone_e.parent = mch_bone_drv_e - ctrl_bone_e.use_connect = False - # First def bone - def_bone_e.parent = eb[self.org_bones[i]].parent - def_bone_e.use_connect = False - # First mch bone - mch_bone_e.parent = eb[self.org_bones[i]].parent - mch_bone_e.use_connect = False - # First mch driver bone - mch_bone_drv_e.parent = eb[self.org_bones[i]].parent - mch_bone_drv_e.use_connect = False - else: - # The rest - ctrl_bone_e.parent = mch_bone_drv_e - ctrl_bone_e.use_connect = False - - def_bone_e.parent = eb[def_chain[i-1]] - def_bone_e.use_connect = True - - mch_bone_drv_e.parent = eb[ctrl_chain[i-1]] - mch_bone_drv_e.use_connect = False - - # Parenting mch bone - mch_bone_e.parent = ctrl_bone_e - mch_bone_e.use_connect = False - - # Creating tip control bone - tip_name = copy_bone(self.obj, org_bones[-1], temp_name) - ctrl_bone_tip = eb[tip_name] - flip_bone(self.obj, tip_name) - ctrl_bone_tip.length /= 2 - - ctrl_bone_tip.parent = eb[ctrl_chain[-1]] - - bpy.ops.object.mode_set(mode='OBJECT') - - pb = self.obj.pose.bones - - # Setting pose bones locks - pb_master = pb[master_name] - pb_master.lock_scale = True, False, True - - pb[tip_name].lock_scale = True, True, True - pb[tip_name].lock_rotation = True, True, True - pb[tip_name].lock_rotation_w = True - - make_property(pb_master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect") - - # Pose settings - for org, ctrl, deform, mch, mch_drv in zip(self.org_bones, ctrl_chain, def_chain, mch_chain, mch_drv_chain): - - # Constraining the deform bones - con = pb[deform].constraints.new('COPY_TRANSFORMS') - con.target = self.obj - con.subtarget = mch - - # Constraining the mch bones - if mch_chain.index(mch) == 0: - con = pb[mch].constraints.new('COPY_LOCATION') - con.target = self.obj - con.subtarget = ctrl - - con = pb[mch].constraints.new('COPY_SCALE') - con.target = self.obj - con.subtarget = ctrl - - con = pb[mch].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - - con = pb[mch].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - con.volume = 'NO_VOLUME' - - elif mch_chain.index(mch) == len(mch_chain) - 1: - con = pb[mch].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = tip_name - - con = pb[mch].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = tip_name - con.volume = 'NO_VOLUME' - else: - con = pb[mch].constraints.new('DAMPED_TRACK') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - - con = pb[mch].constraints.new('STRETCH_TO') - con.target = self.obj - con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] - con.volume = 'NO_VOLUME' - - # Constraining and driving mch driver bones - pb[mch_drv].rotation_mode = 'YZX' - - if mch_drv_chain.index(mch_drv) == 0: - # Constraining to master bone - con = pb[mch_drv].constraints.new('COPY_LOCATION') - con.target = self.obj - con.subtarget = master_name - - con = pb[mch_drv].constraints.new('COPY_ROTATION') - con.target = self.obj - con.subtarget = master_name - con.target_space = 'LOCAL' - con.owner_space = 'LOCAL' - - else: - # Match axis to expression - options = { - "automatic": {"axis": 0, - "expr": '(1-sy)*pi'}, - "X": {"axis": 0, - "expr": '(1-sy)*pi'}, - "-X": {"axis": 0, - "expr": '-((1-sy)*pi)'}, - "Y": {"axis": 1, - "expr": '(1-sy)*pi'}, - "-Y": {"axis": 1, - "expr": '-((1-sy)*pi)'}, - "Z": {"axis": 2, - "expr": '(1-sy)*pi'}, - "-Z": {"axis": 2, - "expr": '-((1-sy)*pi)'} - } - - axis = self.params.primary_rotation_axis - - # Drivers - drv = pb[mch_drv].driver_add("rotation_euler", options[axis]["axis"]).driver - drv.type = 'SCRIPTED' - drv.expression = options[axis]["expr"] - drv_var = drv.variables.new() - drv_var.name = 'sy' - drv_var.type = "SINGLE_PROP" - drv_var.targets[0].id = self.obj - drv_var.targets[0].data_path = pb[master_name].path_from_id() + '.scale.y' - - # Setting bone curvature setting, custom property, and drivers - def_bone = self.obj.data.bones[deform] - - def_bone.bbone_segments = 8 - drv = def_bone.driver_add("bbone_easein").driver # Ease in - - drv.type='SUM' - drv_var = drv.variables.new() - drv_var.name = "curvature" - drv_var.type = "SINGLE_PROP" - drv_var.targets[0].id = self.obj - drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' - - drv = def_bone.driver_add("bbone_easeout").driver # Ease out - - drv.type='SUM' - drv_var = drv.variables.new() - drv_var.name = "curvature" - drv_var.type = "SINGLE_PROP" - drv_var.targets[0].id = self.obj - drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' - - # Assigning shapes to control bones - create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) - - # Create ctrl master widget w = create_widget(self.obj, master_name) if w is not None: mesh = w.data @@ -307,31 +85,189 @@ class Rig: mesh.from_pydata(verts, edges, []) mesh.update() - # Create tip control widget - create_circle_widget(self.obj, tip_name, radius=0.3, head_tail=0.0) + ############################## + # Control chain + + @stage.generate_bones + def make_control_chain(self): + orgs = self.bones.org + self.bones.ctrl.fk = map_list(self.make_control_bone, count(0), orgs) + self.bones.ctrl.fk += [self.make_tip_control_bone(orgs[-1], orgs[0])] + + def make_control_bone(self, i, org): + return self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=False) + + def make_tip_control_bone(self, org, name_org): + name = self.copy_bone(org, make_derived_name(name_org, 'ctrl'), parent=False) + + flip_bone(self.obj, name) + self.get_bone(name).length /= 2 + + return name + + @stage.parent_bones + def parent_control_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(ctrls, self.bones.mch.bend + ctrls[-2:]): + self.set_bone_parent(*args) + + @stage.configure_bones + def configure_control_chain(self): + for args in zip(count(0), self.bones.ctrl.fk, self.bones.org + [None]): + self.configure_control_bone(*args) + + def configure_control_bone(self, i, ctrl, org): + if org: + self.copy_bone_properties(org, ctrl) + else: + bone = self.get_bone(ctrl) + bone.lock_rotation_w = True + bone.lock_rotation = (True, True, True) + bone.lock_scale = (True, True, True) + + def make_control_widget(self, ctrl): + if ctrl == self.bones.ctrl.fk[-1]: + # Tip control + create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.0) + else: + create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) + + ############################## + # MCH bend chain + + @stage.generate_bones + def make_mch_bend_chain(self): + self.bones.mch.bend = map_list(self.make_mch_bend_bone, self.bones.org) + + def make_mch_bend_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), parent=False) + + @stage.parent_bones + def parent_mch_bend_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(self.bones.mch.bend, [self.first_parent] + ctrls): + self.set_bone_parent(*args) + + # Match axis to expression + axis_options = { + "automatic": {"axis": 0, + "expr": '(1-sy)*pi'}, + "X": {"axis": 0, + "expr": '(1-sy)*pi'}, + "-X": {"axis": 0, + "expr": '-((1-sy)*pi)'}, + "Y": {"axis": 1, + "expr": '(1-sy)*pi'}, + "-Y": {"axis": 1, + "expr": '-((1-sy)*pi)'}, + "Z": {"axis": 2, + "expr": '(1-sy)*pi'}, + "-Z": {"axis": 2, + "expr": '-((1-sy)*pi)'} + } + + @stage.rig_bones + def rig_mch_bend_chain(self): + for args in zip(count(0), self.bones.mch.bend): + self.rig_mch_bend_bone(*args) + + def rig_mch_bend_bone(self, i, mch): + master = self.bones.ctrl.master + if i == 0: + self.make_constraint(mch, 'COPY_LOCATION', master) + self.make_constraint(mch, 'COPY_ROTATION', master, space='LOCAL') + else: + axis = self.params.primary_rotation_axis + options = self.axis_options[axis] + + bone = self.get_bone(mch) + bone.rotation_mode = 'YZX' + + self.make_driver( + bone, 'rotation_euler', index=options['axis'], + expression=options['expr'], + variables={'sy': (master, '.scale.y')} + ) + + ############################## + # MCH stretch chain + + @stage.generate_bones + def make_mch_stretch_chain(self): + self.bones.mch.stretch = map_list(self.make_mch_stretch_bone, self.bones.org) + + def make_mch_stretch_bone(self, org): + return self.copy_bone(org, make_derived_name(org, 'mch'), parent=False) + + @stage.parent_bones + def parent_mch_stretch_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(self.bones.mch.stretch, [self.first_parent] + ctrls[1:]): + self.set_bone_parent(*args) + + @stage.rig_bones + def rig_mch_stretch_chain(self): + ctrls = self.bones.ctrl.fk + for args in zip(count(0), self.bones.mch.stretch, ctrls, ctrls[1:]): + self.rig_mch_stretch_bone(*args) + + def rig_mch_stretch_bone(self, i, mch, ctrl, ctrl_next): + if i == 0: + self.make_constraint(mch, 'COPY_LOCATION', ctrl) + self.make_constraint(mch, 'COPY_SCALE', ctrl) + + self.make_constraint(mch, 'DAMPED_TRACK', ctrl_next) + self.make_constraint(mch, 'STRETCH_TO', ctrl_next, volume='NO_VOLUME') + + ############################## + # ORG chain + + @stage.rig_bones + def rig_org_chain(self): + for args in zip(count(0), self.bones.org, self.bones.mch.stretch): + self.rig_org_bone(*args) + + ############################## + # Deform chain + + @stage.configure_bones + def configure_master_properties(self): + master = self.bones.ctrl.master + + self.make_property(master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect") # Create UI - controls_string = ", ".join( - ["'" + x + "'" for x in ctrl_chain] - ) + ", " + "'" + master_name + "'" - return [script % (controls_string, master_name, 'finger_curve')] - - -def add_parameters(params): - """ Add the parameters of this rig type to the - RigifyParameters PropertyGroup - """ - items = [('automatic', 'Automatic', ''), ('X', 'X manual', ''), ('Y', 'Y manual', ''), ('Z', 'Z manual', ''), - ('-X', '-X manual', ''), ('-Y', '-Y manual', ''), ('-Z', '-Z manual', '')] - params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='automatic') - - -def parameters_ui(layout, params): - """ Create the ui for the rig parameters. - """ - r = layout.row() - r.label(text="Bend rotation axis:") - r.prop(params, "primary_rotation_axis", text="") + panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten()) + panel.custom_prop(master, 'finger_curve', text="Curvature", slider=True) + + def rig_deform_bone(self, i, deform, org): + master = self.bones.ctrl.master + bone = self.get_bone(deform) + + self.make_constraint(deform, 'COPY_TRANSFORMS', org) + + self.make_driver(bone.bone, 'bbone_easein', variables=[(master, 'finger_curve')]) + self.make_driver(bone.bone, 'bbone_easeout', variables=[(master, 'finger_curve')]) + + ############### + # OPTIONS + + @classmethod + def add_parameters(self, params): + """ Add the parameters of this rig type to the + RigifyParameters PropertyGroup + """ + items = [('automatic', 'Automatic', ''), ('X', 'X manual', ''), ('Y', 'Y manual', ''), ('Z', 'Z manual', ''), + ('-X', '-X manual', ''), ('-Y', '-Y manual', ''), ('-Z', '-Z manual', '')] + params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='automatic') + + @classmethod + def parameters_ui(self, layout, params): + """ Create the ui for the rig parameters. + """ + r = layout.row() + r.label(text="Bend rotation axis:") + r.prop(params, "primary_rotation_axis", text="") def create_sample(obj): |