def copy_properties(pose_bone_1, pose_bone_2): for key in pose_bone_1.keys(): if key != "_RNA_UI": prop1 = rna_idprop_ui_prop_get(pose_bone_1, key, create=False) pose_bone_2[key] = pose_bone_1[key] if prop1: prop2 = rna_idprop_ui_prop_get(pose_bone_2, key, create=True) for key in prop1.keys(): prop2[key] = prop1[key]
def execute(self, context): from rna_prop_ui import rna_idprop_ui_prop_get data_path = self.data_path item = eval("context.%s" % data_path) def unique_name(names): prop = self.prop_name prop_new = prop i = 1 while prop_new in names: prop_new = prop + str(i) i += 1 return prop_new prop = unique_name(item.keys()) item[prop] = self.default_value prop_type = type(item[prop]) # not essential, but without this we get [#31661] prop_ui = rna_idprop_ui_prop_get(item, prop) # create the prop_ui prop_ui["soft_min"] = prop_ui["min"] = 0.0 prop_ui["soft_max"] = prop_ui["max"] = 1.0 # if prop_type in {float, int}: # prop_ui["soft_min"] = prop_ui["min"] = prop_type(self.default_min) # prop_ui["soft_max"] = prop_ui["max"] = prop_type(self.default_max) return {"FINISHED"}
def execute(self, context): ob = context.active_object prop = self.prop global ob_prop if len(self.new_prop) > 0: prop = self.new_prop if prop[0] != "/": prop = "/" + prop # Create property ob_prop[prop] = True if self.is_bool else 0.0 # Limit to [0, 1] prop_ui = rna_idprop_ui_prop_get(ob_prop, prop, create=True) prop_ui["soft_min"] = False if self.is_bool else 0.0 prop_ui["soft_max"] = True if self.is_bool else 1.0 driver = ob.animation_data.drivers[self.driver_id].driver driver.type = "SUM" var = driver.variables[0] var.type = "SINGLE_PROP" target = var.targets[0] target.id_type = "OBJECT" target.id = ob_prop target.data_path = '["' + prop + '"]' return {"FINISHED"}
def drivers_and_props( self, all_bones ): bpy.ops.object.mode_set(mode ='OBJECT') pb = self.obj.pose.bones # Referencing all relevant bones torso_name = all_bones['torso']['ctrl'] pb_torso = pb[torso_name] ribs_mch_rot_names = all_bones['back']['mch_rot_bones'] neck_mch_rot_names = all_bones['neck']['mch_rot_bones'] head_mch_rot_names = all_bones['head']['mch_rot_bones'] owner_mch_rot_bones = [ ribs_mch_rot_names[0], neck_mch_rot_names[0], head_mch_rot_names[0] ] hips_mch_drv_name = all_bones['hips']['mch_drv'] back_mch_drv_names = all_bones['back']['mch_drv_bones'] neck_mch_drv_names = all_bones['neck']['mch_drv_bones'] head_mch_drv_name = all_bones['head']['mch_drv'] mch_drv_bones = [ hips_mch_drv_name ] + back_mch_drv_names + neck_mch_drv_names + [ head_mch_drv_name ] # Setting the torso's props props_list = [ "ribs_follow", "neck_follow", "head_follow", "IK/FK" ] for prop in props_list: if prop == 'neck_follow': pb_torso[prop] = 0.5 else: pb_torso[prop] = 0.0 prop = rna_idprop_ui_prop_get( pb_torso, prop ) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = prop # driving the follow rotation switches for ribs neck and head for bone, prop, in zip( owner_mch_rot_bones, props_list[:-1] ): drv = pb[ bone ].constraints[ 1 ].driver_add("influence").driver drv.type='SUM' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb_torso.path_from_id() + '['+ '"' + prop + '"' + ']' # driving the fk switch for bone in mch_drv_bones: drv = pb[ bone ].constraints[ -1 ].driver_add("influence").driver drv.type='SUM' var = drv.variables.new() var.name = "fk_switch" var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb_torso.path_from_id() + '["IK/FK"]'
def set_prop_constraint(obj, prop, minimum, maximum): obj_name = obj.name data_path = "data.objects[obj_name]" item = eval("bpy.%s" % data_path) prop_type = type(item[prop]) prop_ui = rna_idprop_ui_prop_get(item, prop) if prop_type in (float, int): prop_ui['soft_min'] = prop_ui['min'] = prop_type(minimum) prop_ui['soft_max'] = prop_ui['max'] = prop_type(maximum)
def create_parent( self ): org_bones = self.org_bones bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones name = get_bone_name( strip_org( org_bones[0] ), 'mch', 'parent' ) mch = copy_bone( self.obj, org_bones[0], name ) orient_bone( self, eb[mch], 'y' ) eb[ mch ].length = eb[ org_bones[0] ].length / 4 eb[ mch ].parent = eb[ org_bones[0] ].parent eb[ mch ].roll = 0.0 # Constraints make_constraint( self, mch, { 'constraint' : 'COPY_ROTATION', 'subtarget' : 'root' }) make_constraint( self, mch, { 'constraint' : 'COPY_SCALE', 'subtarget' : 'root' }) # Limb Follow Driver pb = self.obj.pose.bones name = 'FK_limb_follow' pb[ mch ][ name ] = 0.0 prop = rna_idprop_ui_prop_get( pb[ mch ], name, create = True ) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = name drv = pb[ mch ].constraints[ 0 ].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb[ mch ].path_from_id() + \ '[' + '"' + name + '"' + ']' return mch
def create_drivers(self, bones): bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # Setting the torso's props torso = pb[bones['pivot']['ctrl']] props = [] owners = [] if self.use_head: props += ["head_follow"] owners += [bones['neck']['mch_head']] if bones['neck']['mch_neck']: props += ["neck_follow"] owners += [bones['neck']['mch_neck']] if self.use_tail: props += ["tail_follow"] owners += [bones['tail']['mch_tail']] for prop in props: if prop == 'neck_follow': torso[prop] = 0.5 else: torso[prop] = 0.0 prop = rna_idprop_ui_prop_get(torso, prop, create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = prop # driving the follow rotation switches for neck and head for bone, prop, in zip(owners, props): # Add driver to copy rotation constraint drv = pb[bone].constraints[0].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ torso.path_from_id() + '[' + '"' + prop + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0
def execute(self, context): data_path = self.data_path value = self.value prop = self.property prop_old = getattr(self, "_last_prop", [None])[0] if prop_old is None: self.report({'ERROR'}, "Direct execution not supported") return {'CANCELLED'} try: value_eval = eval(value) except: value_eval = value # First remove item = eval("context.%s" % data_path) rna_idprop_ui_prop_clear(item, prop_old) exec_str = "del item['%s']" % prop_old # print(exec_str) exec(exec_str) # Reassign exec_str = "item['%s'] = %s" % (prop, repr(value_eval)) # print(exec_str) exec(exec_str) self._last_prop[:] = [prop] prop_type = type(item[prop]) prop_ui = rna_idprop_ui_prop_get(item, prop) if prop_type in {float, int}: prop_ui['soft_min'] = prop_ui['min'] = prop_type(self.min) prop_ui['soft_max'] = prop_ui['max'] = prop_type(self.max) prop_ui['description'] = self.description # otherwise existing buttons which reference freed # memory may crash blender [#26510] # context.area.tag_redraw() for win in context.window_manager.windows: for area in win.screen.areas: area.tag_redraw() return {'FINISHED'}
def invoke(self, context, event): self._last_prop = [self.property] item = eval("context.%s" % self.data_path) # setup defaults prop_ui = rna_idprop_ui_prop_get(item, self.property, False) # dont create if prop_ui: self.min = prop_ui.get("min", -1000000000) self.max = prop_ui.get("max", 1000000000) self.description = prop_ui.get("description", "") wm = context.window_manager return wm.invoke_props_dialog(self)
def _onTypeChange(self, context): ob = self.id_data for cat, props in properties.items(): if cat == self.type: for prop, dom in props.items(): ob[prop] = 0.0 prop_ui = rna_idprop_ui_prop_get(ob, prop, create=True) prop_ui['min'] = float(dom[0]) prop_ui['max'] = float(dom[1]) else: # remove values from other categories for prop in props.keys(): try: del ob[prop] except: pass try: del ob['_RNA_UI'][prop] except: pass
def org_parenting_and_switch( self, org, ik, fk, parent ): bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones # re-parent ORGs in a connected chain for i,o in enumerate(org): if i > 0: eb[o].parent = eb[ org[i-1] ] if i <= 2: eb[o].use_connect = True bpy.ops.object.mode_set(mode ='OBJECT') pb = self.obj.pose.bones pb_parent = pb[ parent ] # Create ik/fk switch property pb_parent['IK/FK'] = 0.0 prop = rna_idprop_ui_prop_get( pb_parent, 'IK/FK', create=True ) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = 'IK/FK Switch' # Constrain org to IK and FK bones iks = [ ik['ctrl']['limb'] ] iks += [ ik[k] for k in [ 'mch_ik', 'mch_target'] ] for o, i, f in zip( org, iks, fk ): make_constraint( self, o, { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : i }) make_constraint( self, o, { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : f }) # Add driver to relevant constraint drv = pb[o].constraints[-1].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']'
def create_parent(self): org_bones = self.org_bones bpy.ops.object.mode_set(mode="EDIT") eb = self.obj.data.edit_bones name = get_bone_name(strip_org(org_bones[0]), "mch", "parent") mch = copy_bone(self.obj, org_bones[0], name) orient_bone(self, eb[mch], "y") eb[mch].length = eb[org_bones[0]].length / 4 eb[mch].parent = eb[org_bones[0]].parent eb[mch].roll = 0.0 # Constraints make_constraint(self, mch, {"constraint": "COPY_ROTATION", "subtarget": "root"}) make_constraint(self, mch, {"constraint": "COPY_SCALE", "subtarget": "root"}) # Limb Follow Driver pb = self.obj.pose.bones name = "FK_limb_follow" pb[mch][name] = 0.0 prop = rna_idprop_ui_prop_get(pb[mch], name, create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = name drv = pb[mch].constraints[0].driver_add("influence").driver drv.type = "AVERAGE" var = drv.variables.new() var.name = name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb[mch].path_from_id() + "[" + '"' + name + '"' + "]" return mch
def invoke(self, context, event): data_path = self.data_path if not data_path: self.report({'ERROR'}, "Data path not set") return {'CANCELLED'} self._last_prop = [self.property] item = eval("context.%s" % data_path) # setup defaults prop_ui = rna_idprop_ui_prop_get(item, self.property, False) # dont create if prop_ui: self.min = prop_ui.get("min", -1000000000) self.max = prop_ui.get("max", 1000000000) self.description = prop_ui.get("description", "") wm = context.window_manager return wm.invoke_props_dialog(self)
def org_parenting_and_switch(self, org, ik, fk, parent): bpy.ops.object.mode_set(mode="EDIT") eb = self.obj.data.edit_bones # re-parent ORGs in a connected chain for i, o in enumerate(org): if i > 0: eb[o].parent = eb[org[i - 1]] if i <= len(org) - 1: eb[o].use_connect = True bpy.ops.object.mode_set(mode="OBJECT") pb = self.obj.pose.bones pb_parent = pb[parent] # Create ik/fk switch property pb_parent["IK/FK"] = 0.0 prop = rna_idprop_ui_prop_get(pb_parent, "IK/FK", create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = "IK/FK Switch" # Constrain org to IK and FK bones iks = [ik["ctrl"]["limb"]] iks += [ik[k] for k in ["mch_ik", "mch_target"]] for o, i, f in zip(org, iks, fk): make_constraint(self, o, {"constraint": "COPY_TRANSFORMS", "subtarget": i}) make_constraint(self, o, {"constraint": "COPY_TRANSFORMS", "subtarget": f}) # Add driver to relevant constraint drv = pb[o].constraints[-1].driver_add("influence").driver drv.type = "AVERAGE" var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb_parent.path_from_id() + "[" + '"' + prop.name + '"' + "]"
def execute(self, context): data_path = self.data_path value = self.value prop = self.property prop_old = self._last_prop[0] try: value_eval = eval(value) except: value_eval = value # First remove item = eval("context.%s" % data_path) rna_idprop_ui_prop_clear(item, prop_old) exec_str = "del item['%s']" % prop_old # print(exec_str) exec(exec_str) # Reassign exec_str = "item['%s'] = %s" % (prop, repr(value_eval)) # print(exec_str) exec(exec_str) self._last_prop[:] = [prop] prop_type = type(item[prop]) prop_ui = rna_idprop_ui_prop_get(item, prop) if prop_type in (float, int): prop_ui['soft_min'] = prop_ui['min'] = prop_type(self.min) prop_ui['soft_max'] = prop_ui['max'] = prop_type(self.max) prop_ui['description'] = self.description return {'FINISHED'}
def create_drivers(self, bones): bpy.ops.object.mode_set(mode ='OBJECT') pb = self.obj.pose.bones ctrl = pb[bones['ik']['ctrl']['terminal'][-1]] props = [ "IK_follow", "root/parent" ] for prop in props: if prop == 'IK_follow': ctrl[prop]=True rna_prop = rna_idprop_ui_prop_get( ctrl, prop, create=True ) rna_prop["min"] = False rna_prop["max"] = True rna_prop["description"] = prop drv = ctrl.constraints[ 0 ].driver_add("mute").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 drv = ctrl.constraints[ 1 ].driver_add("mute").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 else: ctrl[prop]=0.0 rna_prop = rna_idprop_ui_prop_get( ctrl, prop, create=True ) rna_prop["min"] = 0.0 rna_prop["max"] = 1.0 rna_prop["soft_min"] = 0.0 rna_prop["soft_max"] = 1.0 rna_prop["description"] = prop drv = ctrl.constraints[ 0 ].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 drv = ctrl.constraints[ 1 ].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']'
def generate(self): bpy.ops.object.mode_set(mode='EDIT') # Create non-scaling parent bone if self.org_parent is not None: loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head) parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_ik") if self.pole_parent is None: self.pole_parent = parent else: parent = None # Create the bones ulimb = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".ik")))) flimb = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".ik")))) elimb = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".ik"))) elimb_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2]))) ulimb_nostr = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".nostr.ik")))) flimb_nostr = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".nostr.ik")))) ulimb_str = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], ".stretch.ik")))) flimb_str = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".stretch.ik")))) pole_target_name = self.pole_target_base_name + "." + insert_before_lr(self.org_bones[0], ".ik").split(".", 1)[1] pole = copy_bone(self.obj, self.org_bones[0], pole_target_name) if self.pole_parent == self.org_bones[2]: self.pole_parent = elimb_mch if self.pole_parent is not None: pole_par = copy_bone(self.obj, self.pole_parent, make_mechanism_name(insert_before_lr(pole_target_name, "_parent"))) viselimb = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], ".ik"))) vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole.ik"))) # Get edit bones eb = self.obj.data.edit_bones if parent is not None: parent_e = eb[parent] ulimb_e = eb[ulimb] flimb_e = eb[flimb] elimb_e = eb[elimb] elimb_mch_e = eb[elimb_mch] ulimb_nostr_e = eb[ulimb_nostr] flimb_nostr_e = eb[flimb_nostr] ulimb_str_e = eb[ulimb_str] flimb_str_e = eb[flimb_str] pole_e = eb[pole] if self.pole_parent is not None: pole_par_e = eb[pole_par] viselimb_e = eb[viselimb] vispole_e = eb[vispole] # Parenting ulimb_e.use_connect = False ulimb_nostr_e.use_connect = False if parent is not None: ulimb_e.parent = parent_e ulimb_nostr_e.parent = parent_e flimb_e.parent = ulimb_e flimb_nostr_e.parent = ulimb_nostr_e elimb_e.use_connect = False elimb_e.parent = None elimb_mch_e.use_connect = False elimb_mch_e.parent = elimb_e ulimb_str_e.use_connect = False ulimb_str_e.parent = ulimb_e.parent flimb_str_e.use_connect = False flimb_str_e.parent = ulimb_e.parent pole_e.use_connect = False if self.pole_parent is not None: pole_par_e.parent = None pole_e.parent = pole_par_e viselimb_e.use_connect = False viselimb_e.parent = None vispole_e.use_connect = False vispole_e.parent = None # Misc elimb_e.use_local_location = False viselimb_e.hide_select = True vispole_e.hide_select = True # Positioning v1 = flimb_e.tail - ulimb_e.head if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: v2 = v1.cross(flimb_e.x_axis) if (v2 @ flimb_e.z_axis) > 0.0: v2 *= -1.0 else: v2 = v1.cross(flimb_e.z_axis) if (v2 @ flimb_e.x_axis) < 0.0: v2 *= -1.0 v2.normalize() v2 *= v1.length if '-' in self.primary_rotation_axis: v2 *= -1 pole_e.head = flimb_e.head + v2 pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) pole_e.roll = 0.0 if parent is not None: pole_par_e.length *= 0.75 viselimb_e.tail = viselimb_e.head + Vector((0, 0, v1.length / 32)) vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) # Determine the pole offset value plane = (flimb_e.tail - ulimb_e.head).normalized() vec1 = ulimb_e.x_axis.normalized() vec2 = (pole_e.head - ulimb_e.head).normalized() pole_offset = angle_on_plane(plane, vec1, vec2) # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones ulimb_p = pb[ulimb] flimb_p = pb[flimb] elimb_p = pb[elimb] ulimb_nostr_p = pb[ulimb_nostr] flimb_nostr_p = pb[flimb_nostr] ulimb_str_p = pb[ulimb_str] flimb_str_p = pb[flimb_str] pole_p = pb[pole] if self.pole_parent is not None: pole_par_p = pb[pole_par] viselimb_p = pb[viselimb] vispole_p = pb[vispole] # Set the elbow to only bend on the primary axis if 'X' in self.primary_rotation_axis: flimb_p.lock_ik_y = True flimb_p.lock_ik_z = True flimb_nostr_p.lock_ik_y = True flimb_nostr_p.lock_ik_z = True elif 'Y' in self.primary_rotation_axis: flimb_p.lock_ik_x = True flimb_p.lock_ik_z = True flimb_nostr_p.lock_ik_x = True flimb_nostr_p.lock_ik_z = True else: flimb_p.lock_ik_x = True flimb_p.lock_ik_y = True flimb_nostr_p.lock_ik_x = True flimb_nostr_p.lock_ik_y = True # Limb stretches ulimb_nostr_p.ik_stretch = 0.0 flimb_nostr_p.ik_stretch = 0.0 # This next bit is weird. The values calculated cause # ulimb and flimb to preserve their relative lengths # while stretching. l1 = ulimb_p.length l2 = flimb_p.length if l1 < l2: ulimb_p.ik_stretch = (l1 ** (1 / 3)) / (l2 ** (1 / 3)) flimb_p.ik_stretch = 1.0 else: ulimb_p.ik_stretch = 1.0 flimb_p.ik_stretch = (l2 ** (1 / 3)) / (l1 ** (1 / 3)) # Pole target only translates pole_p.lock_location = False, False, False pole_p.lock_rotation = True, True, True pole_p.lock_rotation_w = True pole_p.lock_scale = True, True, True # Set up custom properties if self.switch is True: prop = rna_idprop_ui_prop_get(elimb_p, "ikfk_switch", create=True) elimb_p["ikfk_switch"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 if self.pole_parent is not None: prop = rna_idprop_ui_prop_get(pole_p, "follow", create=True) pole_p["follow"] = 1.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 prop = rna_idprop_ui_prop_get(elimb_p, "stretch_length", create=True) elimb_p["stretch_length"] = 1.0 prop["min"] = 0.05 prop["max"] = 20.0 prop["soft_min"] = 0.25 prop["soft_max"] = 4.0 prop = rna_idprop_ui_prop_get(elimb_p, "auto_stretch", create=True) elimb_p["auto_stretch"] = 1.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Stretch parameter drivers def add_stretch_drivers(pose_bone): driver = pose_bone.driver_add("scale", 1).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "stretch_length" driver = pose_bone.driver_add("scale", 0).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "stretch_length" driver = pose_bone.driver_add("scale", 2).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "stretch_length" add_stretch_drivers(ulimb_nostr_p) # Bend direction hint def add_bend_hint(pose_bone, axis): con = pose_bone.constraints.new('LIMIT_ROTATION') con.name = "bend_hint" con.owner_space = 'LOCAL' if axis == 'X': con.use_limit_x = True con.min_x = pi / 10 con.max_x = pi / 10 elif axis == '-X': con.use_limit_x = True con.min_x = -pi / 10 con.max_x = -pi / 10 elif axis == 'Y': con.use_limit_y = True con.min_y = pi / 10 con.max_y = pi / 10 elif axis == '-Y': con.use_limit_y = True con.min_y = -pi / 10 con.max_y = -pi / 10 elif axis == 'Z': con.use_limit_z = True con.min_z = pi / 10 con.max_z = pi / 10 elif axis == '-Z': con.use_limit_z = True con.min_z = -pi / 10 con.max_z = -pi / 10 if self.bend_hint: add_bend_hint(flimb_p, self.primary_rotation_axis) add_bend_hint(flimb_nostr_p, self.primary_rotation_axis) # Constrain normal IK chain to no-stretch IK chain con = ulimb_p.constraints.new('COPY_TRANSFORMS') con.name = "pre_stretch" con.target = self.obj con.subtarget = ulimb_nostr con = flimb_p.constraints.new('COPY_TRANSFORMS') con.name = "pre_stretch" con.target = self.obj con.subtarget = flimb_nostr # IK Constraints con = flimb_nostr_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = elimb_mch con.pole_target = self.obj con.pole_subtarget = pole con.pole_angle = pole_offset con.chain_count = 2 con = flimb_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = elimb_mch con.chain_count = 2 # Driver to enable/disable auto stretching IK chain fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["auto_stretch"]' # Stretch bone constraints con = ulimb_str_p.constraints.new('COPY_TRANSFORMS') con.name = "anchor" con.target = self.obj con.subtarget = ulimb con = ulimb_str_p.constraints.new('MAINTAIN_VOLUME') con.name = "stretch" con.owner_space = 'LOCAL' con = flimb_str_p.constraints.new('COPY_TRANSFORMS') con.name = "anchor" con.target = self.obj con.subtarget = flimb con = flimb_str_p.constraints.new('MAINTAIN_VOLUME') con.name = "stretch" con.owner_space = 'LOCAL' # Pole target parent if self.pole_parent is not None: con = pole_par_p.constraints.new('COPY_TRANSFORMS') con.name = "parent" con.target = self.obj con.subtarget = self.pole_parent driver = con.driver_add("influence").driver var = driver.variables.new() var.name = "follow" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = pole_p.path_from_id() + '["follow"]' driver.type = 'SUM' # Constrain org bones con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = ulimb_str if self.switch is True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = flimb_str if self.switch is True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = elimb_mch if self.switch is True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = elimb_p.path_from_id() + '["ikfk_switch"]' # VIS limb-end constraints con = viselimb_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[2] con = viselimb_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = elimb con.volume = 'NO_VOLUME' con.rest_length = viselimb_p.length # VIS pole constraints con = vispole_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[1] con = vispole_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = pole con.volume = 'NO_VOLUME' con.rest_length = vispole_p.length # Set layers if specified if self.layers: elimb_p.bone.layers = self.layers pole_p.bone.layers = self.layers viselimb_p.bone.layers = self.layers vispole_p.bone.layers = self.layers # Create widgets create_line_widget(self.obj, vispole) create_line_widget(self.obj, viselimb) create_sphere_widget(self.obj, pole) ob = create_widget(self.obj, elimb) if ob is not None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [ulimb, flimb, elimb, elimb_mch, pole, vispole, viselimb]
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 create_circle_widget(self.obj, neck_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[(len(self.org_bones) - 1) // 2]) create_circle_widget(self.obj, head_ctrl, radius=1.0, head_tail=0.5, bone_transform_name=self.org_bones[-1]) # Return control bones return (head_ctrl, neck_ctrl)
def build_crane_rig(context): # Define some useful variables: boneLayer = (False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) # Add the new armature object: bpy.ops.object.armature_add() rig = context.active_object #it will try to name the rig "Dolly_Rig" but if that name exists it will add .000 to the name if "Crane_Rig" not in context.scene.objects: rig.name = "Crane_Rig" else: rig.name = "Crane_Rig.000" rig["rig_id"] = "Crane_Rig" bpy.ops.object.mode_set(mode='EDIT') # Remove default bone: bones = rig.data.edit_bones bones.remove(bones[0]) # Add new bones: root = bones.new("Root") root.tail = (0.0, 0.0, -5.0) ctrlAimChild = bones.new("AIM_child") ctrlAimChild.head = (0.0, 10.0, 1.0) ctrlAimChild.tail = (0.0, 12.0, 1.0) ctrlAimChild.layers = boneLayer ctrlAim = bones.new("AIM") ctrlAim.head = (0.0, 10.0, 1.0) ctrlAim.tail = (0.0, 12.0, 1.0) ctrl = bones.new("CTRL") ctrl.head = (0.0, 1.0, 1.0) ctrl.tail = (0.0, 3.0, 1.0) arm = bones.new("Crane_Arm") arm.head = (0.0, 0.0, 1.0) arm.tail = (0.0, 1.0, 1.0) height = bones.new("Height") height.head = (0.0, 0.0, 0.0) height.tail = (0.0, 0.0, 1.0) # Setup hierarchy: ctrl.parent = arm ctrl.use_inherit_rotation = False ctrl.use_inherit_scale = False arm.parent = height arm.use_inherit_scale = False height.parent = root ctrlAim.parent = root ctrlAimChild.parent = ctrlAim #change display to BBone: it just looks nicer bpy.context.object.data.draw_type = 'BBONE' #change display to wire for object bpy.context.object.draw_type = 'WIRE' # jump into pose mode and change bones to euler bpy.ops.object.mode_set(mode='POSE') for x in bpy.context.object.pose.bones: x.rotation_mode = 'XYZ' #lock the relevant loc, rot and scale bpy.context.object.pose.bones["Crane_Arm"].lock_rotation = [False, True, False] bpy.context.object.pose.bones["Crane_Arm"].lock_scale = [True, False, True] bpy.context.object.pose.bones["Height"].lock_location = [True, True, True] bpy.context.object.pose.bones["Height"].lock_rotation = [True, True, True] bpy.context.object.pose.bones["Height"].lock_scale = [True, False, True] #add the custom bone shapes bpy.context.object.pose.bones["Root"].custom_shape = bpy.data.objects["WDGT_Camera_Root"] #add the widget as custom shape bpy.context.object.data.bones["Root"].show_wire = True #set the wireframe checkbox to true bpy.context.object.pose.bones["AIM"].custom_shape = bpy.data.objects["WDGT_AIM"] bpy.context.object.data.bones["AIM"].show_wire = True bpy.context.object.pose.bones["AIM"].custom_shape_transform = bpy.data.objects[rig.name].pose.bones["AIM_child"] #sets the "At" field to the child bpy.context.object.pose.bones["CTRL"].custom_shape = bpy.data.objects["WDGT_CTRL"] bpy.context.object.data.bones["CTRL"].show_wire = True #jump into object mode bpy.ops.object.mode_set(mode='OBJECT') # Add constraints to bones: con = rig.pose.bones['AIM_child'].constraints.new('COPY_ROTATION') con.target = rig con.subtarget = "CTRL" con = rig.pose.bones['CTRL'].constraints.new('TRACK_TO') con.target = rig con.subtarget = "AIM" con.use_target_z = True #Add custom Bone property to CTRL bone ob = bpy.context.object.pose.bones['CTRL'] prop = rna_idprop_ui_prop_get(ob,"Lock", create=True) ob["Lock"] = 1.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 #Add Driver to Lock/Unlock Camera from Aim Target rig = bpy.context.scene.objects.active pose_bone = bpy.data.objects[rig.name].pose.bones['CTRL'] constraint = pose_bone.constraints["Track To"] inf_driver = constraint.driver_add('influence') inf_driver.driver.type = 'SCRIPTED' var = inf_driver.driver.variables.new() var.name = 'var' var.type = 'SINGLE_PROP' #Target the Custom bone property var.targets[0].id = bpy.data.objects[rig.name] var.targets[0].data_path = 'pose.bones["CTRL"]["Lock"]' inf_driver.driver.expression = 'var' # Add the camera object: bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.camera_add(view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0,0,0)) cam = bpy.context.active_object #this will name the Camera Object if 'Crane_Camera' not in context.scene.objects: cam.name = "Crane_Camera" else: cam.name = "Crane_Camera.000" #this will name the camera Data Object if "Crane_Camera" not in bpy.context.scene.objects.data.camera: cam.data.name = "Crane_Camera" else: cam.data.name = "Crane_Camera.000" cam_data_name = bpy.context.object.data.name bpy.data.cameras[cam_data_name].draw_size = 1.0 cam.rotation_euler[0] = 1.5708 #rotate the camera 90 degrees in x cam.location = (0.0, -2.0, 0.0) #move the camera to the correct postion cam.parent = rig cam.parent_type = "BONE" cam.parent_bone = "CTRL" #Set new camera as active camera bpy.context.scene.camera = cam #make the camera non-selectable (this can be unlocked in the UI) bpy.context.object.hide_select = True #make the rig the active object before finishing bpy.context.scene.objects.active = rig cam.select = False rig.select = True return rig
def create_drivers(self, bones): bpy.ops.object.mode_set(mode="OBJECT") pb = self.obj.pose.bones ctrl = pb[bones["ik"]["mch_hand"][0]] props = ["IK_follow", "root/parent"] for prop in props: if prop == "IK_follow": ctrl[prop] = True rna_prop = rna_idprop_ui_prop_get(ctrl, prop, create=True) rna_prop["min"] = False rna_prop["max"] = True rna_prop["description"] = prop drv = ctrl.constraints[0].driver_add("mute").driver drv.type = "AVERAGE" var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = ctrl.path_from_id() + "[" + '"' + prop + '"' + "]" drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = "POLYNOMIAL" drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 if len(ctrl.constraints) > 1: drv = ctrl.constraints[1].driver_add("mute").driver drv.type = "AVERAGE" var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = ctrl.path_from_id() + "[" + '"' + prop + '"' + "]" drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = "POLYNOMIAL" drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 elif len(ctrl.constraints) > 1: ctrl[prop] = 0.0 rna_prop = rna_idprop_ui_prop_get(ctrl, prop, create=True) rna_prop["min"] = 0.0 rna_prop["max"] = 1.0 rna_prop["soft_min"] = 0.0 rna_prop["soft_max"] = 1.0 rna_prop["description"] = prop # drv = ctrl.constraints[ 0 ].driver_add("influence").driver # drv.type = 'AVERAGE' # # var = drv.variables.new() # var.name = prop # var.type = "SINGLE_PROP" # var.targets[0].id = self.obj # var.targets[0].data_path = \ # ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' # # drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] # # drv_modifier.mode = 'POLYNOMIAL' # drv_modifier.poly_order = 1 # drv_modifier.coefficients[0] = 1.0 # drv_modifier.coefficients[1] = -1.0 drv = ctrl.constraints[1].driver_add("influence").driver drv.type = "AVERAGE" var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = ctrl.path_from_id() + "[" + '"' + prop + '"' + "]"
def generate(self): bpy.ops.object.mode_set(mode='EDIT') # Create non-scaling parent bone if self.org_parent is not None: loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head) parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_rh") else: parent = None if not self.use_complex_limb: # Simple rig # Create bones ulimb = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(self.org_bones[0]))) flimb = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(self.org_bones[1]))) elimb = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) # Get edit bones eb = self.obj.data.edit_bones ulimb_e = eb[ulimb] flimb_e = eb[flimb] elimb_e = eb[elimb] # Parenting elimb_e.parent = flimb_e elimb_e.use_connect = True flimb_e.parent = ulimb_e flimb_e.use_connect = True if parent is not None: elimb_e.use_connect = False ulimb_e.parent = eb[parent] # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones ulimb_p = pb[ulimb] flimb_p = pb[flimb] elimb_p = pb[elimb] # Constrain def bones to org bones con = ulimb_p.constraints.new('COPY_TRANSFORMS') con.name = "def" con.target = self.obj con.subtarget = self.org_bones[0] con = flimb_p.constraints.new('COPY_TRANSFORMS') con.name = "def" con.target = self.obj con.subtarget = self.org_bones[1] con = elimb_p.constraints.new('COPY_TRANSFORMS') con.name = "def" con.target = self.obj con.subtarget = self.org_bones[2] return [] else: # Complex rig # Get the .R or .L off the end of the upper limb name if it exists lr = self.org_bones[0].split(".", 1) if len(lr) == 1: lr = "" else: lr = lr[1] # Create bones # Deformation bones ulimb1 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(insert_before_lr(self.org_bones[0], ".01")))) ulimb2 = copy_bone(self.obj, self.org_bones[0], make_deformer_name(strip_org(insert_before_lr(self.org_bones[0], ".02")))) flimb1 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(insert_before_lr(self.org_bones[1], ".01")))) flimb2 = copy_bone(self.obj, self.org_bones[1], make_deformer_name(strip_org(insert_before_lr(self.org_bones[1], ".02")))) elimb = copy_bone(self.obj, self.org_bones[2], make_deformer_name(strip_org(self.org_bones[2]))) # Bones for switchable smooth bbone transition at elbow/knee ulimb2_smoother = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_smth.02")))) flimb1_smoother = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_smth.01")))) flimb1_pos = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".01")))) # Elbow/knee junction bone junc = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], ".junc")))) # Hose controls uhoseend = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[0], "_hose_end"))) uhose = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[0], "_hose"))) jhose = new_bone(self.obj, self.junc_base_name + "_hose." + lr) fhose = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[1], "_hose"))) fhoseend = new_bone(self.obj, strip_org(insert_before_lr(self.org_bones[1], "_hose_end"))) # Hose control parents uhoseend_par = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(uhoseend, "_p")))) uhose_par = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(uhose, "_p")))) jhose_par = copy_bone(self.obj, junc, make_mechanism_name(strip_org(insert_before_lr(jhose, "_p")))) fhose_par = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(fhose, "_p")))) fhoseend_par = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(fhoseend, "_p")))) # Get edit bones eb = self.obj.data.edit_bones if parent is not None: parent_e = eb[parent] else: parent_e = None ulimb1_e = eb[ulimb1] ulimb2_e = eb[ulimb2] flimb1_e = eb[flimb1] flimb2_e = eb[flimb2] elimb_e = eb[elimb] ulimb2_smoother_e = eb[ulimb2_smoother] flimb1_smoother_e = eb[flimb1_smoother] flimb1_pos_e = eb[flimb1_pos] junc_e = eb[junc] uhoseend_e = eb[uhoseend] uhose_e = eb[uhose] jhose_e = eb[jhose] fhose_e = eb[fhose] fhoseend_e = eb[fhoseend] uhoseend_par_e = eb[uhoseend_par] uhose_par_e = eb[uhose_par] jhose_par_e = eb[jhose_par] fhose_par_e = eb[fhose_par] fhoseend_par_e = eb[fhoseend_par] # Parenting if parent is not None: ulimb1_e.use_connect = False ulimb1_e.parent = parent_e ulimb2_e.use_connect = False ulimb2_e.parent = eb[self.org_bones[0]] ulimb2_smoother_e.use_connect = True ulimb2_smoother_e.parent = ulimb2_e flimb1_e.use_connect = True flimb1_e.parent = flimb1_smoother_e flimb1_smoother_e.use_connect = False flimb1_smoother_e.parent = flimb1_pos_e flimb1_pos_e.use_connect = False flimb1_pos_e.parent = eb[self.org_bones[1]] flimb2_e.use_connect = False flimb2_e.parent = eb[self.org_bones[1]] elimb_e.use_connect = False elimb_e.parent = eb[self.org_bones[2]] junc_e.use_connect = False junc_e.parent = eb[self.org_bones[0]] uhoseend_e.use_connect = False uhoseend_e.parent = uhoseend_par_e uhose_e.use_connect = False uhose_e.parent = uhose_par_e jhose_e.use_connect = False jhose_e.parent = jhose_par_e fhose_e.use_connect = False fhose_e.parent = fhose_par_e fhoseend_e.use_connect = False fhoseend_e.parent = fhoseend_par_e uhoseend_par_e.use_connect = False uhoseend_par_e.parent = parent_e uhose_par_e.use_connect = False uhose_par_e.parent = parent_e jhose_par_e.use_connect = False jhose_par_e.parent = parent_e fhose_par_e.use_connect = False fhose_par_e.parent = parent_e fhoseend_par_e.use_connect = False fhoseend_par_e.parent = parent_e # Positioning ulimb1_e.length *= 0.5 ulimb2_e.head = Vector(ulimb1_e.tail) flimb1_e.length *= 0.5 flimb2_e.head = Vector(flimb1_e.tail) align_bone_roll(self.obj, flimb2, elimb) ulimb2_smoother_e.tail = Vector(flimb1_e.tail) ulimb2_smoother_e.roll = flimb1_e.roll flimb1_smoother_e.head = Vector(ulimb1_e.tail) flimb1_pos_e.length *= 0.5 junc_e.length *= 0.2 uhoseend_par_e.length *= 0.25 uhose_par_e.length *= 0.25 jhose_par_e.length *= 0.15 fhose_par_e.length *= 0.25 fhoseend_par_e.length *= 0.25 put_bone(self.obj, uhoseend_par, Vector(ulimb1_e.head)) put_bone(self.obj, uhose_par, Vector(ulimb1_e.tail)) put_bone(self.obj, jhose_par, Vector(ulimb2_e.tail)) put_bone(self.obj, fhose_par, Vector(flimb1_e.tail)) put_bone(self.obj, fhoseend_par, Vector(flimb2_e.tail)) put_bone(self.obj, uhoseend, Vector(ulimb1_e.head)) put_bone(self.obj, uhose, Vector(ulimb1_e.tail)) put_bone(self.obj, jhose, Vector(ulimb2_e.tail)) put_bone(self.obj, fhose, Vector(flimb1_e.tail)) put_bone(self.obj, fhoseend, Vector(flimb2_e.tail)) if 'X' in self.primary_rotation_axis: upoint = Vector(ulimb1_e.z_axis) fpoint = Vector(flimb1_e.z_axis) elif 'Z' in self.primary_rotation_axis: upoint = Vector(ulimb1_e.x_axis) fpoint = Vector(flimb1_e.x_axis) else: # Y upoint = Vector(ulimb1_e.z_axis) fpoint = Vector(flimb1_e.z_axis) if '-' not in self.primary_rotation_axis: upoint *= -1 fpoint *= -1 if 'Y' in self.primary_rotation_axis: uside = Vector(ulimb1_e.x_axis) fside = Vector(flimb1_e.x_axis) else: uside = Vector(ulimb1_e.y_axis) * -1 fside = Vector(flimb1_e.y_axis) * -1 uhoseend_e.tail = uhoseend_e.head + upoint uhose_e.tail = uhose_e.head + upoint jhose_e.tail = fhose_e.head + upoint + fpoint fhose_e.tail = fhose_e.head + fpoint fhoseend_e.tail = fhoseend_e.head + fpoint align_bone_z_axis(self.obj, uhoseend, uside) align_bone_z_axis(self.obj, uhose, uside) align_bone_z_axis(self.obj, jhose, uside + fside) align_bone_z_axis(self.obj, fhose, fside) align_bone_z_axis(self.obj, fhoseend, fside) l = 0.125 * (ulimb1_e.length + ulimb2_e.length + flimb1_e.length + flimb2_e.length) uhoseend_e.length = l uhose_e.length = l jhose_e.length = l fhose_e.length = l fhoseend_e.length = l # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones ulimb1_p = pb[ulimb1] ulimb2_p = pb[ulimb2] flimb1_p = pb[flimb1] flimb2_p = pb[flimb2] elimb_p = pb[elimb] ulimb2_smoother_p = pb[ulimb2_smoother] flimb1_smoother_p = pb[flimb1_smoother] flimb1_pos_p = pb[flimb1_pos] junc_p = pb[junc] uhoseend_p = pb[uhoseend] uhose_p = pb[uhose] jhose_p = pb[jhose] fhose_p = pb[fhose] fhoseend_p = pb[fhoseend] #uhoseend_par_p = pb[uhoseend_par] uhose_par_p = pb[uhose_par] jhose_par_p = pb[jhose_par] fhose_par_p = pb[fhose_par] fhoseend_par_p = pb[fhoseend_par] # Lock axes uhose_p.lock_rotation = (True, True, True) uhose_p.lock_rotation_w = True uhose_p.lock_scale = (True, True, True) jhose_p.lock_rotation = (True, True, True) jhose_p.lock_rotation_w = True jhose_p.lock_scale = (True, True, True) fhose_p.lock_rotation = (True, True, True) fhose_p.lock_rotation_w = True fhose_p.lock_scale = (True, True, True) # B-bone settings ulimb2_p.bone.bbone_segments = 16 ulimb2_p.bone.bbone_easein = 0.0 ulimb2_p.bone.bbone_easeout = 1.0 ulimb2_smoother_p.bone.bbone_segments = 16 ulimb2_smoother_p.bone.bbone_easein = 1.0 ulimb2_smoother_p.bone.bbone_easeout = 0.0 flimb1_p.bone.bbone_segments = 16 flimb1_p.bone.bbone_easein = 1.0 flimb1_p.bone.bbone_easeout = 0.0 flimb1_smoother_p.bone.bbone_segments = 16 flimb1_smoother_p.bone.bbone_easein = 0.0 flimb1_smoother_p.bone.bbone_easeout = 1.0 # Custom properties prop = rna_idprop_ui_prop_get(jhose_p, "smooth_bend", create=True) jhose_p["smooth_bend"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Constraints con = ulimb1_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = uhoseend con = ulimb1_p.constraints.new('COPY_SCALE') con.name = "anchor" con.target = self.obj con.subtarget = self.org_bones[0] con = ulimb1_p.constraints.new('DAMPED_TRACK') con.name = "track" con.target = self.obj con.subtarget = uhose con = ulimb1_p.constraints.new('STRETCH_TO') con.name = "track" con.target = self.obj con.subtarget = uhose con.volume = 'NO_VOLUME' con = ulimb2_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = uhose con = ulimb2_p.constraints.new('DAMPED_TRACK') con.name = "track" con.target = self.obj con.subtarget = jhose con = ulimb2_p.constraints.new('STRETCH_TO') con.name = "track" con.target = self.obj con.subtarget = jhose con.volume = 'NO_VOLUME' con = ulimb2_smoother_p.constraints.new('COPY_TRANSFORMS') con.name = "smoother" con.target = self.obj con.subtarget = flimb1_pos fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'SUM' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = jhose_p.path_from_id() + '["smooth_bend"]' con = flimb1_pos_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = jhose con = flimb1_pos_p.constraints.new('DAMPED_TRACK') con.name = "track" con.target = self.obj con.subtarget = fhose con = flimb1_pos_p.constraints.new('STRETCH_TO') con.name = "track" con.target = self.obj con.subtarget = fhose con.volume = 'NO_VOLUME' con = flimb1_p.constraints.new('COPY_TRANSFORMS') con.name = "position" con.target = self.obj con.subtarget = flimb1_pos con = flimb1_smoother_p.constraints.new('COPY_TRANSFORMS') con.name = "smoother" con.target = self.obj con.subtarget = ulimb2 fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'SUM' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = jhose_p.path_from_id() + '["smooth_bend"]' con = flimb1_smoother_p.constraints.new('STRETCH_TO') con.name = "track" con.target = self.obj con.subtarget = jhose con.volume = 'NO_VOLUME' con = flimb2_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = fhose con = flimb2_p.constraints.new('COPY_ROTATION') con.name = "twist" con.target = self.obj con.subtarget = elimb con = flimb2_p.constraints.new('DAMPED_TRACK') con.name = "track" con.target = self.obj con.subtarget = fhoseend con = flimb2_p.constraints.new('STRETCH_TO') con.name = "track" con.target = self.obj con.subtarget = fhoseend con.volume = 'NO_VOLUME' con = junc_p.constraints.new('COPY_TRANSFORMS') con.name = "bend" con.target = self.obj con.subtarget = self.org_bones[1] con.influence = 0.5 con = uhose_par_p.constraints.new('COPY_ROTATION') con.name = "follow" con.target = self.obj con.subtarget = self.org_bones[0] con.influence = 1.0 con = uhose_par_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = self.org_bones[0] con.influence = 1.0 con = uhose_par_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = jhose con.influence = 0.5 con = jhose_par_p.constraints.new('COPY_ROTATION') con.name = "follow" con.target = self.obj con.subtarget = junc con.influence = 1.0 con = jhose_par_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = junc con.influence = 1.0 con = fhose_par_p.constraints.new('COPY_ROTATION') con.name = "follow" con.target = self.obj con.subtarget = self.org_bones[1] con.influence = 1.0 con = fhose_par_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = jhose con.influence = 1.0 con = fhose_par_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = self.org_bones[2] con.influence = 0.5 con = fhoseend_par_p.constraints.new('COPY_ROTATION') con.name = "follow" con.target = self.obj con.subtarget = self.org_bones[1] con.influence = 1.0 con = fhoseend_par_p.constraints.new('COPY_LOCATION') con.name = "anchor" con.target = self.obj con.subtarget = self.org_bones[2] con.influence = 1.0 # Layers if self.layers: uhoseend_p.bone.layers = self.layers uhose_p.bone.layers = self.layers jhose_p.bone.layers = self.layers fhose_p.bone.layers = self.layers fhoseend_p.bone.layers = self.layers else: layers = list(pb[self.org_bones[0]].bone.layers) uhoseend_p.bone.layers = layers uhose_p.bone.layers = layers jhose_p.bone.layers = layers fhose_p.bone.layers = layers fhoseend_p.bone.layers = layers # Create widgets create_sphere_widget(self.obj, uhoseend) create_sphere_widget(self.obj, uhose) create_sphere_widget(self.obj, jhose) create_sphere_widget(self.obj, fhose) create_sphere_widget(self.obj, fhoseend) return [uhoseend, uhose, jhose, fhose, fhoseend]
def create_def(self, tweaks): org_bones = self.org_bones bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones def_bones = [] for i, org in enumerate(org_bones): if i < len(org_bones) - 1: # Create segments if specified for j in range(self.segments): name = get_bone_name(strip_org(org), 'def') def_name = copy_bone(self.obj, org, name) eb[def_name].length /= self.segments # If we have more than one segments, place the 2nd and # onwards on the tail of the previous bone if j > 0: put_bone(self.obj, def_name, eb[def_bones[-1]].tail) def_bones += [def_name] else: name = get_bone_name(strip_org(org), 'def') def_name = copy_bone(self.obj, org, name) def_bones.append(def_name) # Parent deform bones for i, b in enumerate(def_bones): if i > 0: # For all bones but the first (which has no parent) eb[b].parent = eb[def_bones[i - 1]] # to previous eb[b].use_connect = True # Constraint def to tweaks for d, t in zip(def_bones, tweaks): tidx = tweaks.index(t) make_constraint(self, d, { 'constraint': 'COPY_TRANSFORMS', 'subtarget': t }) if tidx != len(tweaks) - 1: make_constraint(self, d, { 'constraint': 'DAMPED_TRACK', 'subtarget': tweaks[tidx + 1], }) make_constraint(self, d, { 'constraint': 'STRETCH_TO', 'subtarget': tweaks[tidx + 1], }) # Create bbone segments for bone in def_bones[:-1]: self.obj.data.bones[bone].bbone_segments = self.bbones self.obj.data.bones[def_bones[0]].bbone_in = 0.0 self.obj.data.bones[def_bones[-2]].bbone_out = 0.0 self.obj.data.bones[def_bones[-1]].bbone_in = 0.0 self.obj.data.bones[def_bones[-1]].bbone_out = 0.0 # Rubber hose drivers pb = self.obj.pose.bones for i, t in enumerate(tweaks[1:-1]): # Create custom property on tweak bone to control rubber hose name = 'rubber_tweak' if i == trunc(len(tweaks[1:-1]) / 2): pb[t][name] = 0.0 else: pb[t][name] = 1.0 prop = rna_idprop_ui_prop_get(pb[t], name, create=True) prop["min"] = 0.0 prop["max"] = 2.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = name for j, d in enumerate(def_bones[:-1]): drvs = {} if j != 0: tidx = j drvs[tidx] = self.obj.data.bones[d].driver_add( "bbone_in").driver if j != len(def_bones[:-1]) - 1: tidx = j + 1 drvs[tidx] = self.obj.data.bones[d].driver_add( "bbone_out").driver for d in drvs: drv = drvs[d] name = 'rubber_tweak' drv.type = 'AVERAGE' var = drv.variables.new() var.name = name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb[tweaks[d]].path_from_id() + \ '[' + '"' + name + '"' + ']' return def_bones
def generate(self): context = self.context metarig = self.metarig scene = self.scene id_store = self.id_store view_layer = self.view_layer t = Timer() self.usable_collections = list_layer_collections( view_layer.layer_collection, selectable=True) if self.layer_collection not in self.usable_collections: metarig_collections = filter_layer_collections_by_object( self.usable_collections, self.metarig) self.layer_collection = (metarig_collections + [view_layer.layer_collection])[0] self.collection = self.layer_collection.collection bpy.ops.object.mode_set(mode='OBJECT') #------------------------------------------ # Create/find the rig object and set it up obj = self.__create_rig_object() # Get rid of anim data in case the rig already existed print("Clear rig animation data.") obj.animation_data_clear() obj.data.animation_data_clear() select_object(context, obj, deselect_all=True) #------------------------------------------ # Create Group widget self.__create_widget_group() t.tick("Create main WGTS: ") #------------------------------------------ # Get parented objects to restore later childs = {} # {object: bone} for child in obj.children: childs[child] = child.parent_bone #------------------------------------------ # Copy bones from metarig to obj (adds ORG_PREFIX) self.__duplicate_rig() obj.data.use_mirror_x = False t.tick("Duplicate rig: ") #------------------------------------------ # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "rig_id", create=True) obj.data["rig_id"] = self.rig_id self.script = rig_ui_template.ScriptGenerator(self) #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.instantiate_rig_tree() t.tick("Instantiate rigs: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.invoke_initialize() t.tick("Initialize rigs: ") #------------------------------------------ bpy.ops.object.mode_set(mode='EDIT') self.invoke_prepare_bones() t.tick("Prepare bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='EDIT') self.__create_root_bone() self.invoke_generate_bones() t.tick("Generate bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='EDIT') self.invoke_parent_bones() self.__parent_bones_to_root() t.tick("Parent bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.invoke_configure_bones() t.tick("Configure bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.invoke_preapply_bones() t.tick("Preapply bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='EDIT') self.invoke_apply_bones() t.tick("Apply bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.invoke_rig_bones() t.tick("Rig bones: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') create_root_widget(obj, "root") self.invoke_generate_widgets() t.tick("Generate widgets: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.__lock_transforms() self.__assign_layers() self.__compute_visible_layers() self.__restore_driver_vars() t.tick("Assign layers: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.invoke_finalize() t.tick("Finalize: ") #------------------------------------------ bpy.ops.object.mode_set(mode='OBJECT') self.__assign_widgets() # Create Selection Sets create_selection_sets(obj, metarig) # Create Bone Groups create_bone_groups(obj, metarig, self.layer_group_priorities) t.tick("The rest: ") #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') obj.data.pose_position = 'POSE' # Restore parent to bones for child, sub_parent in childs.items(): if sub_parent in obj.pose.bones: mat = child.matrix_world.copy() child.parent_bone = sub_parent child.matrix_world = mat #---------------------------------- # Restore active collection view_layer.active_layer_collection = self.layer_collection
def generate(self): """ Generate the rig. Do NOT modify any of the original bones, except for adding constraints. The main armature should be selected and active before this is called. """ bpy.ops.object.mode_set(mode='EDIT') # Create the bones uarm = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_ik")))) farm = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_ik")))) hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], "_ik"))) pole = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole"))) vishand = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], "_ik"))) vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole"))) # Get edit bones eb = self.obj.data.edit_bones uarm_e = eb[uarm] farm_e = eb[farm] hand_e = eb[hand] pole_e = eb[pole] vishand_e = eb[vishand] vispole_e = eb[vispole] # Parenting farm_e.parent = uarm_e hand_e.use_connect = False hand_e.parent = None pole_e.use_connect = False vishand_e.use_connect = False vishand_e.parent = None vispole_e.use_connect = False vispole_e.parent = None # Misc hand_e.use_local_location = False vishand_e.hide_select = True vispole_e.hide_select = True # Positioning v1 = farm_e.tail - uarm_e.head if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: v2 = v1.cross(farm_e.x_axis) if (v2 * farm_e.z_axis) > 0.0: v2 *= -1.0 else: v2 = v1.cross(farm_e.z_axis) if (v2 * farm_e.x_axis) < 0.0: v2 *= -1.0 v2.normalize() v2 *= v1.length if '-' in self.primary_rotation_axis: v2 *= -1 pole_e.head = farm_e.head + v2 pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) pole_e.roll = 0.0 vishand_e.tail = vishand_e.head + Vector((0, 0, v1.length / 32)) vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) # Determine the pole offset value plane = (farm_e.tail - uarm_e.head).normalize() vec1 = uarm_e.x_axis.normalize() vec2 = (pole_e.head - uarm_e.head).normalize() pole_offset = angle_on_plane(plane, vec1, vec2) # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones uarm_p = pb[uarm] farm_p = pb[farm] hand_p = pb[hand] pole_p = pb[pole] vishand_p = pb[vishand] vispole_p = pb[vispole] # Set the elbow to only bend on the primary axis if 'X' in self.primary_rotation_axis: farm_p.lock_ik_y = True farm_p.lock_ik_z = True elif 'Y' in self.primary_rotation_axis: farm_p.lock_ik_x = True farm_p.lock_ik_z = True else: farm_p.lock_ik_x = True farm_p.lock_ik_y = True # Pole target only translates pole_p.lock_location = False, False, False pole_p.lock_rotation = True, True, True pole_p.lock_rotation_w = True pole_p.lock_scale = True, True, True # Set up custom properties if self.switch == True: prop = rna_idprop_ui_prop_get(hand_p, "ikfk_switch", create=True) hand_p["ikfk_switch"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # IK Constraint con = farm_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = hand con.pole_target = self.obj con.pole_subtarget = pole con.pole_angle = pole_offset con.chain_count = 2 # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = uarm if self.switch == True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = farm if self.switch == True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = hand if self.switch == True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' # VIS hand constraints con = vishand_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[2] con = vishand_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = hand con.volume = 'NO_VOLUME' con.rest_length = vishand_p.length # VIS pole constraints con = vispole_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[1] con = vispole_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = pole con.volume = 'NO_VOLUME' con.rest_length = vispole_p.length # Set layers if specified if self.layers: hand_p.bone.layers = self.layers pole_p.bone.layers = self.layers vishand_p.bone.layers = self.layers vispole_p.bone.layers = self.layers # Create widgets create_line_widget(self.obj, vispole) create_line_widget(self.obj, vishand) create_sphere_widget(self.obj, pole) ob = create_widget(self.obj, hand) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [hand, pole]
def create_paw(self, bones): org_bones = list([self.org_bones[0]] + connected_children_names(self.obj, self.org_bones[0])) bones['ik']['ctrl']['terminal'] = [] bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones # Create IK paw control ctrl = get_bone_name(org_bones[2], 'ctrl', 'ik') ctrl = copy_bone(self.obj, org_bones[2], ctrl) # clear parent (so that rigify will parent to root) eb[ctrl].parent = None eb[ctrl].use_connect = False # MCH for ik control ctrl_socket = copy_bone( self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'ik_socket')) eb[ctrl_socket].tail = eb[ctrl_socket].head + 0.8 * ( eb[ctrl_socket].tail - eb[ctrl_socket].head) eb[ctrl_socket].parent = None eb[ctrl].parent = eb[ctrl_socket] ctrl_root = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'ik_root')) eb[ctrl_root].tail = eb[ctrl_root].head + 0.7 * (eb[ctrl_root].tail - eb[ctrl_root].head) eb[ctrl_root].use_connect = False eb[ctrl_root].parent = eb['root'] if eb[org_bones[0]].parent: paw_parent = eb[org_bones[0]].parent ctrl_parent = copy_bone( self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'ik_parent')) eb[ctrl_parent].tail = eb[ctrl_parent].head + 0.6 * ( eb[ctrl_parent].tail - eb[ctrl_parent].head) eb[ctrl_parent].use_connect = False eb[ctrl_parent].parent = eb[org_bones[0]].parent else: paw_parent = None # Create heel control bone heel = get_bone_name(org_bones[2], 'ctrl', 'heel_ik') heel = copy_bone(self.obj, org_bones[2], heel) # clear parent eb[heel].parent = None eb[heel].use_connect = False # Parent eb[heel].parent = eb[ctrl] eb[heel].use_connect = False flip_bone(self.obj, heel) eb[bones['ik']['mch_target']].parent = eb[heel] eb[bones['ik']['mch_target']].use_connect = False # Reset control position and orientation l = eb[ctrl].length orient_bone(self, eb[ctrl], 'y', reverse=True) eb[ctrl].length = l # Set up constraints # Constrain ik ctrl to root / parent make_constraint(self, ctrl_socket, { 'constraint': 'COPY_TRANSFORMS', 'subtarget': ctrl_root, }) if paw_parent: make_constraint( self, ctrl_socket, { 'constraint': 'COPY_TRANSFORMS', 'subtarget': ctrl_parent, 'influence': 0.0, }) # Constrain mch target bone to the ik control and mch stretch make_constraint( self, bones['ik']['mch_target'], { 'constraint': 'COPY_LOCATION', 'subtarget': bones['ik']['mch_str'], 'head_tail': 1.0 }) # Constrain mch ik stretch bone to the ik control make_constraint(self, bones['ik']['mch_str'], { 'constraint': 'DAMPED_TRACK', 'subtarget': heel, 'head_tail': 1.0 }) make_constraint(self, bones['ik']['mch_str'], { 'constraint': 'STRETCH_TO', 'subtarget': heel, 'head_tail': 1.0 }) make_constraint( self, bones['ik']['mch_str'], { 'constraint': 'LIMIT_SCALE', 'use_min_y': True, 'use_max_y': True, 'max_y': 1.05, 'owner_space': 'LOCAL' }) # Create ik/fk switch property pb = self.obj.pose.bones pb_parent = pb[bones['parent']] pb_parent['IK_Strertch'] = 1.0 prop = rna_idprop_ui_prop_get(pb_parent, 'IK_Strertch', create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = 'IK Stretch' # Add driver to limit scale constraint influence b = bones['ik']['mch_str'] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create paw widget create_foot_widget(self.obj, ctrl, bone_transform_name=None) # Create heel ctrl locks pb[heel].lock_location = True, True, True # Add ballsocket widget to heel create_ballsocket_widget(self.obj, heel, bone_transform_name=None) bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones if len(org_bones) >= 4: # Create toes control bone toes = get_bone_name(org_bones[3], 'ctrl') toes = copy_bone(self.obj, org_bones[3], toes) eb[toes].use_connect = False eb[toes].parent = eb[org_bones[3]] # Create toes mch bone toes_mch = get_bone_name(org_bones[3], 'mch') toes_mch = copy_bone(self.obj, org_bones[3], toes_mch) eb[toes_mch].use_connect = False eb[toes_mch].parent = eb[ctrl] eb[toes_mch].length /= 4 # Constrain 4th ORG to toes MCH bone make_constraint(self, org_bones[3], { 'constraint': 'COPY_TRANSFORMS', 'subtarget': toes_mch }) make_constraint(self, bones['def'][-1], { 'constraint': 'DAMPED_TRACK', 'subtarget': toes, 'head_tail': 1 }) make_constraint(self, bones['def'][-1], { 'constraint': 'STRETCH_TO', 'subtarget': toes, 'head_tail': 1 }) # Find IK/FK switch property pb = self.obj.pose.bones prop = rna_idprop_ui_prop_get(pb[bones['parent']], 'IK/FK') # Add driver to limit scale constraint influence b = org_bones[3] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create toe circle widget create_circle_widget(self.obj, toes, radius=0.4, head_tail=0.5) bones['ik']['ctrl']['terminal'] += [toes] bones['ik']['ctrl']['terminal'] += [heel, ctrl] if paw_parent: bones['ik']['mch_foot'] = [ctrl_socket, ctrl_root, ctrl_parent] else: bones['ik']['mch_foot'] = [ctrl_socket, ctrl_root] return bones
def copy_bone(armature, bone_name, assign_name=''): """ Makes a copy of the given bone in the given armature object. Returns the resulting bone's name. """ print("copy_bone: Copying " + bone_name + " to " + assign_name) #if bone_name not in obj.data.bones: if bone_name not in armature.data.edit_bones: raise utilities.MetarigError( "copy_bone(): bone '%s' not found, cannot copy it" % bone_name) if armature == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': if assign_name == '': assign_name = bone_name # Copy the edit bone edit_bone_1 = armature.data.edit_bones[bone_name] edit_bone_2 = armature.data.edit_bones.new(assign_name) bone_name_1 = bone_name bone_name_2 = edit_bone_2.name edit_bone_2.parent = edit_bone_1.parent edit_bone_2.use_connect = edit_bone_1.use_connect # Copy edit bone attributes edit_bone_2.layers = list(edit_bone_1.layers) edit_bone_2.head = Vector(edit_bone_1.head) edit_bone_2.tail = Vector(edit_bone_1.tail) edit_bone_2.roll = edit_bone_1.roll edit_bone_2.use_inherit_rotation = edit_bone_1.use_inherit_rotation edit_bone_2.use_inherit_scale = edit_bone_1.use_inherit_scale edit_bone_2.use_local_location = edit_bone_1.use_local_location edit_bone_2.use_deform = edit_bone_1.use_deform edit_bone_2.bbone_segments = edit_bone_1.bbone_segments edit_bone_2.bbone_easein = edit_bone_1.bbone_easein edit_bone_2.bbone_easeout = edit_bone_1.bbone_easeout bpy.ops.object.mode_set(mode='OBJECT') # Get the pose bones pose_bone_1 = armature.pose.bones[bone_name_1] pose_bone_2 = armature.pose.bones[bone_name_2] # Copy pose bone attributes pose_bone_2.rotation_mode = pose_bone_1.rotation_mode pose_bone_2.rotation_axis_angle = tuple( pose_bone_1.rotation_axis_angle) pose_bone_2.rotation_euler = tuple(pose_bone_1.rotation_euler) pose_bone_2.rotation_quaternion = tuple( pose_bone_1.rotation_quaternion) pose_bone_2.lock_location = tuple(pose_bone_1.lock_location) pose_bone_2.lock_scale = tuple(pose_bone_1.lock_scale) pose_bone_2.lock_rotation = tuple(pose_bone_1.lock_rotation) pose_bone_2.lock_rotation_w = pose_bone_1.lock_rotation_w pose_bone_2.lock_rotations_4d = pose_bone_1.lock_rotations_4d # Copy custom properties for key in pose_bone_1.keys(): if key != "_RNA_UI" \ and key != "rigify_parameters" \ and key != "rigify_type": prop1 = rna_idprop_ui_prop_get(pose_bone_1, key, create=False) prop2 = rna_idprop_ui_prop_get(pose_bone_2, key, create=True) pose_bone_2[key] = pose_bone_1[key] for key in prop1.keys(): prop2[key] = prop1[key] bpy.ops.object.mode_set(mode='EDIT') print("copy_bone: End copy") return bone_name_2 else: raise utilities.MetarigError("Cannot copy bones outside of edit mode")
def execute(self, context): sc = bpy.context.scene mode = bpy.context.mode BONE_LENGTH = 0.5 PARENT_NAME = 'Parent' CAMERA_NAME = 'Camera' LEFT_CORNER_NAME = 'Left Corner' RIGHT_CORNER_NAME = 'Right Corner' CENTER_NAME = 'Center' # Create camera camera = bpy.data.cameras.new(CAMERA_NAME) camera.lens = 170.0 camera_obj = bpy.data.objects.new(CAMERA_NAME, camera) sc.collection.objects.link(camera_obj) # Create armature camera_rig = bpy.data.armatures.new('Camera Rig') camera_rig_object = bpy.data.objects.new('Camera Rig', camera_rig) sc.collection.objects.link(camera_rig_object) camera_rig_object.location = sc.cursor.location bpy.context.view_layer.objects.active = camera_rig_object bpy.ops.object.mode_set(mode='EDIT') eb = camera_rig.edit_bones parent_bone = eb.new(PARENT_NAME) parent_bone.tail = Vector((0.0, 0.0, BONE_LENGTH)) camera_bone = eb.new('Camera') camera_bone.tail = Vector((0.0, 0.0, BONE_LENGTH)) camera_bone.parent = parent_bone corners = camera.view_frame(scene=sc)[1:3] left_corner = eb.new(LEFT_CORNER_NAME) left_corner.head = (parent_bone.matrix @ corners[1]).normalized() * 10 left_corner.tail = left_corner.head + Vector((0.0, 0.0, BONE_LENGTH)) left_corner.parent = parent_bone right_corner = eb.new(RIGHT_CORNER_NAME) right_corner.head = (parent_bone.matrix @ corners[0]).normalized() * 10 right_corner.tail = right_corner.head + Vector((0.0, 0.0, BONE_LENGTH)) right_corner.parent = parent_bone corner_distance_x = (left_corner.head - right_corner.head).length corner_distance_y = -left_corner.head.z corner_distance_z = left_corner.head.y center = eb.new(CENTER_NAME) center.head = ((right_corner.head + left_corner.head) / 2.0) center.tail = center.head + Vector((0.0, 0.0, BONE_LENGTH)) center.parent = parent_bone center.layers = [layer == 31 for layer in range(32)] bpy.ops.object.mode_set(mode='POSE') bones = camera_rig_object.data.bones pb = camera_rig_object.pose.bones # Bone drivers center_drivers = pb[CENTER_NAME].driver_add("location") # Center X driver d = center_drivers[0].driver d.type = 'AVERAGE' for corner in ('left', 'right'): var = d.variables.new() var.name = corner var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_X' var.targets[0].transform_space = 'TRANSFORM_SPACE' # Center Y driver d = center_drivers[1].driver d.type = 'SCRIPTED' d.expression = '({distance_x} - (left_x-right_x))*(res_y/res_x)/2 + (left_y + right_y)/2'.format( distance_x=corner_distance_x) for direction in ('x', 'y'): for corner in ('left', 'right'): var = d.variables.new() var.name = '%s_%s' % (corner, direction) var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_' + direction.upper() var.targets[0].transform_space = 'TRANSFORM_SPACE' var = d.variables.new() var.name = 'res_' + direction var.type = 'SINGLE_PROP' var.targets[0].id_type = 'SCENE' var.targets[0].id = sc var.targets[0].data_path = 'render.resolution_' + direction # Center Z driver d = center_drivers[2].driver d.type = 'AVERAGE' for corner in ('left', 'right'): var = d.variables.new() var.name = corner var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_Z' var.targets[0].transform_space = 'TRANSFORM_SPACE' # Bone constraints con = pb[CAMERA_NAME].constraints.new('DAMPED_TRACK') con.target = camera_rig_object con.subtarget = CENTER_NAME con.track_axis = 'TRACK_NEGATIVE_Z' # Bone Display left_shape = create_corner_shape(LEFT_CORNER_NAME, reverse=True) bones[LEFT_CORNER_NAME].show_wire = True pb[LEFT_CORNER_NAME].custom_shape = left_shape right_shape = create_corner_shape(RIGHT_CORNER_NAME) bones[RIGHT_CORNER_NAME].show_wire = True pb[RIGHT_CORNER_NAME].custom_shape = right_shape parent_shape = create_circle_shape(PARENT_NAME) bones[PARENT_NAME].show_wire = True pb[PARENT_NAME].custom_shape = parent_shape camera_shape = create_circle_shape(CAMERA_NAME) bones[CAMERA_NAME].show_wire = True pb[CAMERA_NAME].custom_shape = camera_shape pb[CAMERA_NAME].custom_shape_scale = 0.7 # Bone transforms pb[LEFT_CORNER_NAME].rotation_mode = 'XYZ' pb[LEFT_CORNER_NAME].lock_rotation = (True, ) * 3 pb[RIGHT_CORNER_NAME].rotation_mode = 'XYZ' pb[RIGHT_CORNER_NAME].lock_rotation = (True, ) * 3 pb[PARENT_NAME].rotation_mode = 'XYZ' pb[CAMERA_NAME].rotation_mode = 'XYZ' pb[CAMERA_NAME].lock_rotation = (True, ) * 3 pb[CAMERA_NAME].lock_scale = (True, ) * 3 # Camera settings camera_rig_object['rotation_shift'] = 0.0 prop = rna_idprop_ui_prop_get(camera_rig_object, 'rotation_shift', create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = 'rotation_shift' # Rotation / shift switch d = con.driver_add('influence').driver d.expression = '1 - rotation_shift' var = d.variables.new() var.name = 'rotation_shift' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'OBJECT' var.targets[0].id = camera_rig_object var.targets[0].data_path = '["rotation_shift"]' # Focal length driver d = camera.driver_add('lens').driver d.expression = 'abs({distance_z} - (left_z + right_z)/2 + cam_z) * size / frame_width'.format( distance_z=corner_distance_z) var = d.variables.new() var.name = 'frame_width' var.type = 'LOC_DIFF' var.targets[0].id = camera_rig_object var.targets[0].bone_target = LEFT_CORNER_NAME var.targets[0].transform_space = 'WORLD_SPACE' var.targets[1].id = camera_rig_object var.targets[1].bone_target = RIGHT_CORNER_NAME var.targets[1].transform_space = 'WORLD_SPACE' for corner in ('left', 'right'): var = d.variables.new() var.name = corner + '_z' var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_Z' var.targets[0].transform_space = 'TRANSFORM_SPACE' var = d.variables.new() var.name = 'cam_z' var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = CAMERA_NAME var.targets[0].transform_type = 'LOC_Z' var.targets[0].transform_space = 'TRANSFORM_SPACE' var = d.variables.new() var.name = 'size' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'CAMERA' var.targets[0].id = camera var.targets[0].data_path = 'sensor_width' # Orthographic scale driver d = camera.driver_add('ortho_scale').driver d.expression = 'abs({distance_x} - (left_x - right_x))'.format( distance_x=corner_distance_x) for corner in ('left', 'right'): var = d.variables.new() var.name = corner + '_x' var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_X' var.targets[0].transform_space = 'TRANSFORM_SPACE' # Shift driver X d = camera.driver_add('shift_x').driver d.expression = 'rotation_shift * (((left_x + right_x)/2 - cam_x) * lens / abs({distance_z} - (left_z + right_z)/2) / sensor_width)'.format( distance_z=corner_distance_z) var = d.variables.new() var.name = 'rotation_shift' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'OBJECT' var.targets[0].id = camera_rig_object var.targets[0].data_path = '["rotation_shift"]' for direction in ('x', 'z'): for corner in ('left', 'right'): var = d.variables.new() var.name = '%s_%s' % (corner, direction) var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_' + direction.upper() var.targets[0].transform_space = 'TRANSFORM_SPACE' var = d.variables.new() var.name = 'cam_x' var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = CAMERA_NAME var.targets[0].transform_type = 'LOC_X' var.targets[0].transform_space = 'TRANSFORM_SPACE' var = d.variables.new() var.name = 'lens' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'CAMERA' var.targets[0].id = camera var.targets[0].data_path = 'lens' var = d.variables.new() var.name = 'sensor_width' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'CAMERA' var.targets[0].id = camera var.targets[0].data_path = 'sensor_width' # Shift driver Y d = camera.driver_add('shift_y').driver d.expression = 'rotation_shift * -(({distance_y} - (left_y + right_y)/2 - cam_y) * lens / abs({distance_z} - (left_z + right_z)/2) / sensor_width - (res_y/res_x)/2)'.format( distance_y=corner_distance_y, distance_z=corner_distance_z) var = d.variables.new() var.name = 'rotation_shift' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'OBJECT' var.targets[0].id = camera_rig_object var.targets[0].data_path = '["rotation_shift"]' for direction in ('y', 'z'): for corner in ('left', 'right'): var = d.variables.new() var.name = '%s_%s' % (corner, direction) var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = corner.capitalize() + ' Corner' var.targets[0].transform_type = 'LOC_' + direction.upper() var.targets[0].transform_space = 'TRANSFORM_SPACE' for direction in ('x', 'y'): var = d.variables.new() var.name = 'res_' + direction var.type = 'SINGLE_PROP' var.targets[0].id_type = 'SCENE' var.targets[0].id = sc var.targets[0].data_path = 'render.resolution_' + direction var = d.variables.new() var.name = 'cam_y' var.type = 'TRANSFORMS' var.targets[0].id = camera_rig_object var.targets[0].bone_target = CAMERA_NAME var.targets[0].transform_type = 'LOC_Y' var.targets[0].transform_space = 'TRANSFORM_SPACE' var = d.variables.new() var.name = 'lens' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'CAMERA' var.targets[0].id = camera var.targets[0].data_path = 'lens' var = d.variables.new() var.name = 'sensor_width' var.type = 'SINGLE_PROP' var.targets[0].id_type = 'CAMERA' var.targets[0].id = camera var.targets[0].data_path = 'sensor_width' # Parent camera object to rig camera_obj.parent = camera_rig_object camera_obj.parent_type = 'BONE' camera_obj.parent_bone = 'Camera' camera_obj.location.y = -BONE_LENGTH camera_obj.lock_location = (True, ) * 3 camera_obj.lock_rotation = (True, ) * 3 camera_obj.lock_scale = (True, ) * 3 bpy.ops.object.mode_set(mode='OBJECT') return {"FINISHED"}
def create_arm(cls, bones): org_bones = cls.org_bones bpy.ops.object.mode_set(mode='EDIT') eb = cls.obj.data.edit_bones ctrl = get_bone_name(org_bones[2], 'ctrl', 'ik') # Create IK arm control ctrl = copy_bone(cls.obj, org_bones[2], ctrl) # clear parent (so that rigify will parent to root) eb[ctrl].parent = None eb[ctrl].use_connect = False # Parent eb[bones['ik']['mch_target']].parent = eb[ctrl] eb[bones['ik']['mch_target']].use_connect = False # Set up constraints # Constrain mch target bone to the ik control and mch stretch make_constraint( cls, bones['ik']['mch_target'], { 'constraint': 'COPY_LOCATION', 'subtarget': bones['ik']['mch_str'], 'head_tail': 1.0 }) # Constrain mch ik stretch bone to the ik control make_constraint(cls, bones['ik']['mch_str'], { 'constraint': 'DAMPED_TRACK', 'subtarget': ctrl, }) make_constraint(cls, bones['ik']['mch_str'], { 'constraint': 'STRETCH_TO', 'subtarget': ctrl, }) make_constraint( cls, bones['ik']['mch_str'], { 'constraint': 'LIMIT_SCALE', 'use_min_y': True, 'use_max_y': True, 'max_y': 1.05, 'owner_space': 'LOCAL' }) pb = cls.obj.pose.bones # Modify rotation mode for ik and tweak controls pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' for b in bones['tweak']['ctrl']: pb[b].rotation_mode = 'ZXY' # Create ik/fk switch property pb_parent = pb[bones['parent']] pb_parent['IK_Strertch'] = 1.0 prop = rna_idprop_ui_prop_get(pb_parent, 'IK_Strertch', create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = 'IK Stretch' # Add driver to limit scale constraint influence b = bones['ik']['mch_str'] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = 'SUM' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = cls.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']' drv_modifier = cls.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create hand widget create_hand_widget(cls.obj, ctrl, bone_transform_name=None) bones['ik']['ctrl']['terminal'] = [ctrl] return bones
def create_arm( cls, bones ): org_bones = cls.org_bones bpy.ops.object.mode_set(mode='EDIT') eb = cls.obj.data.edit_bones ctrl = get_bone_name( org_bones[2], 'ctrl', 'ik' ) # Create IK arm control ctrl = copy_bone( cls.obj, org_bones[2], ctrl ) # clear parent (so that rigify will parent to root) eb[ ctrl ].parent = None eb[ ctrl ].use_connect = False # Parent eb[ bones['ik']['mch_target'] ].parent = eb[ ctrl ] eb[ bones['ik']['mch_target'] ].use_connect = False # Set up constraints # Constrain mch target bone to the ik control and mch stretch make_constraint( cls, bones['ik']['mch_target'], { 'constraint' : 'COPY_LOCATION', 'subtarget' : bones['ik']['mch_str'], 'head_tail' : 1.0 }) # Constrain mch ik stretch bone to the ik control make_constraint( cls, bones['ik']['mch_str'], { 'constraint' : 'DAMPED_TRACK', 'subtarget' : ctrl, }) make_constraint( cls, bones['ik']['mch_str'], { 'constraint' : 'STRETCH_TO', 'subtarget' : ctrl, }) make_constraint( cls, bones['ik']['mch_str'], { 'constraint' : 'LIMIT_SCALE', 'use_min_y' : True, 'use_max_y' : True, 'max_y' : 1.05, 'owner_space' : 'LOCAL' }) pb = cls.obj.pose.bones # Modify rotation mode for ik and tweak controls pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' for b in bones['tweak']['ctrl']: pb[b].rotation_mode = 'ZXY' # Create ik/fk switch property pb_parent = pb[ bones['parent'] ] pb_parent['IK_Strertch'] = 1.0 prop = rna_idprop_ui_prop_get( pb_parent, 'IK_Strertch', create=True ) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = 'IK Stretch' # Add driver to limit scale constraint influence b = bones['ik']['mch_str'] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = 'SUM' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = cls.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']' drv_modifier = cls.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create hand widget create_hand_widget(cls.obj, ctrl, bone_transform_name=None) bones['ik']['ctrl']['terminal'] = [ ctrl ] return bones
def generate(self): org_bones = self.org_bones bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones self.orient_org_bones() # Bone name lists ctrl_chain = [] def_chain = [] mch_chain = [] mch_drv_chain = [] # Create ctrl master bone org_name = self.org_bones[0] temp_name = strip_org(self.org_bones[0]) if temp_name[-2:] == '.L' or temp_name[-2:] == '.R': suffix = temp_name[-2:] master_name = temp_name[:-2] + "_master" + suffix else: master_name = temp_name + "_master" master_name = copy_bone(self.obj, org_name, master_name) ctrl_bone_master = eb[master_name] # Parenting bug fix ?? ctrl_bone_master.use_connect = False ctrl_bone_master.parent = None ctrl_bone_master.tail += (eb[org_bones[-1]].tail - eb[org_name].head) * 1.25 for bone in org_bones: eb[bone].use_connect = False if org_bones.index(bone) != 0: eb[bone].parent = None # Creating the bone chains for i in range(len(self.org_bones)): name = self.org_bones[i] ctrl_name = strip_org(name) # Create control bones ctrl_bone = copy_bone(self.obj, name, ctrl_name) ctrl_bone_e = eb[ctrl_name] # Create deformation bones def_name = make_deformer_name(ctrl_name) def_bone = copy_bone(self.obj, name, def_name) # Create mechanism bones mch_name = make_mechanism_name(ctrl_name) mch_bone = copy_bone(self.obj, name, mch_name) # Create mechanism driver bones drv_name = make_mechanism_name(ctrl_name) + "_drv" mch_bone_drv = copy_bone(self.obj, name, drv_name) # Adding to lists ctrl_chain += [ctrl_bone] def_chain += [def_bone] mch_chain += [mch_bone] mch_drv_chain += [mch_bone_drv] # Restoring org chain parenting for bone in org_bones[1:]: eb[bone].parent = eb[org_bones[org_bones.index(bone) - 1]] # Parenting the master bone to the first org ctrl_bone_master = eb[master_name] ctrl_bone_master.parent = eb[org_bones[0]] # Parenting chain bones for i in range(len(self.org_bones)): # Edit bone references def_bone_e = eb[def_chain[i]] ctrl_bone_e = eb[ctrl_chain[i]] mch_bone_e = eb[mch_chain[i]] mch_bone_drv_e = eb[mch_drv_chain[i]] if i == 0: # First ctl bone ctrl_bone_e.parent = mch_bone_drv_e ctrl_bone_e.use_connect = False # First def bone def_bone_e.parent = eb[self.org_bones[i]].parent def_bone_e.use_connect = False # First mch bone mch_bone_e.parent = eb[self.org_bones[i]].parent mch_bone_e.use_connect = False # First mch driver bone mch_bone_drv_e.parent = eb[self.org_bones[i]].parent mch_bone_drv_e.use_connect = False else: # The rest ctrl_bone_e.parent = mch_bone_drv_e ctrl_bone_e.use_connect = False def_bone_e.parent = eb[def_chain[i-1]] def_bone_e.use_connect = True mch_bone_drv_e.parent = eb[ctrl_chain[i-1]] mch_bone_drv_e.use_connect = False # Parenting mch bone mch_bone_e.parent = ctrl_bone_e mch_bone_e.use_connect = False # Creating tip control bone tip_name = copy_bone(self.obj, org_bones[-1], temp_name) ctrl_bone_tip = eb[tip_name] flip_bone(self.obj, tip_name) ctrl_bone_tip.length /= 2 ctrl_bone_tip.parent = eb[ctrl_chain[-1]] bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # Setting pose bones locks pb_master = pb[master_name] pb_master.lock_scale = True, False, True pb[tip_name].lock_scale = True, True, True pb[tip_name].lock_rotation = True, True, True pb[tip_name].lock_rotation_w = True pb_master['finger_curve'] = 0.0 prop = rna_idprop_ui_prop_get(pb_master, 'finger_curve') prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = "Rubber hose finger cartoon effect" # Pose settings for org, ctrl, deform, mch, mch_drv in zip(self.org_bones, ctrl_chain, def_chain, mch_chain, mch_drv_chain): # Constraining the deform bones con = pb[deform].constraints.new('COPY_TRANSFORMS') con.target = self.obj con.subtarget = mch # Constraining the mch bones if mch_chain.index(mch) == 0: con = pb[mch].constraints.new('COPY_LOCATION') con.target = self.obj con.subtarget = ctrl con = pb[mch].constraints.new('COPY_SCALE') con.target = self.obj con.subtarget = ctrl con = pb[mch].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con = pb[mch].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con.volume = 'NO_VOLUME' elif mch_chain.index(mch) == len(mch_chain) - 1: con = pb[mch].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = tip_name con = pb[mch].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = tip_name con.volume = 'NO_VOLUME' else: con = pb[mch].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con = pb[mch].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con.volume = 'NO_VOLUME' # Constraining and driving mch driver bones pb[mch_drv].rotation_mode = 'YZX' if mch_drv_chain.index(mch_drv) == 0: # Constraining to master bone con = pb[mch_drv].constraints.new('COPY_LOCATION') con.target = self.obj con.subtarget = master_name con = pb[mch_drv].constraints.new('COPY_ROTATION') con.target = self.obj con.subtarget = master_name con.target_space = 'LOCAL' con.owner_space = 'LOCAL' else: # Match axis to expression options = { "automatic": {"axis": 0, "expr": '(1-sy)*pi'}, "X": {"axis": 0, "expr": '(1-sy)*pi'}, "-X": {"axis": 0, "expr": '-((1-sy)*pi)'}, "Y": {"axis": 1, "expr": '(1-sy)*pi'}, "-Y": {"axis": 1, "expr": '-((1-sy)*pi)'}, "Z": {"axis": 2, "expr": '(1-sy)*pi'}, "-Z": {"axis": 2, "expr": '-((1-sy)*pi)'} } axis = self.params.primary_rotation_axis # Drivers drv = pb[mch_drv].driver_add("rotation_euler", options[axis]["axis"]).driver drv.type = 'SCRIPTED' drv.expression = options[axis]["expr"] drv_var = drv.variables.new() drv_var.name = 'sy' drv_var.type = "SINGLE_PROP" drv_var.targets[0].id = self.obj drv_var.targets[0].data_path = pb[master_name].path_from_id() + '.scale.y' # Setting bone curvature setting, custom property, and drivers def_bone = self.obj.data.bones[deform] def_bone.bbone_segments = 8 drv = def_bone.driver_add("bbone_easein").driver # Ease in drv.type='SUM' drv_var = drv.variables.new() drv_var.name = "curvature" drv_var.type = "SINGLE_PROP" drv_var.targets[0].id = self.obj drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' drv = def_bone.driver_add("bbone_easeout").driver # Ease out drv.type='SUM' drv_var = drv.variables.new() drv_var.name = "curvature" drv_var.type = "SINGLE_PROP" drv_var.targets[0].id = self.obj drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' # Assigning shapes to control bones create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) # Create ctrl master widget w = create_widget(self.obj, master_name) if w is not None: mesh = w.data verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)] if 'Z' in self.params.primary_rotation_axis: # Flip x/z coordinates temp = [] for v in verts: temp += [(v[2], v[1], v[0])] verts = temp edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] mesh.from_pydata(verts, edges, []) mesh.update() # Create tip control widget create_circle_widget(self.obj, tip_name, radius=0.3, head_tail=0.0) # Create UI controls_string = ", ".join( ["'" + x + "'" for x in ctrl_chain] ) + ", " + "'" + master_name + "'" return [script % (controls_string, master_name, 'finger_curve')]
def generate(self): """ Generate the rig. Do NOT modify any of the original bones, except for adding constraints. The main armature should be selected and active before this is called. """ bpy.ops.object.mode_set(mode='EDIT') # Create the control bones uarm = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) farm = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1])) hand = copy_bone(self.obj, self.org_bones[2], strip_org(self.org_bones[2])) # Create the hinge bones if self.org_parent != None: hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(uarm + ".hinge")) socket1 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket1")) socket2 = copy_bone(self.obj, uarm, make_mechanism_name(uarm + ".socket2")) # Get edit bones eb = self.obj.data.edit_bones uarm_e = eb[uarm] farm_e = eb[farm] hand_e = eb[hand] if self.org_parent != None: hinge_e = eb[hinge] socket1_e = eb[socket1] socket2_e = eb[socket2] # Parenting farm_e.parent = uarm_e hand_e.parent = farm_e if self.org_parent != None: hinge_e.use_connect = False socket1_e.use_connect = False socket2_e.use_connect = False uarm_e.parent = hinge_e hinge_e.parent = socket2_e socket2_e.parent = None # Positioning if self.org_parent != None: center = (hinge_e.head + hinge_e.tail) / 2 hinge_e.head = center socket1_e.length /= 4 socket2_e.length /= 3 # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones uarm_p = pb[uarm] farm_p = pb[farm] hand_p = pb[hand] if self.org_parent != None: hinge_p = pb[hinge] if self.org_parent != None: # socket1_p = pb[socket1] # UNUSED socket2_p = pb[socket2] # Set the elbow to only bend on the x-axis. farm_p.rotation_mode = 'XYZ' if 'X' in self.primary_rotation_axis: farm_p.lock_rotation = (False, True, True) elif 'Y' in self.primary_rotation_axis: farm_p.lock_rotation = (True, False, True) else: farm_p.lock_rotation = (True, True, False) # Hinge transforms are locked, for auto-ik if self.org_parent != None: hinge_p.lock_location = True, True, True hinge_p.lock_rotation = True, True, True hinge_p.lock_rotation_w = True hinge_p.lock_scale = True, True, True # Set up custom properties if self.org_parent != None: prop = rna_idprop_ui_prop_get(uarm_p, "isolate", create=True) uarm_p["isolate"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Hinge constraints / drivers if self.org_parent != None: con = socket2_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = socket1 con = socket2_p.constraints.new('COPY_TRANSFORMS') con.name = "isolate_off" con.target = self.obj con.subtarget = socket1 # Driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = uarm_p.path_from_id() + '["isolate"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = uarm con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = farm con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = hand # Set layers if specified if self.layers: uarm_p.bone.layers = self.layers farm_p.bone.layers = self.layers hand_p.bone.layers = self.layers # Create control widgets create_limb_widget(self.obj, uarm) create_limb_widget(self.obj, farm) ob = create_widget(self.obj, hand) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [uarm, farm, hand]
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) # rigify_type and rigify_parameters bone_gen.rigify_type = bone.rigify_type for prop in dir(bone_gen.rigify_parameters): if (not prop.startswith("_")) \ and (not prop.startswith("bl_")) \ and (prop != "rna_type"): try: setattr(bone_gen.rigify_parameters, prop, \ getattr(bone.rigify_parameters, prop)) except AttributeError: print("FAILED TO COPY PARAMETER: " + str(prop)) # Custom properties for prop in bone.keys(): try: bone_gen[prop] = bone[prop] except KeyError: pass # 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 = [] 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') # Lock transforms on all non-control bones r = re.compile("[A-Z][A-Z][A-Z]-") for bone in bones: if r.match(bone): pb = obj.pose.bones[bone] pb.lock_location = (True, True, True) pb.lock_rotation = (True, True, True) pb.lock_rotation_w = True pb.lock_scale = (True, True, True) # 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 if obj.animation_data: 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 )[:63] # Object names are limited to 63 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) 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 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 controls and sub-controls 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] # 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 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 create_cube_widget(self.obj, main_control) # 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 create_circle_widget(self.obj, name, radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[i]) # Hips pb[controls[0]].custom_shape_transform = pb[self.org_bones[0]] # Create control widgets create_circle_widget(self.obj, controls[0], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[0]) # Ribs pb[controls[-1]].custom_shape_transform = pb[self.org_bones[-1]] # Create control widgets create_circle_widget(self.obj, controls[-1], radius=1.0, head_tail=0.5, with_line=True, bone_transform_name=self.org_bones[-1]) # Layers pb[main_control].bone.layers = pb[self.org_bones[0]].bone.layers return [main_control] + controls
def create_leg(self, bones): org_bones = list( [self.org_bones[0]] + connected_children_names(self.obj, self.org_bones[0]) ) bones['ik']['ctrl']['terminal'] = [] bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones # Create toes def bone toes_def = get_bone_name(org_bones[-1], 'def') toes_def = copy_bone( self.obj, org_bones[-1], toes_def ) eb[ toes_def ].use_connect = False eb[ toes_def ].parent = eb[ bones['def'][-1] ] eb[ toes_def ].use_connect = True bones['def'] += [ toes_def ] pole_target = get_bone_name(org_bones[0], 'ctrl', 'ik_target') # Create IK leg control ctrl = get_bone_name(org_bones[2], 'ctrl', 'ik') ctrl = copy_bone(self.obj, org_bones[2], ctrl) # clear parent (so that rigify will parent to root) eb[ctrl].parent = None eb[ctrl].use_connect = False # MCH for ik control ctrl_socket = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_socket')) eb[ctrl_socket].tail = eb[ctrl_socket].head + 0.8*(eb[ctrl_socket].tail-eb[ctrl_socket].head) eb[ctrl_socket].parent = None eb[ctrl].parent = eb[ctrl_socket] # MCH for pole ik control ctrl_pole_socket = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'pole_ik_socket')) eb[ctrl_pole_socket].tail = eb[ctrl_pole_socket].head + 0.8 * (eb[ctrl_pole_socket].tail - eb[ctrl_pole_socket].head) eb[ctrl_pole_socket].parent = None eb[pole_target].parent = eb[ctrl_pole_socket] ctrl_root = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_root')) eb[ctrl_root].tail = eb[ctrl_root].head + 0.7*(eb[ctrl_root].tail-eb[ctrl_root].head) eb[ctrl_root].use_connect = False eb[ctrl_root].parent = eb['root'] if eb[org_bones[0]].parent: leg_parent = eb[org_bones[0]].parent ctrl_parent = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_parent')) eb[ctrl_parent].tail = eb[ctrl_parent].head + 0.6*(eb[ctrl_parent].tail-eb[ctrl_parent].head) eb[ctrl_parent].use_connect = False if eb[org_bones[0]].parent_recursive: eb[ctrl_parent].parent = eb[org_bones[0]].parent_recursive[-1] else: eb[ctrl_parent].parent = eb[org_bones[0]].parent else: leg_parent = None mch_name = get_bone_name(strip_org(org_bones[0]), 'mch', 'parent_socket') mch_main_parent = copy_bone(self.obj, org_bones[0], mch_name) eb[mch_main_parent].length = eb[org_bones[0]].length / 12 eb[mch_main_parent].parent = eb[bones['parent']] eb[mch_main_parent].roll = 0.0 eb[bones['main_parent']].parent = eb[mch_main_parent] # Create heel ctrl bone heel = get_bone_name(org_bones[2], 'ctrl', 'heel_ik') heel = copy_bone(self.obj, org_bones[2], heel) ax = eb[org_bones[2]].head - eb[org_bones[2]].tail ax[2] = 0 align_bone_y_axis(self.obj, heel, ax) if self.rot_axis == 'x' or self.rot_axis == 'automatic': align_bone_x_axis(self.obj, heel, eb[org_bones[2]].x_axis) elif self.rot_axis == 'z': align_bone_z_axis(self.obj, heel, eb[org_bones[2]].z_axis) eb[heel].length = eb[org_bones[2]].length / 2 # Reset control position and orientation if self.rot_axis == 'automatic' or self.auto_align_extremity: l = eb[ctrl].length orient_bone(self, eb[ctrl], 'y', reverse=True) eb[ctrl].length = l else: flip_bone(self.obj, ctrl) eb[ctrl].tail[2] = eb[ctrl].head[2] eb[ctrl].roll = 0 # Parent eb[ heel ].use_connect = False eb[ heel ].parent = eb[ ctrl ] eb[ bones['ik']['mch_target'] ].parent = eb[ heel ] eb[ bones['ik']['mch_target'] ].use_connect = False # Create foot mch rock and roll bones # Get the tmp heel (floating unconnected without children) tmp_heel = "" for b in self.obj.data.bones[org_bones[2]].children: if not b.use_connect and not b.children: tmp_heel = b.name # roll1 MCH bone roll1_mch = get_bone_name(tmp_heel, 'mch', 'roll') roll1_mch = copy_bone(self.obj, org_bones[2], roll1_mch) # clear parent eb[roll1_mch].use_connect = False eb[roll1_mch].parent = None flip_bone(self.obj, roll1_mch) if self.rot_axis == 'x' or self.rot_axis == 'automatic': align_bone_x_axis(self.obj, roll1_mch, eb[org_bones[2]].x_axis) elif self.rot_axis == 'z': align_bone_z_axis(self.obj, roll1_mch, eb[org_bones[2]].z_axis) # Create 2nd roll mch, and two rock mch bones roll2_mch = get_bone_name(tmp_heel, 'mch', 'roll') roll2_mch = copy_bone(self.obj, org_bones[3], roll2_mch) eb[roll2_mch].use_connect = False eb[roll2_mch].parent = None put_bone( self.obj, roll2_mch, (eb[tmp_heel].head + eb[tmp_heel].tail) / 2 ) eb[ roll2_mch ].length /= 4 # Rock MCH bones rock1_mch = get_bone_name( tmp_heel, 'mch', 'rock' ) rock1_mch = copy_bone( self.obj, tmp_heel, rock1_mch ) eb[ rock1_mch ].use_connect = False eb[ rock1_mch ].parent = None orient_bone( self, eb[ rock1_mch ], 'y', 1.0, reverse = True ) align_bone_y_axis(self.obj, rock1_mch, ax) eb[ rock1_mch ].length = eb[ tmp_heel ].length / 2 rock2_mch = get_bone_name( tmp_heel, 'mch', 'rock' ) rock2_mch = copy_bone( self.obj, tmp_heel, rock2_mch ) eb[ rock2_mch ].use_connect = False eb[ rock2_mch ].parent = None #orient_bone( self, eb[ rock2_mch ], 'y', 1.0 ) align_bone_y_axis(self.obj, rock2_mch, ax) eb[ rock2_mch ].length = eb[ tmp_heel ].length / 2 # Parent rock and roll MCH bones eb[ roll1_mch ].parent = eb[ roll2_mch ] eb[ roll2_mch ].parent = eb[ rock1_mch ] eb[ rock1_mch ].parent = eb[ rock2_mch ] eb[ rock2_mch ].parent = eb[ ctrl ] # make mch toe bone toe = '' foot = eb[self.org_bones[-1]] for c in foot.children: if 'org' in c.name.lower() and c.head == foot.tail: toe = c.name if not toe: raise MetarigError.message("Wrong metarig: can't find ORG-<toe>") toe_mch = get_bone_name(toe, 'mch') toe_mch = copy_bone(self.obj, toe, toe_mch) eb[toe_mch].length /= 3 eb[toe_mch].parent = eb[self.org_bones[2]] eb[toe].use_connect = False eb[toe].parent = eb[toe_mch] # Constrain rock and roll MCH bones make_constraint( self, roll1_mch, { 'constraint' : 'COPY_ROTATION', 'subtarget' : heel, 'owner_space' : 'LOCAL', 'target_space' : 'LOCAL' }) if self.rot_axis == 'x'or self.rot_axis == 'automatic': make_constraint(self, roll1_mch, { 'constraint': 'LIMIT_ROTATION', 'use_limit_x': True, 'max_x': math.radians(360), 'owner_space': 'LOCAL' }) make_constraint(self, roll2_mch, { 'constraint': 'COPY_ROTATION', 'subtarget': heel, 'use_y': False, 'use_z': False, 'invert_x': True, 'owner_space': 'LOCAL', 'target_space': 'LOCAL' }) make_constraint(self, roll2_mch, { 'constraint': 'LIMIT_ROTATION', 'use_limit_x': True, 'max_x': math.radians(360), 'owner_space': 'LOCAL' }) elif self.rot_axis == 'z': make_constraint(self, roll1_mch, { 'constraint': 'LIMIT_ROTATION', 'use_limit_z': True, 'max_z': math.radians(360), 'owner_space': 'LOCAL' }) make_constraint(self, roll2_mch, { 'constraint': 'COPY_ROTATION', 'subtarget': heel, 'use_y': False, 'use_x': False, 'invert_z': True, 'owner_space': 'LOCAL', 'target_space': 'LOCAL' }) make_constraint(self, roll2_mch, { 'constraint': 'LIMIT_ROTATION', 'use_limit_z': True, 'max_z': math.radians(360), 'owner_space': 'LOCAL' }) pb = self.obj.pose.bones if self.rot_axis == 'x'or self.rot_axis == 'automatic': ik_rot_axis = pb[org_bones[0]].x_axis elif self.rot_axis == 'z': ik_rot_axis = pb[org_bones[0]].z_axis heel_x_orientation = pb[tmp_heel].y_axis.dot(ik_rot_axis) for i, b in enumerate([rock1_mch, rock2_mch]): if heel_x_orientation > 0: if not i: min_y = 0 max_y = math.radians(360) else: min_y = math.radians(-360) max_y = 0 else: if not i: min_y = math.radians(-360) max_y = 0 else: min_y = 0 max_y = math.radians(360) make_constraint( self, b, { 'constraint' : 'COPY_ROTATION', 'subtarget' : heel, 'use_x' : False, 'use_z' : False, 'owner_space' : 'LOCAL', 'target_space' : 'LOCAL' }) make_constraint( self, b, { 'constraint' : 'LIMIT_ROTATION', 'use_limit_y' : True, 'min_y' : min_y, 'max_y' : max_y, 'owner_space' : 'LOCAL' }) # Cns toe_mch to MCH roll2 make_constraint( self, toe_mch, { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : roll2_mch }) # Set up constraints # Constrain ik ctrl to root / parent make_constraint( self, ctrl_socket, { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : ctrl_root, }) make_constraint(self, ctrl_pole_socket, { 'constraint': 'COPY_TRANSFORMS', 'subtarget': ctrl_root, }) if leg_parent: make_constraint( self, ctrl_socket, { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : ctrl_parent, 'influence' : 0.0, }) make_constraint(self, ctrl_pole_socket, { 'constraint': 'COPY_TRANSFORMS', 'subtarget': bones['ik']['mch_target'], }) # Constrain mch target bone to the ik control and mch stretch make_constraint( self, bones['ik']['mch_target'], { 'constraint' : 'COPY_LOCATION', 'subtarget' : bones['ik']['mch_str'], 'head_tail' : 1.0 }) # Constrain mch ik stretch bone to the ik control make_constraint( self, bones['ik']['mch_str'], { 'constraint' : 'DAMPED_TRACK', 'subtarget' : roll1_mch, 'head_tail' : 1.0 }) make_constraint( self, bones['ik']['mch_str'], { 'constraint' : 'STRETCH_TO', 'subtarget' : roll1_mch, 'head_tail' : 1.0 }) make_constraint( self, bones['ik']['mch_str'], { 'constraint' : 'LIMIT_SCALE', 'use_min_y' : True, 'use_max_y' : True, 'max_y' : 1.05, 'owner_space' : 'LOCAL' }) make_constraint(self, mch_main_parent, { 'constraint': 'COPY_ROTATION', 'subtarget': org_bones[0] }) # Create ik/fk switch property pb_parent = pb[bones['main_parent']] pb_parent.lock_location = True, True, True pb_parent.lock_rotation = True, True, True pb_parent.lock_scale = True, True, True prop = make_property(pb_parent, 'IK_Stretch', 1.0, description='IK Stretch') # Add driver to limit scale constraint influence b = bones['ik']['mch_str'] make_driver(pb[b].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)], polynomial=[1.0, -1.0]) # Create leg widget create_foot_widget(self.obj, ctrl, bone_transform_name=None) # Create heel ctrl locks pb[heel].lock_location = True, True, True if self.rot_axis == 'x'or self.rot_axis == 'automatic': pb[heel].lock_rotation = False, False, True elif self.rot_axis == 'z': pb[heel].lock_rotation = True, False, False pb[heel].lock_scale = True, True, True # Add ballsocket widget to heel create_ballsocket_widget(self.obj, heel, bone_transform_name=None) bpy.ops.object.mode_set(mode='EDIT') eb = self.obj.data.edit_bones if len(org_bones) >= 4: # Create toes control bone toes = get_bone_name(org_bones[3], 'ctrl') toes = copy_bone(self.obj, org_bones[3], toes) eb[toes].use_connect = False eb[toes].parent = eb[toe_mch] # Constrain 4th ORG to toes make_constraint(self, org_bones[3], { 'constraint': 'COPY_TRANSFORMS', # 'subtarget' : roll2_mch 'subtarget': toes }) # Constrain toes def bones make_constraint(self, bones['def'][-2], { 'constraint': 'DAMPED_TRACK', 'subtarget': toes }) make_constraint(self, bones['def'][-2], { 'constraint': 'STRETCH_TO', 'subtarget': toes }) make_constraint(self, bones['def'][-1], { 'constraint': 'COPY_TRANSFORMS', 'subtarget': toes }) # Find IK/FK switch property pb = self.obj.pose.bones prop = rna_idprop_ui_prop_get( pb[bones['fk']['ctrl'][-1]], 'IK_FK' ) # Modify rotation mode for ik and tweak controls pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' for b in bones['tweak']['ctrl']: pb[b].rotation_mode = 'ZXY' # Add driver to limit scale constraint influence b = toe_mch make_driver(pb[b].constraints[-1], "influence", variables=[(self.obj, pb_parent, prop.name)], polynomial=[1.0, -1.0]) # Create toe circle widget create_circle_widget(self.obj, toes, radius=0.4, head_tail=0.5) bones['ik']['ctrl']['terminal'] += [toes] bones['ik']['ctrl']['terminal'] += [ heel, ctrl ] if leg_parent: bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root, ctrl_parent] else: bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root] return bones
def copy_bone(obj, bone_name, assign_name=''): """ Makes a copy of the given bone in the given armature object. Returns the resulting bone's name. """ #if bone_name not in obj.data.bones: if bone_name not in obj.data.edit_bones: raise MetarigError("copy_bone(): bone '%s' not found, cannot copy it" % bone_name) if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': if assign_name == '': assign_name = bone_name # Copy the edit bone edit_bone_1 = obj.data.edit_bones[bone_name] edit_bone_2 = obj.data.edit_bones.new(assign_name) bone_name_1 = bone_name bone_name_2 = edit_bone_2.name edit_bone_2.parent = edit_bone_1.parent edit_bone_2.use_connect = edit_bone_1.use_connect # Copy edit bone attributes edit_bone_2.layers = list(edit_bone_1.layers) edit_bone_2.head = Vector(edit_bone_1.head) edit_bone_2.tail = Vector(edit_bone_1.tail) edit_bone_2.roll = edit_bone_1.roll edit_bone_2.use_inherit_rotation = edit_bone_1.use_inherit_rotation edit_bone_2.use_inherit_scale = edit_bone_1.use_inherit_scale edit_bone_2.use_local_location = edit_bone_1.use_local_location edit_bone_2.use_deform = edit_bone_1.use_deform edit_bone_2.bbone_segments = edit_bone_1.bbone_segments edit_bone_2.bbone_in = edit_bone_1.bbone_in edit_bone_2.bbone_out = edit_bone_1.bbone_out bpy.ops.object.mode_set(mode='OBJECT') # Get the pose bones pose_bone_1 = obj.pose.bones[bone_name_1] pose_bone_2 = obj.pose.bones[bone_name_2] # Copy pose bone attributes pose_bone_2.rotation_mode = pose_bone_1.rotation_mode pose_bone_2.rotation_axis_angle = tuple(pose_bone_1.rotation_axis_angle) pose_bone_2.rotation_euler = tuple(pose_bone_1.rotation_euler) pose_bone_2.rotation_quaternion = tuple(pose_bone_1.rotation_quaternion) pose_bone_2.lock_location = tuple(pose_bone_1.lock_location) pose_bone_2.lock_scale = tuple(pose_bone_1.lock_scale) pose_bone_2.lock_rotation = tuple(pose_bone_1.lock_rotation) pose_bone_2.lock_rotation_w = pose_bone_1.lock_rotation_w pose_bone_2.lock_rotations_4d = pose_bone_1.lock_rotations_4d # Copy custom properties for key in pose_bone_1.keys(): if key != "_RNA_UI" \ and key != "rigify_parameters" \ and key != "rigify_type": prop1 = rna_idprop_ui_prop_get(pose_bone_1, key, create=False) prop2 = rna_idprop_ui_prop_get(pose_bone_2, key, create=True) pose_bone_2[key] = pose_bone_1[key] for key in prop1.keys(): prop2[key] = prop1[key] bpy.ops.object.mode_set(mode='EDIT') return bone_name_2 else: raise MetarigError("Cannot copy bones outside of edit mode")
def generate_rig(context, metarig): """ Generates a rig from a metarig. """ t = Timer() # clear created widget list create_widget.created_widgets = None # Find overwrite target rig if exists rig_name = get_rig_name(metarig) # store rig name to property if rig name already not stored. if not metarig.data.gamerig_rig_name: metarig.data.gamerig_rig_name = rig_name print("Fetch rig (%s)." % rig_name) obj = next( (i for i in context.collection.objects if i != metarig and i.type == 'ARMATURE' and i.name == rig_name), None) # Random string with time appended so that # different rigs don't collide id's rig_id = (obj.data.get("gamerig_id") if obj else None) or random_id() # Initial configuration rest_backup = metarig.data.pose_position metarig.data.pose_position = 'REST' bpy.ops.object.mode_set(mode='OBJECT') scene = context.scene view_layer = context.view_layer collection = context.collection layer_collection = context.layer_collection id_store = context.window_manager #------------------------------------------ # 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. toggledArmatureModifiers = [] if obj is not None: print("Overwrite existing rig.") try: # toggle armature object to metarig if it using generated rig. # (referensing rig overwriting makes script runs very slowly) for i in collection.objects: for j in i.modifiers: if j.type == 'ARMATURE' and j.object == obj: toggledArmatureModifiers.append(j) j.object = metarig # Get rid of anim data in case the rig already existed print("Clear rig animation data.") obj.animation_data_clear() except KeyError: print("Overwrite failed.") obj = None if obj is None: print("Create new rig.") name = metarig.data.get("gamerig_rig_name") or "rig" obj = bpy.data.objects.new(name, bpy.data.armatures.new( name)) # in case name 'rig' exists it will be rig.001 obj.display_type = 'WIRE' collection.objects.link(obj) # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "gamerig_id", create=True) obj.data["gamerig_id"] = rig_id obj.data.pose_position = 'POSE' # Select generated rig object metarig.select_set(False) obj.select_set(True) obj.hide_viewport = False view_layer.objects.active = obj # Get parented objects to restore later childs = {} # {object: bone} for child in obj.children: childs[child] = child.parent_bone # 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() collection.objects.link(temp_rig_1) temp_rig_2 = metarig.copy() temp_rig_2.data = obj.data collection.objects.link(temp_rig_2) # Select the temp rigs for merging for objt in collection.objects: objt.select_set(False) # deselect all objects temp_rig_1.select_set(True) temp_rig_2.select_set(True) view_layer.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_set(False) # deselect all objects obj.select_set(True) view_layer.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_easein = bone.bbone_easein bone_gen.bbone_easeout = bone.bbone_easeout # 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) # gamerig_type and gamerig_parameters bone_gen.gamerig_type = bone.gamerig_type for prop in dir(bone_gen.gamerig_parameters): if (not prop.startswith("_")) and ( not prop.startswith("bl_")) and (prop != "rna_type"): try: setattr(bone_gen.gamerig_parameters, prop, getattr(bone.gamerig_parameters, prop)) except AttributeError: print("FAILED TO COPY PARAMETER: " + str(prop)) # Custom properties for prop in bone.keys(): try: bone_gen[prop] = bone[prop] except KeyError: pass # 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 # Clear drivers if obj.animation_data: for d in obj.animation_data.drivers: try: obj.driver_remove(d.data_path) except expression as TypeError: pass # 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 target = v2.targets[i] # If a custom property if v2.type == 'SINGLE_PROP' and re.match( '^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path): target.data_path = "GAMERIG-" + target.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 = org(original_bones[i]) original_bones[i] = org(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.append(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: ") #---------------------------------- try: # Collect/initialize all the rigs. rigs = [] rigtypes = set() for bone in bones_sorted: bpy.ops.object.mode_set(mode='EDIT') rigs += get_bone_rigs(obj, bone, rigtypes) t.tick("Initialize rigs: ") # Generate all the rigs. tt = Timer() ui_scripts = [] for rig in rigs: # Go into editmode in the rig armature bpy.ops.object.mode_set(mode='OBJECT') context.view_layer.objects.active = obj obj.select_set(True) bpy.ops.object.mode_set(mode='EDIT') scripts = rig.generate(context) if scripts is not None: ui_scripts.append(scripts[0]) tt.tick("Generate rig : %s: " % rig) t.tick("Generate rigs: ") except Exception as e: # Cleanup if something goes wrong print("GameRig: 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] metabones = [bone.name for bone in metarig.data.bones] # All the others make non-deforming. (except for bone that already has 'ORG-' prefix from metarig.) for bone in bones: if not (is_org(bone) or bone in metabones): b = obj.data.bones[bone] b.use_deform = False # Alter marked driver targets if obj.animation_data: for d in obj.animation_data.drivers: for v in d.driver.variables: for target in v.targets: if target.data_path.startswith("GAMERIG-"): temp, bone, prop = tuple([ x.strip('"]') for x in target.data_path.split('["') ]) if bone in obj.data.bones and prop in obj.pose.bones[ bone].keys(): target.data_path = target.data_path[7:] else: target.data_path = 'pose.bones["%s"]["%s"]' % ( org(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 is_mch(obj.data.bones[bone].name): obj.data.bones[bone].layers = MCH_LAYER # Assign shapes to bones assign_and_unlink_all_widgets(collection, obj) # 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]) obj.data.layers = vis_layers # Ensure the collection of layer names exists for i in range(1 + len(metarig.data.gamerig_layers), 30): metarig.data.gamerig_layers.add() # Create list of layer name/row pairs layer_layout = [] for l in metarig.data.gamerig_layers: #print(l.name) layer_layout.append((l.name, l.row)) # Generate the UI script rig_ui_name = 'gamerig_ui_%s.py' % rig_id if rig_ui_name in bpy.data.texts.keys(): script = bpy.data.texts[rig_ui_name] script.clear() else: script = bpy.data.texts.new(rig_ui_name) operator_scripts = '' for rigt in rigtypes: try: rigt.operator_script except AttributeError: pass else: operator_scripts += rigt.operator_script(rig_id) uitemplate = rig_lists.riguitemplate_dic[ metarig.data.gamerig_rig_ui_template] script.write(uitemplate[0].format(rig_id=rig_id, operators=operator_scripts, properties=properties_ui(ui_scripts), layers=layers_ui(vis_layers, layer_layout))) script.use_module = True # Run UI script exec(script.as_string(), {}) # Create Selection Sets create_selection_sets(obj, metarig) # Create Bone Groups create_bone_groups(obj, metarig) # Add rig_ui to logic create_persistent_rig_ui(obj, script) # Remove all jig bones. bpy.ops.object.mode_set(mode='EDIT') for bone in [bone.name for bone in obj.data.edit_bones]: if is_jig(bone): obj.data.edit_bones.remove(obj.data.edit_bones[bone]) #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' # Restore toggled armature modifiers for i in toggledArmatureModifiers: i.object = obj # Restore parent to bones for child, sub_parent in childs.items(): if sub_parent in obj.pose.bones: mat = child.matrix_world.copy() child.parent_bone = sub_parent child.matrix_world = mat # Restore active collection view_layer.active_layer_collection = layer_collection t.tick("The rest: ") # set location generated rig to metarig location obj.location = metarig.location obj.rotation_mode = metarig.rotation_mode obj.rotation_euler = metarig.rotation_euler obj.rotation_quaternion = metarig.rotation_quaternion obj.rotation_axis_angle = metarig.rotation_axis_angle obj.scale = metarig.scale t.tick("The rest: ")
def generate(self): org_bones = self.org_bones bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones # Bone name lists ctrl_chain = [] def_chain = [] mch_chain = [] mch_drv_chain = [] # Create ctrl master bone org_name = self.org_bones[0] temp_name = strip_org(self.org_bones[0]) suffix = temp_name[-2:] master_name = temp_name[:-5] + "_master" + suffix master_name = copy_bone( self.obj, org_name, master_name ) ctrl_bone_master = eb[ master_name ] ## Parenting bug fix ?? ctrl_bone_master.use_connect = False ctrl_bone_master.parent = None ctrl_bone_master.tail += ( eb[ org_bones[-1] ].tail - eb[org_name].head ) * 1.25 for bone in org_bones: eb[bone].use_connect = False if org_bones.index( bone ) != 0: eb[bone].parent = None # Creating the bone chains for i in range(len(self.org_bones)): name = self.org_bones[i] ctrl_name = strip_org(name) # Create control bones ctrl_bone = copy_bone( self.obj, name, ctrl_name ) ctrl_bone_e = eb[ ctrl_name ] # Create deformation bones def_name = make_deformer_name( ctrl_name ) def_bone = copy_bone( self.obj, name, def_name ) # Create mechanism bones mch_name = make_mechanism_name( ctrl_name ) mch_bone = copy_bone( self.obj, name, mch_name ) # Create mechanism driver bones drv_name = make_mechanism_name(ctrl_name) + "_drv" mch_bone_drv = copy_bone(self.obj, name, drv_name) mch_bone_drv_e = eb[drv_name] # Adding to lists ctrl_chain += [ctrl_name] def_chain += [def_bone] mch_chain += [mch_bone] mch_drv_chain += [drv_name] # Restoring org chain parenting for bone in org_bones[1:]: eb[bone].parent = eb[ org_bones[ org_bones.index(bone) - 1 ] ] # Parenting the master bone to the first org ctrl_bone_master = eb[ master_name ] ctrl_bone_master.parent = eb[ org_bones[0] ] # Parenting chain bones for i in range(len(self.org_bones)): # Edit bone references def_bone_e = eb[def_chain[i]] ctrl_bone_e = eb[ctrl_chain[i]] mch_bone_e = eb[mch_chain[i]] mch_bone_drv_e = eb[mch_drv_chain[i]] if i == 0: # First ctl bone ctrl_bone_e.parent = mch_bone_drv_e ctrl_bone_e.use_connect = False # First def bone def_bone_e.parent = eb[self.org_bones[i]].parent def_bone_e.use_connect = False # First mch bone mch_bone_e.parent = eb[self.org_bones[i]].parent mch_bone_e.use_connect = False # First mch driver bone mch_bone_drv_e.parent = eb[self.org_bones[i]].parent mch_bone_drv_e.use_connect = False else: # The rest ctrl_bone_e.parent = mch_bone_drv_e ctrl_bone_e.use_connect = False def_bone_e.parent = eb[def_chain[i-1]] def_bone_e.use_connect = True mch_bone_drv_e.parent = eb[ctrl_chain[i-1]] mch_bone_drv_e.use_connect = False # Parenting mch bone mch_bone_e.parent = ctrl_bone_e mch_bone_e.use_connect = False # Creating tip conrtol bone tip_name = copy_bone( self.obj, org_bones[-1], temp_name ) ctrl_bone_tip = eb[ tip_name ] flip_bone( self.obj, tip_name ) ctrl_bone_tip.length /= 2 ctrl_bone_tip.parent = eb[ctrl_chain[-1]] bpy.ops.object.mode_set(mode ='OBJECT') pb = self.obj.pose.bones # Setting pose bones locks pb_master = pb[master_name] pb_master.lock_scale = True,False,True pb[tip_name].lock_scale = True,True,True pb[tip_name].lock_rotation = True,True,True pb[tip_name].lock_rotation_w = True pb_master['finger_curve'] = 0.0 prop = rna_idprop_ui_prop_get(pb_master, 'finger_curve') prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = "Rubber hose finger cartoon effect" # Pose settings for org, ctrl, deform, mch, mch_drv in zip(self.org_bones, ctrl_chain, def_chain, mch_chain, mch_drv_chain): # Constraining the org bones #con = pb[org].constraints.new('COPY_TRANSFORMS') #con.target = self.obj #con.subtarget = ctrl # Constraining the deform bones con = pb[deform].constraints.new('COPY_TRANSFORMS') con.target = self.obj con.subtarget = mch # Constraining the mch bones if mch_chain.index(mch) == 0: con = pb[mch].constraints.new('COPY_LOCATION') con.target = self.obj con.subtarget = ctrl con = pb[mch].constraints.new('COPY_SCALE') con.target = self.obj con.subtarget = ctrl con = pb[mch].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con = pb[mch].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con.volume = 'NO_VOLUME' elif mch_chain.index(mch) == len(mch_chain) - 1: con = pb[mch].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = tip_name con = pb[mch].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = tip_name con.volume = 'NO_VOLUME' else: con = pb[mch].constraints.new('DAMPED_TRACK') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con = pb[mch].constraints.new('STRETCH_TO') con.target = self.obj con.subtarget = ctrl_chain[ctrl_chain.index(ctrl)+1] con.volume = 'NO_VOLUME' # Constraining and driving mch driver bones pb[mch_drv].rotation_mode = 'YZX' if mch_drv_chain.index(mch_drv) == 0: # Constraining to master bone con = pb[mch_drv].constraints.new('COPY_LOCATION') con.target = self.obj con.subtarget = master_name con = pb[mch_drv].constraints.new('COPY_ROTATION') con.target = self.obj con.subtarget = master_name con.target_space = 'LOCAL' con.owner_space = 'LOCAL' else: # Match axis to expression options = { "X" : { "axis" : 0, "expr" : '(1-sy)*pi' }, "-X" : { "axis" : 0, "expr" : '-((1-sy)*pi)' }, "Y" : { "axis" : 1, "expr" : '(1-sy)*pi' }, "-Y" : { "axis" : 1, "expr" : '-((1-sy)*pi)' }, "Z" : { "axis" : 2, "expr" : '(1-sy)*pi' }, "-Z" : { "axis" : 2, "expr" : '-((1-sy)*pi)' } } axis = self.params.primary_rotation_axis # Drivers drv = pb[mch_drv].driver_add("rotation_euler", options[axis]["axis"]).driver drv.type = 'SCRIPTED' drv.expression = options[axis]["expr"] drv_var = drv.variables.new() drv_var.name = 'sy' drv_var.type = "SINGLE_PROP" drv_var.targets[0].id = self.obj drv_var.targets[0].data_path = pb[master_name].path_from_id() + '.scale.y' # Setting bone curvature setting, costum property, and drivers def_bone = self.obj.data.bones[deform] def_bone.bbone_segments = 8 drv = def_bone.driver_add("bbone_in").driver # Ease in drv.type='SUM' drv_var = drv.variables.new() drv_var.name = "curvature" drv_var.type = "SINGLE_PROP" drv_var.targets[0].id = self.obj drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' drv = def_bone.driver_add("bbone_out").driver # Ease out drv.type='SUM' drv_var = drv.variables.new() drv_var.name = "curvature" drv_var.type = "SINGLE_PROP" drv_var.targets[0].id = self.obj drv_var.targets[0].data_path = pb_master.path_from_id() + '["finger_curve"]' # Assigning shapes to control bones create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5) # Create ctrl master widget w = create_widget(self.obj, master_name) if w != None: mesh = w.data verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)] if 'Z' in self.params.primary_rotation_axis: # Flip x/z coordinates temp = [] for v in verts: temp += [(v[2], v[1], v[0])] verts = temp edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] mesh.from_pydata(verts, edges, []) mesh.update() # Create tip control widget create_circle_widget(self.obj, tip_name, radius=0.3, head_tail=0.0) # Create UI controls_string = ", ".join( ["'" + x + "'" for x in ctrl_chain] ) + ", " + "'" + master_name + "'" return [script % (controls_string, master_name, 'finger_curve')]
def build_crane_rig(context): # Define some useful variables: boneLayer = (False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) # Add the new armature object: view_layer = bpy.context.view_layer bpy.ops.object.armature_add() rig = context.active_object # it will try to name the rig "Dolly_Rig" but if that name exists it will # add .000 to the name if "Crane_Rig" not in context.scene.objects: rig.name = "Crane_Rig" else: rig.name = "Crane_Rig.000" rig["rig_id"] = "Crane_Rig" bpy.ops.object.mode_set(mode='EDIT') # Remove default bone: bones = rig.data.edit_bones bones.remove(bones[0]) # Add new bones: root = bones.new("Root") root.tail = (0.0, 0.0, -5.0) ctrlAimChild = bones.new("AIM_child") ctrlAimChild.head = (0.0, 10.0, 1.0) ctrlAimChild.tail = (0.0, 12.0, 1.0) ctrlAimChild.layers = boneLayer ctrlAim = bones.new("AIM") ctrlAim.head = (0.0, 10.0, 1.0) ctrlAim.tail = (0.0, 12.0, 1.0) ctrl = bones.new("CTRL") ctrl.head = (0.0, 1.0, 1.0) ctrl.tail = (0.0, 3.0, 1.0) arm = bones.new("Crane_Arm") arm.head = (0.0, 0.0, 1.0) arm.tail = (0.0, 1.0, 1.0) height = bones.new("Height") height.head = (0.0, 0.0, 0.0) height.tail = (0.0, 0.0, 1.0) # Setup hierarchy: ctrl.parent = arm ctrl.use_inherit_rotation = False ctrl.use_inherit_scale = False arm.parent = height arm.use_inherit_scale = False height.parent = root ctrlAim.parent = root ctrlAimChild.parent = ctrlAim # change display to BBone: it just looks nicer bpy.context.object.data.display_type = 'BBONE' # change display to wire for object bpy.context.object.display_type = 'WIRE' # jump into pose mode and change bones to euler bpy.ops.object.mode_set(mode='POSE') for x in bpy.context.object.pose.bones: x.rotation_mode = 'XYZ' # lock the relevant loc, rot and scale bpy.context.object.pose.bones["Crane_Arm"].lock_rotation = [ False, True, False ] bpy.context.object.pose.bones["Crane_Arm"].lock_scale = [True, False, True] bpy.context.object.pose.bones["Height"].lock_location = [True, True, True] bpy.context.object.pose.bones["Height"].lock_rotation = [True, True, True] bpy.context.object.pose.bones["Height"].lock_scale = [True, False, True] # add the custom bone shapes bpy.context.object.pose.bones["Root"].custom_shape = bpy.data.objects[ "WDGT_Camera_Root"] # add the widget as custom shape # set the wireframe checkbox to true bpy.context.object.data.bones["Root"].show_wire = True bpy.context.object.pose.bones["AIM"].custom_shape = bpy.data.objects[ "WDGT_AIM"] bpy.context.object.data.bones["AIM"].show_wire = True bpy.context.object.pose.bones[ "AIM"].custom_shape_transform = bpy.data.objects[rig.name].pose.bones[ "AIM_child"] # sets the "At" field to the child bpy.context.object.pose.bones["CTRL"].custom_shape = bpy.data.objects[ "WDGT_CTRL"] bpy.context.object.data.bones["CTRL"].show_wire = True # jump into object mode bpy.ops.object.mode_set(mode='OBJECT') # Add constraints to bones: con = rig.pose.bones['AIM_child'].constraints.new('COPY_ROTATION') con.target = rig con.subtarget = "CTRL" con = rig.pose.bones['CTRL'].constraints.new('TRACK_TO') con.target = rig con.subtarget = "AIM" con.use_target_z = True # Add custom Bone property to CTRL bone ob = bpy.context.object.pose.bones['CTRL'] prop = rna_idprop_ui_prop_get(ob, "Lock", create=True) ob["Lock"] = 1.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Add Driver to Lock/Unlock Camera from Aim Target rig = view_layer.objects.active pose_bone = bpy.data.objects[rig.name].pose.bones['CTRL'] constraint = pose_bone.constraints["Track To"] inf_driver = constraint.driver_add('influence') inf_driver.driver.type = 'SCRIPTED' var = inf_driver.driver.variables.new() var.name = 'var' var.type = 'SINGLE_PROP' # Target the Custom bone property var.targets[0].id = bpy.data.objects[rig.name] var.targets[0].data_path = 'pose.bones["CTRL"]["Lock"]' inf_driver.driver.expression = 'var' # Add the camera object: bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.camera_add(view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0)) cam = bpy.context.active_object # this will name the Camera Object if 'Crane_Camera' not in context.scene.objects: cam.name = "Crane_Camera" else: cam.name = "Crane_Camera.000" # this will name the camera Data Object if "Crane_Camera" not in bpy.context.scene.objects.data.camera: cam.data.name = "Crane_Camera" else: cam.data.name = "Crane_Camera.000" cam_data_name = bpy.context.object.data.name bpy.data.cameras[cam_data_name].display_size = 1.0 cam.rotation_euler[0] = 1.5708 # rotate the camera 90 degrees in x cam.location = (0.0, -2.0, 0.0) # move the camera to the correct position cam.parent = rig cam.parent_type = "BONE" cam.parent_bone = "CTRL" # Add blank drivers to lock the camera loc, rot scale cam.driver_add('location', 0) cam.driver_add('location', 1) cam.driver_add('location', 2) cam.driver_add('rotation_euler', 0) cam.driver_add('rotation_euler', 1) cam.driver_add('rotation_euler', 2) cam.driver_add('scale', 0) cam.driver_add('scale', 1) cam.driver_add('scale', 2) # Set new camera as active camera bpy.context.scene.camera = cam # make sure the camera is selectable by default (this can be locked in the UI) bpy.context.object.hide_select = False # make the rig the active object before finishing view_layer.objects.active = rig cam.select_set(False) rig.select_set(True) return rig
def generate(self): """ Generate the rig. Do NOT modify any of the original bones, except for adding constraints. The main armature should be selected and active before this is called. """ bpy.ops.object.mode_set(mode='EDIT') # Create the bones uarm = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_ik")))) farm = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_ik")))) hand = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], "_ik"))) pole = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], "_pole"))) vishand = copy_bone(self.obj, self.org_bones[2], "VIS-" + strip_org(insert_before_lr(self.org_bones[2], "_ik"))) vispole = copy_bone(self.obj, self.org_bones[1], "VIS-" + strip_org(insert_before_lr(self.org_bones[0], "_pole"))) # Get edit bones eb = self.obj.data.edit_bones uarm_e = eb[uarm] farm_e = eb[farm] hand_e = eb[hand] pole_e = eb[pole] vishand_e = eb[vishand] vispole_e = eb[vispole] # Parenting farm_e.parent = uarm_e hand_e.use_connect = False hand_e.parent = None pole_e.use_connect = False vishand_e.use_connect = False vishand_e.parent = None vispole_e.use_connect = False vispole_e.parent = None # Misc hand_e.use_local_location = False vishand_e.hide_select = True vispole_e.hide_select = True # Positioning v1 = farm_e.tail - uarm_e.head if 'X' in self.primary_rotation_axis or 'Y' in self.primary_rotation_axis: v2 = v1.cross(farm_e.x_axis) if (v2 * farm_e.z_axis) > 0.0: v2 *= -1.0 else: v2 = v1.cross(farm_e.z_axis) if (v2 * farm_e.x_axis) < 0.0: v2 *= -1.0 v2.normalize() v2 *= v1.length if '-' in self.primary_rotation_axis: v2 *= -1 pole_e.head = farm_e.head + v2 pole_e.tail = pole_e.head + (Vector((0, 1, 0)) * (v1.length / 8)) pole_e.roll = 0.0 vishand_e.tail = vishand_e.head + Vector((0, 0, v1.length / 32)) vispole_e.tail = vispole_e.head + Vector((0, 0, v1.length / 32)) # Determine the pole offset value plane = (farm_e.tail - uarm_e.head).normalized() vec1 = uarm_e.x_axis.normalized() vec2 = (pole_e.head - uarm_e.head).normalized() pole_offset = angle_on_plane(plane, vec1, vec2) # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones # uarm_p = pb[uarm] # UNUSED farm_p = pb[farm] hand_p = pb[hand] pole_p = pb[pole] vishand_p = pb[vishand] vispole_p = pb[vispole] # Set the elbow to only bend on the primary axis if 'X' in self.primary_rotation_axis: farm_p.lock_ik_y = True farm_p.lock_ik_z = True elif 'Y' in self.primary_rotation_axis: farm_p.lock_ik_x = True farm_p.lock_ik_z = True else: farm_p.lock_ik_x = True farm_p.lock_ik_y = True # Pole target only translates pole_p.lock_location = False, False, False pole_p.lock_rotation = True, True, True pole_p.lock_rotation_w = True pole_p.lock_scale = True, True, True # Set up custom properties if self.switch == True: prop = rna_idprop_ui_prop_get(hand_p, "ikfk_switch", create=True) hand_p["ikfk_switch"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Bend direction hint if self.bend_hint: con = farm_p.constraints.new('LIMIT_ROTATION') con.name = "bend_hint" con.owner_space = 'LOCAL' if self.primary_rotation_axis == 'X': con.use_limit_x = True con.min_x = pi / 10 con.max_x = pi / 10 elif self.primary_rotation_axis == '-X': con.use_limit_x = True con.min_x = -pi / 10 con.max_x = -pi / 10 elif self.primary_rotation_axis == 'Y': con.use_limit_y = True con.min_y = pi / 10 con.max_y = pi / 10 elif self.primary_rotation_axis == '-Y': con.use_limit_y = True con.min_y = -pi / 10 con.max_y = -pi / 10 elif self.primary_rotation_axis == 'Z': con.use_limit_z = True con.min_z = pi / 10 con.max_z = pi / 10 elif self.primary_rotation_axis == '-Z': con.use_limit_z = True con.min_z = -pi / 10 con.max_z = -pi / 10 # IK Constraint con = farm_p.constraints.new('IK') con.name = "ik" con.target = self.obj con.subtarget = hand con.pole_target = self.obj con.pole_subtarget = pole con.pole_angle = pole_offset con.chain_count = 2 # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = uarm if self.switch == True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = farm if self.switch == True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "ik" con.target = self.obj con.subtarget = hand if self.switch == True: # IK/FK switch driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = hand_p.path_from_id() + '["ikfk_switch"]' # VIS hand constraints con = vishand_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[2] con = vishand_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = hand con.volume = 'NO_VOLUME' con.rest_length = vishand_p.length # VIS pole constraints con = vispole_p.constraints.new('COPY_LOCATION') con.name = "copy_loc" con.target = self.obj con.subtarget = self.org_bones[1] con = vispole_p.constraints.new('STRETCH_TO') con.name = "stretch_to" con.target = self.obj con.subtarget = pole con.volume = 'NO_VOLUME' con.rest_length = vispole_p.length # Set layers if specified if self.layers: hand_p.bone.layers = self.layers pole_p.bone.layers = self.layers vishand_p.bone.layers = self.layers vispole_p.bone.layers = self.layers # Create widgets create_line_widget(self.obj, vispole) create_line_widget(self.obj, vishand) create_sphere_widget(self.obj, pole) ob = create_widget(self.obj, hand) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [uarm, farm, hand, pole]
def generate_rig(context, metarig): """ Generates a rig from a metarig. """ t = Timer() # clear created widget list if hasattr(create_widget, 'created_widgets'): del create_widget.created_widgets # clear copied bone list if hasattr(copy_bone, 'copied'): del copy_bone.copied # Find overwrite target rig if exists rig_name = get_rig_name(metarig) # store rig name to property if rig name already not stored. if not metarig.data.gamerig.rig_name: metarig.data.gamerig.rig_name = rig_name print("Fetch rig (%s)." % rig_name) obj = next((i for i in context.collection.objects if i != metarig and i.type == 'ARMATURE' and i.name == rig_name), None) if obj and not obj in context.visible_objects: return "GAMERIG ERROR: Overwritee rig '%s' is hidden. Cannot Operate." % obj.name # Random string with time appended so that # different rigs don't collide id's rig_id = (obj.data.get("gamerig_id") if obj else None) or random_id() # Initial configuration rest_backup = metarig.data.pose_position metarig.data.pose_position = 'REST' bpy.ops.object.mode_set(mode='OBJECT') view_layer = context.view_layer collection = context.collection layer_collection = context.layer_collection #------------------------------------------ # 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. previous_action = None previous_nla_tracks = None previous_nla_strips = {} toggledArmatureModifiers = [] if obj: print("Overwrite existing rig.") try: # toggle armature object to metarig if it using generated rig. # (referensing rig overwriting makes script runs very slowly) for i in collection.objects: for j in i.modifiers: if j.type == 'ARMATURE' and j.object == obj: toggledArmatureModifiers.append(j) j.object = metarig # Get rid of anim data in case the rig already existed print("Clear rig animation data.") previous_action = obj.animation_data.action previous_nla_tracks = tuple(obj.animation_data.nla_tracks) for i in previous_nla_tracks: previous_nla_strips[i] = tuple(i.strips) obj.animation_data_clear() obj.data.animation_data_clear() except KeyError: print("Overwrite failed.") obj = None if obj is None: print("Create new rig.") name = metarig.data.gamerig.rig_name or "rig" obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) # in case name 'rig' exists it will be rig.001 obj.display_type = 'WIRE' collection.objects.link(obj) # Put the rig_name in the armature custom properties rna_idprop_ui_prop_get(obj.data, "gamerig_id", create=True) obj.data["gamerig_id"] = rig_id obj.location = metarig.location obj.rotation_mode = metarig.rotation_mode obj.rotation_euler = metarig.rotation_euler obj.rotation_quaternion = metarig.rotation_quaternion obj.rotation_axis_angle = metarig.rotation_axis_angle obj.scale = metarig.scale # apply rotation for metarig / rig bpy.ops.object.select_all(action='DESELECT') metarig.select_set(True) obj.select_set(True) metarig_rotation_euler_backup = metarig.rotation_euler.copy() metarig_rotation_quaternion_backup = metarig.rotation_quaternion.copy() metarig_rotation_axis_angle_backup = Vector(metarig.rotation_axis_angle) rig_rotation_euler_backup = obj.rotation_euler.copy() rig_rotation_quaternion_backup = obj.rotation_quaternion.copy() rig_rotation_axis_angle_backup = Vector(obj.rotation_axis_angle) bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) obj.data.pose_position = 'POSE' # Select generated rig object metarig.select_set(False) obj.select_set(True) view_layer.objects.active = obj # Get parented objects to restore later childs = {} # {object: bone} for child in obj.children: childs[child] = child.parent_bone # 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() collection.objects.link(temp_rig_1) temp_rig_2 = metarig.copy() temp_rig_2.data = obj.data collection.objects.link(temp_rig_2) # Select the temp rigs for merging for objt in collection.objects: objt.select_set(False) # deselect all objects temp_rig_1.select_set(True) temp_rig_2.select_set(True) view_layer.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 view_layer.objects: objt.select_set(False) # deselect all objects obj.select_set(True) view_layer.objects.active = obj # Copy metarig's Custom properties to rig for prop in metarig.data.keys(): try: if prop != "_RNA_UI" and prop != "gamerig" and prop != "gamerig_id": obj.data[prop] = metarig.data[prop] except KeyError: pass # 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_easein = bone.bbone_easein bone_gen.bbone_easeout = bone.bbone_easeout # 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(): try: bone_gen[prop] = bone[prop] except KeyError: pass # Clear drivers if obj.animation_data: for d in obj.animation_data.drivers: try: obj.driver_remove(d.data_path) except TypeError: pass 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] # 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.append(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: ") #---------------------------------- error = None def append_error(bone, rig, e): nonlocal error errorstr = "failed at rig '%s' (%s)." % (bone, rig.__class__.__module__) if rig else "failed at rig '%s'." % bone errorstr += "\n " + e.message print("GameRig: %s" % errorstr) if error: error += '\n' + errorstr else: error = errorstr try: # Collect/initialize all the rigs. rigs = {} rigtypes = set() bpy.ops.object.mode_set(mode='EDIT') for bone in bones_sorted: try: rig = get_bone_rig(metarig, obj, bone, rigtypes) if rig: rigs[bone] = rig except MetarigError as e: append_error(bone, None, e) t.tick("Initialize rigs: ") begin_progress(len(rigs.keys()) * 2) # Generate all the rigs. bpy.ops.object.mode_set(mode='OBJECT') context.view_layer.objects.active = obj obj.select_set(True) tt = Timer() ui_scripts = [] # Go into editmode in the rig armature bpy.ops.object.mode_set(mode='EDIT') for bone, rig in dict(rigs.items()).items(): try: script = rig.generate(context) if script and len(script) > 0: ui_scripts.append(script) except MetarigError as e: append_error(bone, rig, e) del rigs[bone] tt.tick("Generate rig : %s (%s): " % (bone, rig.__class__.__module__)) update_progress() # Go into objectmode in the rig armature bpy.ops.object.mode_set(mode='OBJECT') # Copy Constraints for bone in metarig.pose.bones: bone_gen = obj.pose.bones[bone.name] 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 target = v2.targets[i] # If a custom property if v2.type == 'SINGLE_PROP' and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path): target.data_path = "GAMERIG-" + target.data_path # Copy key frames try: 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) except TypeError: pass for bone, rig in rigs.items(): try: rig.postprocess(context) except MetarigError as e: append_error(bone, rig, e) tt.tick("PostProcess rig : %s (%s): " % (bone, rig.__class__.__module__)) update_progress() t.tick("Generate rigs: ") except Exception as e: # Cleanup if something goes wrong print("GameRig: 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 finally: end_progress() # Alter marked driver targets if obj.animation_data: for d in obj.animation_data.drivers: for v in d.driver.variables: for target in v.targets: if target.data_path.startswith("GAMERIG-"): temp, bone, prop = tuple([x.strip('"]') for x in target.data_path.split('["')]) if bone in obj.data.bones and prop in obj.pose.bones[bone].keys(): target.data_path = target.data_path[7:] else: target.data_path = 'pose.bones["%s"]["%s"]' % (basename(bone), prop) #? # Get a list of all the bones in the armature bones = [bone.name for bone in obj.data.bones] metabones = [bone.name for bone in metarig.data.bones] # All the others make non-deforming. for bone in bones: if not (is_org(bone) or bone in metabones): b = obj.data.bones[bone] b.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 is_mch(obj.data.bones[bone].name): obj.data.bones[bone].layers = MCH_LAYER # Assign shapes to bones assign_all_widgets(obj) # 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]) obj.data.layers = vis_layers # Ensure the collection of layer names exists for i in range(1 + len(metarig.data.gamerig.layers), 30): metarig.data.gamerig.layers.add() # Create list of layer name/row pairs layer_layout = [] for l in metarig.data.gamerig.layers: #print(l.name) layer_layout.append((l.name, l.row)) # Generate the UI script rig_ui_name = 'gamerig_ui_%s.py' % rig_id if rig_ui_name in bpy.data.texts.keys(): script = bpy.data.texts[rig_ui_name] try: script.as_module().unregister() except: pass script.clear() else: script = bpy.data.texts.new(rig_ui_name) operator_scripts = '' for rigt in rigtypes: try: rigt.operator_script except AttributeError: pass else: operator_scripts += rigt.operator_script(rig_id) uitemplate = rig_lists.riguitemplate_dic[metarig.data.gamerig.rig_ui_template] script.write( uitemplate[0].format( rig_id=rig_id, operators=operator_scripts, properties=properties_ui(ui_scripts), layers=layers_ui(vis_layers, layer_layout) ) ) script.use_module = True # Register UI script script.as_module().register() # Create Selection Sets create_selection_sets(obj, metarig) # Create Bone Groups create_bone_groups(obj, metarig) # Remove all jig bones. bpy.ops.object.mode_set(mode='EDIT') for bone in [bone.name for bone in obj.data.edit_bones]: if is_jig(bone): obj.data.edit_bones.remove(obj.data.edit_bones[bone]) #---------------------------------- # Deconfigure bpy.ops.object.mode_set(mode='OBJECT') # Restore original rotation metarig_rotation_euler_inverted = metarig_rotation_euler_backup.copy() metarig_rotation_euler_inverted.x *= -1 metarig_rotation_euler_inverted.y *= -1 metarig_rotation_euler_inverted.z *= -1 metarig_rotation_axis_angle_inverted = metarig_rotation_axis_angle_backup.copy() metarig_rotation_axis_angle_inverted.w *= -1 metarig.rotation_euler = metarig_rotation_euler_inverted metarig.rotation_quaternion = metarig_rotation_quaternion_backup.inverted() metarig.rotation_axis_angle = metarig_rotation_axis_angle_inverted rig_rotation_euler_inverted = rig_rotation_euler_backup.copy() rig_rotation_euler_inverted.x *= -1 rig_rotation_euler_inverted.y *= -1 rig_rotation_euler_inverted.z *= -1 rig_rotation_axis_angle_inverted = rig_rotation_axis_angle_backup.copy() rig_rotation_axis_angle_inverted.w *= -1 obj.rotation_euler = rig_rotation_euler_inverted obj.rotation_quaternion = rig_rotation_quaternion_backup.inverted() obj.rotation_axis_angle = rig_rotation_axis_angle_inverted bpy.ops.object.select_all(action='DESELECT') metarig.select_set(True) obj.select_set(True) bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) for ob in bpy.data.objects: if ob.parent == obj: for i in range(len(ob.rotation_euler)): if abs(ob.rotation_euler[i]) <= sys.float_info.epsilon: ob.rotation_euler[i] = 0 for i in range(len(ob.rotation_quaternion)): if abs(ob.rotation_quaternion[i]) <= sys.float_info.epsilon: ob.rotation_quaternion[i] = 0 for i in range(len(ob.rotation_axis_angle)): if abs(ob.rotation_axis_angle[i]) <= sys.float_info.epsilon: ob.rotation_axis_angle[i] = 0 metarig.rotation_euler = metarig_rotation_euler_backup metarig.rotation_quaternion = metarig_rotation_quaternion_backup metarig.rotation_axis_angle = metarig_rotation_axis_angle_backup obj.rotation_euler = rig_rotation_euler_backup obj.rotation_quaternion = rig_rotation_quaternion_backup obj.rotation_axis_angle = rig_rotation_axis_angle_backup metarig.select_set(False) obj.select_set(True) metarig.data.pose_position = rest_backup obj.data.pose_position = 'POSE' # Restore toggled armature modifiers for i in toggledArmatureModifiers: i.object = obj # Restore parent to bones for child, sub_parent in childs.items(): if sub_parent in obj.pose.bones: mat = child.matrix_world.copy() child.parent_bone = sub_parent child.matrix_world = mat # Restore active collection view_layer.active_layer_collection = layer_collection # Restore action and NLA tracks if previous_action: obj.animation_data.action = previous_action if previous_nla_tracks: try: for s in previous_nla_tracks: d = obj.animation_data.nla_tracks.new() copy_attributes(s, d) for ss in previous_nla_strips[s]: dd = d.strips.new(ss.name, ss.frame_start, ss.action) copy_attributes(ss, dd) except Exception as e: print("GameRig: Warning. failed to restore NLA tracks.") t.tick("The rest: ") return error
def generate(self): bpy.ops.object.mode_set(mode='EDIT') # Create non-scaling parent bone if self.org_parent is not None: loc = Vector(self.obj.data.edit_bones[self.org_bones[0]].head) parent = make_nonscaling_child(self.obj, self.org_parent, loc, "_fk") else: parent = None # Create the control bones ulimb = copy_bone(self.obj, self.org_bones[0], strip_org(insert_before_lr(self.org_bones[0], ".fk"))) flimb = copy_bone(self.obj, self.org_bones[1], strip_org(insert_before_lr(self.org_bones[1], ".fk"))) elimb = copy_bone(self.obj, self.org_bones[2], strip_org(insert_before_lr(self.org_bones[2], ".fk"))) # Create the end-limb mechanism bone elimb_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2]))) # Create the anti-stretch bones # These sit between a parent and its child, and counteract the # stretching of the parent so that the child is unaffected fantistr = copy_bone(self.obj, self.org_bones[0], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[0], "_antistr.fk")))) eantistr = copy_bone(self.obj, self.org_bones[1], make_mechanism_name(strip_org(insert_before_lr(self.org_bones[1], "_antistr.fk")))) # Create the hinge bones if parent is not None: socket1 = copy_bone(self.obj, ulimb, make_mechanism_name(ulimb + ".socket1")) socket2 = copy_bone(self.obj, ulimb, make_mechanism_name(ulimb + ".socket2")) # Get edit bones eb = self.obj.data.edit_bones ulimb_e = eb[ulimb] flimb_e = eb[flimb] elimb_e = eb[elimb] fantistr_e = eb[fantistr] eantistr_e = eb[eantistr] elimb_mch_e = eb[elimb_mch] if parent is not None: socket1_e = eb[socket1] socket2_e = eb[socket2] # Parenting elimb_mch_e.use_connect = False elimb_mch_e.parent = elimb_e elimb_e.use_connect = False elimb_e.parent = eantistr_e eantistr_e.use_connect = False eantistr_e.parent = flimb_e flimb_e.use_connect = False flimb_e.parent = fantistr_e fantistr_e.use_connect = False fantistr_e.parent = ulimb_e if parent is not None: socket1_e.use_connect = False socket1_e.parent = eb[parent] socket2_e.use_connect = False socket2_e.parent = None ulimb_e.use_connect = False ulimb_e.parent = socket2_e # Positioning fantistr_e.length /= 8 put_bone(self.obj, fantistr, Vector(ulimb_e.tail)) eantistr_e.length /= 8 put_bone(self.obj, eantistr, Vector(flimb_e.tail)) if parent is not None: socket1_e.length /= 4 socket2_e.length /= 3 # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones ulimb_p = pb[ulimb] flimb_p = pb[flimb] elimb_p = pb[elimb] fantistr_p = pb[fantistr] eantistr_p = pb[eantistr] if parent is not None: socket2_p = pb[socket2] # Lock axes ulimb_p.lock_location = (True, True, True) flimb_p.lock_location = (True, True, True) elimb_p.lock_location = (True, True, True) # Set the elbow to only bend on the x-axis. flimb_p.rotation_mode = 'XYZ' if 'X' in self.primary_rotation_axis: flimb_p.lock_rotation = (False, True, True) elif 'Y' in self.primary_rotation_axis: flimb_p.lock_rotation = (True, False, True) else: flimb_p.lock_rotation = (True, True, False) # Set up custom properties if parent is not None: prop = rna_idprop_ui_prop_get(ulimb_p, "isolate", create=True) ulimb_p["isolate"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 prop = rna_idprop_ui_prop_get(ulimb_p, "stretch_length", create=True) ulimb_p["stretch_length"] = 1.0 prop["min"] = 0.05 prop["max"] = 20.0 prop["soft_min"] = 0.25 prop["soft_max"] = 4.0 # Stretch drivers def add_stretch_drivers(pose_bone): driver = pose_bone.driver_add("scale", 1).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "stretch_length" driver = pose_bone.driver_add("scale", 0).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "1/sqrt(stretch_length)" driver = pose_bone.driver_add("scale", 2).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "1/sqrt(stretch_length)" def add_antistretch_drivers(pose_bone): driver = pose_bone.driver_add("scale", 1).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "1/stretch_length" driver = pose_bone.driver_add("scale", 0).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "sqrt(stretch_length)" driver = pose_bone.driver_add("scale", 2).driver var = driver.variables.new() var.name = "stretch_length" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["stretch_length"]' driver.type = 'SCRIPTED' driver.expression = "sqrt(stretch_length)" add_stretch_drivers(ulimb_p) add_stretch_drivers(flimb_p) add_antistretch_drivers(fantistr_p) add_antistretch_drivers(eantistr_p) # Hinge constraints / drivers if parent is not None: con = socket2_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = socket1 con = socket2_p.constraints.new('COPY_TRANSFORMS') con.name = "isolate_off" con.target = self.obj con.subtarget = socket1 # Driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = ulimb_p.path_from_id() + '["isolate"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = ulimb con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = flimb con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = elimb_mch # Set layers if specified if self.layers: ulimb_p.bone.layers = self.layers flimb_p.bone.layers = self.layers elimb_p.bone.layers = self.layers # Create control widgets create_limb_widget(self.obj, ulimb) create_limb_widget(self.obj, flimb) ob = create_widget(self.obj, elimb) if ob is not None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [ulimb, flimb, elimb, elimb_mch]
def generate(self): """ Generate the rig. Do NOT modify any of the original bones, except for adding constraints. The main armature should be selected and active before this is called. """ bpy.ops.object.mode_set(mode='EDIT') # Create the control bones thigh = copy_bone(self.obj, self.org_bones[0], strip_org(self.org_bones[0])) shin = copy_bone(self.obj, self.org_bones[1], strip_org(self.org_bones[1])) foot = copy_bone(self.obj, self.org_bones[2], strip_org(self.org_bones[2])) # Create the foot mechanism bone foot_mch = copy_bone(self.obj, self.org_bones[2], make_mechanism_name(strip_org(self.org_bones[2]))) # Create the hinge bones if self.org_parent != None: hinge = copy_bone(self.obj, self.org_parent, make_mechanism_name(thigh + ".hinge")) socket1 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket1")) socket2 = copy_bone(self.obj, thigh, make_mechanism_name(thigh + ".socket2")) # Get edit bones eb = self.obj.data.edit_bones thigh_e = eb[thigh] shin_e = eb[shin] foot_e = eb[foot] foot_mch_e = eb[foot_mch] if self.org_parent != None: hinge_e = eb[hinge] socket1_e = eb[socket1] socket2_e = eb[socket2] # Parenting shin_e.parent = thigh_e foot_e.parent = shin_e foot_mch_e.use_connect = False foot_mch_e.parent = foot_e if self.org_parent != None: hinge_e.use_connect = False socket1_e.use_connect = False socket2_e.use_connect = False thigh_e.parent = hinge_e hinge_e.parent = socket2_e socket2_e.parent = None # Positioning vec = Vector(eb[self.org_bones[3]].vector) vec = vec.normalize() foot_e.tail = foot_e.head + (vec * foot_e.length) foot_e.roll = eb[self.org_bones[3]].roll if self.org_parent != None: center = (hinge_e.head + hinge_e.tail) / 2 hinge_e.head = center socket1_e.length /= 4 socket2_e.length /= 3 # Object mode, get pose bones bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones thigh_p = pb[thigh] shin_p = pb[shin] foot_p = pb[foot] if self.org_parent != None: socket1_p = pb[socket1] socket2_p = pb[socket2] # Set the elbow to only bend on the x-axis. shin_p.rotation_mode = 'XYZ' if 'X' in self.primary_rotation_axis: shin_p.lock_rotation = (False, True, True) elif 'Y' in self.primary_rotation_axis: shin_p.lock_rotation = (True, False, True) else: shin_p.lock_rotation = (True, True, False) # Set up custom properties if self.org_parent != None: prop = rna_idprop_ui_prop_get(thigh_p, "isolate", create=True) thigh_p["isolate"] = 0.0 prop["soft_min"] = prop["min"] = 0.0 prop["soft_max"] = prop["max"] = 1.0 # Hinge constraints / drivers if self.org_parent != None: con = socket2_p.constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = socket1 con = socket2_p.constraints.new('COPY_TRANSFORMS') con.name = "isolate_off" con.target = self.obj con.subtarget = socket1 # Driver fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = thigh_p.path_from_id() + '["isolate"]' mod = fcurve.modifiers[0] mod.poly_order = 1 mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 # Constrain org bones to controls con = pb[self.org_bones[0]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = thigh con = pb[self.org_bones[1]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = shin con = pb[self.org_bones[2]].constraints.new('COPY_TRANSFORMS') con.name = "fk" con.target = self.obj con.subtarget = foot_mch # Set layers if specified if self.layers: thigh_p.bone.layers = self.layers shin_p.bone.layers = self.layers foot_p.bone.layers = self.layers # Create control widgets create_limb_widget(self.obj, thigh) create_limb_widget(self.obj, shin) ob = create_widget(self.obj, foot) if ob != None: verts = [(0.7, 1.5, 0.0), (0.7, -0.25, 0.0), (-0.7, -0.25, 0.0), (-0.7, 1.5, 0.0), (0.7, 0.723, 0.0), (-0.7, 0.723, 0.0), (0.7, 0.0, 0.0), (-0.7, 0.0, 0.0)] edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)] mesh = ob.data mesh.from_pydata(verts, edges, []) mesh.update() mod = ob.modifiers.new("subsurf", 'SUBSURF') mod.levels = 2 return [thigh, shin, foot]
def create_def( self, tweaks ): org_bones = self.org_bones bpy.ops.object.mode_set(mode ='EDIT') eb = self.obj.data.edit_bones def_bones = [] for i,org in enumerate(org_bones): if i < len(org_bones) - 1: # Create segments if specified for j in range( self.segments ): name = get_bone_name( strip_org(org), 'def' ) def_name = copy_bone( self.obj, org, name ) eb[ def_name ].length /= self.segments # If we have more than one segments, place the 2nd and # onwards on the tail of the previous bone if j > 0: put_bone(self.obj, def_name, eb[ def_bones[-1] ].tail) def_bones += [ def_name ] else: name = get_bone_name( strip_org(org), 'def' ) def_name = copy_bone( self.obj, org, name ) def_bones.append( def_name ) # Parent deform bones for i,b in enumerate( def_bones ): if i > 0: # For all bones but the first (which has no parent) eb[b].parent = eb[ def_bones[i-1] ] # to previous eb[b].use_connect = True # Constraint def to tweaks for d,t in zip(def_bones, tweaks): tidx = tweaks.index(t) make_constraint( self, d, { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : t }) if tidx != len(tweaks) - 1: make_constraint( self, d, { 'constraint' : 'DAMPED_TRACK', 'subtarget' : tweaks[ tidx + 1 ], }) make_constraint( self, d, { 'constraint' : 'STRETCH_TO', 'subtarget' : tweaks[ tidx + 1 ], }) # Create bbone segments for bone in def_bones[:-1]: self.obj.data.bones[bone].bbone_segments = self.bbones self.obj.data.bones[ def_bones[0] ].bbone_in = 0.0 self.obj.data.bones[ def_bones[-2] ].bbone_out = 0.0 self.obj.data.bones[ def_bones[-1] ].bbone_in = 0.0 self.obj.data.bones[ def_bones[-1] ].bbone_out = 0.0 # Rubber hose drivers pb = self.obj.pose.bones for i,t in enumerate( tweaks[1:-1] ): # Create custom property on tweak bone to control rubber hose name = 'rubber_tweak' if i == trunc( len( tweaks[1:-1] ) / 2 ): pb[t][name] = 0.0 else: pb[t][name] = 1.0 prop = rna_idprop_ui_prop_get( pb[t], name, create=True ) prop["min"] = 0.0 prop["max"] = 2.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = name for j,d in enumerate(def_bones[:-1]): drvs = {} if j != 0: tidx = j drvs[tidx] = self.obj.data.bones[d].driver_add("bbone_in").driver if j != len( def_bones[:-1] ) - 1: tidx = j + 1 drvs[tidx] = self.obj.data.bones[d].driver_add("bbone_out").driver for d in drvs: drv = drvs[d] name = 'rubber_tweak' drv.type = 'AVERAGE' var = drv.variables.new() var.name = name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb[tweaks[d]].path_from_id() + \ '[' + '"' + name + '"' + ']' return def_bones
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) # rigify_type and rigify_parameters bone_gen.rigify_type = bone.rigify_type for prop in dir(bone_gen.rigify_parameters): if (not prop.startswith("_")) \ and (not prop.startswith("bl_")) \ and (prop != "rna_type"): try: setattr(bone_gen.rigify_parameters, prop, \ getattr(bone.rigify_parameters, prop)) except AttributeError: print("FAILED TO COPY PARAMETER: " + str(prop)) # Custom properties for prop in bone.keys(): try: bone_gen[prop] = bone[prop] except KeyError: pass # 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 = [] 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') # Lock transforms on all non-control bones r = re.compile("[A-Z][A-Z][A-Z]-") for bone in bones: if r.match(bone): pb = obj.pose.bones[bone] pb.lock_location = (True, True, True) pb.lock_rotation = (True, True, True) pb.lock_rotation_w = True pb.lock_scale = (True, True, True) # 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 if obj.animation_data: 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)[:63] # Object names are limited to 63 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 control(self): """ Generate the control rig. """ bpy.ops.object.mode_set(mode='EDIT') # Figure out the name for the control bone (remove the last .##) ctrl_name = re.sub("([0-9]+\.)", "", strip_org(self.org_bones[0])[::-1], count=1)[::-1] # Create the bones ctrl = copy_bone(self.obj, self.org_bones[0], ctrl_name) helpers = [] bones = [] for bone in self.org_bones: bones += [copy_bone(self.obj, bone, strip_org(bone))] helpers += [ copy_bone(self.obj, bone, make_mechanism_name(strip_org(bone))) ] # Position bones eb = self.obj.data.edit_bones length = 0.0 for bone in helpers: length += eb[bone].length eb[bone].length /= 2 eb[ctrl].length = length * 1.5 # Parent bones prev = eb[self.org_bones[0]].parent for (b, h) in zip(bones, helpers): b_e = eb[b] h_e = eb[h] b_e.use_connect = False h_e.use_connect = False b_e.parent = h_e h_e.parent = prev prev = b_e # Transform locks and rotation mode bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones for bone in bones[1:]: pb[bone].lock_location = True, True, True if pb[self.org_bones[0]].bone.use_connect is True: pb[bones[0]].lock_location = True, True, True pb[ctrl].lock_scale = True, False, True for bone in helpers: pb[bone].rotation_mode = 'XYZ' # Drivers i = 1 val = 1.2 / (len(self.org_bones) - 1) for bone in helpers: # Add custom prop prop_name = "bend_%02d" % i prop = rna_idprop_ui_prop_get(pb[ctrl], prop_name, create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 if i == 1: pb[ctrl][prop_name] = 0.0 else: pb[ctrl][prop_name] = val # Add driver if 'X' in self.primary_rotation_axis: fcurve = pb[bone].driver_add("rotation_euler", 0) elif 'Y' in self.primary_rotation_axis: fcurve = pb[bone].driver_add("rotation_euler", 1) else: fcurve = pb[bone].driver_add("rotation_euler", 2) driver = fcurve.driver driver.type = 'SCRIPTED' var = driver.variables.new() var.name = "ctrl_y" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = pb[ctrl].path_from_id() + '.scale[1]' var = driver.variables.new() var.name = "bend" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = pb[ctrl].path_from_id( ) + '["' + prop_name + '"]' if '-' in self.primary_rotation_axis: driver.expression = "-(1.0-ctrl_y) * bend * 3.14159 * 2" else: driver.expression = "(1.0-ctrl_y) * bend * 3.14159 * 2" i += 1 # Constraints con = pb[helpers[0]].constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = ctrl con = pb[helpers[0]].constraints.new('COPY_ROTATION') con.name = "copy_rotation" con.target = self.obj con.subtarget = ctrl # Constrain org bones to the control bones for (bone, org) in zip(bones, self.org_bones): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = bone # Set layers for extra control bones if self.ex_layers: for bone in bones: pb[bone].bone.layers = self.ex_layers # Create control widgets w = create_widget(self.obj, ctrl) if w is not None: mesh = w.data verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)] if 'Z' in self.primary_rotation_axis: # Flip x/z coordinates temp = [] for v in verts: temp += [(v[2], v[1], v[0])] verts = temp edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] mesh.from_pydata(verts, edges, []) mesh.update() for bone in bones: create_limb_widget(self.obj, bone)
def control(self): """ Generate the control rig. """ bpy.ops.object.mode_set(mode='EDIT') # Figure out the name for the control bone (remove the last .##) ctrl_name = re.sub("([0-9]+\.)", "", strip_org(self.org_bones[0])[::-1], count=1)[::-1] # Create the bones ctrl = copy_bone(self.obj, self.org_bones[0], ctrl_name) helpers = [] bones = [] for bone in self.org_bones: bones += [copy_bone(self.obj, bone, strip_org(bone))] helpers += [copy_bone(self.obj, bone, make_mechanism_name(strip_org(bone)))] # Position bones eb = self.obj.data.edit_bones length = 0.0 for bone in helpers: length += eb[bone].length eb[bone].length /= 2 eb[ctrl].length = length * 1.5 # Parent bones prev = eb[self.org_bones[0]].parent for (b, h) in zip(bones, helpers): b_e = eb[b] h_e = eb[h] b_e.use_connect = False h_e.use_connect = False b_e.parent = h_e h_e.parent = prev prev = b_e # Transform locks and rotation mode bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones for bone in bones[1:]: pb[bone].lock_location = True, True, True if pb[self.org_bones[0]].bone.use_connect is True: pb[bones[0]].lock_location = True, True, True pb[ctrl].lock_scale = True, False, True for bone in helpers: pb[bone].rotation_mode = 'XYZ' # Drivers i = 1 val = 1.2 / (len(self.org_bones) - 1) for bone in helpers: # Add custom prop prop_name = "bend_%02d" % i prop = rna_idprop_ui_prop_get(pb[ctrl], prop_name, create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 if i == 1: pb[ctrl][prop_name] = 0.0 else: pb[ctrl][prop_name] = val # Add driver if 'X' in self.primary_rotation_axis: fcurve = pb[bone].driver_add("rotation_euler", 0) elif 'Y' in self.primary_rotation_axis: fcurve = pb[bone].driver_add("rotation_euler", 1) else: fcurve = pb[bone].driver_add("rotation_euler", 2) driver = fcurve.driver driver.type = 'SCRIPTED' var = driver.variables.new() var.name = "ctrl_y" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = pb[ctrl].path_from_id() + '.scale[1]' var = driver.variables.new() var.name = "bend" var.targets[0].id_type = 'OBJECT' var.targets[0].id = self.obj var.targets[0].data_path = pb[ctrl].path_from_id() + '["' + prop_name + '"]' if '-' in self.primary_rotation_axis: driver.expression = "-(1.0-ctrl_y) * bend * 3.14159 * 2" else: driver.expression = "(1.0-ctrl_y) * bend * 3.14159 * 2" i += 1 # Constraints con = pb[helpers[0]].constraints.new('COPY_LOCATION') con.name = "copy_location" con.target = self.obj con.subtarget = ctrl con = pb[helpers[0]].constraints.new('COPY_ROTATION') con.name = "copy_rotation" con.target = self.obj con.subtarget = ctrl # Constrain org bones to the control bones for (bone, org) in zip(bones, self.org_bones): con = pb[org].constraints.new('COPY_TRANSFORMS') con.name = "copy_transforms" con.target = self.obj con.subtarget = bone # Set layers for extra control bones if self.ex_layers: for bone in bones: pb[bone].bone.layers = self.ex_layers # Create control widgets w = create_widget(self.obj, ctrl) if w != None: mesh = w.data verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)] if 'Z' in self.primary_rotation_axis: # Flip x/z coordinates temp = [] for v in verts: temp += [(v[2], v[1], v[0])] verts = temp edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] mesh.from_pydata(verts, edges, []) mesh.update() for bone in bones: create_limb_widget(self.obj, bone)
def create_arm(self, bones): org_bones = self.org_bones bpy.ops.object.mode_set(mode="EDIT") eb = self.obj.data.edit_bones ctrl = get_bone_name(org_bones[2], "ctrl", "ik") # Create IK arm control ctrl = copy_bone(self.obj, org_bones[2], ctrl) # clear parent (so that rigify will parent to root) eb[ctrl].parent = None eb[ctrl].use_connect = False # Parent eb[bones["ik"]["mch_target"]].parent = eb[ctrl] eb[bones["ik"]["mch_target"]].use_connect = False # MCH for ik control ctrl_socket = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], "mch", "ik_socket")) eb[ctrl_socket].tail = eb[ctrl_socket].head + 0.8 * (eb[ctrl_socket].tail - eb[ctrl_socket].head) eb[ctrl_socket].parent = None eb[ctrl].parent = eb[ctrl_socket] ctrl_root = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], "mch", "ik_root")) eb[ctrl_root].tail = eb[ctrl_root].head + 0.7 * (eb[ctrl_root].tail - eb[ctrl_root].head) eb[ctrl_root].use_connect = False eb[ctrl_root].parent = eb["root"] if eb[org_bones[0]].parent: arm_parent = eb[org_bones[0]].parent ctrl_parent = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], "mch", "ik_parent")) eb[ctrl_parent].tail = eb[ctrl_parent].head + 0.6 * (eb[ctrl_parent].tail - eb[ctrl_parent].head) eb[ctrl_parent].use_connect = False eb[ctrl_parent].parent = eb[org_bones[0]].parent else: arm_parent = None # Set up constraints # Constrain ik ctrl to root / parent make_constraint(self, ctrl_socket, {"constraint": "COPY_TRANSFORMS", "subtarget": ctrl_root}) if arm_parent: make_constraint(self, ctrl_socket, {"constraint": "COPY_TRANSFORMS", "subtarget": ctrl_parent}) # Constrain mch target bone to the ik control and mch stretch make_constraint( self, bones["ik"]["mch_target"], {"constraint": "COPY_LOCATION", "subtarget": bones["ik"]["mch_str"], "head_tail": 1.0}, ) # Constrain mch ik stretch bone to the ik control make_constraint(self, bones["ik"]["mch_str"], {"constraint": "DAMPED_TRACK", "subtarget": ctrl}) make_constraint(self, bones["ik"]["mch_str"], {"constraint": "STRETCH_TO", "subtarget": ctrl}) make_constraint( self, bones["ik"]["mch_str"], {"constraint": "LIMIT_SCALE", "use_min_y": True, "use_max_y": True, "max_y": 1.05, "owner_space": "LOCAL"}, ) # Create ik/fk switch property pb = self.obj.pose.bones pb_parent = pb[bones["parent"]] # Modify rotation mode for ik and tweak controls pb[bones["ik"]["ctrl"]["limb"]].rotation_mode = "ZXY" for b in bones["tweak"]["ctrl"]: pb[b].rotation_mode = "ZXY" pb_parent["IK_Strertch"] = 1.0 prop = rna_idprop_ui_prop_get(pb_parent, "IK_Strertch", create=True) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = "IK Stretch" # Add driver to limit scale constraint influence b = bones["ik"]["mch_str"] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = "SUM" var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = pb_parent.path_from_id() + "[" + '"' + prop.name + '"' + "]" drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = "POLYNOMIAL" drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create hand widget create_hand_widget(self.obj, ctrl, bone_transform_name=None) bones["ik"]["ctrl"]["terminal"] = [ctrl] if arm_parent: bones["ik"]["mch_hand"] = [ctrl_socket, ctrl_root, ctrl_parent] else: bones["ik"]["mch_hand"] = [ctrl_socket, ctrl_root] return bones
def create_leg( cls, bones ): org_bones = list( [cls.org_bones[0]] + connected_children_names(cls.obj, cls.org_bones[0]) ) bones['ik']['ctrl']['terminal'] = [] bpy.ops.object.mode_set(mode='EDIT') eb = cls.obj.data.edit_bones # Create toes def bone toes_def = get_bone_name( org_bones[-1], 'def' ) toes_def = copy_bone( cls.obj, org_bones[-1], toes_def ) eb[ toes_def ].use_connect = False eb[ toes_def ].parent = eb[ bones['def'][-1] ] eb[ toes_def ].use_connect = True bones['def'] += [ toes_def ] # Create IK leg control ctrl = get_bone_name( org_bones[2], 'ctrl', 'ik' ) ctrl = copy_bone( cls.obj, org_bones[2], ctrl ) # clear parent (so that rigify will parent to root) eb[ ctrl ].parent = None eb[ ctrl ].use_connect = False # Create heel ctrl bone heel = get_bone_name( org_bones[2], 'ctrl', 'heel_ik' ) heel = copy_bone( cls.obj, org_bones[2], heel ) orient_bone( cls, eb[ heel ], 'y', 0.5 ) eb[ heel ].length = eb[ org_bones[2] ].length / 2 # Reset control position and orientation l = eb[ ctrl ].length orient_bone( cls, eb[ ctrl ], 'y', reverse = True ) eb[ ctrl ].length = l # Parent eb[ heel ].use_connect = False eb[ heel ].parent = eb[ ctrl ] eb[ bones['ik']['mch_target'] ].parent = eb[ heel ] eb[ bones['ik']['mch_target'] ].use_connect = False # Create foot mch rock and roll bones # Get the tmp heel (floating unconnected without children) tmp_heel = "" for b in cls.obj.data.bones[ org_bones[2] ].children: if not b.use_connect and not b.children: tmp_heel = b.name # roll1 MCH bone roll1_mch = get_bone_name( tmp_heel, 'mch', 'roll' ) roll1_mch = copy_bone( cls.obj, org_bones[2], roll1_mch ) # clear parent eb[ roll1_mch ].use_connect = False eb[ roll1_mch ].parent = None flip_bone( cls.obj, roll1_mch ) # Create 2nd roll mch, and two rock mch bones roll2_mch = get_bone_name( tmp_heel, 'mch', 'roll' ) roll2_mch = copy_bone( cls.obj, org_bones[3], roll2_mch ) eb[ roll2_mch ].use_connect = False eb[ roll2_mch ].parent = None put_bone( cls.obj, roll2_mch, ( eb[ tmp_heel ].head + eb[ tmp_heel ].tail ) / 2 ) eb[ roll2_mch ].length /= 4 # Rock MCH bones rock1_mch = get_bone_name( tmp_heel, 'mch', 'rock' ) rock1_mch = copy_bone( cls.obj, tmp_heel, rock1_mch ) eb[ rock1_mch ].use_connect = False eb[ rock1_mch ].parent = None orient_bone( cls, eb[ rock1_mch ], 'y', 1.0, reverse = True ) eb[ rock1_mch ].length = eb[ tmp_heel ].length / 2 rock2_mch = get_bone_name( tmp_heel, 'mch', 'rock' ) rock2_mch = copy_bone( cls.obj, tmp_heel, rock2_mch ) eb[ rock2_mch ].use_connect = False eb[ rock2_mch ].parent = None orient_bone( cls, eb[ rock2_mch ], 'y', 1.0 ) eb[ rock2_mch ].length = eb[ tmp_heel ].length / 2 # Parent rock and roll MCH bones eb[ roll1_mch ].parent = eb[ roll2_mch ] eb[ roll2_mch ].parent = eb[ rock1_mch ] eb[ rock1_mch ].parent = eb[ rock2_mch ] eb[ rock2_mch ].parent = eb[ ctrl ] # Constrain rock and roll MCH bones make_constraint( cls, roll1_mch, { 'constraint' : 'COPY_ROTATION', 'subtarget' : heel, 'owner_space' : 'LOCAL', 'target_space' : 'LOCAL' }) make_constraint( cls, roll1_mch, { 'constraint' : 'LIMIT_ROTATION', 'use_limit_x' : True, 'max_x' : math.radians(360), 'owner_space' : 'LOCAL' }) make_constraint( cls, roll2_mch, { 'constraint' : 'COPY_ROTATION', 'subtarget' : heel, 'use_y' : False, 'use_z' : False, 'invert_x' : True, 'owner_space' : 'LOCAL', 'target_space' : 'LOCAL' }) make_constraint( cls, roll2_mch, { 'constraint' : 'LIMIT_ROTATION', 'use_limit_x' : True, 'max_x' : math.radians(360), 'owner_space' : 'LOCAL' }) pb = cls.obj.pose.bones for i,b in enumerate([ rock1_mch, rock2_mch ]): head_tail = pb[b].head - pb[tmp_heel].head if '.L' in b: if not i: min_y = 0 max_y = math.radians(360) else: min_y = math.radians(-360) max_y = 0 else: if not i: min_y = math.radians(-360) max_y = 0 else: min_y = 0 max_y = math.radians(360) make_constraint( cls, b, { 'constraint' : 'COPY_ROTATION', 'subtarget' : heel, 'use_x' : False, 'use_z' : False, 'owner_space' : 'LOCAL', 'target_space' : 'LOCAL' }) make_constraint( cls, b, { 'constraint' : 'LIMIT_ROTATION', 'use_limit_y' : True, 'min_y' : min_y, 'max_y' : max_y, 'owner_space' : 'LOCAL' }) # Constrain 4th ORG to roll2 MCH bone make_constraint( cls, org_bones[3], { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : roll2_mch }) # Set up constraints # Constrain mch target bone to the ik control and mch stretch make_constraint( cls, bones['ik']['mch_target'], { 'constraint' : 'COPY_LOCATION', 'subtarget' : bones['ik']['mch_str'], 'head_tail' : 1.0 }) # Constrain mch ik stretch bone to the ik control make_constraint( cls, bones['ik']['mch_str'], { 'constraint' : 'DAMPED_TRACK', 'subtarget' : roll1_mch, 'head_tail' : 1.0 }) make_constraint( cls, bones['ik']['mch_str'], { 'constraint' : 'STRETCH_TO', 'subtarget' : roll1_mch, 'head_tail' : 1.0 }) make_constraint( cls, bones['ik']['mch_str'], { 'constraint' : 'LIMIT_SCALE', 'use_min_y' : True, 'use_max_y' : True, 'max_y' : 1.05, 'owner_space' : 'LOCAL' }) # Modify rotation mode for ik and tweak controls pb[bones['ik']['ctrl']['limb']].rotation_mode = 'ZXY' for b in bones['tweak']['ctrl']: pb[b].rotation_mode = 'ZXY' # Create ik/fk switch property pb_parent = pb[ bones['parent'] ] pb_parent['IK_Strertch'] = 1.0 prop = rna_idprop_ui_prop_get( pb_parent, 'IK_Strertch', create=True ) prop["min"] = 0.0 prop["max"] = 1.0 prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 prop["description"] = 'IK Stretch' # Add driver to limit scale constraint influence b = bones['ik']['mch_str'] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = cls.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']' drv_modifier = cls.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create leg widget create_foot_widget(cls.obj, ctrl, bone_transform_name=None) # Create heel ctrl locks pb[ heel ].lock_location = True, True, True pb[ heel ].lock_rotation = False, False, True pb[ heel ].lock_scale = True, True, True # Add ballsocket widget to heel create_ballsocket_widget(cls.obj, heel, bone_transform_name=None) bpy.ops.object.mode_set(mode='EDIT') eb = cls.obj.data.edit_bones if len( org_bones ) >= 4: # Create toes control bone toes = get_bone_name( org_bones[3], 'ctrl' ) toes = copy_bone( cls.obj, org_bones[3], toes ) eb[ toes ].use_connect = False eb[ toes ].parent = eb[ org_bones[3] ] # Constrain toes def bones make_constraint( cls, bones['def'][-2], { 'constraint' : 'DAMPED_TRACK', 'subtarget' : toes }) make_constraint( cls, bones['def'][-2], { 'constraint' : 'STRETCH_TO', 'subtarget' : toes }) make_constraint( cls, bones['def'][-1], { 'constraint' : 'COPY_TRANSFORMS', 'subtarget' : toes }) # Find IK/FK switch property pb = cls.obj.pose.bones prop = rna_idprop_ui_prop_get( pb[ bones['parent'] ], 'IK/FK' ) # Add driver to limit scale constraint influence b = org_bones[3] drv = pb[b].constraints[-1].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop.name var.type = "SINGLE_PROP" var.targets[0].id = cls.obj var.targets[0].data_path = \ pb_parent.path_from_id() + '['+ '"' + prop.name + '"' + ']' drv_modifier = cls.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 # Create toe circle widget create_circle_widget(cls.obj, toes, radius=0.4, head_tail=0.5) bones['ik']['ctrl']['terminal'] += [ toes ] bones['ik']['ctrl']['terminal'] += [ heel, ctrl ] return bones
def create_drivers(self, bones): bpy.ops.object.mode_set(mode='OBJECT') pb = self.obj.pose.bones ctrl = pb[bones['ik']['mch_foot'][0]] props = ["IK_follow", "root/parent"] for prop in props: if prop == 'IK_follow': ctrl[prop] = True rna_prop = rna_idprop_ui_prop_get(ctrl, prop, create=True) rna_prop["min"] = False rna_prop["max"] = True rna_prop["description"] = prop drv = ctrl.constraints[0].driver_add("mute").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 if len(ctrl.constraints) > 1: drv = ctrl.constraints[1].driver_add("mute").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' drv_modifier = self.obj.animation_data.drivers[ -1].modifiers[0] drv_modifier.mode = 'POLYNOMIAL' drv_modifier.poly_order = 1 drv_modifier.coefficients[0] = 1.0 drv_modifier.coefficients[1] = -1.0 elif len(ctrl.constraints) > 1: ctrl[prop] = 0.0 rna_prop = rna_idprop_ui_prop_get(ctrl, prop, create=True) rna_prop["min"] = 0.0 rna_prop["max"] = 1.0 rna_prop["soft_min"] = 0.0 rna_prop["soft_max"] = 1.0 rna_prop["description"] = prop # drv = ctrl.constraints[ 0 ].driver_add("influence").driver # drv.type = 'AVERAGE' # # var = drv.variables.new() # var.name = prop # var.type = "SINGLE_PROP" # var.targets[0].id = self.obj # var.targets[0].data_path = \ # ctrl.path_from_id() + '['+ '"' + prop + '"' + ']' # # drv_modifier = self.obj.animation_data.drivers[-1].modifiers[0] # # drv_modifier.mode = 'POLYNOMIAL' # drv_modifier.poly_order = 1 # drv_modifier.coefficients[0] = 1.0 # drv_modifier.coefficients[1] = -1.0 drv = ctrl.constraints[1].driver_add("influence").driver drv.type = 'AVERAGE' var = drv.variables.new() var.name = prop var.type = "SINGLE_PROP" var.targets[0].id = self.obj var.targets[0].data_path = \ ctrl.path_from_id() + '['+ '"' + prop + '"' + ']'