def create_mch(self): bpy.ops.object.mode_set(mode='EDIT') edit_bones = self.obj.data.edit_bones super().create_mch() self.bones['tail_mch'] = {} org_chain = self.get_chain_bones(self.base_bone) if edit_bones[org_chain[0]].parent: bone_to_copy = edit_bones[org_chain[0]].parent.name else: bone_to_copy = self.orientation_bone mch_rot_tail = make_mechanism_name('ROT-' + strip_org(self.base_bone)) mch_rot_tail = copy_bone(self.obj, bone_to_copy, assign_name=mch_rot_tail) self.bones['tail_mch']['rot_tail_mch'] = mch_rot_tail main_chain = self.get_chain_bones(self.base_bone) flip_bone(self.obj, mch_rot_tail) edit_bones[mch_rot_tail].parent = None put_bone(self.obj, mch_rot_tail, edit_bones[main_chain[0]].head)
def create_def(self): super().create_def() chain = strip_org(self.base_bone) for def_bone in self.bones['def'][chain]: flip_bone(self.obj, def_bone) self.bones['def'][chain].reverse()
def create_controls(self): bpy.ops.object.mode_set(mode='EDIT') edit_bones = self.obj.data.edit_bones super().create_controls() self.bones['tongue_ctrl'] = {} tongue_master_name = strip_org(self.base_bone) + '_master' tongue_master_name = copy_bone(self.obj, self.base_bone, assign_name=tongue_master_name) flip_bone(self.obj, tongue_master_name) self.bones['tongue_ctrl']['tongue_master'] = tongue_master_name
def create_mch(self): bpy.ops.object.mode_set(mode='EDIT') edit_bones = self.obj.data.edit_bones super().create_mch() self.bones['tongue_mch'] = {} self.bones['tongue_mch']['tongue_tip'] = [] for i, mch in enumerate(self.bones['mch'][strip_org(self.base_bone)][:-1]): if i == 0: edit_bones[mch].length = edit_bones[self.base_bone].length flip_bone(self.obj, mch) self.bones['tongue_mch']['tongue_tip'].append(mch) else: put_bone(self.obj, mch, edit_bones[self.base_bone].tail) edit_bones[mch].tail = edit_bones[self.base_bone].head self.bones['tongue_mch']['tongue_tip'].append(mch)
def gen_control(self): """ Generate the control rig. """ bpy.ops.object.mode_set(mode='EDIT') 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")) eb = self.obj.data.edit_bones # 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 # 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"]' 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 bpy.ops.object.mode_set(mode='EDIT') flex_bones = [] 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")) sub = new_bone(self.obj, make_mechanism_name(strip_org(b) + ".flex_s")) flex_bones += [bone] flex_subs += [sub] eb = self.obj.data.edit_bones bone_e = eb[bone] sub_e = eb[sub] # Parenting bone_e.use_connect = False sub_e.use_connect = False if prev_bone is None: sub_e.parent = eb[controls[0]] else: sub_e.parent = eb[prev_bone] bone_e.parent = sub_e # Position put_bone(self.obj, sub, bone_e.head) sub_e.length = bone_e.length / 4 if prev_bone is not None: sub_e.use_connect = True prev_bone = bone #---------------------------- # Create reverse spine chain # Create bones/parenting/positioning bpy.ops.object.mode_set(mode='EDIT') rev_bones = [] prev_bone = None for b in zip(flex_bones, self.org_bones): # Create bones bone = copy_bone(self.obj, b[1], make_mechanism_name(strip_org(b[1]) + ".reverse")) rev_bones += [bone] eb = self.obj.data.edit_bones bone_e = eb[bone] # Parenting bone_e.use_connect = False bone_e.parent = eb[b[0]] # Position flip_bone(self.obj, bone) bone_e.tail = Vector(eb[b[0]].head) #bone_e.head = Vector(eb[b[0]].tail) if prev_bone is None: put_bone(self.obj, bone, pivot_rest_pos) else: put_bone(self.obj, bone, eb[prev_bone].tail) prev_bone = bone # Constraints bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones prev_bone = None for bone in rev_bones: bone_p = pb[bone] con = bone_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj if prev_bone is None: con.subtarget = main_control else: con.subtarget = prev_bone con.head_tail = 1.0 prev_bone = bone #---------------------------------------- # Constrain original bones to flex spine bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones 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 = fbone #--------------------------- # Create pivot slide system pb = self.obj.pose.bones bone_p = pb[self.org_bones[0]] main_control_p = pb[main_control] # Custom pivot_slide property 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 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) for rb in rev_bones: con = bone_p.constraints.new('COPY_LOCATION') con.name = "slide." + str(i) con.target = self.obj con.subtarget = rb con.head_tail = 1.0 # 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 - 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 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]) # 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]) # Layers pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers return [main_control] + controls
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. """ # Generate base IK limb bone_list = self.ik_limb.generate() thigh = bone_list[0] shin = bone_list[1] foot = bone_list[2] foot_mch = bone_list[3] pole = bone_list[4] # vispole = bone_list[5] # visfoot = bone_list[6] # Build IK foot rig bpy.ops.object.mode_set(mode='EDIT') make_rocker = False if self.org_bones[5] is not None: make_rocker = True # Create the bones toe = copy_bone(self.obj, self.org_bones[3], strip_org(self.org_bones[3])) toe_parent = copy_bone( self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".parent"))) toe_parent_socket1 = copy_bone( self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket1"))) toe_parent_socket2 = copy_bone( self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket2"))) foot_roll = copy_bone( self.obj, self.org_bones[4], strip_org(insert_before_lr(self.org_bones[2], "_roll.ik"))) roll1 = copy_bone( self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.01"))) roll2 = copy_bone( self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.02"))) if make_rocker: rocker1 = copy_bone( self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.01"))) rocker2 = copy_bone( self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.02"))) # Get edit bones eb = self.obj.data.edit_bones org_foot_e = eb[self.org_bones[2]] foot_e = eb[foot] foot_ik_target_e = eb[foot_mch] toe_e = eb[toe] toe_parent_e = eb[toe_parent] toe_parent_socket1_e = eb[toe_parent_socket1] toe_parent_socket2_e = eb[toe_parent_socket2] foot_roll_e = eb[foot_roll] roll1_e = eb[roll1] roll2_e = eb[roll2] if make_rocker: rocker1_e = eb[rocker1] rocker2_e = eb[rocker2] # Parenting foot_ik_target_e.use_connect = False foot_ik_target_e.parent = roll2_e toe_e.parent = toe_parent_e toe_parent_e.use_connect = False toe_parent_e.parent = toe_parent_socket1_e toe_parent_socket1_e.use_connect = False toe_parent_socket1_e.parent = roll1_e toe_parent_socket2_e.use_connect = False toe_parent_socket2_e.parent = eb[self.org_bones[2]] foot_roll_e.use_connect = False foot_roll_e.parent = foot_e roll1_e.use_connect = False roll1_e.parent = foot_e roll2_e.use_connect = False roll2_e.parent = roll1_e if make_rocker: rocker1_e.use_connect = False rocker2_e.use_connect = False roll1_e.parent = rocker2_e rocker2_e.parent = rocker1_e rocker1_e.parent = foot_e # Positioning vec = Vector(toe_e.vector) vec.normalize() foot_e.tail = foot_e.head + (vec * foot_e.length) foot_e.roll = toe_e.roll flip_bone(self.obj, toe_parent_socket1) flip_bone(self.obj, toe_parent_socket2) toe_parent_socket1_e.head = Vector(org_foot_e.tail) toe_parent_socket2_e.head = Vector(org_foot_e.tail) toe_parent_socket1_e.tail = Vector(org_foot_e.tail) + (Vector( (0, 0, 1)) * foot_e.length / 2) toe_parent_socket2_e.tail = Vector(org_foot_e.tail) + (Vector( (0, 0, 1)) * foot_e.length / 3) toe_parent_socket2_e.roll = toe_parent_socket1_e.roll tail = Vector(roll1_e.tail) roll1_e.tail = Vector(org_foot_e.tail) roll1_e.tail = Vector(org_foot_e.tail) roll1_e.head = tail roll2_e.head = Vector(org_foot_e.tail) foot_roll_e.head = Vector(org_foot_e.tail) put_bone(self.obj, foot_roll, roll1_e.head) foot_roll_e.length /= 2 roll_axis = roll1_e.vector.cross(org_foot_e.vector) align_bone_x_axis(self.obj, roll1, roll_axis) align_bone_x_axis(self.obj, roll2, roll_axis) foot_roll_e.roll = roll2_e.roll if make_rocker: d = toe_e.y_axis.dot(rocker1_e.x_axis) if d >= 0.0: flip_bone(self.obj, rocker2) else: flip_bone(self.obj, rocker1) # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones foot_p = pb[foot] foot_roll_p = pb[foot_roll] roll1_p = pb[roll1] roll2_p = pb[roll2] if make_rocker: rocker1_p = pb[rocker1] rocker2_p = pb[rocker2] toe_p = pb[toe] # toe_parent_p = pb[toe_parent] toe_parent_socket1_p = pb[toe_parent_socket1] # Foot roll control only rotates on x-axis, or x and y if rocker. foot_roll_p.rotation_mode = 'XYZ' if make_rocker: foot_roll_p.lock_rotation = False, False, True else: foot_roll_p.lock_rotation = False, True, True foot_roll_p.lock_location = True, True, True foot_roll_p.lock_scale = True, True, True # roll and rocker bones set to euler rotation roll1_p.rotation_mode = 'XYZ' roll2_p.rotation_mode = 'XYZ' if make_rocker: rocker1_p.rotation_mode = 'XYZ' rocker2_p.rotation_mode = 'XYZ' # toe_parent constraint con = toe_parent_socket1_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = toe_parent_socket2 con = toe_parent_socket1_p.constraints.new('COPY_SCALE') con.name = "copy_scale" con.target = self.obj con.subtarget = toe_parent_socket2 con = toe_parent_socket1_p.constraints.new( 'COPY_TRANSFORMS') # drive with IK switch con.name = "fk" con.target = self.obj con.subtarget = toe_parent_socket2 fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 # Foot roll drivers fcurve = roll1_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "min(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[0]' fcurve = roll2_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[0]' if make_rocker: fcurve = rocker1_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,-var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[1]' fcurve = rocker2_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[1]' # Constrain toe bone to toe control con = pb[self.org_bones[3]].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = toe # Set layers if specified if self.layers: foot_roll_p.bone.layers = self.layers toe_p.bone.layers = [ (i[0] or i[1]) for i in zip(toe_p.bone.layers, self.layers) ] # Both FK and IK layers # Create widgets create_circle_widget(self.obj, toe, radius=0.7, head_tail=0.5) ob = create_widget(self.obj, foot_roll) if ob is not None: verts = [ (0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247) ] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 ob = create_widget(self.obj, foot) if ob is not None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [thigh, shin, foot, pole, foot_roll, foot_mch]
def gen_control(self): """ 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 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 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 # 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]] #------------------------- # Create flex spine chain # Create bones/parenting/positiong bpy.ops.object.mode_set(mode='EDIT') flex_bones = [] flex_helpers = [] 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")) flex_bones += [bone] flex_helpers += [helper] eb = self.obj.data.edit_bones bone_e = eb[bone] helper_e = eb[helper] # Parenting bone_e.use_connect = False helper_e.use_connect = False if prev_bone == None: helper_e.parent = eb[hip_control] bone_e.parent = helper_e # Position put_bone(self.obj, helper, bone_e.head) helper_e.length /= 4 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 # Create bones/parenting/positioning bpy.ops.object.mode_set(mode='EDIT') rev_bones = [] prev_bone = None for b in zip(flex_bones, self.org_bones): # Create bones bone = copy_bone(self.obj, b[1], make_mechanism_name(strip_org(b[1]) + ".reverse")) rev_bones += [bone] eb = self.obj.data.edit_bones bone_e = eb[bone] # Parenting bone_e.use_connect = False bone_e.parent = eb[b[0]] # Position flip_bone(self.obj, bone) bone_e.tail = Vector(eb[b[0]].head) #bone_e.head = Vector(eb[b[0]].tail) if prev_bone == None: pass # Position base bone wherever you want, for now do nothing (i.e. position at hips) else: put_bone(self.obj, bone, eb[prev_bone].tail) prev_bone = bone # Constraints bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones prev_bone = None for bone in rev_bones: bone_p = pb[bone] con = bone_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj if prev_bone == None: con.subtarget = hip_control # Position base bone wherever you want, for now hips else: con.subtarget = prev_bone con.head_tail = 1.0 prev_bone = bone #--------------------------------------------- # Constrain org bones to flex bone's rotation 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" con.target = self.obj con.subtarget = b[1] #--------------------------- # Create pivot slide system pb = self.obj.pose.bones bone_p = pb[self.org_bones[0]] rib_control_p = pb[rib_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["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 con = bone_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = rev_bones[0] # Slide constraints i = 1 tot = len(rev_bones) for rb in rev_bones: con = bone_p.constraints.new('COPY_LOCATION') con.name = "slide." + str(i) con.target = self.obj con.subtarget = rb con.head_tail = 1.0 # 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 = rib_control_p.path_from_id( ) + '["pivot_slide"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1 - i mod.coefficients[1] = tot i += 1 # 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) 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]) # Return control names return hip_control, rib_control
def gen_control(self): """ Generate the control rig. """ bpy.ops.object.mode_set(mode='EDIT') 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")) eb = self.obj.data.edit_bones # 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 # 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"]' 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 bpy.ops.object.mode_set(mode='EDIT') flex_bones = [] 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")) sub = new_bone(self.obj, make_mechanism_name(strip_org(b) + ".flex_s")) flex_bones += [bone] flex_subs += [sub] eb = self.obj.data.edit_bones bone_e = eb[bone] sub_e = eb[sub] # Parenting bone_e.use_connect = False sub_e.use_connect = False if prev_bone is None: sub_e.parent = eb[controls[0]] else: sub_e.parent = eb[prev_bone] bone_e.parent = sub_e # Position put_bone(self.obj, sub, bone_e.head) sub_e.length = bone_e.length / 4 if prev_bone is not None: sub_e.use_connect = True prev_bone = bone #---------------------------- # Create reverse spine chain # Create bones/parenting/positioning bpy.ops.object.mode_set(mode='EDIT') rev_bones = [] prev_bone = None for b in zip(flex_bones, self.org_bones): # Create bones bone = copy_bone(self.obj, b[1], make_mechanism_name(strip_org(b[1]) + ".reverse")) rev_bones += [bone] eb = self.obj.data.edit_bones bone_e = eb[bone] # Parenting bone_e.use_connect = False bone_e.parent = eb[b[0]] # Position flip_bone(self.obj, bone) bone_e.tail = Vector(eb[b[0]].head) #bone_e.head = Vector(eb[b[0]].tail) if prev_bone is None: put_bone(self.obj, bone, pivot_rest_pos) else: put_bone(self.obj, bone, eb[prev_bone].tail) prev_bone = bone # Constraints bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones prev_bone = None for bone in rev_bones: bone_p = pb[bone] con = bone_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj if prev_bone is None: con.subtarget = main_control else: con.subtarget = prev_bone con.head_tail = 1.0 prev_bone = bone #---------------------------------------- # Constrain original bones to flex spine bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones 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 = fbone #--------------------------- # Create pivot slide system pb = self.obj.pose.bones bone_p = pb[self.org_bones[0]] main_control_p = pb[main_control] # Custom pivot_slide property 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 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) for rb in rev_bones: con = bone_p.constraints.new('COPY_LOCATION') con.name = "slide." + str(i) con.target = self.obj con.subtarget = rb con.head_tail = 1.0 # 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 - 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 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]) # 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]) # Layers pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers return [main_control] + controls
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. """ bpy.ops.object.mode_set(mode='EDIT') make_rocker = False if self.org_bones[5] is not None: make_rocker = True # Create the bones thigh = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_ik")))) shin = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_ik")))) foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], "_ik"))) foot_ik_target = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[2], "_ik_target")))) pole = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole"))) toe = copy_bone(self.obj, self.org_bones[3], strip_org(self.org_bones[3])) toe_parent = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".parent"))) toe_parent_socket1 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket1"))) toe_parent_socket2 = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket2"))) foot_roll = copy_bone(self.obj, self.org_bones[4], strip_org(insert_before_lr(self.org_bones[2], "_roll"))) roll1 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.01"))) roll2 = copy_bone(self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.02"))) if make_rocker: rocker1 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.01"))) rocker2 = copy_bone(self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.02"))) visfoot = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], "_ik"))) vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole"))) # Get edit bones eb = self.obj.data.edit_bones org_foot_e = eb[self.org_bones[2]] thigh_e = eb[thigh] shin_e = eb[shin] foot_e = eb[foot] foot_ik_target_e = eb[foot_ik_target] pole_e = eb[pole] toe_e = eb[toe] toe_parent_e = eb[toe_parent] toe_parent_socket1_e = eb[toe_parent_socket1] toe_parent_socket2_e = eb[toe_parent_socket2] foot_roll_e = eb[foot_roll] roll1_e = eb[roll1] roll2_e = eb[roll2] if make_rocker: rocker1_e = eb[rocker1] rocker2_e = eb[rocker2] visfoot_e = eb[visfoot] vispole_e = eb[vispole] # Parenting shin_e.parent = thigh_e foot_e.use_connect = False foot_e.parent = None foot_ik_target_e.use_connect = False foot_ik_target_e.parent = roll2_e pole_e.use_connect = False pole_e.parent = foot_e toe_e.parent = toe_parent_e toe_parent_e.use_connect = False toe_parent_e.parent = toe_parent_socket1_e toe_parent_socket1_e.use_connect = False toe_parent_socket1_e.parent = roll1_e toe_parent_socket2_e.use_connect = False toe_parent_socket2_e.parent = eb[self.org_bones[2]] foot_roll_e.use_connect = False foot_roll_e.parent = foot_e roll1_e.use_connect = False roll1_e.parent = foot_e roll2_e.use_connect = False roll2_e.parent = roll1_e visfoot_e.use_connect = False visfoot_e.parent = None vispole_e.use_connect = False vispole_e.parent = None if make_rocker: rocker1_e.use_connect = False rocker2_e.use_connect = False roll1_e.parent = rocker2_e rocker2_e.parent = rocker1_e rocker1_e.parent = foot_e # Misc foot_e.use_local_location = False visfoot_e.hide_select = True vispole_e.hide_select = True # Positioning vec = Vector(toe_e.vector) vec.normalize() foot_e.tail = foot_e.head + (vec * foot_e.length) foot_e.roll = toe_e.roll v1 = shin_e.tail - thigh_e.head if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: v2 = v1.cross(shin_e.x_axis) if (v2 * shin_e.z_axis) > 0.0: v2 *= -1.0 else: v2 = v1.cross(shin_e.z_axis) if (v2 * shin_e.x_axis) < 0.0: v2 *= -1.0 v2.normalize() v2 *= v1.length if '-' in self.primary_rotation_axis: v2 *= -1 pole_e.head = shin_e.head + v2 pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) pole_e.roll = 0.0 flip_bone(self.obj, toe_parent_socket1) flip_bone(self.obj, toe_parent_socket2) toe_parent_socket1_e.head = Vector(org_foot_e.tail) toe_parent_socket2_e.head = Vector(org_foot_e.tail) toe_parent_socket1_e.tail = Vector(org_foot_e.tail) + (Vector((0, 0, 1)) * foot_e.length / 2) toe_parent_socket2_e.tail = Vector(org_foot_e.tail) + (Vector((0, 0, 1)) * foot_e.length / 3) toe_parent_socket2_e.roll = toe_parent_socket1_e.roll tail = Vector(roll1_e.tail) roll1_e.tail = Vector(org_foot_e.tail) roll1_e.tail = Vector(org_foot_e.tail) roll1_e.head = tail roll2_e.head = Vector(org_foot_e.tail) foot_roll_e.head = Vector(org_foot_e.tail) put_bone(self.obj, foot_roll, roll1_e.head) foot_roll_e.length /= 2 roll_axis = roll1_e.vector.cross(org_foot_e.vector) align_x_axis(self.obj, roll1, roll_axis) align_x_axis(self.obj, roll2, roll_axis) foot_roll_e.roll = roll2_e.roll visfoot_e.tail = visfoot_e.head + Vector((0, 0, v1.length / 32)) vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) if make_rocker: d = toe_e.y_axis.dot(rocker1_e.x_axis) if d >= 0.0: flip_bone(self.obj, rocker2) else: flip_bone(self.obj, rocker1) # Weird alignment issues. Fix. toe_parent_e.head = Vector(org_foot_e.head) toe_parent_e.tail = Vector(org_foot_e.tail) toe_parent_e.roll = org_foot_e.roll foot_e.head = Vector(org_foot_e.head) foot_ik_target_e.head = Vector(org_foot_e.head) foot_ik_target_e.tail = Vector(org_foot_e.tail) # Determine the pole offset value plane = (shin_e.tail - thigh_e.head).normalized() vec1 = thigh_e.x_axis.normalized() vec2 = (pole_e.head - thigh_e.head).normalized() pole_offset = angle_on_plane(plane, vec1, vec2) # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # thigh_p = pb[thigh] # UNUSED shin_p = pb[shin] foot_p = pb[foot] pole_p = pb[pole] foot_roll_p = pb[foot_roll] roll1_p = pb[roll1] roll2_p = pb[roll2] if make_rocker: rocker1_p = pb[rocker1] rocker2_p = pb[rocker2] toe_p = pb[toe] toe_parent_p = pb[toe_parent] toe_parent_socket1_p = pb[toe_parent_socket1] visfoot_p = pb[visfoot] vispole_p = pb[vispole] # Set the knee to only bend on the primary axis. if 'X' in self.primary_rotation_axis: shin_p.lock_ik_y = True shin_p.lock_ik_z = True elif 'Y' in self.primary_rotation_axis: shin_p.lock_ik_x = True shin_p.lock_ik_z = True else: shin_p.lock_ik_x = True shin_p.lock_ik_y = True # Foot roll control only rotates on x-axis, or x and y if rocker. foot_roll_p.rotation_mode = 'XYZ' if make_rocker: foot_roll_p.lock_rotation = False, False, True else: foot_roll_p.lock_rotation = False, True, True foot_roll_p.lock_location = True, True, True foot_roll_p.lock_scale = True, True, True # roll and rocker bones set to euler rotation roll1_p.rotation_mode = 'XYZ' roll2_p.rotation_mode = 'XYZ' if make_rocker: rocker1_p.rotation_mode = 'XYZ' rocker2_p.rotation_mode = 'XYZ' # Pole target only translates pole_p.lock_location = False, False, False pole_p.lock_rotation = True, True, True pole_p.lock_rotation_w = True pole_p.lock_scale = True, True, True # Set up custom properties if self.switch == True: prop = rna_idprop_ui_prop_get(foot_p, "ikfk_switch", create=True) foot_p["ikfk_switch"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Bend direction hint if self.bend_hint: con = shin_p.constraints.new('LIMIT_ROTATION') con.name = "bend_hint" con.owner_space = 'LOCAL' if self.primary_rotation_axis == 'X': con.use_limit_x = True con.min_x = pi / 10 con.max_x = pi / 10 elif self.primary_rotation_axis == '-X': con.use_limit_x = True con.min_x = -pi / 10 con.max_x = -pi / 10 elif self.primary_rotation_axis == 'Y': con.use_limit_y = True con.min_y = pi / 10 con.max_y = pi / 10 elif self.primary_rotation_axis == '-Y': con.use_limit_y = True con.min_y = -pi / 10 con.max_y = -pi / 10 elif self.primary_rotation_axis == 'Z': con.use_limit_z = True con.min_z = pi / 10 con.max_z = pi / 10 elif self.primary_rotation_axis == '-Z': con.use_limit_z = True con.min_z = -pi / 10 con.max_z = -pi / 10 # IK Constraint con = shin_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = foot_ik_target con.pole_target = self.obj con.pole_subtarget = pole con.pole_angle = pole_offset con.chain_count = 2 # toe_parent constraint con = toe_parent_socket1_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = toe_parent_socket2 con = toe_parent_socket1_p.constraints.new('COPY_SCALE') con.name = "copy_scale" con.target = self.obj con.subtarget = toe_parent_socket2 con = toe_parent_socket1_p.constraints.new('COPY_TRANSFORMS') # drive with IK switch con.name = "fk" con.target = self.obj con.subtarget = toe_parent_socket2 fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 # Foot roll drivers fcurve = roll1_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "min(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[0]' fcurve = roll2_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[0]' if make_rocker: fcurve = rocker1_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,-var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]' fcurve = rocker2_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id() + '.rotation_euler[1]' # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = thigh if self.switch == True: # IK/FK switch driver fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = shin if self.switch == True: # IK/FK switch driver fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = foot_ik_target if self.switch == True: # IK/FK switch driver fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[3]].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = toe # VIS foot constraints con = visfoot_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[2] con = visfoot_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = foot con.volume = 'NO_VOLUME' con.rest_length = visfoot_p.length # VIS pole constraints con = vispole_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[1] con = vispole_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = pole con.volume = 'NO_VOLUME' con.rest_length = vispole_p.length # Set layers if specified if self.layers: foot_p.bone.layers = self.layers pole_p.bone.layers = self.layers foot_roll_p.bone.layers = self.layers visfoot_p.bone.layers = self.layers vispole_p.bone.layers = self.layers toe_p.bone.layers = [(i[0] or i[1]) for i in zip(toe_p.bone.layers, self.layers)] # Both FK and IK layers # Create widgets create_line_widget(self.obj, vispole) create_line_widget(self.obj, visfoot) create_sphere_widget(self.obj, pole) create_circle_widget(self.obj, toe, radius=0.7, head_tail=0.5) ob = create_widget(self.obj, foot) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 ob = create_widget(self.obj, foot_roll) if ob != None: verts = [(0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [thigh, shin, foot, pole, foot_roll, foot_ik_target]
def generate(self): ui_script = "" for s, limb in self.sides.items(): side_org_bones, ik_limb, fk_limb = limb (ulimb_ik, ulimb_str, flimb_ik, flimb_str, joint_str, elimb_ik, elimb_str) = ik_limb.generate() ulimb_fk, flimb_fk, elimb_fk = fk_limb.generate() bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones # Foot rig foot_fr = copy_bone( self.obj, self.org_bones[2], pantin_utils.strip_LR_numbers( make_mechanism_name(strip_org(self.org_bones[2]))) + '.fr' + s) foot_tgt = copy_bone( self.obj, self.org_bones[2], pantin_utils.strip_LR_numbers( make_mechanism_name(strip_org(self.org_bones[2]))) + '.tgt' + s) heel_fr = copy_bone( self.obj, self.org_bones[3], pantin_utils.strip_LR_numbers( make_mechanism_name(strip_org(self.org_bones[3]))) + '.fr' + s) toe_fr = copy_bone( self.obj, self.org_bones[4], pantin_utils.strip_LR_numbers( make_mechanism_name(strip_org(self.org_bones[4]))) + '.fr' + s) toe_ik_ctl = copy_bone( self.obj, self.org_bones[4], pantin_utils.strip_LR_numbers(strip_org(self.org_bones[4])) + '.IK' + s) toe_fk_ctl = copy_bone( self.obj, self.org_bones[4], pantin_utils.strip_LR_numbers(strip_org(self.org_bones[4])) + '.FK' + s) toe_pos = copy_bone( self.obj, self.org_bones[4], pantin_utils.strip_LR_numbers( make_mechanism_name(strip_org(self.org_bones[4]))) + '.pos' + s) roll_fr = new_bone(self.obj, "Foot roll" + s) # Position eb[roll_fr].head = (eb[elimb_str].head + Vector( (-1, 0, 0)) * eb[elimb_str].length * 2) eb[roll_fr].tail = (eb[elimb_str].head + Vector( (-1, 0, 1)) * eb[elimb_str].length * 2) eb[roll_fr].layers = eb[elimb_ik].layers # IK foot horizontal and starting at heel eb[elimb_ik].head = eb[heel_fr].tail eb[elimb_ik].tail.z = eb[heel_fr].tail.z align_bone_x_axis(self.obj, elimb_ik, Vector((0, 0, 1))) align_bone_x_axis(self.obj, roll_fr, Vector((-1, 0, 0))) align_bone_x_axis(self.obj, foot_fr, Vector((-1, 0, 0))) align_bone_x_axis(self.obj, heel_fr, Vector((-1, 0, 0))) align_bone_x_axis(self.obj, toe_fr, Vector((-1, 0, 0))) # Parenting eb[foot_fr].parent = None eb[heel_fr].parent = None eb[toe_fr].parent = None flip_bone(self.obj, foot_fr) flip_bone(self.obj, heel_fr) flip_bone(self.obj, toe_fr) eb[foot_fr].use_connect = True eb[foot_fr].parent = eb[toe_fr] eb[foot_tgt].use_connect = True eb[foot_tgt].parent = eb[foot_fr] eb[toe_fr].use_connect = False eb[toe_fr].parent = eb[heel_fr] eb[toe_ik_ctl].use_connect = True eb[toe_ik_ctl].parent = eb[toe_fr] eb[toe_fk_ctl].use_connect = True eb[toe_fk_ctl].parent = eb[elimb_fk] eb[toe_pos].use_connect = False eb[toe_pos].parent = eb[elimb_str] eb[heel_fr].use_connect = False eb[heel_fr].parent = eb[elimb_ik] eb[roll_fr].use_connect = False eb[roll_fr].parent = eb[elimb_ik] if self.params.do_stretch: eb[elimb_str].use_connect = False # Def bones if s == '.L': Z_index = -self.params.Z_index else: Z_index = self.params.Z_index side_org_bones = side_org_bones[:3] + side_org_bones[ -1:] # Ignore heel for i, b in enumerate(side_org_bones): def_bone_name = pantin_utils.strip_LR_numbers(strip_org(b)) def_bone = pantin_utils.create_deformation( self.obj, b, member_index=Z_index, bone_index=i, new_name=def_bone_name + s) # Set layers if specified active_layer = pantin_utils.layers_to_index(eb[ulimb_ik].layers) right_offset = self.params.right_offset if self.params.duplicate_lr else 0 if s == '.R': for b in (ulimb_ik, joint_str, elimb_ik, roll_fr, toe_ik_ctl): eb[b].layers = get_layers(active_layer + right_offset) for b in (ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl): eb[b].layers = get_layers(active_layer + self.params.fk_offset + right_offset) elif s == '.L': for b in (ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl): eb[b].layers = get_layers(active_layer + self.params.fk_offset) bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # Bone settings pb[roll_fr].rotation_mode = 'XZY' pb[roll_fr].lock_location = [True] * 3 pb[roll_fr].lock_rotation = [True, True, False] pb[roll_fr].lock_scale = [True] * 3 pb[foot_fr].rotation_mode = 'XZY' # Widgets # IK global_scale = self.obj.dimensions[2] member_factor = 0.06 if s == '.R': side_factor = 1.2 else: side_factor = 1.0 widget_size = global_scale * member_factor * side_factor pantin_utils.create_aligned_circle_widget(self.obj, ulimb_ik, number_verts=3, radius=widget_size) pantin_utils.create_aligned_circle_widget(self.obj, joint_str, radius=widget_size) # FK widget_size = 0.5 for bone in (ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl): pantin_utils.create_capsule_widget(self.obj, bone, length=widget_size, width=widget_size * 0.2, head_tail=0.5, horizontal=False, overshoot=True) # Default follow value pb[ulimb_fk]["follow"] = 0.0 # Foot WGT down = pb[heel_fr].head.z left = pb[heel_fr].head.x - (side_factor - 1) * pb[heel_fr].length right = pb[foot_fr].head.x up = pb[foot_fr].tail.z + (side_factor - 1) * pb[heel_fr].length foot_verts = ((left, down), (left, up), (right, down + (up - down) * 2 / 3), (right, down)) pantin_utils.create_aligned_polygon_widget(self.obj, elimb_ik, foot_verts) # Toe WGT left = right right = pb[toe_fr].head.x + (side_factor - 1) * pb[heel_fr].length up = down + (up - down) * 2 / 3 # TODO investigate why moving an edit bones # screws up widgets' matrices (foot_ik has moved) toe_verts = ((left, down), (left, up), (right, down + (up - down) * 2 / 3), (right, down)) pantin_utils.create_aligned_polygon_widget(self.obj, toe_ik_ctl, toe_verts) pantin_utils.create_aligned_crescent_widget(self.obj, roll_fr, radius=side_factor * pb[roll_fr].length / 2) # Constraints foot_fr_p = pb[foot_fr] heel_fr_p = pb[heel_fr] toe_fr_p = pb[toe_fr] roll_fr_p = pb[roll_fr] hor_vector = Vector((1, 0, 0)) foot_rotation = (foot_fr_p.vector.rotation_difference( hor_vector).to_euler('XZY')) toe_rotation = toe_fr_p.vector.rotation_difference( hor_vector).to_euler('XZY') foot_vertical_rot = foot_rotation[1] - pi / 2 toe_vertical_rot = toe_rotation[1] - pi / 2 con = foot_fr_p.constraints.new('TRANSFORM') con.name = "roll" con.target = self.obj con.subtarget = roll_fr con.map_from = 'ROTATION' con.map_to = 'ROTATION' con.from_min_z_rot = radians(0.0) con.from_max_z_rot = radians(60.0) con.to_min_z_rot = radians(0.0) con.to_max_z_rot = foot_vertical_rot con.target_space = 'LOCAL' con.owner_space = 'LOCAL' con = heel_fr_p.constraints.new('TRANSFORM') con.name = "roll" con.target = self.obj con.subtarget = roll_fr con.map_from = 'ROTATION' con.map_to = 'ROTATION' con.from_min_z_rot = radians(-60.0) con.from_max_z_rot = 0.0 con.to_min_z_rot = radians(-60.0) con.to_max_z_rot = 0.0 con.target_space = 'LOCAL' con.owner_space = 'LOCAL' con = toe_fr_p.constraints.new('TRANSFORM') con.name = "roll" con.target = self.obj con.subtarget = roll_fr con.map_from = 'ROTATION' con.map_to = 'ROTATION' con.from_min_z_rot = radians(60.0) con.from_max_z_rot = radians(90.0) con.to_min_z_rot = radians(0.0) con.to_max_z_rot = toe_vertical_rot con.target_space = 'LOCAL' con.owner_space = 'LOCAL' # Compensate foot for heel con = foot_fr_p.constraints.new('TRANSFORM') con.name = "roll_compensate" con.target = self.obj con.subtarget = roll_fr con.map_from = 'ROTATION' con.map_to = 'ROTATION' con.from_min_z_rot = radians(60.0) con.from_max_z_rot = radians(90.0) con.to_min_z_rot = radians(0.0) con.to_max_z_rot = -toe_vertical_rot con.target_space = 'LOCAL' con.owner_space = 'LOCAL' # Roll limits con = roll_fr_p.constraints.new('LIMIT_ROTATION') con.name = "limit rotation" con.use_limit_z = True con.min_z = radians(-60.0) con.max_z = radians(90.0) con.owner_space = 'LOCAL' # Edit IK to follow the rolled foot instead of ik control con = pb[flimb_ik].constraints["ik"] con.subtarget = foot_tgt con = pb[elimb_str].constraints["copy rotation"] con.subtarget = foot_tgt con = pb[toe_pos].constraints.new('COPY_ROTATION') con.name = "copy rotation" con.target = self.obj con.subtarget = toe_ik_ctl #con.invert_x = True con.target_space = 'POSE' con.owner_space = 'POSE' # Optional stretch to the foot if self.params.do_stretch: con1 = pb[flimb_str].constraints.new('STRETCH_TO') con1.name = "stretch to foot" con1.target = self.obj con1.subtarget = foot_tgt con1.volume = 'NO_VOLUME' con1.rest_length = pb[flimb_str].length con1.keep_axis = 'PLANE_Z' con2 = pb[elimb_str].constraints.new('COPY_LOCATION') con2.name = "copy foot location" con2.target = self.obj con2.subtarget = foot_tgt con2.target_space = 'POSE' con2.owner_space = 'POSE' # Set up custom properties prop = rna_idprop_ui_prop_get(pb[elimb_ik], "foot_stretch", create=True) pb[elimb_ik]["foot_stretch"] = 1 # int(self.foot_stretch) prop["soft_min"] = 0 prop["soft_max"] = 1 prop["min"] = 0 prop["max"] = 1 # Drivers for c in (con1, con2): driver = self.obj.driver_add(c.path_from_id("influence")) driver.driver.expression = 'foot_stretch' var_fs = driver.driver.variables.new() var_fs.type = 'SINGLE_PROP' var_fs.name = 'foot_stretch' var_fs.targets[0].id_type = 'OBJECT' var_fs.targets[0].id = self.obj var_fs.targets[0].data_path = ( pb[elimb_ik].path_from_id() + '["foot_stretch"]') for org, ctrl in zip( side_org_bones, [ulimb_str, flimb_str, elimb_str, toe_ik_ctl]): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_ik" con.target = self.obj con.subtarget = ctrl for org, ctrl in zip(side_org_bones, [ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl]): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_fk" con.target = self.obj con.subtarget = ctrl # Drivers driver = self.obj.driver_add(con.path_from_id("influence")) driver.driver.expression = 'fk' var_fk = driver.driver.variables.new() var_fk.type = 'SINGLE_PROP' var_fk.name = 'fk' var_fk.targets[0].id_type = 'OBJECT' var_fk.targets[0].id = self.obj var_fk.targets[ 0].data_path = 'pose.bones["{}"]["IK_FK"]'.format(elimb_ik) ui_script += UI_PANTIN_LIMB_SCRIPT % ( '"{}", "{}", "{}", "{}"'.format( ulimb_ik, joint_str, elimb_ik, toe_ik_ctl), '"{}", "{}", "{}", "{}"'.format( ulimb_fk, flimb_fk, elimb_fk, toe_fk_ctl)) if self.params.do_stretch: ui_script += """ layout.prop(pose_bones[ik_leg[2]], \ '["foot_stretch"]', \ text="Foot stretch (" + ik_leg[2] + ")", slider=True) """ return { 'script': [ui_script], 'imports': UI_IMPORTS, 'utilities': PANTIN_UTILS + [UTILITIES_PANTIN_LIMBS], 'register': PANTIN_REGISTER + REGISTER_PANTIN_LIMBS, 'register_props': REGISTER_PANTIN_PROPS, }
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. """ bpy.ops.object.mode_set(mode='EDIT') make_rocker = False if self.org_bones[5] is not None: make_rocker = True # Create the bones thigh = copy_bone( self.obj, self.org_bones[0], make_mechanism_name( strip_org(insert_before_lr(self.org_bones[0], "_ik")))) shin = copy_bone( self.obj, self.org_bones[1], make_mechanism_name( strip_org(insert_before_lr(self.org_bones[1], "_ik")))) foot = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], "_ik"))) foot_ik_target = copy_bone( self.obj, self.org_bones[2], make_mechanism_name( strip_org(insert_before_lr(self.org_bones[2], "_ik_target")))) pole = copy_bone( self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole"))) toe = copy_bone(self.obj, self.org_bones[3], strip_org(self.org_bones[3])) toe_parent = copy_bone( self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".parent"))) toe_parent_socket1 = copy_bone( self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket1"))) toe_parent_socket2 = copy_bone( self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[3] + ".socket2"))) foot_roll = copy_bone( self.obj, self.org_bones[4], strip_org(insert_before_lr(self.org_bones[2], "_roll"))) roll1 = copy_bone( self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.01"))) roll2 = copy_bone( self.obj, self.org_bones[4], make_mechanism_name(strip_org(self.org_bones[2] + ".roll.02"))) if make_rocker: rocker1 = copy_bone( self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.01"))) rocker2 = copy_bone( self.obj, self.org_bones[5], make_mechanism_name(strip_org(self.org_bones[2] + ".rocker.02"))) visfoot = copy_bone( self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], "_ik"))) vispole = copy_bone( self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole"))) # Get edit bones eb = self.obj.data.edit_bones org_foot_e = eb[self.org_bones[2]] thigh_e = eb[thigh] shin_e = eb[shin] foot_e = eb[foot] foot_ik_target_e = eb[foot_ik_target] pole_e = eb[pole] toe_e = eb[toe] toe_parent_e = eb[toe_parent] toe_parent_socket1_e = eb[toe_parent_socket1] toe_parent_socket2_e = eb[toe_parent_socket2] foot_roll_e = eb[foot_roll] roll1_e = eb[roll1] roll2_e = eb[roll2] if make_rocker: rocker1_e = eb[rocker1] rocker2_e = eb[rocker2] visfoot_e = eb[visfoot] vispole_e = eb[vispole] # Parenting shin_e.parent = thigh_e foot_e.use_connect = False foot_e.parent = None foot_ik_target_e.use_connect = False foot_ik_target_e.parent = roll2_e pole_e.use_connect = False pole_e.parent = foot_e toe_e.parent = toe_parent_e toe_parent_e.use_connect = False toe_parent_e.parent = toe_parent_socket1_e toe_parent_socket1_e.use_connect = False toe_parent_socket1_e.parent = roll1_e toe_parent_socket2_e.use_connect = False toe_parent_socket2_e.parent = eb[self.org_bones[2]] foot_roll_e.use_connect = False foot_roll_e.parent = foot_e roll1_e.use_connect = False roll1_e.parent = foot_e roll2_e.use_connect = False roll2_e.parent = roll1_e visfoot_e.use_connect = False visfoot_e.parent = None vispole_e.use_connect = False vispole_e.parent = None if make_rocker: rocker1_e.use_connect = False rocker2_e.use_connect = False roll1_e.parent = rocker2_e rocker2_e.parent = rocker1_e rocker1_e.parent = foot_e # Misc foot_e.use_local_location = False visfoot_e.hide_select = True vispole_e.hide_select = True # Positioning vec = Vector(toe_e.vector) vec.normalize() foot_e.tail = foot_e.head + (vec * foot_e.length) foot_e.roll = toe_e.roll v1 = shin_e.tail - thigh_e.head if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: v2 = v1.cross(shin_e.x_axis) if (v2 * shin_e.z_axis) > 0.0: v2 *= -1.0 else: v2 = v1.cross(shin_e.z_axis) if (v2 * shin_e.x_axis) < 0.0: v2 *= -1.0 v2.normalize() v2 *= v1.length if '-' in self.primary_rotation_axis: v2 *= -1 pole_e.head = shin_e.head + v2 pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) pole_e.roll = 0.0 flip_bone(self.obj, toe_parent_socket1) flip_bone(self.obj, toe_parent_socket2) toe_parent_socket1_e.head = Vector(org_foot_e.tail) toe_parent_socket2_e.head = Vector(org_foot_e.tail) toe_parent_socket1_e.tail = Vector(org_foot_e.tail) + (Vector( (0, 0, 1)) * foot_e.length / 2) toe_parent_socket2_e.tail = Vector(org_foot_e.tail) + (Vector( (0, 0, 1)) * foot_e.length / 3) toe_parent_socket2_e.roll = toe_parent_socket1_e.roll tail = Vector(roll1_e.tail) roll1_e.tail = Vector(org_foot_e.tail) roll1_e.tail = Vector(org_foot_e.tail) roll1_e.head = tail roll2_e.head = Vector(org_foot_e.tail) foot_roll_e.head = Vector(org_foot_e.tail) put_bone(self.obj, foot_roll, roll1_e.head) foot_roll_e.length /= 2 roll_axis = roll1_e.vector.cross(org_foot_e.vector) align_x_axis(self.obj, roll1, roll_axis) align_x_axis(self.obj, roll2, roll_axis) foot_roll_e.roll = roll2_e.roll visfoot_e.tail = visfoot_e.head + Vector((0, 0, v1.length / 32)) vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) if make_rocker: d = toe_e.y_axis.dot(rocker1_e.x_axis) if d >= 0.0: flip_bone(self.obj, rocker2) else: flip_bone(self.obj, rocker1) # Weird alignment issues. Fix. toe_parent_e.head = Vector(org_foot_e.head) toe_parent_e.tail = Vector(org_foot_e.tail) toe_parent_e.roll = org_foot_e.roll foot_e.head = Vector(org_foot_e.head) foot_ik_target_e.head = Vector(org_foot_e.head) foot_ik_target_e.tail = Vector(org_foot_e.tail) # Determine the pole offset value plane = (shin_e.tail - thigh_e.head).normalized() vec1 = thigh_e.x_axis.normalized() vec2 = (pole_e.head - thigh_e.head).normalized() pole_offset = angle_on_plane(plane, vec1, vec2) # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # thigh_p = pb[thigh] # UNUSED shin_p = pb[shin] foot_p = pb[foot] pole_p = pb[pole] foot_roll_p = pb[foot_roll] roll1_p = pb[roll1] roll2_p = pb[roll2] if make_rocker: rocker1_p = pb[rocker1] rocker2_p = pb[rocker2] toe_p = pb[toe] toe_parent_p = pb[toe_parent] toe_parent_socket1_p = pb[toe_parent_socket1] visfoot_p = pb[visfoot] vispole_p = pb[vispole] # Set the knee to only bend on the primary axis. if 'X' in self.primary_rotation_axis: shin_p.lock_ik_y = True shin_p.lock_ik_z = True elif 'Y' in self.primary_rotation_axis: shin_p.lock_ik_x = True shin_p.lock_ik_z = True else: shin_p.lock_ik_x = True shin_p.lock_ik_y = True # Foot roll control only rotates on x-axis, or x and y if rocker. foot_roll_p.rotation_mode = 'XYZ' if make_rocker: foot_roll_p.lock_rotation = False, False, True else: foot_roll_p.lock_rotation = False, True, True foot_roll_p.lock_location = True, True, True foot_roll_p.lock_scale = True, True, True # roll and rocker bones set to euler rotation roll1_p.rotation_mode = 'XYZ' roll2_p.rotation_mode = 'XYZ' if make_rocker: rocker1_p.rotation_mode = 'XYZ' rocker2_p.rotation_mode = 'XYZ' # Pole target only translates pole_p.lock_location = False, False, False pole_p.lock_rotation = True, True, True pole_p.lock_rotation_w = True pole_p.lock_scale = True, True, True # Set up custom properties if self.switch == True: prop = rna_idprop_ui_prop_get(foot_p, "ikfk_switch", create=True) foot_p["ikfk_switch"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Bend direction hint if self.bend_hint: con = shin_p.constraints.new('LIMIT_ROTATION') con.name = "bend_hint" con.owner_space = 'LOCAL' if self.primary_rotation_axis == 'X': con.use_limit_x = True con.min_x = pi / 10 con.max_x = pi / 10 elif self.primary_rotation_axis == '-X': con.use_limit_x = True con.min_x = -pi / 10 con.max_x = -pi / 10 elif self.primary_rotation_axis == 'Y': con.use_limit_y = True con.min_y = pi / 10 con.max_y = pi / 10 elif self.primary_rotation_axis == '-Y': con.use_limit_y = True con.min_y = -pi / 10 con.max_y = -pi / 10 elif self.primary_rotation_axis == 'Z': con.use_limit_z = True con.min_z = pi / 10 con.max_z = pi / 10 elif self.primary_rotation_axis == '-Z': con.use_limit_z = True con.min_z = -pi / 10 con.max_z = -pi / 10 # IK Constraint con = shin_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = foot_ik_target con.pole_target = self.obj con.pole_subtarget = pole con.pole_angle = pole_offset con.chain_count = 2 # toe_parent constraint con = toe_parent_socket1_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = toe_parent_socket2 con = toe_parent_socket1_p.constraints.new('COPY_SCALE') con.name = "copy_scale" con.target = self.obj con.subtarget = toe_parent_socket2 con = toe_parent_socket1_p.constraints.new( 'COPY_TRANSFORMS') # drive with IK switch con.name = "fk" con.target = self.obj con.subtarget = toe_parent_socket2 fcurve = con.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 = foot_p.path_from_id() + '["ikfk_switch"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 # Foot roll drivers fcurve = roll1_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "min(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[0]' fcurve = roll2_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[0]' if make_rocker: fcurve = rocker1_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,-var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[1]' fcurve = rocker2_p.driver_add("rotation_euler", 0) driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' driver.expression = "max(0,var)" var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = foot_roll_p.path_from_id( ) + '.rotation_euler[1]' # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = thigh if self.switch == True: # IK/FK switch driver fcurve = con.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 = foot_p.path_from_id( ) + '["ikfk_switch"]' con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = shin if self.switch == True: # IK/FK switch driver fcurve = con.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 = foot_p.path_from_id( ) + '["ikfk_switch"]' con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = foot_ik_target if self.switch == True: # IK/FK switch driver fcurve = con.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 = foot_p.path_from_id( ) + '["ikfk_switch"]' con = pb[self.org_bones[3]].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = toe # VIS foot constraints con = visfoot_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[2] con = visfoot_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = foot con.volume = 'NO_VOLUME' con.rest_length = visfoot_p.length # VIS pole constraints con = vispole_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[1] con = vispole_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = pole con.volume = 'NO_VOLUME' con.rest_length = vispole_p.length # Set layers if specified if self.layers: foot_p.bone.layers = self.layers pole_p.bone.layers = self.layers foot_roll_p.bone.layers = self.layers visfoot_p.bone.layers = self.layers vispole_p.bone.layers = self.layers toe_p.bone.layers = [ (i[0] or i[1]) for i in zip(toe_p.bone.layers, self.layers) ] # Both FK and IK layers # Create widgets create_line_widget(self.obj, vispole) create_line_widget(self.obj, visfoot) create_sphere_widget(self.obj, pole) create_circle_widget(self.obj, toe, radius=0.7, head_tail=0.5) ob = create_widget(self.obj, foot) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 ob = create_widget(self.obj, foot_roll) if ob != None: verts = [ (0.3999999761581421, 0.766044557094574, 0.6427875757217407), (0.17668449878692627, 3.823702598992895e-08, 3.2084670920085046e-08), (-0.17668461799621582, 9.874240447516058e-08, 8.285470443070153e-08), (-0.39999961853027344, 0.7660449147224426, 0.6427879333496094), (0.3562471270561218, 0.6159579753875732, 0.5168500542640686), (-0.35624682903289795, 0.6159582138061523, 0.5168502926826477), (0.20492683351039886, 0.09688037633895874, 0.0812922865152359), (-0.20492687821388245, 0.0968804731965065, 0.08129236847162247) ] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (1, 6), (4, 6), (2, 7), (5, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [thigh, shin, foot, pole, foot_roll, foot_ik_target]