Sun Position: fix error in HDRI mode when no env tex is selected
[blender-addons.git] / rigify / generate.py
blob2102c9d14851408062f855dcacf87aa359f209df
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 import bpy
4 import re
5 import time
7 from typing import Optional
9 from .utils.errors import MetarigError
10 from .utils.bones import new_bone
11 from .utils.layers import ORG_LAYER, MCH_LAYER, DEF_LAYER, ROOT_LAYER
12 from .utils.naming import (ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name,
13 change_name_side, get_name_side, Side)
14 from .utils.widgets import WGT_PREFIX, WGT_GROUP_PREFIX
15 from .utils.widgets_special import create_root_widget
16 from .utils.mechanism import refresh_all_drivers
17 from .utils.misc import gamma_correct, select_object, ArmatureObject, verify_armature_obj
18 from .utils.collections import (ensure_collection, list_layer_collections,
19 filter_layer_collections_by_object)
20 from .utils.rig import get_rigify_type, get_rigify_layers, get_rigify_target_rig,\
21 get_rigify_rig_basename, get_rigify_force_widget_update, get_rigify_finalize_script,\
22 get_rigify_mirror_widgets
23 from .utils.action_layers import ActionLayerBuilder
25 from . import base_generate
26 from . import rig_ui_template
27 from . import rig_lists
30 RIG_MODULE = "rigs"
33 class Timer:
34 def __init__(self):
35 self.time_val = time.time()
37 def tick(self, string):
38 t = time.time()
39 print(string + "%.3f" % (t - self.time_val))
40 self.time_val = t
43 class Generator(base_generate.BaseGenerator):
44 usable_collections: list[bpy.types.LayerCollection]
45 action_layers: ActionLayerBuilder
47 def __init__(self, context, metarig):
48 super().__init__(context, metarig)
50 self.id_store = context.window_manager
52 def find_rig_class(self, rig_type):
53 rig_module = rig_lists.rigs[rig_type]["module"]
55 return rig_module.Rig
57 def __switch_to_usable_collection(self, obj, fallback=False):
58 collections = filter_layer_collections_by_object(self.usable_collections, obj)
60 if collections:
61 self.layer_collection = collections[0]
62 elif fallback:
63 self.layer_collection = self.view_layer.layer_collection
65 self.collection = self.layer_collection.collection
67 def ensure_rig_object(self) -> ArmatureObject:
68 """Check if the generated rig already exists, so we can
69 regenerate in the same object. If not, create a new
70 object to generate the rig in.
71 """
72 print("Fetch rig.")
73 meta_data = self.metarig.data
75 target_rig = get_rigify_target_rig(meta_data)
77 if not target_rig:
78 rig_basename = get_rigify_rig_basename(meta_data)
80 if rig_basename:
81 rig_new_name = rig_basename
82 elif "metarig" in self.metarig.name:
83 rig_new_name = self.metarig.name.replace("metarig", "rig")
84 elif "META" in self.metarig.name:
85 rig_new_name = self.metarig.name.replace("META", "RIG")
86 else:
87 rig_new_name = "RIG-" + self.metarig.name
89 arm = bpy.data.armatures.new(rig_new_name)
90 target_rig = verify_armature_obj(bpy.data.objects.new(rig_new_name, arm))
91 target_rig.display_type = 'WIRE'
93 # If the object is already added to the scene, switch to its collection
94 if target_rig in list(self.context.scene.collection.all_objects):
95 self.__switch_to_usable_collection(target_rig)
96 else:
97 # Otherwise, add to the selected collection or the metarig collection if unusable
98 if (self.layer_collection not in self.usable_collections
99 or self.layer_collection == self.view_layer.layer_collection):
100 self.__switch_to_usable_collection(self.metarig, True)
102 self.collection.objects.link(target_rig)
104 # Configure and remember the object
105 meta_data.rigify_target_rig = target_rig
106 target_rig.data.pose_position = 'POSE'
108 return target_rig
110 def __unhide_rig_object(self, obj: bpy.types.Object):
111 # Ensure the object is visible and selectable
112 obj.hide_set(False, view_layer=self.view_layer)
113 obj.hide_viewport = False
115 if not obj.visible_get(view_layer=self.view_layer):
116 raise Exception('Could not generate: Target rig is not visible')
118 obj.select_set(True, view_layer=self.view_layer)
120 if not obj.select_get(view_layer=self.view_layer):
121 raise Exception('Could not generate: Cannot select target rig')
123 if self.layer_collection not in self.usable_collections:
124 raise Exception('Could not generate: Could not find a usable collection.')
126 def __find_legacy_collection(self) -> bpy.types.Collection:
127 """For backwards comp, matching by name to find a legacy collection.
128 (For before there was a Widget Collection PointerProperty)
130 widgets_group_name = WGT_GROUP_PREFIX + self.obj.name
131 old_collection = bpy.data.collections.get(widgets_group_name)
133 if old_collection and old_collection.library:
134 old_collection = None
136 if not old_collection:
137 # Update the old 'Widgets' collection
138 legacy_collection = bpy.data.collections.get('Widgets')
140 if legacy_collection and widgets_group_name in legacy_collection.objects\
141 and not legacy_collection.library:
142 legacy_collection.name = widgets_group_name
143 old_collection = legacy_collection
145 if old_collection:
146 # Rename the collection
147 old_collection.name = widgets_group_name
149 return old_collection
151 def ensure_widget_collection(self):
152 # Create/find widget collection
153 self.widget_collection = self.metarig.data.rigify_widgets_collection
154 if not self.widget_collection:
155 self.widget_collection = self.__find_legacy_collection()
156 if not self.widget_collection:
157 widgets_group_name = WGT_GROUP_PREFIX + self.obj.name.replace("RIG-", "")
158 self.widget_collection = ensure_collection(
159 self.context, widgets_group_name, hidden=True)
161 self.metarig.data.rigify_widgets_collection = self.widget_collection
163 self.use_mirror_widgets = get_rigify_mirror_widgets(self.metarig.data)
165 # Build tables for existing widgets
166 self.old_widget_table = {}
167 self.new_widget_table = {}
168 self.widget_mirror_mesh = {}
170 if get_rigify_force_widget_update(self.metarig.data):
171 # Remove widgets if force update is set
172 for obj in list(self.widget_collection.objects):
173 bpy.data.objects.remove(obj)
174 elif self.obj.pose:
175 # Find all widgets from the collection referenced by the old rig
176 known_widgets = set(obj.name for obj in self.widget_collection.objects)
178 for bone in self.obj.pose.bones:
179 if bone.custom_shape and bone.custom_shape.name in known_widgets:
180 self.old_widget_table[bone.name] = bone.custom_shape
182 # Rename widgets in case the rig was renamed
183 name_prefix = WGT_PREFIX + self.obj.name + "_"
185 for bone_name, widget in self.old_widget_table.items():
186 old_data_name = change_name_side(widget.name, get_name_side(widget.data.name))
188 widget.name = name_prefix + bone_name
190 # If the mesh name is the same as the object, rename it too
191 if widget.data.name == old_data_name:
192 widget.data.name = change_name_side(
193 widget.name, get_name_side(widget.data.name))
195 # Find meshes for mirroring
196 if self.use_mirror_widgets:
197 for bone_name, widget in self.old_widget_table.items():
198 mid_name = change_name_side(bone_name, Side.MIDDLE)
199 if bone_name != mid_name:
200 assert isinstance(widget.data, bpy.types.Mesh)
201 self.widget_mirror_mesh[mid_name] = widget.data
203 def __duplicate_rig(self):
204 obj = self.obj
205 metarig = self.metarig
206 context = self.context
208 # Remove all bones from the generated rig armature.
209 bpy.ops.object.mode_set(mode='EDIT')
210 for bone in obj.data.edit_bones:
211 obj.data.edit_bones.remove(bone)
212 bpy.ops.object.mode_set(mode='OBJECT')
214 # Select and duplicate metarig
215 select_object(context, metarig, deselect_all=True)
217 bpy.ops.object.duplicate()
219 # Rename org bones in the temporary object
220 temp_obj = verify_armature_obj(context.view_layer.objects.active)
222 assert temp_obj and temp_obj != metarig
224 self.__freeze_driver_vars(temp_obj)
225 self.__rename_org_bones(temp_obj)
227 # Select the target rig and join
228 select_object(context, obj)
230 saved_matrix = obj.matrix_world.copy()
231 obj.matrix_world = metarig.matrix_world
233 bpy.ops.object.join()
235 obj.matrix_world = saved_matrix
237 # Select the generated rig
238 select_object(context, obj, deselect_all=True)
240 # Clean up animation data
241 if obj.animation_data:
242 obj.animation_data.action = None
244 for track in obj.animation_data.nla_tracks:
245 obj.animation_data.nla_tracks.remove(track)
247 @staticmethod
248 def __freeze_driver_vars(obj: bpy.types.Object):
249 if obj.animation_data:
250 # Freeze drivers referring to custom properties
251 for d in obj.animation_data.drivers:
252 for var in d.driver.variables:
253 for tar in var.targets:
254 # If a custom property
255 if var.type == 'SINGLE_PROP' \
256 and re.match(r'^pose.bones\["[^"\]]*"]\["[^"\]]*"]$',
257 tar.data_path):
258 tar.data_path = "RIGIFY-" + tar.data_path
260 def __rename_org_bones(self, obj: ArmatureObject):
261 # Make a list of the original bones, so we can keep track of them.
262 original_bones = [bone.name for bone in obj.data.bones]
264 # Add the ORG_PREFIX to the original bones.
265 for i in range(0, len(original_bones)):
266 bone = obj.pose.bones[original_bones[i]]
268 # Preserve the root bone as is if present
269 if bone.name == ROOT_NAME:
270 if bone.parent:
271 raise MetarigError('Root bone must have no parent')
272 if get_rigify_type(bone) not in ('', 'basic.raw_copy'):
273 raise MetarigError('Root bone must have no rig, or use basic.raw_copy')
274 continue
276 # This rig type is special in that it preserves the name of the bone.
277 if get_rigify_type(bone) != 'basic.raw_copy':
278 bone.name = make_original_name(original_bones[i])
279 original_bones[i] = bone.name
281 self.original_bones = original_bones
283 def __create_root_bone(self):
284 obj = self.obj
285 metarig = self.metarig
287 if ROOT_NAME in obj.data.bones:
288 # Use the existing root bone
289 root_bone = ROOT_NAME
290 else:
291 # Create the root bone.
292 root_bone = new_bone(obj, ROOT_NAME)
293 spread = get_xy_spread(metarig.data.bones) or metarig.data.bones[0].length
294 spread = float('%.3g' % spread)
295 scale = spread/0.589
296 obj.data.edit_bones[root_bone].head = (0, 0, 0)
297 obj.data.edit_bones[root_bone].tail = (0, scale, 0)
298 obj.data.edit_bones[root_bone].roll = 0
300 self.root_bone = root_bone
301 self.bone_owners[root_bone] = None
302 self.noparent_bones.add(root_bone)
304 def __parent_bones_to_root(self):
305 eb = self.obj.data.edit_bones
307 # Parent loose bones to root
308 for bone in eb:
309 if bone.name in self.noparent_bones:
310 continue
311 elif bone.parent is None:
312 bone.use_connect = False
313 bone.parent = eb[self.root_bone]
315 def __lock_transforms(self):
316 # Lock transforms on all non-control bones
317 r = re.compile("[A-Z][A-Z][A-Z]-")
318 for pb in self.obj.pose.bones:
319 if r.match(pb.name):
320 pb.lock_location = (True, True, True)
321 pb.lock_rotation = (True, True, True)
322 pb.lock_rotation_w = True
323 pb.lock_scale = (True, True, True)
325 def __assign_layers(self):
326 pose_bones = self.obj.pose.bones
328 pose_bones[self.root_bone].bone.layers = ROOT_LAYER
330 # Every bone that has a name starting with "DEF-" make deforming. All the
331 # others make non-deforming.
332 for pbone in pose_bones:
333 bone = pbone.bone
334 name = bone.name
335 layers = None
337 bone.use_deform = name.startswith(DEF_PREFIX)
339 # Move all the original bones to their layer.
340 if name.startswith(ORG_PREFIX):
341 layers = ORG_LAYER
342 # Move all the bones with names starting with "MCH-" to their layer.
343 elif name.startswith(MCH_PREFIX):
344 layers = MCH_LAYER
345 # Move all the bones with names starting with "DEF-" to their layer.
346 elif name.startswith(DEF_PREFIX):
347 layers = DEF_LAYER
349 if layers is not None:
350 bone.layers = layers
352 # Remove custom shapes from non-control bones
353 pbone.custom_shape = None
355 bone.bbone_x = bone.bbone_z = bone.length * 0.05
357 def __restore_driver_vars(self):
358 obj = self.obj
360 # Alter marked driver targets
361 if obj.animation_data:
362 for d in obj.animation_data.drivers:
363 for v in d.driver.variables:
364 for tar in v.targets:
365 if tar.data_path.startswith("RIGIFY-"):
366 temp, bone, prop = tuple(
367 [x.strip('"]') for x in tar.data_path.split('["')])
368 if bone in obj.data.bones and prop in obj.pose.bones[bone].keys():
369 tar.data_path = tar.data_path[7:]
370 else:
371 org_name = make_original_name(bone)
372 org_name = self.org_rename_table.get(org_name, org_name)
373 tar.data_path = 'pose.bones["%s"]["%s"]' % (org_name, prop)
375 def __assign_widgets(self):
376 obj_table = {obj.name: obj for obj in self.scene.objects}
378 # Assign shapes to bones
379 # Object's with name WGT-<bone_name> get used as that bone's shape.
380 for bone in self.obj.pose.bones:
381 # First check the table built by create_widget
382 if bone.name in self.new_widget_table:
383 bone.custom_shape = self.new_widget_table[bone.name]
384 continue
386 # Object names are limited to 63 characters... arg
387 wgt_name = (WGT_PREFIX + self.obj.name + '_' + bone.name)[:63]
389 if wgt_name in obj_table:
390 bone.custom_shape = obj_table[wgt_name]
392 def __compute_visible_layers(self):
393 # Reveal all the layers with control bones on them
394 vis_layers = [False for _ in range(0, 32)]
396 for bone in self.obj.data.bones:
397 for i in range(0, 32):
398 vis_layers[i] = vis_layers[i] or bone.layers[i]
400 for i in range(0, 32):
401 vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i])
403 self.obj.data.layers = vis_layers
405 def generate(self):
406 context = self.context
407 metarig = self.metarig
408 view_layer = self.view_layer
409 t = Timer()
411 self.usable_collections = list_layer_collections(
412 view_layer.layer_collection, selectable=True)
414 bpy.ops.object.mode_set(mode='OBJECT')
416 ###########################################
417 # Create/find the rig object and set it up
418 self.obj = obj = self.ensure_rig_object()
420 self.__unhide_rig_object(obj)
422 # Select the chosen working collection in case it changed
423 self.view_layer.active_layer_collection = self.layer_collection
425 # Get rid of anim data in case the rig already existed
426 print("Clear rig animation data.")
428 obj.animation_data_clear()
429 obj.data.animation_data_clear()
431 select_object(context, obj, deselect_all=True)
433 ###########################################
434 # Create Widget Collection
435 self.ensure_widget_collection()
437 t.tick("Create widgets collection: ")
439 ###########################################
440 # Get parented objects to restore later
442 child_parent_bones = {} # {object: bone}
444 for child in obj.children:
445 child_parent_bones[child] = child.parent_bone
447 ###########################################
448 # Copy bones from metarig to obj (adds ORG_PREFIX)
449 self.__duplicate_rig()
451 obj.data.use_mirror_x = False
453 t.tick("Duplicate rig: ")
455 ###########################################
456 # Put the rig_name in the armature custom properties
457 obj.data["rig_id"] = self.rig_id
459 self.script = rig_ui_template.ScriptGenerator(self)
460 self.action_layers = ActionLayerBuilder(self)
462 ###########################################
463 bpy.ops.object.mode_set(mode='OBJECT')
465 self.instantiate_rig_tree()
467 t.tick("Instantiate rigs: ")
469 ###########################################
470 bpy.ops.object.mode_set(mode='OBJECT')
472 self.invoke_initialize()
474 t.tick("Initialize rigs: ")
476 ###########################################
477 bpy.ops.object.mode_set(mode='EDIT')
479 self.invoke_prepare_bones()
481 t.tick("Prepare bones: ")
483 ###########################################
484 bpy.ops.object.mode_set(mode='OBJECT')
485 bpy.ops.object.mode_set(mode='EDIT')
487 self.__create_root_bone()
489 self.invoke_generate_bones()
491 t.tick("Generate bones: ")
493 ###########################################
494 bpy.ops.object.mode_set(mode='OBJECT')
495 bpy.ops.object.mode_set(mode='EDIT')
497 self.invoke_parent_bones()
499 self.__parent_bones_to_root()
501 t.tick("Parent bones: ")
503 ###########################################
504 bpy.ops.object.mode_set(mode='OBJECT')
506 self.invoke_configure_bones()
508 t.tick("Configure bones: ")
510 ###########################################
511 bpy.ops.object.mode_set(mode='OBJECT')
513 self.invoke_preapply_bones()
515 t.tick("Preapply bones: ")
517 ###########################################
518 bpy.ops.object.mode_set(mode='EDIT')
520 self.invoke_apply_bones()
522 t.tick("Apply bones: ")
524 ###########################################
525 bpy.ops.object.mode_set(mode='OBJECT')
527 self.invoke_rig_bones()
529 t.tick("Rig bones: ")
531 ###########################################
532 bpy.ops.object.mode_set(mode='OBJECT')
534 self.invoke_generate_widgets()
536 # Generate the default root widget last in case it's rigged with raw_copy
537 create_root_widget(obj, self.root_bone)
539 t.tick("Generate widgets: ")
541 ###########################################
542 bpy.ops.object.mode_set(mode='OBJECT')
544 self.__lock_transforms()
545 self.__assign_layers()
546 self.__compute_visible_layers()
547 self.__restore_driver_vars()
549 t.tick("Assign layers: ")
551 ###########################################
552 bpy.ops.object.mode_set(mode='OBJECT')
554 self.invoke_finalize()
556 t.tick("Finalize: ")
558 ###########################################
559 bpy.ops.object.mode_set(mode='OBJECT')
561 self.__assign_widgets()
563 # Create Selection Sets
564 create_selection_sets(obj, metarig)
566 # Create Bone Groups
567 create_bone_groups(obj, metarig, self.layer_group_priorities)
569 t.tick("The rest: ")
571 ###########################################
572 # Restore state
574 bpy.ops.object.mode_set(mode='OBJECT')
575 obj.data.pose_position = 'POSE'
577 # Restore parent to bones
578 for child, sub_parent in child_parent_bones.items():
579 if sub_parent in obj.pose.bones:
580 mat = child.matrix_world.copy()
581 child.parent_bone = sub_parent
582 child.matrix_world = mat
584 # Clear any transient errors in drivers
585 refresh_all_drivers()
587 ###########################################
588 # Execute the finalize script
590 finalize_script = get_rigify_finalize_script(metarig.data)
592 if finalize_script:
593 bpy.ops.object.mode_set(mode='OBJECT')
594 exec(finalize_script.as_string(), {})
595 bpy.ops.object.mode_set(mode='OBJECT')
597 ###########################################
598 # Restore active collection
599 view_layer.active_layer_collection = self.layer_collection
602 def generate_rig(context, metarig):
603 """ Generates a rig from a metarig.
606 # Initial configuration
607 rest_backup = metarig.data.pose_position
608 metarig.data.pose_position = 'REST'
610 try:
611 generator = Generator(context, metarig)
613 base_generate.BaseGenerator.instance = generator
615 generator.generate()
617 metarig.data.pose_position = rest_backup
619 except Exception as e:
620 # Cleanup if something goes wrong
621 print("Rigify: failed to generate rig.")
623 bpy.ops.object.mode_set(mode='OBJECT')
624 metarig.data.pose_position = rest_backup
626 # Continue the exception
627 raise e
629 finally:
630 base_generate.BaseGenerator.instance = None
633 def create_selection_set_for_rig_layer(rig: ArmatureObject, set_name: str, layer_idx: int) -> None:
634 """Create a single selection set on a rig.
636 The set will contain all bones on the rig layer with the given index.
638 sel_set = rig.selection_sets.add() # noqa
639 sel_set.name = set_name
641 for b in rig.pose.bones:
642 if not b.bone.layers[layer_idx] or b.name in sel_set.bone_ids:
643 continue
645 bone_id = sel_set.bone_ids.add()
646 bone_id.name = b.name
649 def create_selection_sets(obj: ArmatureObject, metarig: ArmatureObject):
650 """Create selection sets if the Selection Sets addon is enabled.
652 Whether a selection set for a rig layer is created is controlled in the
653 Rigify Layer Names panel.
655 # Check if selection sets addon is installed
656 if 'bone_selection_groups' not in bpy.context.preferences.addons \
657 and 'bone_selection_sets' not in bpy.context.preferences.addons:
658 return
660 obj.selection_sets.clear() # noqa
662 rigify_layers = get_rigify_layers(metarig.data)
664 for i, layer in enumerate(rigify_layers):
665 if layer.name == '' or not layer.selset:
666 continue
668 create_selection_set_for_rig_layer(obj, layer.name, i)
671 def create_bone_groups(obj, metarig, priorities: Optional[dict[str, dict[int, float]]] = None):
672 bpy.ops.object.mode_set(mode='OBJECT')
673 pb = obj.pose.bones
674 layers = metarig.data.rigify_layers
675 groups = metarig.data.rigify_colors
676 priorities = priorities or {}
677 dummy = {}
679 # Create BGs
680 for layer in layers:
681 if layer.group == 0:
682 continue
683 g_id = layer.group - 1
684 name = groups[g_id].name
685 if name not in obj.pose.bone_groups.keys():
686 bg = obj.pose.bone_groups.new(name=name)
687 bg.color_set = 'CUSTOM'
688 bg.colors.normal = gamma_correct(groups[g_id].normal)
689 bg.colors.select = gamma_correct(groups[g_id].select)
690 bg.colors.active = gamma_correct(groups[g_id].active)
692 for b in pb:
693 try:
694 bone_priorities = priorities.get(b.name, dummy)
695 enabled = [i for i, v in enumerate(b.bone.layers) if v]
696 layer_index = max(enabled, key=lambda i: bone_priorities.get(i, 0))
697 except ValueError:
698 continue
699 if layer_index > len(layers) - 1: # bone is on reserved layers
700 continue
701 g_id = layers[layer_index].group - 1
702 if g_id >= 0:
703 name = groups[g_id].name
704 b.bone_group = obj.pose.bone_groups[name]
707 def get_xy_spread(bones):
708 x_max = 0
709 y_max = 0
710 for b in bones:
711 x_max = max((x_max, abs(b.head[0]), abs(b.tail[0])))
712 y_max = max((y_max, abs(b.head[1]), abs(b.tail[1])))
714 return max((x_max, y_max))