import bpy from ...utils import copy_bone from ...utils import strip_org, make_deformer_name, connected_children_names from ...utils import make_mechanism_name, put_bone, create_sphere_widget from ...utils import create_widget, create_circle_widget from ...utils import MetarigError from ....utils.mechanism import make_property script = """ controls = [%s] master_name = '%s' if is_selected( controls ): layout.prop( pose_bones[ master_name ], '["%s"]', slider = True ) layout.prop( pose_bones[ master_name ], '["%s"]', slider = True ) """ class Rig: def __init__(self, obj, bone_name, params): self.obj = obj self.org_bones = [bone_name] + connected_children_names(obj, bone_name) self.params = params if params.tweak_extra_layers: self.tweak_layers = list( params.tweak_layers ) else: self.tweak_layers = None if len(self.org_bones) <= 1: raise MetarigError( "RIGIFY ERROR: invalid rig structure" % (strip_org(bone_name)) ) def make_mch( self ): bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones org_bones = self.org_bones mch_parent = self.obj.data.bones[ org_bones[0] ].parent mch_parent_name = mch_parent.name # Storing the mch parent's name if not mch_parent: mch_parent = self.obj.data.edit_bones[ org_bones[0] ] mch_bone = copy_bone( self.obj, mch_parent_name, make_mechanism_name( strip_org( org_bones[0] ) ) ) else: mch_bone = copy_bone( self.obj, mch_parent_name, make_mechanism_name( strip_org( org_bones[0] ) ) ) put_bone( self.obj, mch_bone, eb[ mch_parent_name ].tail ) eb[ mch_bone ].length /= 4 # reduce length to fourth of original return mch_bone def make_master( self ): bpy.ops.object.mode_set(mode ='EDIT') org_bones = self.org_bones master_bone = copy_bone( self.obj, org_bones[0], "master_" + strip_org( org_bones[0] ) ) # Make widgets bpy.ops.object.mode_set(mode ='OBJECT') create_square_widget( self.obj, master_bone ) return master_bone def make_controls( self ): bpy.ops.object.mode_set(mode ='EDIT') org_bones = self.org_bones ctrl_chain = [] for i in range( len( org_bones ) ): name = org_bones[i] ctrl_bone = copy_bone( self.obj, name, strip_org(name) ) ctrl_chain.append( ctrl_bone ) # Make widgets bpy.ops.object.mode_set(mode ='OBJECT') for ctrl in ctrl_chain: create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) return ctrl_chain def make_tweaks( self ): bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones org_bones = self.org_bones tweak_chain = [] for i in range( len( org_bones ) + 1 ): if i == len( org_bones ): # Make final tweak at the tip of the tentacle name = org_bones[i-1] else: name = org_bones[i] tweak_bone = copy_bone( self.obj, name, "tweak_" + strip_org(name) ) tweak_e = eb[ tweak_bone ] tweak_e.length /= 2 # Set size to half if i == len( org_bones ): # Position final tweak at the tip put_bone( self.obj, tweak_bone, eb[ org_bones[-1]].tail ) tweak_chain.append( tweak_bone ) # Make widgets bpy.ops.object.mode_set(mode = 'OBJECT') for tweak in tweak_chain: create_sphere_widget( self.obj, tweak ) tweak_pb = self.obj.pose.bones[ tweak ] # Set locks if tweak_chain.index( tweak ) != len( tweak_chain ) - 1: tweak_pb.lock_rotation = (True, False, True) tweak_pb.lock_scale = (False, True, False) else: tweak_pb.lock_rotation_w = True tweak_pb.lock_rotation = (True, True, True) tweak_pb.lock_scale = (True, True, True) # Set up tweak bone layers if self.tweak_layers: tweak_pb.bone.layers = self.tweak_layers return tweak_chain def make_deform( self ): bpy.ops.object.mode_set(mode ='EDIT') org_bones = self.org_bones def_chain = [] for i in range( len( org_bones ) ): name = org_bones[i] def_bone = copy_bone( self.obj, name, make_deformer_name(strip_org(name)) ) def_chain.append( def_bone ) return def_chain def parent_bones( self, all_bones ): bpy.ops.object.mode_set(mode ='EDIT') org_bones = self.org_bones eb = self.obj.data.edit_bones """ for category in all_bones: if isinstance( all_bones[category], list ): for bone in all_bones[category]: print( "Bone: " + bone ) eb[bone].parent = None else: eb[ all_bones[category] ].parent = None """ # mch bone remains parentless and will be parented to root by rigify # Parent master bone # eb[ all_bones['master'] ].parent = eb[ all_bones['mch'] ] # Parent control bones # ctrls_n_parent = [ all_bones['master'] ] + all_bones['control'] for bone in ctrls_n_parent[1:]: previous_index = ctrls_n_parent.index( bone ) - 1 eb[ bone ].parent = eb[ ctrls_n_parent[previous_index] ] # Parent tweak bones tweaks = all_bones['tweak'] for tweak in all_bones['tweak']: parent = '' if tweaks.index( tweak ) == len( tweaks ) - 1: parent = all_bones['control'][ -1 ] else: parent = all_bones['control'][ tweaks.index( tweak ) ] eb[ tweak ].parent = eb[ parent ] # Parent deform bones for bone in all_bones['deform'][1:]: previous_index = all_bones['deform'].index( bone ) - 1 eb[ bone ].parent = eb[ all_bones['deform'][previous_index] ] eb[ bone ].use_connect = True # Parent org bones ( to tweaks by default, or to the controls ) for org, tweak in zip( org_bones, all_bones['tweak'] ): eb[ org ].parent = eb[ tweak ] def make_constraints( self, all_bones ): bpy.ops.object.mode_set(mode ='OBJECT') org_bones = self.org_bones pb = self.obj.pose.bones ## MCH bone constraints if pb[ org_bones[0] ].parent: mch_pb = pb[ all_bones['mch'] ] con = mch_pb.constraints.new('COPY_LOCATION') con.target = self.obj con.subtarget = pb[ org_bones[0] ].parent.name con.head_tail = 1.0 con = mch_pb.constraints.new('COPY_ROTATION') con.target = self.obj con.subtarget = pb[ org_bones[0] ].parent.name con = mch_pb.constraints.new('COPY_SCALE') con.target = self.obj con.subtarget = pb[ org_bones[0] ].parent.name """ # Setting the MCH prop master_pb = pb[ all_bones['master'] ] prop_name_r = "rotation_follow" prop_name_s = "scale_follow" prop_names = [ prop_name_r, prop_name_s ] for prop_name in prop_names: make_property(master_pb, prop_name, 1.0) # driving the MCH follow rotation switch drv = mch_pb.constraints[ prop_names.index(prop_name) +1 ].driver_add("influence").driver drv.type='SUM' var = drv.variables.new() var.name = prop_name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ master_pb.path_from_id() + '['+ '"' + prop_name + '"' + ']' """ ## Deform bones' constraints ctrls = all_bones['control'] tweaks = all_bones['tweak' ] deforms = all_bones['deform' ] for deform, tweak, ctrl in zip( deforms, tweaks, ctrls ): con = pb[deform].constraints.new('COPY_TRANSFORMS') con.target = self.obj con.subtarget = tweak con = pb[deform].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = tweaks[ tweaks.index( tweak ) + 1 ] con = pb[deform].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = tweaks[ tweaks.index( tweak ) + 1 ] ## Control bones' constraints if self.params.make_rotations: if ctrl != ctrls[0]: con = pb[ctrl].constraints.new('COPY_ROTATION') con.target = self.obj con.subtarget = ctrls[ ctrls.index(ctrl) - 1 ] con.use_offset = True con.target_space = 'LOCAL' con.owner_space = 'LOCAL' def generate(self): bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones # Clear all initial parenting for bone in self.org_bones: # eb[ bone ].parent = None eb[ bone ].use_connect = False # Creating all bones mch = self.make_mch() # master = self.make_master() ctrl_chain = self.make_controls() tweak_chain = self.make_tweaks() def_chain = self.make_deform() all_bones = { 'mch' : mch, # 'master' : master, 'control' : ctrl_chain, 'tweak' : tweak_chain, 'deform' : def_chain } self.make_constraints( all_bones ) self.parent_bones( all_bones ) """ # Create UI all_controls = all_bones['control'] + all_bones['tweak'] # + [ all_bones['master'] ] controls_string = ", ".join(["'" + x + "'" for x in all_controls]) return [script % ( controls_string, 'rotation_follow', 'scale_follow' )] """ def add_parameters(params): """ Add the parameters of this rig type to the RigifyParameters PropertyGroup """ params.make_rotations = bpy.props.BoolProperty( name = "Rotations", default = True, description = "Make bones follow parent rotation" ) # Setting up extra tweak layers params.tweak_extra_layers = bpy.props.BoolProperty( name = "tweak_extra_layers", default = True, description = "" ) params.tweak_layers = bpy.props.BoolVectorProperty( size = 32, description = "Layers for the tweak controls to be on", default = tuple( [ i == 1 for i in range(0, 32) ] ) ) def parameters_ui(layout, params): """ Create the ui for the rig parameters. """ r = layout.row() r.prop(params, "make_rotations") r = layout.row() r.prop(params, "tweak_extra_layers") r.active = params.tweak_extra_layers col = r.column(align=True) row = col.row(align=True) for i in range( 8 ): # Layers 0-7 row.prop(params, "tweak_layers", index=i, toggle=True, text="") row = col.row(align=True) for i in range( 16, 24 ): # Layers 16-23 row.prop(params, "tweak_layers", index=i, toggle=True, text="") col = r.column(align=True) row = col.row(align=True) for i in range( 8, 16 ): # Layers 8-15 row.prop(params, "tweak_layers", index=i, toggle=True, text="") row = col.row(align=True) for i in range( 24, 32 ): # Layers 24-31 row.prop(params, "tweak_layers", index=i, toggle=True, text="") def create_square_widget(rig, bone_name, size=1.0, bone_transform_name=None): obj = create_widget(rig, bone_name, bone_transform_name) if obj is not None: verts = [ ( 0.5 * size, -2.9802322387695312e-08 * size, 0.5 * size ), ( -0.5 * size, -2.9802322387695312e-08 * size, 0.5 * size ), ( 0.5 * size, 2.9802322387695312e-08 * size, -0.5 * size ), ( -0.5 * size, 2.9802322387695312e-08 * size, -0.5 * size ), ] edges = [(0, 1), (2, 3), (0, 2), (3, 1) ] faces = [] mesh = obj.data mesh.from_pydata(verts, edges, faces) mesh.update() mesh.update() return obj else: return None def create_sample(obj): # generated by rigify.utils.write_metarig bpy.ops.object.mode_set(mode='EDIT') arm = obj.data bones = {} bone = arm.edit_bones.new('tentacle') bone.head[:] = 0.0000, 0.0000, 0.0000 bone.tail[:] = 0.0000, 0.0000, 1.0000 bone.roll = 0.0000 bone.use_connect = False bones['tentacle'] = bone.name bone = arm.edit_bones.new('tentacle.001') bone.head[:] = 0.0000, 0.0000, 1.0000 bone.tail[:] = 0.0000, 0.0000, 2.0000 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['tentacle']] bones['tentacle.001'] = bone.name bone = arm.edit_bones.new('tentacle.002') bone.head[:] = 0.0000, 0.0000, 2.0000 bone.tail[:] = 0.0000, 0.0000, 3.0000 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['tentacle.001']] bones['tentacle.002'] = bone.name bpy.ops.object.mode_set(mode='OBJECT') pbone = obj.pose.bones[bones['tentacle']] pbone.rigify_type = 'tentacle' 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' pbone = obj.pose.bones[bones['tentacle.001']] 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' pbone = obj.pose.bones[bones['tentacle.002']] 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