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_rig(context, metarig): """ Generates a rig from a metarig. """ t = Timer() # Random string with time appended so that # different rigs don't collide id's rig_id = random_id(16) # Initial configuration # mode_orig = context.mode # UNUSED rest_backup = metarig.data.pose_position metarig.data.pose_position = 'REST' bpy.ops.object.mode_set(mode='OBJECT') scene = context.scene #------------------------------------------ # Create/find the rig object and set it up # Check if the generated rig already exists, so we can # regenerate in the same object. If not, create a new # object to generate the rig in. print("Fetch rig.") try: name = metarig["rig_object_name"] except KeyError: name = "rig" try: obj = scene.objects[name] except KeyError: obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) obj.draw_type = 'WIRE' scene.objects.link(obj) obj.data.pose_position = 'POSE' # Get rid of anim data in case the rig already existed print("Clear rig animation data.") obj.animation_data_clear() # Select generated rig object metarig.select = False obj.select = True scene.objects.active = obj # Remove all bones from the generated rig armature. bpy.ops.object.mode_set(mode='EDIT') for bone in obj.data.edit_bones: obj.data.edit_bones.remove(bone) bpy.ops.object.mode_set(mode='OBJECT') # Create temporary duplicates for merging temp_rig_1 = metarig.copy() temp_rig_1.data = metarig.data.copy() scene.objects.link(temp_rig_1) temp_rig_2 = metarig.copy() temp_rig_2.data = obj.data scene.objects.link(temp_rig_2) # Select the temp rigs for merging for objt in scene.objects: objt.select = False # deselect all objects temp_rig_1.select = True temp_rig_2.select = True scene.objects.active = temp_rig_2 # Merge the temporary rigs bpy.ops.object.join() # Delete the second temp rig bpy.ops.object.delete() # Select the generated rig for objt in scene.objects: objt.select = False # deselect all objects obj.select = True scene.objects.active = obj # Copy over bone properties for bone in metarig.data.bones: bone_gen = obj.data.bones[bone.name] # B-bone stuff bone_gen.bbone_segments = bone.bbone_segments bone_gen.bbone_in = bone.bbone_in bone_gen.bbone_out = bone.bbone_out # Copy over the pose_bone properties for bone in metarig.pose.bones: bone_gen = obj.pose.bones[bone.name] # Rotation mode and transform locks bone_gen.rotation_mode = bone.rotation_mode bone_gen.lock_rotation = tuple(bone.lock_rotation) bone_gen.lock_rotation_w = bone.lock_rotation_w bone_gen.lock_rotations_4d = bone.lock_rotations_4d bone_gen.lock_location = tuple(bone.lock_location) bone_gen.lock_scale = tuple(bone.lock_scale) # Custom properties for prop in bone.keys(): bone_gen[prop] = bone[prop] # Constraints for con1 in bone.constraints: con2 = bone_gen.constraints.new(type=con1.type) copy_attributes(con1, con2) # Set metarig target to rig target if "target" in dir(con2): if con2.target == metarig: con2.target = obj # Copy drivers if metarig.animation_data: for d1 in metarig.animation_data.drivers: d2 = obj.driver_add(d1.data_path) copy_attributes(d1, d2) copy_attributes(d1.driver, d2.driver) # Remove default modifiers, variables, etc. for m in d2.modifiers: d2.modifiers.remove(m) for v in d2.driver.variables: d2.driver.variables.remove(v) # Copy modifiers for m1 in d1.modifiers: m2 = d2.modifiers.new(type=m1.type) copy_attributes(m1, m2) # Copy variables for v1 in d1.driver.variables: v2 = d2.driver.variables.new() copy_attributes(v1, v2) for i in range(len(v1.targets)): copy_attributes(v1.targets[i], v2.targets[i]) # Switch metarig targets to rig targets if v2.targets[i].id == metarig: v2.targets[i].id = obj # Mark targets that may need to be altered after rig generation tar = v2.targets[i] # If a custom property if v2.type == 'SINGLE_PROP' \ and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path): tar.data_path = "RIGIFY-" + tar.data_path # Copy key frames for i in range(len(d1.keyframe_points)): d2.keyframe_points.add() k1 = d1.keyframe_points[i] k2 = d2.keyframe_points[i] copy_attributes(k1, k2) t.tick("Duplicate rig: ") #---------------------------------- # Make a list of the original bones so we can keep track of them. original_bones = [bone.name for bone in obj.data.bones] # Add the ORG_PREFIX to the original bones. bpy.ops.object.mode_set(mode='OBJECT') for i in range(0, len(original_bones)): obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i]) original_bones[i] = make_original_name(original_bones[i]) # Create a sorted list of the original bones, sorted in the order we're # going to traverse them for rigging. # (root-most -> leaf-most, alphabetical) bones_sorted = [] for name in original_bones: bones_sorted += [name] bones_sorted.sort() # first sort by names bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive)) # then parents before children t.tick("Make list of org bones: ") #---------------------------------- # Create the root bone. bpy.ops.object.mode_set(mode='EDIT') root_bone = new_bone(obj, ROOT_NAME) obj.data.edit_bones[root_bone].head = (0, 0, 0) obj.data.edit_bones[root_bone].tail = (0, 1, 0) obj.data.edit_bones[root_bone].roll = 0 bpy.ops.object.mode_set(mode='OBJECT') obj.data.bones[root_bone].layers = ROOT_LAYER # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) obj.data["rig_id"] = rig_id t.tick("Create root bone: ") #---------------------------------- try: # Collect/initialize all the rigs. rigs = [] deformation_rigs = [] for bone in bones_sorted: bpy.ops.object.mode_set(mode='EDIT') rigs += get_bone_rigs(obj, bone) t.tick("Initialize rigs: ") # Generate all the rigs. ui_scripts = [] for rig in rigs: # Go into editmode in the rig armature bpy.ops.object.mode_set(mode='OBJECT') context.scene.objects.active = obj obj.select = True bpy.ops.object.mode_set(mode='EDIT') scripts = rig.generate() if scripts != None: ui_scripts += [scripts[0]] t.tick("Generate rigs: ") except Exception as e: # Cleanup if something goes wrong print("Rigify: failed to generate rig.") metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' bpy.ops.object.mode_set(mode='OBJECT') # Continue the exception raise e #---------------------------------- bpy.ops.object.mode_set(mode='OBJECT') # Get a list of all the bones in the armature bones = [bone.name for bone in obj.data.bones] # Parent any free-floating bones to the root. bpy.ops.object.mode_set(mode='EDIT') for bone in bones: if obj.data.edit_bones[bone].parent is None: obj.data.edit_bones[bone].use_connect = False obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone] bpy.ops.object.mode_set(mode='OBJECT') # Every bone that has a name starting with "DEF-" make deforming. All the # others make non-deforming. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].use_deform = True else: obj.data.bones[bone].use_deform = False # Alter marked driver targets for d in obj.animation_data.drivers: for v in d.driver.variables: for tar in v.targets: if tar.data_path.startswith("RIGIFY-"): temp, bone, prop = tuple([x.strip('"]') for x in tar.data_path.split('["')]) if bone in obj.data.bones \ and prop in obj.pose.bones[bone].keys(): tar.data_path = tar.data_path[7:] else: tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop) # Move all the original bones to their layer. for bone in original_bones: obj.data.bones[bone].layers = ORG_LAYER # Move all the bones with names starting with "MCH-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(MCH_PREFIX): obj.data.bones[bone].layers = MCH_LAYER # Move all the bones with names starting with "DEF-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].layers = DEF_LAYER # Create root bone widget create_root_widget(obj, "root") # Assign shapes to bones # Object's with name WGT-<bone_name> get used as that bone's shape. for bone in bones: wgt_name = (WGT_PREFIX + obj.data.bones[bone].name)[:21] # Object names are limited to 21 characters... arg if wgt_name in context.scene.objects: # Weird temp thing because it won't let me index by object name for ob in context.scene.objects: if ob.name == wgt_name: obj.pose.bones[bone].custom_shape = ob break # This is what it should do: # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name] # Reveal all the layers with control bones on them vis_layers = [False for n in range(0, 32)] for bone in bones: for i in range(0, 32): vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i] for i in range(0, 32): vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) obj.data.layers = vis_layers # Ensure the collection of layer names exists for i in range(1 + len(metarig.data.rigify_layers), 29): metarig.data.rigify_layers.add() # Create list of layer name/row pairs layer_layout = [] for l in metarig.data.rigify_layers: layer_layout += [(l.name, l.row)] # Generate the UI script if "rig_ui.py" in bpy.data.texts: script = bpy.data.texts["rig_ui.py"] script.clear() else: script = bpy.data.texts.new("rig_ui.py") script.write(UI_SLIDERS % rig_id) for s in ui_scripts: script.write("\n " + s.replace("\n", "\n ") + "\n") script.write(layers_ui(vis_layers, layer_layout)) script.write(UI_REGISTER) script.use_module = True # Run UI script exec(script.as_string(), {}) t.tick("The rest: ") #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE'
def generate(self): bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones # Create the control bones ulimb_ik = copy_bone( self.obj, self.org_bones[0], pantin_utils.strip_LR_numbers(strip_org(self.org_bones[0])) + '.IK' + self.side_suffix) flimb_ik = copy_bone( self.obj, self.org_bones[1], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[1])) + '.IK' + self.side_suffix)) elimb_ik = copy_bone( self.obj, self.org_bones[2], pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) + '.IK' + self.side_suffix) ulimb_str = copy_bone( self.obj, self.org_bones[0], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[0])) + ".stretch.ik" + self.side_suffix)) flimb_str = copy_bone( self.obj, self.org_bones[1], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[1])) + ".stretch.ik" + self.side_suffix)) elimb_str = copy_bone( self.obj, self.org_bones[2], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) + ".stretch.ik" + self.side_suffix)) joint_str = new_bone( self.obj, pantin_utils.strip_LR_numbers(strip_org(self.org_bones[1])) + '.IK' + self.side_suffix) eb[joint_str].head = eb[flimb_str].head eb[joint_str].tail = (eb[flimb_str].head + Vector( (0, 0, 1)) * eb[flimb_str].length / 2) align_bone_x_axis(self.obj, joint_str, Vector((-1, 0, 0))) # put_bone(self.obj, joint_str, Vector(eb[flimb_str].head)) # Pelvis follow elimb_ik_root = copy_bone( self.obj, self.org_bones[2], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) + ".ik_root" + self.side_suffix)) elimb_ik_parent = copy_bone( self.obj, self.org_bones[2], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) + ".ik_parent" + self.side_suffix)) elimb_ik_socket = copy_bone( self.obj, self.org_bones[2], make_mechanism_name( pantin_utils.strip_LR_numbers(strip_org(self.org_bones[2])) + ".ik_socket" + self.side_suffix)) # Get edit bones ulimb_ik_e = eb[ulimb_ik] flimb_ik_e = eb[flimb_ik] elimb_ik_e = eb[elimb_ik] ulimb_str_e = eb[ulimb_str] flimb_str_e = eb[flimb_str] elimb_str_e = eb[elimb_str] joint_str_e = eb[joint_str] elimb_ik_root_e = eb[elimb_ik_root] elimb_ik_parent_e = eb[elimb_ik_parent] elimb_ik_socket_e = eb[elimb_ik_socket] # Parenting # Side org chain for b, o_b in zip(self.side_org_bones[1:], self.org_bones[1:]): eb[b].parent = eb[ pantin_utils.strip_LR_numbers(eb[o_b].parent.name) + self.side_suffix] if self.org_parent is not None: ulimb_ik_e.use_connect = False ulimb_ik_e.parent = eb[self.org_parent] flimb_ik_e.use_connect = False flimb_ik_e.parent = ulimb_ik_e elimb_ik_root_e.use_connect = False elimb_ik_root_e.parent = None elimb_ik_parent_e.use_connect = False elimb_ik_parent_e.parent = eb[self.side_org_bones[0]].parent elimb_ik_socket_e.use_connect = False elimb_ik_socket_e.parent = None elimb_ik_e.use_connect = False elimb_ik_e.parent = elimb_ik_socket_e if self.org_parent is not None: ulimb_str_e.use_connect = False ulimb_str_e.parent = eb[self.org_parent] flimb_str_e.use_connect = False flimb_str_e.parent = joint_str_e elimb_str_e.use_connect = True elimb_str_e.parent = flimb_ik_e joint_str_e.use_connect = False joint_str_e.parent = ulimb_ik_e # Layers joint_str_e.layers = elimb_str_e.layers # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones ulimb_ik_p = pb[ulimb_ik] flimb_ik_p = pb[flimb_ik] elimb_ik_p = pb[elimb_ik] ulimb_str_p = pb[ulimb_str] flimb_str_p = pb[flimb_str] elimb_str_p = pb[elimb_str] joint_str_p = pb[joint_str] joint_str_p.lock_location = (False, False, True) joint_str_p.lock_rotation = (True, True, False) joint_str_p.lock_rotation_w = False joint_str_p.lock_scale = (False, False, False) joint_str_p.rotation_mode = 'XZY' # Set up custom properties prop = rna_idprop_ui_prop_get(elimb_ik_p, "pelvis_follow", create=True) elimb_ik_p["pelvis_follow"] = int(self.pelvis_follow) prop["soft_min"] = 0 prop["soft_max"] = 1 prop["min"] = 0 prop["max"] = 1 prop = rna_idprop_ui_prop_get(elimb_ik_p, "IK_FK", create=True) elimb_ik_p["IK_FK"] = 0 prop["soft_min"] = 0 prop["soft_max"] = 1 prop["min"] = 0 prop["max"] = 1 # Constraints # Bend hint, ripped off from Rigify' biped con = flimb_ik_p.constraints.new('LIMIT_ROTATION') con.name = "bend_hint" con.use_limit_z = True con.min_z = radians(45) con.max_z = radians(45) con.owner_space = 'LOCAL' con = flimb_ik_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = elimb_ik con.chain_count = 2 con = ulimb_str_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = ulimb_ik con.target_space = 'LOCAL' con.owner_space = 'LOCAL' con = elimb_str_p.constraints.new('COPY_ROTATION') con.name = "copy rotation" con.target = self.obj con.subtarget = elimb_ik con.target_space = 'POSE' con.owner_space = 'POSE' con = ulimb_str_p.constraints.new('STRETCH_TO') con.name = "stretch to" con.target = self.obj con.subtarget = joint_str con.volume = 'NO_VOLUME' con.rest_length = ulimb_str_p.length con.keep_axis = 'PLANE_Z' con = flimb_str_p.constraints.new('STRETCH_TO') con.name = "stretch to" con.target = self.obj con.subtarget = elimb_str con.volume = 'NO_VOLUME' con.rest_length = flimb_str_p.length con.keep_axis = 'PLANE_Z' # Pelvis follow con = pb[elimb_ik_socket].constraints.new('COPY_TRANSFORMS') con.name = "copy root" con.target = self.obj con.subtarget = elimb_ik_root con = pb[elimb_ik_socket].constraints.new('COPY_TRANSFORMS') con.name = "copy socket" con.target = self.obj con.subtarget = elimb_ik_parent # Drivers driver = self.obj.driver_add(con.path_from_id("influence")) driver.driver.expression = 'pelvis_follow' var = driver.driver.variables.new() var.type = 'SINGLE_PROP' var.name = 'pelvis_follow' var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = (pb[elimb_ik].path_from_id() + '["pelvis_follow"]') # IK Limits ulimb_ik_p.lock_ik_x = True ulimb_ik_p.lock_ik_y = True flimb_ik_p.lock_ik_x = True flimb_ik_p.lock_ik_y = True flimb_ik_p.use_ik_limit_z = True flimb_ik_p.ik_min_z = radians(self.ik_limits[0]) # 0.0 flimb_ik_p.ik_max_z = radians(self.ik_limits[1]) # radians(160.0) # Arm ik angle fix limb_angle = ulimb_ik_p.vector.xz.angle_signed(flimb_ik_p.vector.xz) if self.ik_limits[0] < 0: # folds counterclockwise (arms) # has to be slightly less than the original angle flimb_ik_p.ik_max_z = -limb_angle - .02 else: # has to be slightly more than the original angle flimb_ik_p.ik_min_z = -limb_angle + .02 return [ ulimb_ik, ulimb_str, flimb_ik, flimb_str, joint_str, elimb_ik, elimb_str ]
def gen_control(self): """ Generate the control rig. """ #--------------------------------- # Create the neck and head controls bpy.ops.object.mode_set(mode='EDIT') # Create bones neck_ctrl = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) neck_follow = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[0] + ".follow"))) neck_child = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[0] + ".child"))) head_ctrl = copy_bone(self.obj, self.org_bones[-1], strip_org(self.org_bones[-1])) head_mch = new_bone(self.obj, make_mechanism_name(strip_org(self.org_bones[-1]))) if self.isolate: head_socket1 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket1"))) head_socket2 = copy_bone(self.obj, self.org_bones[-1], make_mechanism_name(strip_org(self.org_bones[-1] + ".socket2"))) # Create neck chain bones neck = [] helpers = [] for name in self.org_bones: neck += [copy_bone(self.obj, name, make_mechanism_name(strip_org(name)))] helpers += [copy_bone(self.obj, neck_child, make_mechanism_name(strip_org(name + ".02")))] # Fetch edit bones eb = self.obj.data.edit_bones neck_ctrl_e = eb[neck_ctrl] neck_follow_e = eb[neck_follow] neck_child_e = eb[neck_child] head_ctrl_e = eb[head_ctrl] head_mch_e = eb[head_mch] if self.isolate: head_socket1_e = eb[head_socket1] head_socket2_e = eb[head_socket2] # Parenting head_ctrl_e.use_connect = False head_ctrl_e.parent = neck_ctrl_e.parent head_mch_e.use_connect = False head_mch_e.parent = head_ctrl_e if self.isolate: head_socket1_e.use_connect = False head_socket1_e.parent = neck_ctrl_e.parent head_socket2_e.use_connect = False head_socket2_e.parent = None head_ctrl_e.parent = head_socket2_e for (name1, name2) in zip(neck, helpers): eb[name1].use_connect = False eb[name1].parent = eb[name2] eb[name2].use_connect = False eb[name2].parent = neck_ctrl_e.parent neck_follow_e.use_connect = False neck_follow_e.parent = neck_ctrl_e.parent neck_child_e.use_connect = False neck_child_e.parent = neck_ctrl_e neck_ctrl_e.parent = neck_follow_e # Position put_bone(self.obj, neck_follow, neck_ctrl_e.head) put_bone(self.obj, neck_child, neck_ctrl_e.head) put_bone(self.obj, head_ctrl, neck_ctrl_e.head) put_bone(self.obj, head_mch, neck_ctrl_e.head) head_mch_e.length = head_ctrl_e.length / 2 neck_child_e.length = neck_ctrl_e.length / 2 if self.isolate: put_bone(self.obj, head_socket1, neck_ctrl_e.head) head_mch_e.length /= 2 put_bone(self.obj, head_socket2, neck_ctrl_e.head) head_mch_e.length /= 3 for (name1, name2) in zip(neck, helpers): put_bone(self.obj, name2, eb[name1].head) eb[name2].length = eb[name1].length / 2 # Switch to object mode bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones neck_ctrl_p = pb[neck_ctrl] neck_follow_p = pb[neck_follow] # neck_child_p = pb[neck_child] # UNUSED head_ctrl_p = pb[head_ctrl] if self.isolate: # head_socket1_p = pb[head_socket1] # UNUSED head_socket2_p = pb[head_socket2] # Custom bone appearance neck_ctrl_p.custom_shape_transform = pb[self.org_bones[(len(self.org_bones) - 1) // 2]] head_ctrl_p.custom_shape_transform = pb[self.org_bones[-1]] # Custom properties prop = rna_idprop_ui_prop_get(head_ctrl_p, "inf_extent", create=True) head_ctrl_p["inf_extent"] = 0.5 prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop = rna_idprop_ui_prop_get(head_ctrl_p, "neck_follow", create=True) head_ctrl_p["neck_follow"] = 1.0 prop["min"] = 0.0 prop["max"] = 2.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 if self.isolate: prop = rna_idprop_ui_prop_get(head_ctrl_p, "isolate", create=True) head_ctrl_p["isolate"] = 0.0 prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 # Constraints # Neck follow con = neck_follow_p.constraints.new('COPY_ROTATION') con.name = "copy_rotation" con.target = self.obj con.subtarget = head_ctrl fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' var.name = "follow" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = head_ctrl_p.path_from_id() + '["neck_follow"]' driver.expression = "follow / 2" # Isolate if self.isolate: con = head_socket2_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = head_socket1 con = head_socket2_p.constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = head_socket1 fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' var.name = "isolate" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = head_ctrl_p.path_from_id() + '["isolate"]' driver.expression = "1.0 - isolate" # Neck chain first = True prev = None i = 0 l = len(neck) for (name1, name2, org_name) in zip(neck, helpers, self.org_bones): con = pb[org_name].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = name1 n_con = pb[name2].constraints.new('COPY_TRANSFORMS') n_con.name = "neck" n_con.target = self.obj n_con.subtarget = neck_child h_con = pb[name2].constraints.new('COPY_TRANSFORMS') h_con.name = "head" h_con.target = self.obj h_con.subtarget = head_mch con = pb[name2].constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj if first: con.subtarget = neck_ctrl else: con.subtarget = prev con.head_tail = 1.0 # Drivers n = (i + 1) / l # Neck influence fcurve = n_con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' var.name = "ext" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]' driver.expression = "1.0 if (%.4f > (1.0-ext) or (1.0-ext) == 0.0) else (%.4f / (1.0-ext))" % (n, n) # Head influence if (i + 1) == l: h_con.influence = 1.0 else: fcurve = h_con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'SCRIPTED' var.name = "ext" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = head_ctrl_p.path_from_id() + '["inf_extent"]' driver.expression = "0.0 if (%.4f <= (1.0-ext)) else ((%.4f - (1.0-ext)) / ext)" % (n, n) first = False prev = name1 i += 1 # Create control widgets w1 = create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5) w2 = create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5) if w1 != None: obj_to_bone(w1, self.obj, self.org_bones[(len(self.org_bones) - 1) // 2]) if w2 != None: obj_to_bone(w2, self.obj, self.org_bones[-1]) # Return control bones return (head_ctrl, neck_ctrl)
def generate_rig(context, metarig): """ Generates a rig from a metarig. """ t = Timer() # Random string with time appended so that # different rigs don't collide id's rig_id = random_id(16) # Initial configuration # mode_orig = context.mode # UNUSED rest_backup = metarig.data.pose_position metarig.data.pose_position = 'REST' bpy.ops.object.mode_set(mode='OBJECT') scene = context.scene #------------------------------------------ # Create/find the rig object and set it up # Check if the generated rig already exists, so we can # regenerate in the same object. If not, create a new # object to generate the rig in. print("Fetch rig.") try: name = metarig["rig_object_name"] except KeyError: name = "rig" try: obj = scene.objects[name] except KeyError: obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) obj.draw_type = 'WIRE' scene.objects.link(obj) obj.data.pose_position = 'POSE' # Get rid of anim data in case the rig already existed print("Clear rig animation data.") obj.animation_data_clear() # Select generated rig object metarig.select = False obj.select = True scene.objects.active = obj # Remove all bones from the generated rig armature. bpy.ops.object.mode_set(mode='EDIT') for bone in obj.data.edit_bones: obj.data.edit_bones.remove(bone) bpy.ops.object.mode_set(mode='OBJECT') # Create temporary duplicates for merging temp_rig_1 = metarig.copy() temp_rig_1.data = metarig.data.copy() scene.objects.link(temp_rig_1) temp_rig_2 = metarig.copy() temp_rig_2.data = obj.data scene.objects.link(temp_rig_2) # Select the temp rigs for merging for objt in scene.objects: objt.select = False # deselect all objects temp_rig_1.select = True temp_rig_2.select = True scene.objects.active = temp_rig_2 # Merge the temporary rigs bpy.ops.object.join() # Delete the second temp rig bpy.ops.object.delete() # Select the generated rig for objt in scene.objects: objt.select = False # deselect all objects obj.select = True scene.objects.active = obj # Copy over bone properties for bone in metarig.data.bones: bone_gen = obj.data.bones[bone.name] # B-bone stuff bone_gen.bbone_segments = bone.bbone_segments bone_gen.bbone_in = bone.bbone_in bone_gen.bbone_out = bone.bbone_out # Copy over the pose_bone properties for bone in metarig.pose.bones: bone_gen = obj.pose.bones[bone.name] # Rotation mode and transform locks bone_gen.rotation_mode = bone.rotation_mode bone_gen.lock_rotation = tuple(bone.lock_rotation) bone_gen.lock_rotation_w = bone.lock_rotation_w bone_gen.lock_rotations_4d = bone.lock_rotations_4d bone_gen.lock_location = tuple(bone.lock_location) bone_gen.lock_scale = tuple(bone.lock_scale) # Custom properties for prop in bone.keys(): bone_gen[prop] = bone[prop] # Constraints for con1 in bone.constraints: con2 = bone_gen.constraints.new(type=con1.type) copy_attributes(con1, con2) # Set metarig target to rig target if "target" in dir(con2): if con2.target == metarig: con2.target = obj # Copy drivers if metarig.animation_data: for d1 in metarig.animation_data.drivers: d2 = obj.driver_add(d1.data_path) copy_attributes(d1, d2) copy_attributes(d1.driver, d2.driver) # Remove default modifiers, variables, etc. for m in d2.modifiers: d2.modifiers.remove(m) for v in d2.driver.variables: d2.driver.variables.remove(v) # Copy modifiers for m1 in d1.modifiers: m2 = d2.modifiers.new(type=m1.type) copy_attributes(m1, m2) # Copy variables for v1 in d1.driver.variables: v2 = d2.driver.variables.new() copy_attributes(v1, v2) for i in range(len(v1.targets)): copy_attributes(v1.targets[i], v2.targets[i]) # Switch metarig targets to rig targets if v2.targets[i].id == metarig: v2.targets[i].id = obj # Mark targets that may need to be altered after rig generation tar = v2.targets[i] # If a custom property if v2.type == 'SINGLE_PROP' \ and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path): tar.data_path = "RIGIFY-" + tar.data_path # Copy key frames for i in range(len(d1.keyframe_points)): d2.keyframe_points.add() k1 = d1.keyframe_points[i] k2 = d2.keyframe_points[i] copy_attributes(k1, k2) t.tick("Duplicate rig: ") #---------------------------------- # Make a list of the original bones so we can keep track of them. original_bones = [bone.name for bone in obj.data.bones] # Add the ORG_PREFIX to the original bones. bpy.ops.object.mode_set(mode='OBJECT') for i in range(0, len(original_bones)): obj.data.bones[original_bones[i]].name = make_original_name( original_bones[i]) original_bones[i] = make_original_name(original_bones[i]) # Create a sorted list of the original bones, sorted in the order we're # going to traverse them for rigging. # (root-most -> leaf-most, alphabetical) bones_sorted = [] for name in original_bones: bones_sorted += [name] bones_sorted.sort() # first sort by names bones_sorted.sort(key=lambda bone: len(obj.pose.bones[ bone].parent_recursive)) # then parents before children t.tick("Make list of org bones: ") #---------------------------------- # Create the root bone. bpy.ops.object.mode_set(mode='EDIT') root_bone = new_bone(obj, ROOT_NAME) obj.data.edit_bones[root_bone].head = (0, 0, 0) obj.data.edit_bones[root_bone].tail = (0, 1, 0) obj.data.edit_bones[root_bone].roll = 0 bpy.ops.object.mode_set(mode='OBJECT') obj.data.bones[root_bone].layers = ROOT_LAYER # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) obj.data["rig_id"] = rig_id t.tick("Create root bone: ") #---------------------------------- try: # Collect/initialize all the rigs. rigs = [] deformation_rigs = [] for bone in bones_sorted: bpy.ops.object.mode_set(mode='EDIT') rigs += get_bone_rigs(obj, bone) t.tick("Initialize rigs: ") # Generate all the rigs. ui_scripts = [] for rig in rigs: # Go into editmode in the rig armature bpy.ops.object.mode_set(mode='OBJECT') context.scene.objects.active = obj obj.select = True bpy.ops.object.mode_set(mode='EDIT') scripts = rig.generate() if scripts != None: ui_scripts += [scripts[0]] t.tick("Generate rigs: ") except Exception as e: # Cleanup if something goes wrong print("Rigify: failed to generate rig.") metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' bpy.ops.object.mode_set(mode='OBJECT') # Continue the exception raise e #---------------------------------- bpy.ops.object.mode_set(mode='OBJECT') # Get a list of all the bones in the armature bones = [bone.name for bone in obj.data.bones] # Parent any free-floating bones to the root. bpy.ops.object.mode_set(mode='EDIT') for bone in bones: if obj.data.edit_bones[bone].parent is None: obj.data.edit_bones[bone].use_connect = False obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone] bpy.ops.object.mode_set(mode='OBJECT') # Every bone that has a name starting with "DEF-" make deforming. All the # others make non-deforming. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].use_deform = True else: obj.data.bones[bone].use_deform = False # Alter marked driver targets for d in obj.animation_data.drivers: for v in d.driver.variables: for tar in v.targets: if tar.data_path.startswith("RIGIFY-"): temp, bone, prop = tuple( [x.strip('"]') for x in tar.data_path.split('["')]) if bone in obj.data.bones \ and prop in obj.pose.bones[bone].keys(): tar.data_path = tar.data_path[7:] else: tar.data_path = 'pose.bones["%s"]["%s"]' % ( make_original_name(bone), prop) # Move all the original bones to their layer. for bone in original_bones: obj.data.bones[bone].layers = ORG_LAYER # Move all the bones with names starting with "MCH-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(MCH_PREFIX): obj.data.bones[bone].layers = MCH_LAYER # Move all the bones with names starting with "DEF-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].layers = DEF_LAYER # Create root bone widget create_root_widget(obj, "root") # Assign shapes to bones # Object's with name WGT-<bone_name> get used as that bone's shape. for bone in bones: wgt_name = (WGT_PREFIX + obj.data.bones[bone].name )[:21] # Object names are limited to 21 characters... arg if wgt_name in context.scene.objects: # Weird temp thing because it won't let me index by object name for ob in context.scene.objects: if ob.name == wgt_name: obj.pose.bones[bone].custom_shape = ob break # This is what it should do: # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name] # Reveal all the layers with control bones on them vis_layers = [False for n in range(0, 32)] for bone in bones: for i in range(0, 32): vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i] for i in range(0, 32): vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) obj.data.layers = vis_layers # Ensure the collection of layer names exists for i in range(1 + len(metarig.data.rigify_layers), 29): metarig.data.rigify_layers.add() # Create list of layer name/row pairs layer_layout = [] for l in metarig.data.rigify_layers: layer_layout += [(l.name, l.row)] # Generate the UI script if "rig_ui.py" in bpy.data.texts: script = bpy.data.texts["rig_ui.py"] script.clear() else: script = bpy.data.texts.new("rig_ui.py") script.write(UI_SLIDERS % rig_id) for s in ui_scripts: script.write("\n " + s.replace("\n", "\n ") + "\n") script.write(layers_ui(vis_layers, layer_layout)) script.write(UI_REGISTER) script.use_module = True # Run UI script exec(script.as_string(), {}) t.tick("The rest: ") #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE'
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): bpy.ops.object.mode_set(mode='EDIT') ctrl_chain = [] eb = self.obj.data.edit_bones # Control bones # Global control ctrl_g = copy_bone(self.obj, self.mouth, strip_org(self.mouth)) ctrl_g_eb = eb[ctrl_g] ctrl_chain.append(ctrl_g) if self.org_parent is not None: ctrl_g_eb.use_connect = False ctrl_g_eb.parent = eb[self.org_parent] # Right control ctrl_r_name = strip_org(self.ur.split('_')[0] + '.R') ctrl_r = new_bone(self.obj, ctrl_r_name) eb[ctrl_r].head = eb[self.ur].head eb[ctrl_r].tail = eb[self.ur].head + Vector( (0, 0, 1)) * eb[self.ur].length # Left control ctrl_l_name = strip_org(self.ul.split('_')[0] + '.L') ctrl_l = new_bone(self.obj, ctrl_l_name) eb[ctrl_l].head = eb[self.ul].tail eb[ctrl_l].tail = eb[self.ul].tail + Vector( (0, 0, 1)) * eb[self.ul].length # Up control ctrl_uc_name = strip_org(self.uc) ctrl_uc = new_bone(self.obj, ctrl_uc_name) eb[ctrl_uc].head = (eb[self.uc].head + eb[self.uc].tail) / 2 eb[ctrl_uc].tail = eb[ctrl_uc].head + Vector( (0, 0, 1)) * eb[self.uc].length # Down control ctrl_lc_name = strip_org(self.lc) ctrl_lc = new_bone(self.obj, ctrl_lc_name) eb[ctrl_lc].head = (eb[self.lc].head + eb[self.lc].tail) / 2 eb[ctrl_lc].tail = eb[ctrl_lc].head + Vector( (0, 0, 1)) * eb[self.lc].length for b in [ctrl_r, ctrl_uc, ctrl_lc, ctrl_l]: align_bone_x_axis(self.obj, b, Vector((-1, 0, 0))) eb[b].layers = eb[self.lc].layers eb[b].parent = eb[self.mouth] ctrl_chain.append(b) bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones for b in [ctrl_r, ctrl_uc, ctrl_lc, ctrl_l]: pb[b].lock_location = (False, False, True) pb[b].lock_rotation = (True, True, False) pb[b].lock_rotation_w = False pb[b].lock_scale = (False, False, False) pb[b].rotation_mode = 'XZY' bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones # Stretch # RIGHT stretch_r_chain = [] for bone in [self.ur, self.lr]: mch_b = copy_bone( self.obj, bone, pantin_utils.strip_LR(make_mechanism_name(strip_org(bone))) + '.stretch.R') mch_eb = eb[mch_b] mch_eb.parent = eb[ctrl_r] stretch_r_chain.append(mch_b) # CENTER stretch_c_chain = [] for bone in [self.uc, self.lc]: mch_b = copy_bone( self.obj, bone, pantin_utils.strip_LR(make_mechanism_name(strip_org(bone))) + '.stretch') mch_eb = eb[mch_b] mch_eb.use_connect = False mch_eb.parent = eb[strip_org(bone)] stretch_c_chain.append(mch_b) #LEFT stretch_l_chain = [] for i, bone in enumerate([self.ul, self.ll]): mch_b = copy_bone( self.obj, bone, pantin_utils.strip_LR(make_mechanism_name(strip_org(bone))) + '.stretch.L') mch_eb = eb[mch_b] mch_eb.parent = eb[stretch_c_chain[i]] stretch_l_chain.append(mch_b) # Def bones Z_index = self.params.Z_index for i, b in enumerate([ self.mouth, stretch_l_chain[1], stretch_r_chain[1], stretch_c_chain[1], stretch_l_chain[0], stretch_r_chain[0], stretch_c_chain[0], ]): def_bone_name = b.split('.')[0][4:] if b[-1] in ('L', 'R'): def_bone_name += '.' + b[-1] def_bone = pantin_utils.create_deformation(self.obj, b, member_index=Z_index, bone_index=i, new_name=def_bone_name) if b == stretch_c_chain[1]: pantin_utils.create_deformation( self.obj, b, member_index=Z_index, bone_index=i + 1, new_name=strip_org(self.org_bones[0]) + '_int') bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # Widgets wgt_radius = pb[ctrl_uc].length / 3 for b in [ctrl_r, ctrl_l, ctrl_uc, ctrl_lc]: pantin_utils.create_aligned_circle_widget(self.obj, b, radius=wgt_radius) pantin_utils.create_aligned_circle_widget(self.obj, ctrl_g, radius=pb[ctrl_g].length, head_tail=0.0, width_ratio=2.0) # Constraints for src_b, target_b in zip(stretch_r_chain, stretch_c_chain): mch_pb = pb[src_b] con = mch_pb.constraints.new('STRETCH_TO') con.name = "stretch to" con.target = self.obj con.subtarget = target_b con.volume = 'NO_VOLUME' con.rest_length = mch_pb.length con.keep_axis = 'PLANE_Z' for src_b in stretch_l_chain: mch_pb = pb[src_b] con = mch_pb.constraints.new('STRETCH_TO') con.name = "stretch to" con.target = self.obj con.subtarget = ctrl_l con.volume = 'NO_VOLUME' con.rest_length = mch_pb.length con.keep_axis = 'PLANE_Z' for org, ctrl in zip(self.org_bones, [ctrl_g] + stretch_r_chain + stretch_c_chain + stretch_l_chain): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = ctrl return { 'imports': UI_IMPORTS, 'utilities': PANTIN_UTILS, 'register': PANTIN_REGISTER, 'register_props': REGISTER_PANTIN_PROPS, }
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_rig(context, metarig): """ Generates a rig from a metarig. """ t = Timer() rig_id = random_string(12) # Random so that different rigs don't collide id's # Initial configuration use_global_undo = context.user_preferences.edit.use_global_undo context.user_preferences.edit.use_global_undo = False mode_orig = context.mode rest_backup = metarig.data.pose_position metarig.data.pose_position = 'REST' bpy.ops.object.mode_set(mode='OBJECT') scene = context.scene #------------------------------------------ # Create/find the rig object and set it up # Check if the generated rig already exists, so we can # regenerate in the same object. If not, create a new # object to generate the rig in. print("Fetch rig.") try: name = metarig["rig_object_name"] except KeyError: name = "rig" try: obj = scene.objects[name] except KeyError: obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) obj.draw_type = 'WIRE' scene.objects.link(obj) obj.data.pose_position = 'POSE' # Get rid of anim data in case the rig already existed print("Clear rig animation data.") obj.animation_data_clear() # Select generated rig object metarig.select = False obj.select = True scene.objects.active = obj # Remove all bones from the generated rig armature. bpy.ops.object.mode_set(mode='EDIT') for bone in obj.data.edit_bones: obj.data.edit_bones.remove(bone) bpy.ops.object.mode_set(mode='OBJECT') # Create temporary duplicates for merging temp_rig_1 = metarig.copy() temp_rig_1.data = metarig.data.copy() scene.objects.link(temp_rig_1) temp_rig_2 = metarig.copy() temp_rig_2.data = obj.data scene.objects.link(temp_rig_2) # Select the temp rigs for merging for objt in scene.objects: objt.select = False # deselect all objects temp_rig_1.select = True temp_rig_2.select = True scene.objects.active = temp_rig_2 # Merge the temporary rigs bpy.ops.object.join() # Delete the second temp rig bpy.ops.object.delete() # Select the generated rig for objt in scene.objects: objt.select = False # deselect all objects obj.select = True scene.objects.active = obj # Copy over the pose_bone properties for bone in metarig.pose.bones: bone_gen = obj.pose.bones[bone.name] # Rotation mode and transform locks bone_gen.rotation_mode = bone.rotation_mode bone_gen.lock_rotation = tuple(bone.lock_rotation) bone_gen.lock_rotation_w = bone.lock_rotation_w bone_gen.lock_rotations_4d = bone.lock_rotations_4d bone_gen.lock_location = tuple(bone.lock_location) bone_gen.lock_scale = tuple(bone.lock_scale) # Custom properties for prop in bone.keys(): bone_gen[prop] = bone[prop] # Copy over bone properties for bone in metarig.data.bones: bone_gen = obj.data.bones[bone.name] # B-bone stuff bone_gen.bbone_segments = bone.bbone_segments bone_gen.bbone_in = bone.bbone_in bone_gen.bbone_out = bone.bbone_out t.tick("Duplicate rig: ") #---------------------------------- # Make a list of the original bones so we can keep track of them. original_bones = [bone.name for bone in obj.data.bones] # Add the ORG_PREFIX to the original bones. bpy.ops.object.mode_set(mode='OBJECT') for i in range(0, len(original_bones)): obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i]) original_bones[i] = make_original_name(original_bones[i]) # Create a sorted list of the original bones, sorted in the order we're # going to traverse them for rigging. # (root-most -> leaf-most, alphabetical) bones_sorted = [] for name in original_bones: bones_sorted += [name] bones_sorted.sort() # first sort by names bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive)) # then parents before children t.tick("Make list of org bones: ") #---------------------------------- # Create the root bone. bpy.ops.object.mode_set(mode='EDIT') root_bone = new_bone(obj, ROOT_NAME) obj.data.edit_bones[root_bone].head = (0, 0, 0) obj.data.edit_bones[root_bone].tail = (0, 1, 0) obj.data.edit_bones[root_bone].roll = 0 bpy.ops.object.mode_set(mode='OBJECT') obj.data.bones[root_bone].layers = ROOT_LAYER # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) obj.data["rig_id"] = rig_id t.tick("Create root bone: ") #---------------------------------- try: # Collect/initialize all the rigs. rigs = [] deformation_rigs = [] for bone in bones_sorted: bpy.ops.object.mode_set(mode='EDIT') rigs += get_bone_rigs(obj, bone) t.tick("Initialize rigs: ") # Generate all the rigs. ui_scripts = [] for rig in rigs: # Go into editmode in the rig armature bpy.ops.object.mode_set(mode='OBJECT') context.scene.objects.active = obj obj.select = True bpy.ops.object.mode_set(mode='EDIT') scripts = rig.generate() if scripts != None: ui_scripts += [scripts[0]] t.tick("Generate rigs: ") except Exception as e: # Cleanup if something goes wrong print("Rigify: failed to generate rig.") context.user_preferences.edit.use_global_undo = use_global_undo metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' bpy.ops.object.mode_set(mode='OBJECT') # Continue the exception raise e #---------------------------------- bpy.ops.object.mode_set(mode='OBJECT') # Get a list of all the bones in the armature bones = [bone.name for bone in obj.data.bones] # Parent any free-floating bones to the root. bpy.ops.object.mode_set(mode='EDIT') for bone in bones: if obj.data.edit_bones[bone].parent == None: obj.data.edit_bones[bone].use_connect = False obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone] bpy.ops.object.mode_set(mode='OBJECT') # Every bone that has a name starting with "DEF-" make deforming. All the # others make non-deforming. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].use_deform = True else: obj.data.bones[bone].use_deform = False # Move all the original bones to their layer. for bone in original_bones: obj.data.bones[bone].layers = ORG_LAYER # Move all the bones with names starting with "MCH-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(MCH_PREFIX): obj.data.bones[bone].layers = MCH_LAYER # Move all the bones with names starting with "DEF-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].layers = DEF_LAYER # Create root bone widget create_root_widget(obj, "root") # Assign shapes to bones # Object's with name WGT-<bone_name> get used as that bone's shape. for bone in bones: wgt_name = (WGT_PREFIX + obj.data.bones[bone].name)[:21] # Object names are limited to 21 characters... arg if wgt_name in context.scene.objects: # Weird temp thing because it won't let me index by object name for ob in context.scene.objects: if ob.name == wgt_name: obj.pose.bones[bone].custom_shape = ob break # This is what it should do: # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name] # Reveal all the layers with control bones on them vis_layers = [False for n in range(0, 32)] for bone in bones: for i in range(0, 32): vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i] for i in range(0, 32): vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) obj.data.layers = vis_layers # Generate the UI script if "rig_ui.py" in bpy.data.texts: script = bpy.data.texts["rig_ui.py"] script.clear() else: script = bpy.data.texts.new("rig_ui.py") script.write(UI_SLIDERS % rig_id) for s in ui_scripts: script.write("\n " + s.replace("\n", "\n ") + "\n") script.write(layers_ui(vis_layers)) script.use_module = True t.tick("The rest: ") #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' context.user_preferences.edit.use_global_undo = use_global_undo
def generate_rig(context, metarig): """ Generates a rig from a metarig. """ t = Timer() rig_id = random_string( 12) # Random so that different rigs don't collide id's # Initial configuration use_global_undo = context.user_preferences.edit.use_global_undo context.user_preferences.edit.use_global_undo = False mode_orig = context.mode rest_backup = metarig.data.pose_position metarig.data.pose_position = 'REST' bpy.ops.object.mode_set(mode='OBJECT') scene = context.scene #------------------------------------------ # Create/find the rig object and set it up # Check if the generated rig already exists, so we can # regenerate in the same object. If not, create a new # object to generate the rig in. print("Fetch rig.") try: name = metarig["rig_object_name"] except KeyError: name = "rig" try: obj = scene.objects[name] except KeyError: obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) obj.draw_type = 'WIRE' scene.objects.link(obj) obj.data.pose_position = 'POSE' # Get rid of anim data in case the rig already existed print("Clear rig animation data.") obj.animation_data_clear() # Select generated rig object metarig.select = False obj.select = True scene.objects.active = obj # Remove all bones from the generated rig armature. bpy.ops.object.mode_set(mode='EDIT') for bone in obj.data.edit_bones: obj.data.edit_bones.remove(bone) bpy.ops.object.mode_set(mode='OBJECT') # Create temporary duplicates for merging temp_rig_1 = metarig.copy() temp_rig_1.data = metarig.data.copy() scene.objects.link(temp_rig_1) temp_rig_2 = metarig.copy() temp_rig_2.data = obj.data scene.objects.link(temp_rig_2) # Select the temp rigs for merging for objt in scene.objects: objt.select = False # deselect all objects temp_rig_1.select = True temp_rig_2.select = True scene.objects.active = temp_rig_2 # Merge the temporary rigs bpy.ops.object.join() # Delete the second temp rig bpy.ops.object.delete() # Select the generated rig for objt in scene.objects: objt.select = False # deselect all objects obj.select = True scene.objects.active = obj # Copy over the pose_bone properties for bone in metarig.pose.bones: bone_gen = obj.pose.bones[bone.name] # Rotation mode and transform locks bone_gen.rotation_mode = bone.rotation_mode bone_gen.lock_rotation = tuple(bone.lock_rotation) bone_gen.lock_rotation_w = bone.lock_rotation_w bone_gen.lock_rotations_4d = bone.lock_rotations_4d bone_gen.lock_location = tuple(bone.lock_location) bone_gen.lock_scale = tuple(bone.lock_scale) # Custom properties for prop in bone.keys(): bone_gen[prop] = bone[prop] # Copy over bone properties for bone in metarig.data.bones: bone_gen = obj.data.bones[bone.name] # B-bone stuff bone_gen.bbone_segments = bone.bbone_segments bone_gen.bbone_in = bone.bbone_in bone_gen.bbone_out = bone.bbone_out t.tick("Duplicate rig: ") #---------------------------------- # Make a list of the original bones so we can keep track of them. original_bones = [bone.name for bone in obj.data.bones] # Add the ORG_PREFIX to the original bones. bpy.ops.object.mode_set(mode='OBJECT') for i in range(0, len(original_bones)): obj.data.bones[original_bones[i]].name = make_original_name( original_bones[i]) original_bones[i] = make_original_name(original_bones[i]) # Create a sorted list of the original bones, sorted in the order we're # going to traverse them for rigging. # (root-most -> leaf-most, alphabetical) bones_sorted = [] for name in original_bones: bones_sorted += [name] bones_sorted.sort() # first sort by names bones_sorted.sort(key=lambda bone: len(obj.pose.bones[ bone].parent_recursive)) # then parents before children t.tick("Make list of org bones: ") #---------------------------------- # Create the root bone. bpy.ops.object.mode_set(mode='EDIT') root_bone = new_bone(obj, ROOT_NAME) obj.data.edit_bones[root_bone].head = (0, 0, 0) obj.data.edit_bones[root_bone].tail = (0, 1, 0) obj.data.edit_bones[root_bone].roll = 0 bpy.ops.object.mode_set(mode='OBJECT') obj.data.bones[root_bone].layers = ROOT_LAYER # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) obj.data["rig_id"] = rig_id t.tick("Create root bone: ") #---------------------------------- try: # Collect/initialize all the rigs. rigs = [] deformation_rigs = [] for bone in bones_sorted: bpy.ops.object.mode_set(mode='EDIT') rigs += get_bone_rigs(obj, bone) t.tick("Initialize rigs: ") # Generate all the rigs. ui_scripts = [] for rig in rigs: # Go into editmode in the rig armature bpy.ops.object.mode_set(mode='OBJECT') context.scene.objects.active = obj obj.select = True bpy.ops.object.mode_set(mode='EDIT') scripts = rig.generate() if scripts != None: ui_scripts += [scripts[0]] t.tick("Generate rigs: ") except Exception as e: # Cleanup if something goes wrong print("Rigify: failed to generate rig.") context.user_preferences.edit.use_global_undo = use_global_undo metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' bpy.ops.object.mode_set(mode='OBJECT') # Continue the exception raise e #---------------------------------- bpy.ops.object.mode_set(mode='OBJECT') # Get a list of all the bones in the armature bones = [bone.name for bone in obj.data.bones] # Parent any free-floating bones to the root. bpy.ops.object.mode_set(mode='EDIT') for bone in bones: if obj.data.edit_bones[bone].parent == None: obj.data.edit_bones[bone].use_connect = False obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone] bpy.ops.object.mode_set(mode='OBJECT') # Every bone that has a name starting with "DEF-" make deforming. All the # others make non-deforming. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].use_deform = True else: obj.data.bones[bone].use_deform = False # Move all the original bones to their layer. for bone in original_bones: obj.data.bones[bone].layers = ORG_LAYER # Move all the bones with names starting with "MCH-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(MCH_PREFIX): obj.data.bones[bone].layers = MCH_LAYER # Move all the bones with names starting with "DEF-" to their layer. for bone in bones: if obj.data.bones[bone].name.startswith(DEF_PREFIX): obj.data.bones[bone].layers = DEF_LAYER # Create root bone widget create_root_widget(obj, "root") # Assign shapes to bones # Object's with name WGT-<bone_name> get used as that bone's shape. for bone in bones: wgt_name = (WGT_PREFIX + obj.data.bones[bone].name )[:21] # Object names are limited to 21 characters... arg if wgt_name in context.scene.objects: # Weird temp thing because it won't let me index by object name for ob in context.scene.objects: if ob.name == wgt_name: obj.pose.bones[bone].custom_shape = ob break # This is what it should do: # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name] # Reveal all the layers with control bones on them vis_layers = [False for n in range(0, 32)] for bone in bones: for i in range(0, 32): vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i] for i in range(0, 32): vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i]) obj.data.layers = vis_layers # Generate the UI script if "rig_ui.py" in bpy.data.texts: script = bpy.data.texts["rig_ui.py"] script.clear() else: script = bpy.data.texts.new("rig_ui.py") script.write(UI_SLIDERS % rig_id) for s in ui_scripts: script.write("\n " + s.replace("\n", "\n ") + "\n") script.write(layers_ui(vis_layers)) script.use_module = True t.tick("The rest: ") #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' context.user_preferences.edit.use_global_undo = use_global_undo
def generate(self): def parent_ctrl(eb, child, parent_to=None): """This tries to parent the child to a .L/.R bone.""" # Get parent if none supplied if parent_to is None and eb[child].parent is not None: parent_to = eb[child].parent.name print(parent_to) if parent_to + self.params.object_side in eb: eb[child].parent = (eb[parent_to + self.params.object_side]) elif parent_to in eb: eb[child].parent = eb[parent_to] elif 'MCH-Flip' in eb: eb[child].parent = eb['MCH-Flip'] else: raise Exception( "RIGIFY ERROR: Bone %s does not have a %s side" % (strip_org(eb[b].parent.name), side)) if self.params.use_parent_Z_index and self.org_parent is not None: # Get parent's Z indices bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones def_parent_name = make_deformer_name(strip_org(self.org_parent)) if (self.params.object_side != ".C" and def_parent_name[-2:] not in ['.L', '.R']): def_parent_name += self.params.object_side if not def_parent_name in pb: raise Exception( "RIGIFY ERROR: Bone %s does not have a %s side" % (strip_org(self.org_parent), self.params.object_side)) parent_p = pb[def_parent_name] member_Z_index = parent_p['member_index'] bone_Z_index = 0 for b in pb: if b.bone.use_deform and b.name.startswith('DEF-'): if not 'member_index' in b: raise MetarigError( "RIGIFY ERROR: One rig bone with should not be connected. " "Check armature for connected bones.") if (b['member_index'] == member_Z_index and b['bone_index'] > bone_Z_index): bone_Z_index = b['bone_index'] bone_Z_index += 1 bpy.ops.object.mode_set(mode='EDIT') else: member_Z_index = self.params.member_Z_index bone_Z_index = self.params.first_bone_Z_index eb = self.obj.data.edit_bones ctrl_chain = [] mch_chain = [] # def_chain = [] # if self.params.duplicate_lr: # sides = ['.L', '.R'] # else: side = self.params.object_side if side == '.C': side = '' # IK control bone if self.params.chain_type == 'IK': last_bone = self.org_bones[-1] ctrl_bone = new_bone(self.obj, strip_org(last_bone) + ".ik" + side) ctrl_bone_e = eb[ctrl_bone] ctrl_bone_e.head = eb[last_bone].tail ctrl_bone_e.tail = eb[last_bone].tail + Vector((0.3, 0, 0)) align_bone_z_axis(self.obj, ctrl_bone, Vector((0, 1, 0))) ctrl_chain.append(ctrl_bone) # ctrl_bone_e.layers = layers for i, b in enumerate(self.org_bones): # Control bones if self.params.chain_type in {'Normal', 'Curve', 'Dynamic'}: if side in b: ctrl_bone_name = b else: ctrl_bone_name = b + side ctrl_bone_name = strip_org(ctrl_bone_name) ctrl_bone = copy_bone(self.obj, b, ctrl_bone_name) ctrl_bone_e = eb[ctrl_bone] ctrl_chain.append(ctrl_bone) if self.params.chain_type == 'Curve': ctrl_bone_e.use_connect = False if self.params.curve_parent_to_first: if i > 0: parent_ctrl(eb, ctrl_bone, parent_to=ctrl_chain[0]) else: parent_ctrl(eb, ctrl_bone, parent_to=self.org_parent) else: parent_ctrl(eb, ctrl_bone, parent_to=self.org_parent) ctrl_bone_e.tail = (ctrl_bone_e.head + Vector( (0, 0, 1)) * ctrl_bone_e.length) align_bone_z_axis(self.obj, ctrl_bone, Vector((0, 1, 0))) elif self.params.chain_type == 'Dynamic': # Create an empty object to use slow parent # What follows is quite dirty. # ctrl_bone_e.parent = eb[self.org_parent] parent_ctrl(eb, ctrl_bone) empty_name = self.obj.name + "_" + strip_org(b) + '.dyn' if empty_name in bpy.data.objects: empty_obj = bpy.data.objects[empty_name] else: empty_obj = bpy.data.objects.new(empty_name, None) if not empty_name in bpy.context.scene.objects: bpy.context.scene.objects.link(empty_obj) empty_obj.empty_draw_type = 'SPHERE' empty_obj.empty_draw_size = self.obj.data.bones[ b].length / 10 empty_obj.hide = True empty_obj.parent = self.obj empty_obj.parent_type = 'BONE' empty_obj.parent_bone = ctrl_bone empty_obj.matrix_local = Matrix() empty_obj.use_slow_parent = True empty_obj.slow_parent_offset = 1.0 # Mechanism bones if self.params.chain_type == 'Curve': stretch_bone = copy_bone( self.obj, b, make_mechanism_name(strip_org(b)) + '.stretch' + side) stretch_bone_e = eb[stretch_bone] stretch_bone_e.use_connect = False stretch_bone_e.parent = eb[ctrl_bone] mch_chain.append(stretch_bone) elif self.params.chain_type == 'IK': ik_bone = copy_bone( self.obj, b, make_mechanism_name(strip_org(b)) + '.ik' + side) ik_bone_e = eb[ik_bone] ik_bone_e.parent = eb[mch_chain[-1] if i > 0 else self. org_parent] mch_chain.append(ik_bone) elif self.params.chain_type == 'Dynamic': dyn_bone = copy_bone( self.obj, b, make_mechanism_name(strip_org(b)) + '.dyn' + side) dyn_bone_e = eb[dyn_bone] dyn_bone_e.parent = eb[ctrl_bone] mch_chain.append(dyn_bone) # Parenting # if self.params.chain_type == 'Normal': if (i == 0 and self.params.chain_type in ('Normal', 'Dynamic')): parent_ctrl(eb, ctrl_bone) # Def bones def_bone = pantin_utils.create_deformation(self.obj, b, member_Z_index, bone_Z_index + i, 0.0, b + side) if self.params.chain_type == 'Curve': # Curve end bone last_bone = self.org_bones[-1] ctrl_bone = new_bone(self.obj, strip_org(last_bone) + ".end" + side) ctrl_bone_e = eb[ctrl_bone] last_bone_e = eb[last_bone] ctrl_bone_e.use_connect = False if self.params.curve_parent_to_first: ctrl_bone_e.parent = eb[ctrl_chain[0]] else: parent_ctrl(eb, ctrl_bone, parent_to=self.org_parent) ctrl_bone_e.head = last_bone_e.tail ctrl_bone_e.tail = (ctrl_bone_e.head + (last_bone_e.tail - last_bone_e.head)) align_bone_z_axis(self.obj, ctrl_bone, Vector((0, 1, 0))) ctrl_chain.append(ctrl_bone) ctrl_bone_e.layers = last_bone_e.layers # ctrl_bone_e.layers = layers bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # Pose bone settings if self.params.chain_type in ('IK', 'Curve', 'Dynamic'): pbone = pb[ctrl_chain[-1]] pbone.rotation_mode = 'XZY' pbone.lock_location = (False, False, True) pbone.lock_rotation = (True, True, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) if self.params.chain_type in ('IK', 'Curve', 'Dynamic'): # Widgets for ctrl_bone in ctrl_chain: global_scale = pb[ctrl_bone].length # self.obj.dimensions[2] # member_factor = 0.06 widget_size = global_scale * 0.3 # * member_factor pantin_utils.create_aligned_circle_widget(self.obj, ctrl_bone, radius=widget_size) # Constraints for org, mch in zip(self.org_bones, mch_chain): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = mch else: # Widgets widget_size = 0.5 for bone in ctrl_chain: pantin_utils.create_capsule_widget(self.obj, bone, length=widget_size, width=widget_size * 0.1, head_tail=0.5, horizontal=False) # Constraints for org, ctrl in zip(self.org_bones, ctrl_chain): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = ctrl ui_script = "" if self.params.chain_type == 'Curve': for ctrl, mch in zip(ctrl_chain[1:], mch_chain): con = pb[mch].constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = ctrl con.volume = 'NO_VOLUME' con.keep_axis = 'PLANE_Z' elif self.params.chain_type == 'IK': last_bone = mch_chain[-1] con = pb[last_bone].constraints.new('IK') con.target = self.obj con.subtarget = ctrl_bone con.chain_count = len(self.org_bones) # # Pelvis follow # if self.params.do_flip: # pantin_utils.create_ik_child_of( # self.obj, ctrl_bone, self.params.pelvis_name) elif self.params.chain_type == 'Dynamic': for ctrl, mch in zip(ctrl_chain, mch_chain): con = pb[mch].constraints.new('DAMPED_TRACK') con.name = "damped_track" con.target = empty_obj # con.volume = 'NO_VOLUME' # con.keep_axis = 'PLANE_Z' # con.rest_length = pb[ctrl].length ui_script = script % (ctrl, dyn_bone) # % ctrl_bone, MCH-bone.dyn elif self.params.chain_type == 'Def': # Modify constraints to add side suffix if side in ('.L', '.R'): for b in self.org_bones: for con in pb[b].constraints: if (hasattr(con, 'subtarget') and con.subtarget + side in pb): con.subtarget += side return { 'script': [ui_script], 'imports': UI_IMPORTS, 'utilities': PANTIN_UTILS, 'register': PANTIN_REGISTER, 'register_props': REGISTER_PANTIN_PROPS, }