Rigify: use separate widget collection for each generated rig.

As pointed out in comments to T73114, using the same name has downsides.

In addition, remove the widget parent object that was inherited
from the old pre-collection way Rigify worked and has no other
purpose than grouping objects like the collection already does.

Rename the widgets and the collection when renaming the rig.

Finally as an aside add a couple of options to create_widget.
This commit is contained in:
Alexander Gavrilov 2020-12-07 22:11:00 +03:00
parent c93dc35588
commit f8d6489fb6
3 changed files with 58 additions and 49 deletions

View File

@ -112,35 +112,47 @@ class Generator(base_generate.BaseGenerator):
return obj
def __create_widget_group(self, new_group_name):
context = self.context
scene = self.scene
id_store = self.id_store
# Create/find widge collection
self.widget_collection = ensure_widget_collection(context)
# Remove wgts if force update is set
def __create_widget_group(self):
new_group_name = "WGTS_" + self.obj.name
wgts_group_name = "WGTS_" + (self.rig_old_name or self.obj.name)
if wgts_group_name in scene.objects and self.metarig.data.rigify_force_widget_update:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
for wgt in bpy.data.objects[wgts_group_name].children:
wgt.select_set(True)
bpy.ops.object.delete(use_global=False)
# Find the old widgets collection
old_collection = bpy.data.collections.get(wgts_group_name)
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:
legacy_collection.name = wgts_group_name
old_collection = legacy_collection
if old_collection:
# Remove widgets if force update is set
if self.metarig.data.rigify_force_widget_update:
for obj in list(old_collection.objects):
bpy.data.objects.remove(obj)
# Rename widgets and collection if renaming
if self.rig_old_name:
bpy.data.objects[wgts_group_name].name = new_group_name
old_prefix = WGT_PREFIX + self.rig_old_name + "_"
new_prefix = WGT_PREFIX + self.obj.name + "_"
# Create Group widget
wgts_group_name = new_group_name
if wgts_group_name not in scene.objects:
if wgts_group_name in bpy.data.objects:
bpy.data.objects[wgts_group_name].user_clear()
bpy.data.objects.remove(bpy.data.objects[wgts_group_name])
mesh = bpy.data.meshes.new(wgts_group_name)
wgts_obj = bpy.data.objects.new(wgts_group_name, mesh)
self.widget_collection.objects.link(wgts_obj)
for obj in list(old_collection.objects):
if obj.name.startswith(old_prefix):
new_name = new_prefix + obj.name[len(old_prefix):]
elif obj.name == wgts_group_name:
new_name = new_group_name
else:
continue
obj.data.name = new_name
obj.name = new_name
old_collection.name = new_group_name
# Create/find widget collection
self.widget_collection = ensure_widget_collection(self.context, new_group_name)
self.wgts_group_name = new_group_name
@ -351,7 +363,7 @@ class Generator(base_generate.BaseGenerator):
#------------------------------------------
# Create Group widget
self.__create_widget_group("WGTS_" + obj.name)
self.__create_widget_group()
t.tick("Create main WGTS: ")

View File

@ -65,9 +65,7 @@ def filter_layer_collections_by_object(layer_collections, obj):
return [lc for lc in layer_collections if obj in lc.collection.objects.values()]
def ensure_widget_collection(context):
wgts_collection_name = "Widgets"
def ensure_widget_collection(context, wgts_collection_name):
view_layer = context.view_layer
layer_collection = bpy.context.layer_collection
collection = layer_collection.collection
@ -89,6 +87,8 @@ def ensure_widget_collection(context):
collection.children.link(widget_collection)
widget_layer_collection = [c for c in layer_collection.children if c.collection == widget_collection][0]
widget_layer_collection.exclude = True
# Make the widget the active collection for the upcoming added (widget) objects
view_layer.active_layer_collection = widget_layer_collection
return widget_collection

View File

@ -54,7 +54,7 @@ def obj_to_bone(obj, rig, bone_name, bone_transform_name=None):
obj.matrix_basis = rig.matrix_world @ bone.bone.matrix_local @ Matrix.Scale(scale, 4)
def create_widget(rig, bone_name, bone_transform_name=None):
def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, widget_force_new=False):
""" Creates an empty widget object for a bone, and returns the object.
"""
assert rig.mode != 'EDIT'
@ -64,37 +64,34 @@ def create_widget(rig, bone_name, bone_transform_name=None):
if bone.custom_shape:
return None
obj_name = WGT_PREFIX + rig.name + '_' + bone_name
obj_name = widget_name or WGT_PREFIX + rig.name + '_' + bone_name
scene = bpy.context.scene
collection = ensure_widget_collection(bpy.context)
collection = ensure_widget_collection(bpy.context, 'WGTS_' + rig.name)
# Check if it already exists in the scene
if obj_name in scene.objects:
# Move object to bone position, in case it changed
obj = scene.objects[obj_name]
obj_to_bone(obj, rig, bone_name, bone_transform_name)
if not widget_force_new:
if obj_name in scene.objects:
# Move object to bone position, in case it changed
obj = scene.objects[obj_name]
obj_to_bone(obj, rig, bone_name, bone_transform_name)
return None
return None
else:
# Delete object if it exists in blend data but not scene data.
# This is necessary so we can then create the object without
# name conflicts.
if obj_name in bpy.data.objects:
bpy.data.objects[obj_name].user_clear()
bpy.data.objects.remove(bpy.data.objects[obj_name])
# Create mesh object
mesh = bpy.data.meshes.new(obj_name)
obj = bpy.data.objects.new(obj_name, mesh)
collection.objects.link(obj)
# Create mesh object
mesh = bpy.data.meshes.new(obj_name)
obj = bpy.data.objects.new(obj_name, mesh)
collection.objects.link(obj)
# Move object to bone position and set layers
obj_to_bone(obj, rig, bone_name, bone_transform_name)
wgts_group_name = 'WGTS_' + rig.name
if wgts_group_name in bpy.data.objects.keys():
obj.parent = bpy.data.objects[wgts_group_name]
# Move object to bone position and set layers
obj_to_bone(obj, rig, bone_name, bone_transform_name)
return obj
return obj
def create_circle_polygon(number_verts, axis, radius=1.0, head_tail=0.0):