# SPDX-License-Identifier: GPL-2.0-or-later import bpy from ...utils.naming import strip_org, strip_prefix, choose_derived_bone, is_control_bone from ...utils.mechanism import copy_custom_properties_with_ui, move_all_constraints from ...utils.widgets import layout_widget_dropdown, create_registered_widget from ...base_rig import BaseRig from ...base_generate import SubstitutionRig from itertools import repeat ''' Due to T80764, bone name handling for 'limbs.raw_copy' was hard-coded in generate.py class Rig(SubstitutionRig): """ A raw copy rig, preserving the metarig bone as is, without the ORG prefix. """ def substitute(self): # Strip the ORG prefix during the rig instantiation phase new_name = strip_org(self.base_bone) new_name = self.generator.rename_org_bone(self.base_bone, new_name) return [ self.instantiate_rig(InstanceRig, new_name) ] ''' class RelinkConstraintsMixin: """ Utilities for constraint relinking. """ def relink_bone_constraints(self, bone_name): if self.params.relink_constraints: for con in self.get_bone(bone_name).constraints: self.relink_single_constraint(con) relink_unmarked_constraints = False def relink_single_constraint(self, con): if self.params.relink_constraints: parts = con.name.split('@') if len(parts) > 1: self.relink_constraint(con, parts[1:]) elif self.relink_unmarked_constraints: self.relink_constraint(con, ['']) def relink_move_constraints(self, from_bone, to_bone, *, prefix=''): if self.params.relink_constraints: move_all_constraints(self.obj, from_bone, to_bone, prefix=prefix) def relink_bone_parent(self, bone_name): if self.params.relink_constraints: self.generator.disable_auto_parent(bone_name) parent_spec = self.params.parent_bone if parent_spec: old_parent = self.get_bone_parent(bone_name) new_parent = self.find_relink_target(parent_spec, old_parent or '') or None self.set_bone_parent(bone_name, new_parent) return new_parent def relink_constraint(self, con, specs): if con.type == 'ARMATURE': if len(specs) == 1: specs = repeat(specs[0]) elif len(specs) != len(con.targets): self.raise_error("Constraint {} actually has {} targets", con.name, len(con.targets)) for tgt, spec in zip(con.targets, specs): if tgt.target == self.obj: tgt.subtarget = self.find_relink_target(spec, tgt.subtarget) elif hasattr(con, 'subtarget'): if len(specs) > 1: self.raise_error("Only the Armature constraint can have multiple '@' targets: {}", con.name) if con.target == self.obj: con.subtarget = self.find_relink_target(specs[0], con.subtarget) def find_relink_target(self, spec, old_target): if spec == '': return old_target elif spec in {'CTRL', 'DEF', 'MCH'}: result = choose_derived_bone(self.generator, old_target, spec.lower()) if not result: result = choose_derived_bone(self.generator, old_target, spec.lower(), by_owner=False) if not result: self.raise_error("Cannot find derived {} bone of bone '{}' for relinking", spec, old_target) return result else: if spec not in self.obj.pose.bones: self.raise_error("Cannot find bone '{}' for relinking", spec) return spec @classmethod def add_relink_constraints_params(self, params): params.relink_constraints = bpy.props.BoolProperty( name = "Relink Constraints", default = False, description = "For constraints with names formed like 'base@bonename', use the part after '@' as the new subtarget after all bones are created. Use '@CTRL', '@DEF' or '@MCH' to simply replace the prefix" ) params.parent_bone = bpy.props.StringProperty( name = "Parent", default = "", description = "Replace the parent with a different bone after all bones are created. Using simply CTRL, DEF or MCH will replace the prefix instead" ) @classmethod def add_relink_constraints_ui(self, layout, params): r = layout.row() r.prop(params, "relink_constraints") if params.relink_constraints: r = layout.row() r.prop(params, "parent_bone") layout.label(text="Constraint names have special meanings.", icon='ERROR') class Rig(BaseRig, RelinkConstraintsMixin): def find_org_bones(self, pose_bone): return pose_bone.name def initialize(self): self.relink = self.params.relink_constraints def parent_bones(self): self.relink_bone_parent(self.bones.org) def configure_bones(self): org = self.bones.org if is_control_bone(org): copy_custom_properties_with_ui(self, org, org, ui_controls=[org]) def rig_bones(self): self.relink_bone_constraints(self.bones.org) def generate_widgets(self): org = self.bones.org widget = self.params.optional_widget_type if widget and is_control_bone(org): create_registered_widget(self.obj, org, widget) @classmethod def add_parameters(self, params): self.add_relink_constraints_params(params) params.optional_widget_type = bpy.props.StringProperty( name = "Widget Type", default = '', description = "Choose the type of the widget to create" ) @classmethod def parameters_ui(self, layout, params): col = layout.column() col.label(text='This rig type does not add the ORG prefix.') col.label(text='Manually add ORG, MCH or DEF as needed.') self.add_relink_constraints_ui(layout, params) pbone = bpy.context.active_pose_bone if pbone and is_control_bone(pbone.name): layout_widget_dropdown(layout, params, "optional_widget_type") #add_parameters = InstanceRig.add_parameters #parameters_ui = InstanceRig.parameters_ui def create_sample(obj): """ Create a sample metarig for this rig type. """ # generated by rigify.utils.write_metarig bpy.ops.object.mode_set(mode='EDIT') arm = obj.data bones = {} bone = arm.edit_bones.new('DEF-bone') bone.head[:] = 0.0000, 0.0000, 0.0000 bone.tail[:] = 0.0000, 0.0000, 0.2000 bone.roll = 0.0000 bone.use_connect = False bones['DEF-bone'] = bone.name bpy.ops.object.mode_set(mode='OBJECT') pbone = obj.pose.bones[bones['DEF-bone']] pbone.rigify_type = 'basic.raw_copy' pbone.lock_location = (False, False, False) pbone.lock_rotation = (False, False, False) pbone.lock_rotation_w = False pbone.lock_scale = (False, False, False) pbone.rotation_mode = 'QUATERNION' bpy.ops.object.mode_set(mode='EDIT') for bone in arm.edit_bones: bone.select = False bone.select_head = False bone.select_tail = False for b in bones: bone = arm.edit_bones[bones[b]] bone.select = True bone.select_head = True bone.select_tail = True arm.edit_bones.active = bone return bones