Import Images: fix crash in Animate Sequence when no image selected
[blender-addons.git] / rigify / generate.py
blob527e46b082c2c3338ffb7da78f300e22bb97f05c
1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 import bpy
6 import re
7 import time
9 from typing import Optional, TYPE_CHECKING
11 from .utils.errors import MetarigError
12 from .utils.bones import new_bone
13 from .utils.layers import (ORG_COLLECTION, MCH_COLLECTION, DEF_COLLECTION, ROOT_COLLECTION, set_bone_layers,
14 validate_collection_references)
15 from .utils.naming import (ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name,
16 change_name_side, get_name_side, Side)
17 from .utils.widgets import WGT_PREFIX, WGT_GROUP_PREFIX
18 from .utils.widgets_special import create_root_widget
19 from .utils.mechanism import refresh_all_drivers
20 from .utils.misc import select_object, ArmatureObject, verify_armature_obj, choose_next_uid
21 from .utils.collections import (ensure_collection, list_layer_collections,
22 filter_layer_collections_by_object)
23 from .utils.rig import get_rigify_type, get_rigify_target_rig,\
24 get_rigify_rig_basename, get_rigify_force_widget_update, get_rigify_finalize_script,\
25 get_rigify_mirror_widgets, get_rigify_colors
26 from .utils.action_layers import ActionLayerBuilder
27 from .utils.objects import ArtifactManager
29 from . import base_generate
30 from . import rig_ui_template
31 from . import rig_lists
33 if TYPE_CHECKING:
34 from . import RigifyColorSet
37 RIG_MODULE = "rigs"
40 class Timer:
41 def __init__(self):
42 self.time_val = time.time()
44 def tick(self, string):
45 t = time.time()
46 print(string + "%.3f" % (t - self.time_val))
47 self.time_val = t
50 class Generator(base_generate.BaseGenerator):
51 usable_collections: list[bpy.types.LayerCollection]
52 action_layers: ActionLayerBuilder
54 def __init__(self, context, metarig):
55 super().__init__(context, metarig)
57 self.id_store = context.window_manager
58 self.saved_visible_layers = {}
60 def find_rig_class(self, rig_type):
61 rig_module = rig_lists.rigs[rig_type]["module"]
63 return rig_module.Rig
65 def __switch_to_usable_collection(self, obj, fallback=False):
66 collections = filter_layer_collections_by_object(self.usable_collections, obj)
68 if collections:
69 self.layer_collection = collections[0]
70 elif fallback:
71 self.layer_collection = self.view_layer.layer_collection
73 self.collection = self.layer_collection.collection
75 def ensure_rig_object(self) -> tuple[bool, ArmatureObject]:
76 """Check if the generated rig already exists, so we can
77 regenerate in the same object. If not, create a new
78 object to generate the rig in.
79 """
80 print("Fetch rig.")
81 meta_data = self.metarig.data
83 target_rig = get_rigify_target_rig(meta_data)
84 found = bool(target_rig)
86 if not found:
87 rig_basename = get_rigify_rig_basename(meta_data)
89 if rig_basename:
90 rig_new_name = rig_basename
91 elif "metarig" in self.metarig.name:
92 rig_new_name = self.metarig.name.replace("metarig", "rig")
93 elif "META" in self.metarig.name:
94 rig_new_name = self.metarig.name.replace("META", "RIG")
95 else:
96 rig_new_name = "RIG-" + self.metarig.name
98 arm = bpy.data.armatures.new(rig_new_name)
99 target_rig = verify_armature_obj(bpy.data.objects.new(rig_new_name, arm))
100 target_rig.display_type = 'WIRE'
102 # If the object is already added to the scene, switch to its collection
103 if target_rig in list(self.context.scene.collection.all_objects):
104 self.__switch_to_usable_collection(target_rig)
105 else:
106 # Otherwise, add to the selected collection or the metarig collection if unusable
107 if (self.layer_collection not in self.usable_collections
108 or self.layer_collection == self.view_layer.layer_collection):
109 self.__switch_to_usable_collection(self.metarig, True)
111 self.collection.objects.link(target_rig)
113 # Configure and remember the object
114 meta_data.rigify_target_rig = target_rig
115 target_rig.data.pose_position = 'POSE'
117 return found, target_rig
119 def __unhide_rig_object(self, obj: bpy.types.Object):
120 # Ensure the object is visible and selectable
121 obj.hide_set(False, view_layer=self.view_layer)
122 obj.hide_viewport = False
124 if not obj.visible_get(view_layer=self.view_layer):
125 raise Exception('Could not generate: Target rig is not visible')
127 obj.select_set(True, view_layer=self.view_layer)
129 if not obj.select_get(view_layer=self.view_layer):
130 raise Exception('Could not generate: Cannot select target rig')
132 if self.layer_collection not in self.usable_collections:
133 raise Exception('Could not generate: Could not find a usable collection.')
135 def __save_rig_data(self, obj: ArmatureObject, obj_found: bool):
136 if obj_found:
137 self.saved_visible_layers = {coll.name: coll.is_visible for coll in obj.data.collections}
139 self.artifacts.generate_init_existing(obj)
141 def __find_legacy_collection(self) -> bpy.types.Collection:
142 """For backwards comp, matching by name to find a legacy collection.
143 (For before there was a Widget Collection PointerProperty)
145 widgets_group_name = WGT_GROUP_PREFIX + self.obj.name
146 old_collection = bpy.data.collections.get(widgets_group_name)
148 if old_collection and old_collection.library:
149 old_collection = None
151 if not old_collection:
152 # Update the old 'Widgets' collection
153 legacy_collection = bpy.data.collections.get('Widgets')
155 if legacy_collection and widgets_group_name in legacy_collection.objects\
156 and not legacy_collection.library:
157 legacy_collection.name = widgets_group_name
158 old_collection = legacy_collection
160 if old_collection:
161 # Rename the collection
162 old_collection.name = widgets_group_name
164 return old_collection
166 def ensure_widget_collection(self):
167 # Create/find widget collection
168 self.widget_collection = self.metarig.data.rigify_widgets_collection
169 if not self.widget_collection:
170 self.widget_collection = self.__find_legacy_collection()
171 if not self.widget_collection:
172 widgets_group_name = WGT_GROUP_PREFIX + self.obj.name.replace("RIG-", "")
173 self.widget_collection = ensure_collection(
174 self.context, widgets_group_name, hidden=True)
176 self.metarig.data.rigify_widgets_collection = self.widget_collection
178 self.use_mirror_widgets = get_rigify_mirror_widgets(self.metarig.data)
180 # Build tables for existing widgets
181 self.old_widget_table = {}
182 self.new_widget_table = {}
183 self.widget_mirror_mesh = {}
185 if get_rigify_force_widget_update(self.metarig.data):
186 # Remove widgets if force update is set
187 for obj in list(self.widget_collection.objects):
188 bpy.data.objects.remove(obj)
189 elif self.obj.pose:
190 # Find all widgets from the collection referenced by the old rig
191 known_widgets = set(obj.name for obj in self.widget_collection.objects)
193 for bone in self.obj.pose.bones:
194 if bone.custom_shape and bone.custom_shape.name in known_widgets:
195 self.old_widget_table[bone.name] = bone.custom_shape
197 # Rename widgets in case the rig was renamed
198 name_prefix = WGT_PREFIX + self.obj.name + "_"
200 for bone_name, widget in self.old_widget_table.items():
201 old_data_name = change_name_side(widget.name, get_name_side(widget.data.name))
203 widget.name = name_prefix + bone_name
205 # If the mesh name is the same as the object, rename it too
206 if widget.data.name == old_data_name:
207 widget.data.name = change_name_side(
208 widget.name, get_name_side(widget.data.name))
210 # Find meshes for mirroring
211 if self.use_mirror_widgets:
212 for bone_name, widget in self.old_widget_table.items():
213 mid_name = change_name_side(bone_name, Side.MIDDLE)
214 if bone_name != mid_name:
215 assert isinstance(widget.data, bpy.types.Mesh)
216 self.widget_mirror_mesh[mid_name] = widget.data
218 def ensure_root_bone_collection(self):
219 collections = self.metarig.data.collections
221 validate_collection_references(self.metarig)
223 coll = collections.get(ROOT_COLLECTION)
225 if not coll:
226 coll = collections.new(ROOT_COLLECTION)
228 if coll.rigify_ui_row <= 0:
229 coll.rigify_ui_row = 2 + choose_next_uid(collections, 'rigify_ui_row', min_value=1)
231 def __duplicate_rig(self):
232 obj = self.obj
233 metarig = self.metarig
234 context = self.context
236 # Remove all bones from the generated rig armature.
237 bpy.ops.object.mode_set(mode='EDIT')
238 for bone in obj.data.edit_bones:
239 obj.data.edit_bones.remove(bone)
240 bpy.ops.object.mode_set(mode='OBJECT')
242 # Remove all bone collections from the target armature.
243 for coll in list(obj.data.collections):
244 obj.data.collections.remove(coll)
246 # Select and duplicate metarig
247 select_object(context, metarig, deselect_all=True)
249 bpy.ops.object.duplicate()
251 # Rename org bones in the temporary object
252 temp_obj = verify_armature_obj(context.view_layer.objects.active)
254 assert temp_obj and temp_obj != metarig
256 self.__freeze_driver_vars(temp_obj)
257 self.__rename_org_bones(temp_obj)
259 # Select the target rig and join
260 select_object(context, obj)
262 saved_matrix = obj.matrix_world.copy()
263 obj.matrix_world = metarig.matrix_world
265 bpy.ops.object.join()
267 obj.matrix_world = saved_matrix
269 # Select the generated rig
270 select_object(context, obj, deselect_all=True)
272 # Clean up animation data
273 if obj.animation_data:
274 obj.animation_data.action = None
276 for track in obj.animation_data.nla_tracks:
277 obj.animation_data.nla_tracks.remove(track)
279 @staticmethod
280 def __freeze_driver_vars(obj: bpy.types.Object):
281 if obj.animation_data:
282 # Freeze drivers referring to custom properties
283 for d in obj.animation_data.drivers:
284 for var in d.driver.variables:
285 for tar in var.targets:
286 # If a custom property
287 if var.type == 'SINGLE_PROP' \
288 and re.match(r'^pose.bones\["[^"\]]*"]\["[^"\]]*"]$',
289 tar.data_path):
290 tar.data_path = "RIGIFY-" + tar.data_path
292 def __rename_org_bones(self, obj: ArmatureObject):
293 # Make a list of the original bones, so we can keep track of them.
294 original_bones = [bone.name for bone in obj.data.bones]
296 # Add the ORG_PREFIX to the original bones.
297 for i in range(0, len(original_bones)):
298 bone = obj.pose.bones[original_bones[i]]
300 # Preserve the root bone as is if present
301 if bone.name == ROOT_NAME:
302 if bone.parent:
303 raise MetarigError('Root bone must have no parent')
304 if get_rigify_type(bone) not in ('', 'basic.raw_copy'):
305 raise MetarigError('Root bone must have no rig, or use basic.raw_copy')
306 continue
308 # This rig type is special in that it preserves the name of the bone.
309 if get_rigify_type(bone) != 'basic.raw_copy':
310 bone.name = make_original_name(original_bones[i])
311 original_bones[i] = bone.name
313 self.original_bones = original_bones
315 def __create_root_bone(self):
316 obj = self.obj
317 metarig = self.metarig
319 if ROOT_NAME in obj.data.bones:
320 # Use the existing root bone
321 root_bone = ROOT_NAME
322 else:
323 # Create the root bone.
324 root_bone = new_bone(obj, ROOT_NAME)
325 spread = get_xy_spread(metarig.data.bones) or metarig.data.bones[0].length
326 spread = float('%.3g' % spread)
327 scale = spread/0.589
328 obj.data.edit_bones[root_bone].head = (0, 0, 0)
329 obj.data.edit_bones[root_bone].tail = (0, scale, 0)
330 obj.data.edit_bones[root_bone].roll = 0
332 self.root_bone = root_bone
333 self.bone_owners[root_bone] = None
334 self.noparent_bones.add(root_bone)
336 def __parent_bones_to_root(self):
337 eb = self.obj.data.edit_bones
339 # Parent loose bones to root
340 for bone in eb:
341 if bone.name in self.noparent_bones:
342 continue
343 elif bone.parent is None:
344 bone.use_connect = False
345 bone.parent = eb[self.root_bone]
347 def __lock_transforms(self):
348 # Lock transforms on all non-control bones
349 r = re.compile("[A-Z][A-Z][A-Z]-")
350 for pb in self.obj.pose.bones:
351 if r.match(pb.name):
352 pb.lock_location = (True, True, True)
353 pb.lock_rotation = (True, True, True)
354 pb.lock_rotation_w = True
355 pb.lock_scale = (True, True, True)
357 def ensure_bone_collection(self, name):
358 coll = self.obj.data.collections.get(name)
360 if not coll:
361 coll = self.obj.data.collections.new(name)
363 return coll
365 def __assign_layers(self):
366 pose_bones = self.obj.pose.bones
368 root_coll = self.ensure_bone_collection(ROOT_COLLECTION)
369 org_coll = self.ensure_bone_collection(ORG_COLLECTION)
370 mch_coll = self.ensure_bone_collection(MCH_COLLECTION)
371 def_coll = self.ensure_bone_collection(DEF_COLLECTION)
373 set_bone_layers(pose_bones[self.root_bone].bone, [root_coll])
375 # Every bone that has a name starting with "DEF-" make deforming. All the
376 # others make non-deforming.
377 for pbone in pose_bones:
378 bone = pbone.bone
379 name = bone.name
380 layers = None
382 bone.use_deform = name.startswith(DEF_PREFIX)
384 # Move all the original bones to their layer.
385 if name.startswith(ORG_PREFIX):
386 layers = [org_coll]
387 # Move all the bones with names starting with "MCH-" to their layer.
388 elif name.startswith(MCH_PREFIX):
389 layers = [mch_coll]
390 # Move all the bones with names starting with "DEF-" to their layer.
391 elif name.startswith(DEF_PREFIX):
392 layers = [def_coll]
394 if layers is not None:
395 set_bone_layers(bone, layers)
397 # Remove custom shapes from non-control bones
398 pbone.custom_shape = None
400 bone.bbone_x = bone.bbone_z = bone.length * 0.05
402 def __restore_driver_vars(self):
403 obj = self.obj
405 # Alter marked driver targets
406 if obj.animation_data:
407 for d in obj.animation_data.drivers:
408 for v in d.driver.variables:
409 for tar in v.targets:
410 if tar.data_path.startswith("RIGIFY-"):
411 temp, bone, prop = tuple(
412 [x.strip('"]') for x in tar.data_path.split('["')])
413 if bone in obj.data.bones and prop in obj.pose.bones[bone].keys():
414 tar.data_path = tar.data_path[7:]
415 else:
416 org_name = make_original_name(bone)
417 org_name = self.org_rename_table.get(org_name, org_name)
418 tar.data_path = 'pose.bones["%s"]["%s"]' % (org_name, prop)
420 def __assign_widgets(self):
421 obj_table = {obj.name: obj for obj in self.scene.objects}
423 # Assign shapes to bones
424 # Object's with name WGT-<bone_name> get used as that bone's shape.
425 for bone in self.obj.pose.bones:
426 # First check the table built by create_widget
427 if bone.name in self.new_widget_table:
428 bone.custom_shape = self.new_widget_table[bone.name]
429 continue
431 # Object names are limited to 63 characters... arg
432 wgt_name = (WGT_PREFIX + self.obj.name + '_' + bone.name)[:63]
434 if wgt_name in obj_table:
435 bone.custom_shape = obj_table[wgt_name]
437 def __compute_visible_layers(self):
438 # Hide all layers without UI buttons
439 for coll in self.obj.data.collections:
440 user_visible = self.saved_visible_layers.get(coll.name, coll.is_visible)
441 coll.is_visible = user_visible and coll.rigify_ui_row > 0
443 def generate(self):
444 context = self.context
445 metarig = self.metarig
446 view_layer = self.view_layer
447 t = Timer()
449 self.usable_collections = list_layer_collections(
450 view_layer.layer_collection, selectable=True)
452 bpy.ops.object.mode_set(mode='OBJECT')
454 ###########################################
455 # Create/find the rig object and set it up
456 obj_found, obj = self.ensure_rig_object()
458 self.obj = obj
459 self.__unhide_rig_object(obj)
461 # Collect data from the existing rig
462 self.artifacts = ArtifactManager(self)
464 self.__save_rig_data(obj, obj_found)
466 # Select the chosen working collection in case it changed
467 self.view_layer.active_layer_collection = self.layer_collection
469 self.ensure_root_bone_collection()
471 # Get rid of anim data in case the rig already existed
472 print("Clear rig animation data.")
474 obj.animation_data_clear()
475 obj.data.animation_data_clear()
477 select_object(context, obj, deselect_all=True)
479 ###########################################
480 # Create Widget Collection
481 self.ensure_widget_collection()
483 t.tick("Create widgets collection: ")
485 ###########################################
486 # Get parented objects to restore later
488 child_parent_bones = {} # {object: bone}
490 for child in obj.children:
491 child_parent_bones[child] = child.parent_bone
493 ###########################################
494 # Copy bones from metarig to obj (adds ORG_PREFIX)
495 self.__duplicate_rig()
497 obj.data.use_mirror_x = False
499 t.tick("Duplicate rig: ")
501 ###########################################
502 # Put the rig_name in the armature custom properties
503 obj.data["rig_id"] = self.rig_id
505 self.script = rig_ui_template.ScriptGenerator(self)
506 self.action_layers = ActionLayerBuilder(self)
508 ###########################################
509 bpy.ops.object.mode_set(mode='OBJECT')
511 self.instantiate_rig_tree()
513 t.tick("Instantiate rigs: ")
515 ###########################################
516 bpy.ops.object.mode_set(mode='OBJECT')
518 self.invoke_initialize()
520 t.tick("Initialize rigs: ")
522 ###########################################
523 bpy.ops.object.mode_set(mode='EDIT')
525 self.invoke_prepare_bones()
527 t.tick("Prepare bones: ")
529 ###########################################
530 bpy.ops.object.mode_set(mode='OBJECT')
531 bpy.ops.object.mode_set(mode='EDIT')
533 self.__create_root_bone()
535 self.invoke_generate_bones()
537 t.tick("Generate bones: ")
539 ###########################################
540 bpy.ops.object.mode_set(mode='OBJECT')
541 bpy.ops.object.mode_set(mode='EDIT')
543 self.invoke_parent_bones()
545 self.__parent_bones_to_root()
547 t.tick("Parent bones: ")
549 ###########################################
550 bpy.ops.object.mode_set(mode='OBJECT')
552 self.invoke_configure_bones()
554 t.tick("Configure bones: ")
556 ###########################################
557 bpy.ops.object.mode_set(mode='OBJECT')
559 self.invoke_preapply_bones()
561 t.tick("Preapply bones: ")
563 ###########################################
564 bpy.ops.object.mode_set(mode='EDIT')
566 self.invoke_apply_bones()
568 t.tick("Apply bones: ")
570 ###########################################
571 bpy.ops.object.mode_set(mode='OBJECT')
573 self.invoke_rig_bones()
575 t.tick("Rig bones: ")
577 ###########################################
578 bpy.ops.object.mode_set(mode='OBJECT')
580 self.invoke_generate_widgets()
582 # Generate the default root widget last in case it's rigged with raw_copy
583 create_root_widget(obj, self.root_bone)
585 t.tick("Generate widgets: ")
587 ###########################################
588 bpy.ops.object.mode_set(mode='OBJECT')
590 self.__lock_transforms()
591 self.__assign_layers()
592 self.__compute_visible_layers()
593 self.__restore_driver_vars()
595 t.tick("Assign layers: ")
597 ###########################################
598 bpy.ops.object.mode_set(mode='OBJECT')
600 self.invoke_finalize()
602 t.tick("Finalize: ")
604 ###########################################
605 bpy.ops.object.mode_set(mode='OBJECT')
607 self.__assign_widgets()
609 # Create Selection Sets
610 create_selection_sets(obj, metarig)
612 # Create Bone Groups
613 apply_bone_colors(obj, metarig, self.layer_group_priorities)
615 t.tick("The rest: ")
617 ###########################################
618 # Restore state
620 bpy.ops.object.mode_set(mode='OBJECT')
621 obj.data.pose_position = 'POSE'
623 # Restore parent to bones
624 for child, sub_parent in child_parent_bones.items():
625 if sub_parent in obj.pose.bones:
626 mat = child.matrix_world.copy()
627 child.parent_bone = sub_parent
628 child.matrix_world = mat
630 # Clear any transient errors in drivers
631 refresh_all_drivers()
633 ###########################################
634 # Execute the finalize script
636 finalize_script = get_rigify_finalize_script(metarig.data)
638 if finalize_script:
639 bpy.ops.object.mode_set(mode='OBJECT')
640 exec(finalize_script.as_string(), {})
641 bpy.ops.object.mode_set(mode='OBJECT')
643 obj.data.collections.active_index = 0
645 self.artifacts.generate_cleanup()
647 ###########################################
648 # Restore active collection
649 view_layer.active_layer_collection = self.layer_collection
652 def generate_rig(context, metarig):
653 """ Generates a rig from a metarig.
656 # Initial configuration
657 rest_backup = metarig.data.pose_position
658 metarig.data.pose_position = 'REST'
660 try:
661 generator = Generator(context, metarig)
663 base_generate.BaseGenerator.instance = generator
665 generator.generate()
667 metarig.data.pose_position = rest_backup
669 except Exception as e:
670 # Cleanup if something goes wrong
671 print("Rigify: failed to generate rig.")
673 bpy.ops.object.mode_set(mode='OBJECT')
674 metarig.data.pose_position = rest_backup
676 # Continue the exception
677 raise e
679 finally:
680 base_generate.BaseGenerator.instance = None
683 def create_selection_set_for_rig_layer(rig: ArmatureObject, set_name: str, coll: bpy.types.BoneCollection) -> None:
684 """Create a single selection set on a rig.
686 The set will contain all bones on the rig layer with the given index.
688 sel_set = rig.selection_sets.add() # noqa
689 sel_set.name = set_name
691 for b in rig.pose.bones:
692 if coll not in b.bone.collections or b.name in sel_set.bone_ids:
693 continue
695 bone_id = sel_set.bone_ids.add()
696 bone_id.name = b.name
699 def create_selection_sets(obj: ArmatureObject, _metarig: ArmatureObject):
700 """Create selection sets if the Selection Sets addon is enabled.
702 Whether a selection set for a rig layer is created is controlled in the
703 Rigify Layer Names panel.
705 # Check if selection sets addon is installed
706 if 'bone_selection_groups' not in bpy.context.preferences.addons \
707 and 'bone_selection_sets' not in bpy.context.preferences.addons:
708 return
710 obj.selection_sets.clear() # noqa
712 for coll in obj.data.collections:
713 if not coll.rigify_sel_set:
714 continue
716 create_selection_set_for_rig_layer(obj, coll.name, coll)
719 def apply_bone_colors(obj, metarig, priorities: Optional[dict[str, dict[str, float]]] = None):
720 bpy.ops.object.mode_set(mode='OBJECT')
721 pb = obj.pose.bones
723 color_sets = get_rigify_colors(metarig.data)
724 color_map = {i+1: cset for i, cset in enumerate(color_sets)}
726 collection_table: dict[str, tuple[int, 'RigifyColorSet']] = {
727 coll.name: (i, color_map[coll.rigify_color_set_id])
728 for i, coll in enumerate(obj.data.collections)
729 if coll.rigify_color_set_id in color_map
732 priorities = priorities or {}
733 dummy = {}
735 for b in pb:
736 bone_priorities = priorities.get(b.name, dummy)
737 cset_collections = [coll.name for coll in b.bone.collections if coll.name in collection_table]
738 if cset_collections:
739 best_name = max(
740 cset_collections,
741 key=lambda n: (bone_priorities.get(n, 0), -collection_table[n][0])
743 _, cset = collection_table[best_name]
744 cset.apply(b.bone.color)
745 cset.apply(b.color)
748 def get_xy_spread(bones):
749 x_max = 0
750 y_max = 0
751 for b in bones:
752 x_max = max((x_max, abs(b.head[0]), abs(b.tail[0])))
753 y_max = max((y_max, abs(b.head[1]), abs(b.tail[1])))
755 return max((x_max, y_max))