diff options
Diffstat (limited to 'rigify')
-rw-r--r-- | rigify/__init__.py | 12 | ||||
-rw-r--r-- | rigify/generate.py | 11 | ||||
-rw-r--r-- | rigify/metarigs/Animals/horse.py | 8 | ||||
-rw-r--r-- | rigify/metarigs/Animals/wolf.py | 12 | ||||
-rw-r--r-- | rigify/rig_ui_template.py | 3 | ||||
-rw-r--r-- | rigify/rigs/limbs/limb_rigs.py | 50 | ||||
-rw-r--r-- | rigify/rigs/spines/super_head.py | 2 | ||||
-rw-r--r-- | rigify/ui.py | 16 | ||||
-rw-r--r-- | rigify/utils/collections.py | 2 | ||||
-rw-r--r-- | rigify/utils/node_merger.py | 4 | ||||
-rw-r--r-- | rigify/utils/widgets.py | 3 |
11 files changed, 88 insertions, 35 deletions
diff --git a/rigify/__init__.py b/rigify/__init__.py index 10e90b4c..386c8055 100644 --- a/rigify/__init__.py +++ b/rigify/__init__.py @@ -539,8 +539,8 @@ def register(): IDStore.rigify_types = CollectionProperty(type=RigifyName) IDStore.rigify_active_type = IntProperty(name="Rigify Active Type", description="The selected rig type") - bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Force Widget Update", - description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created", + bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Overwrite Widget Meshes", + description="Forces Rigify to delete and rebuild all of the rig widget objects. By default, already existing widgets are reused as-is to facilitate manual editing", default=False) bpy.types.Armature.rigify_mirror_widgets = BoolProperty(name="Mirror Widgets", @@ -550,14 +550,18 @@ def register(): name="Widgets Collection", description="Defines which collection to place widget objects in. If unset, a new one will be created based on the name of the rig") + bpy.types.Armature.rigify_rig_basename = StringProperty(name="Rigify Rig Name", + description="Optional. If specified, this name will be used for the newly generated rig, widget collection and script. Otherwise, a name is generated based on the name of the metarig object by replacing 'metarig' with 'rig', 'META' with 'RIG', or prefixing with 'RIG-'. When updating an already generated rig its name is never changed", + default="") + bpy.types.Armature.rigify_target_rig = PointerProperty(type=bpy.types.Object, name="Rigify Target Rig", - description="Defines which rig to overwrite. If unset, a new one called 'rig' will be created", + description="Defines which rig to overwrite. If unset, a new one will be created with name based on the Rig Name option or the name of the metarig", poll=lambda self, obj: obj.type == 'ARMATURE' and obj.data is not self) bpy.types.Armature.rigify_rig_ui = PointerProperty(type=bpy.types.Text, name="Rigify Target Rig UI", - description="Defines the UI to overwrite. If unset, 'rig_ui.py' will be used") + description="Defines the UI to overwrite. If unset, a new one will be created and named based on the name of the rig") bpy.types.Armature.rigify_finalize_script = PointerProperty(type=bpy.types.Text, name="Finalize Script", diff --git a/rigify/generate.py b/rigify/generate.py index 3acc2e40..8a2aa942 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -65,7 +65,9 @@ class Generator(base_generate.BaseGenerator): target_rig = meta_data.rigify_target_rig if not target_rig: - if "metarig" in self.metarig.name: + if meta_data.rigify_rig_basename: + rig_new_name = meta_data.rigify_rig_basename + elif "metarig" in self.metarig.name: rig_new_name = self.metarig.name.replace("metarig", "rig") elif "META" in self.metarig.name: rig_new_name = self.metarig.name.replace("META", "RIG") @@ -76,7 +78,7 @@ class Generator(base_generate.BaseGenerator): target_rig.display_type = 'WIRE' # If the object is already added to the scene, switch to its collection - if target_rig.name in self.context.scene.collection.all_objects: + if target_rig in list(self.context.scene.collection.all_objects): self.__switch_to_usable_collection(target_rig) else: # Otherwise, add to the selected collection or the metarig collection if unusable @@ -117,11 +119,14 @@ class Generator(base_generate.BaseGenerator): wgts_group_name = "WGTS_" + self.obj.name old_collection = bpy.data.collections.get(wgts_group_name) + if old_collection and old_collection.library: + old_collection = None + if not old_collection: # Update the old 'Widgets' collection legacy_collection = bpy.data.collections.get('Widgets') - if legacy_collection and wgts_group_name in legacy_collection.objects: + if legacy_collection and wgts_group_name in legacy_collection.objects and not legacy_collection.library: legacy_collection.name = wgts_group_name old_collection = legacy_collection diff --git a/rigify/metarigs/Animals/horse.py b/rigify/metarigs/Animals/horse.py index cd393c5c..c9be9d54 100644 --- a/rigify/metarigs/Animals/horse.py +++ b/rigify/metarigs/Animals/horse.py @@ -618,28 +618,28 @@ def create(obj): bone.tail = 0.1990, -1.4668, 1.7420 bone.roll = 0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['eye.L'] = bone.name bone = arm.edit_bones.new('nose.L') bone.head = 0.0450, -1.6240, 1.4228 bone.tail = 0.1039, -1.6613, 1.4269 bone.roll = 0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['nose.L'] = bone.name bone = arm.edit_bones.new('eye.R') bone.head = -0.0988, -1.4596, 1.7351 bone.tail = -0.1990, -1.4668, 1.7420 bone.roll = -0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['eye.R'] = bone.name bone = arm.edit_bones.new('nose.R') bone.head = -0.0450, -1.6240, 1.4228 bone.tail = -0.1039, -1.6613, 1.4269 bone.roll = -0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['nose.R'] = bone.name bone = arm.edit_bones.new('ear.R.001') bone.head = -0.1056, -1.4118, 1.9537 diff --git a/rigify/metarigs/Animals/wolf.py b/rigify/metarigs/Animals/wolf.py index a2ade910..87d74c87 100644 --- a/rigify/metarigs/Animals/wolf.py +++ b/rigify/metarigs/Animals/wolf.py @@ -169,7 +169,7 @@ def create(obj): bone = arm.edit_bones.new('spine.004') bone.head = 0.0000, 0.4085, 0.7928 - bone.tail = 0.0000, 0.2416, 0.7928 + bone.tail = 0.0000, 0.2416, 0.7927 bone.roll = 0.0000 bone.use_connect = False bones['spine.004'] = bone.name @@ -181,7 +181,7 @@ def create(obj): bone.parent = arm.edit_bones[bones['spine.004']] bones['spine.003'] = bone.name bone = arm.edit_bones.new('spine.005') - bone.head = 0.0000, 0.2416, 0.7928 + bone.head = 0.0000, 0.2416, 0.7927 bone.tail = 0.0000, 0.1202, 0.7773 bone.roll = 0.0000 bone.use_connect = True @@ -189,14 +189,14 @@ def create(obj): bones['spine.005'] = bone.name bone = arm.edit_bones.new('spine.002') bone.head = 0.0000, 0.5555, 0.7567 - bone.tail = 0.0000, 0.7816, 0.7412 + bone.tail = 0.0000, 0.7816, 0.7411 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['spine.003']] bones['spine.002'] = bone.name bone = arm.edit_bones.new('spine.006') bone.head = 0.0000, 0.1202, 0.7773 - bone.tail = 0.0000, 0.0096, 0.7773 + bone.tail = 0.0000, 0.0096, 0.7772 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['spine.005']] @@ -230,14 +230,14 @@ def create(obj): bone.parent = arm.edit_bones[bones['spine.005']] bones['thigh.R'] = bone.name bone = arm.edit_bones.new('spine.001') - bone.head = 0.0000, 0.7816, 0.7412 + bone.head = 0.0000, 0.7816, 0.7411 bone.tail = 0.0000, 0.9624, 0.7412 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['spine.002']] bones['spine.001'] = bone.name bone = arm.edit_bones.new('spine.007') - bone.head = 0.0000, 0.0096, 0.7773 + bone.head = 0.0000, 0.0096, 0.7772 bone.tail = 0.0000, -0.0980, 0.7945 bone.roll = 0.0000 bone.use_connect = True diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py index b98907ee..d581805f 100644 --- a/rigify/rig_ui_template.py +++ b/rigify/rig_ui_template.py @@ -1159,7 +1159,8 @@ class ScriptGenerator(base_generate.GeneratorPlugin): if script: script.clear() else: - script = bpy.data.texts.new("rig_ui.py") + script_name = self.generator.obj.name + "_ui.py" + script = bpy.data.texts.new(script_name) metarig.data.rigify_rig_ui = script for s in OrderedDict.fromkeys(self.ui_imports): diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py index fa20dd31..a094e176 100644 --- a/rigify/rigs/limbs/limb_rigs.py +++ b/rigify/rigs/limbs/limb_rigs.py @@ -52,6 +52,7 @@ class BaseLimbRig(BaseRig): self.segments = self.params.segments self.bbone_segments = self.params.bbones self.use_ik_pivot = self.params.make_custom_pivot + self.use_uniform_scale = self.params.limb_uniform_scale rot_axis = self.params.rotation_axis @@ -144,6 +145,8 @@ class BaseLimbRig(BaseRig): # FK chain parents (or None) # ik_pivot # Custom IK pivot result (optional). + # ik_scale + # Helper bone that implements uniform scaling. # ik_swing # Bone that tracks ik_target to manually handle limb swing. # ik_target @@ -178,14 +181,15 @@ class BaseLimbRig(BaseRig): bone = self.get_bone(self.bones.ctrl.master) bone.lock_location = (True, True, True) bone.lock_rotation = (True, True, True) - bone.lock_scale = (False, False, False) + bone.lock_scale = (not self.use_uniform_scale,) * 3 bone.lock_rotation_w = True @stage.rig_bones def rig_master_control(self): mch = self.bones.mch self.make_constraint(mch.master, 'COPY_SCALE', 'root', use_make_uniform=True) - self.make_constraint(mch.master, 'COPY_SCALE', self.bones.ctrl.master, use_offset=True, space='LOCAL') + if self.use_uniform_scale: + self.make_constraint(mch.master, 'COPY_SCALE', self.bones.ctrl.master, use_offset=True, space='LOCAL') @stage.generate_widgets def make_master_control_widget(self): @@ -221,10 +225,12 @@ class BaseLimbRig(BaseRig): mch = self.bones.mch.follow self.make_constraint(mch, 'COPY_SCALE', 'root', use_make_uniform=True) - self.make_constraint( - mch, 'COPY_SCALE', self.bones.ctrl.master, - use_make_uniform=True, use_offset=True, space='LOCAL' - ) + + if self.use_uniform_scale: + self.make_constraint( + mch, 'COPY_SCALE', self.bones.ctrl.master, + use_make_uniform=True, use_offset=True, space='LOCAL' + ) con = self.make_constraint(mch, 'COPY_ROTATION', 'root') @@ -361,9 +367,12 @@ class BaseLimbRig(BaseRig): self.bones.ctrl.ik_base = self.make_ik_base_bone(orgs) self.bones.ctrl.ik_pole = self.make_ik_pole_bone(orgs) - self.bones.ctrl.ik = ik_name = self.make_ik_control_bone(orgs) + self.bones.ctrl.ik = ik_name = parent = self.make_ik_control_bone(orgs) + + if self.use_uniform_scale: + self.bones.mch.ik_scale = parent = self.make_ik_scale_bone(ik_name, orgs) - self.component_ik_pivot = self.build_ik_pivot(ik_name) + self.component_ik_pivot = self.build_ik_pivot(ik_name, parent=parent) self.build_ik_parent_switch(SwitchParentBuilder(self.generator)) def make_ik_base_bone(self, orgs): @@ -382,13 +391,18 @@ class BaseLimbRig(BaseRig): def make_ik_control_bone(self, orgs): return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik')) + def make_ik_scale_bone(self, ctrl, orgs): + return self.copy_bone(ctrl, make_derived_name(orgs[2], 'mch', '_ik_scale'), scale=1/2) + def build_ik_pivot(self, ik_name, **args): if self.use_ik_pivot: - return CustomPivotControl(self, 'ik_pivot', ik_name, parent=ik_name, **args) + return CustomPivotControl(self, 'ik_pivot', ik_name, **args) def get_ik_control_output(self): if self.component_ik_pivot: return self.component_ik_pivot.output + elif self.use_uniform_scale: + return self.bones.mch.ik_scale else: return self.bones.ctrl.ik @@ -430,6 +444,9 @@ class BaseLimbRig(BaseRig): else: self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.ik_swing) + if self.use_uniform_scale: + self.set_bone_parent(self.bones.mch.ik_scale, self.bones.ctrl.ik) + self.set_ik_local_location(self.bones.ctrl.ik) self.set_ik_local_location(self.bones.ctrl.ik_pole) @@ -445,11 +462,13 @@ class BaseLimbRig(BaseRig): @stage.rig_bones def rig_ik_controls(self): self.rig_hide_pole_control(self.bones.ctrl.ik_pole) - self.rig_ik_control_scale(self.bones.ctrl.ik) - def rig_ik_control_scale(self, ctrl): + if self.use_uniform_scale: + self.rig_ik_control_scale(self.bones.mch.ik_scale) + + def rig_ik_control_scale(self, mch): self.make_constraint( - ctrl, 'COPY_SCALE', self.bones.ctrl.master, + mch, 'COPY_SCALE', self.bones.ctrl.master, use_make_uniform=True, use_offset=True, space='LOCAL', ) @@ -937,6 +956,12 @@ class BaseLimbRig(BaseRig): description = "Specifies the value of the Local Location option for IK controls, which decides if the location channels are aligned to the local control orientation or world", ) + params.limb_uniform_scale = bpy.props.BoolProperty( + name = "Support Uniform Scaling", + default = False, + description = "Support uniformly scaling the limb via the gear control at the base" + ) + # Setting up extra layers for the FK and tweak ControlLayersOption.FK.add_parameters(params) ControlLayersOption.TWEAK.add_parameters(params) @@ -958,6 +983,7 @@ class BaseLimbRig(BaseRig): r = layout.row() r.prop(params, "bbones") + layout.prop(params, 'limb_uniform_scale') layout.prop(params, 'make_custom_pivot', text="Custom IK Pivot") layout.prop(params, 'ik_local_location') diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py index e2932ad2..49f1ce3e 100644 --- a/rigify/rigs/spines/super_head.py +++ b/rigify/rigs/spines/super_head.py @@ -292,6 +292,8 @@ class Rig(BaseHeadTailRig): influence=nfactor, space='LOCAL' ) + self.make_constraint(mch, 'COPY_SCALE', ctrl.neck) + #################################################### # Tweak bones diff --git a/rigify/ui.py b/rigify/ui.py index 68cfd330..8b719a3f 100644 --- a/rigify/ui.py +++ b/rigify/ui.py @@ -137,10 +137,20 @@ class DATA_PT_rigify_advanced(bpy.types.Panel): armature_id_store = context.object.data col = layout.column() - col.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig") - col.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script") + + row = col.row() + row.active = not armature_id_store.rigify_target_rig + row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name") + + col.separator() + + col2 = col.box().column() + col2.label(text="Overwrite Existing:") + col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig") + col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script") + col2.row().prop(armature_id_store, "rigify_widgets_collection") + col.separator() - col.row().prop(armature_id_store, "rigify_widgets_collection") col.row().prop(armature_id_store, "rigify_force_widget_update") col.row().prop(armature_id_store, "rigify_mirror_widgets") col.separator() diff --git a/rigify/utils/collections.py b/rigify/utils/collections.py index 8a299a9b..9eeaac51 100644 --- a/rigify/utils/collections.py +++ b/rigify/utils/collections.py @@ -53,7 +53,7 @@ def ensure_collection(context, collection_name, hidden=False) -> bpy.types.Colle active_collection = active_layer_coll.collection collection = bpy.data.collections.get(collection_name) - if not collection: + if not collection or collection.library: # Create the collection collection = bpy.data.collections.new(collection_name) collection.hide_viewport = hidden diff --git a/rigify/utils/node_merger.py b/rigify/utils/node_merger.py index 617b99df..2da48ada 100644 --- a/rigify/utils/node_merger.py +++ b/rigify/utils/node_merger.py @@ -73,7 +73,9 @@ class NodeMerger(GeneratorPlugin): while pending: added = set() for j in pending: - for co, idx, dist in tree.find_range(nodes[j].point, self.epsilon): + point = nodes[j].point + eps = max(1, point.length) * self.epsilon + for co, idx, dist in tree.find_range(point, eps): added.add(idx) pending = added.difference(merge_set) merge_set.update(added) diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py index e02f3387..5a16065b 100644 --- a/rigify/utils/widgets.py +++ b/rigify/utils/widgets.py @@ -86,6 +86,9 @@ def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, if not obj: # Search the scene by name obj = scene.objects.get(obj_name) + if obj and obj.library: + local_objs = [obj for obj in scene.objects if obj.name == obj_name and not obj.library] + obj = local_objs[0] if local_objs else None if obj: # Record the generated widget |