1 # SPDX-License-Identifier: GPL-2.0-or-later
7 from .utils
.errors
import MetarigError
8 from .utils
.bones
import new_bone
9 from .utils
.layers
import ORG_LAYER
, MCH_LAYER
, DEF_LAYER
, ROOT_LAYER
10 from .utils
.naming
import ORG_PREFIX
, MCH_PREFIX
, DEF_PREFIX
, ROOT_NAME
, make_original_name
, change_name_side
, get_name_side
, Side
11 from .utils
.widgets
import WGT_PREFIX
12 from .utils
.widgets_special
import create_root_widget
13 from .utils
.mechanism
import refresh_all_drivers
14 from .utils
.misc
import gamma_correct
, select_object
15 from .utils
.collections
import ensure_collection
, list_layer_collections
, filter_layer_collections_by_object
16 from .utils
.rig
import get_rigify_type
18 from . import base_generate
19 from . import rig_ui_template
20 from . import rig_lists
26 self
.timez
= time
.time()
28 def tick(self
, string
):
30 print(string
+ "%.3f" % (t
- self
.timez
))
34 class Generator(base_generate
.BaseGenerator
):
35 def __init__(self
, context
, metarig
):
36 super().__init
__(context
, metarig
)
38 self
.id_store
= context
.window_manager
41 def find_rig_class(self
, rig_type
):
42 rig_module
= rig_lists
.rigs
[rig_type
]["module"]
47 def __switch_to_usable_collection(self
, obj
, fallback
=False):
48 collections
= filter_layer_collections_by_object(self
.usable_collections
, obj
)
51 self
.layer_collection
= collections
[0]
53 self
.layer_collection
= self
.view_layer
.layer_collection
55 self
.collection
= self
.layer_collection
.collection
58 def ensure_rig_object(self
) -> bpy
.types
.Object
:
59 """Check if the generated rig already exists, so we can
60 regenerate in the same object. If not, create a new
61 object to generate the rig in.
64 meta_data
= self
.metarig
.data
66 target_rig
= meta_data
.rigify_target_rig
68 if meta_data
.rigify_rig_basename
:
69 rig_new_name
= meta_data
.rigify_rig_basename
70 elif "metarig" in self
.metarig
.name
:
71 rig_new_name
= self
.metarig
.name
.replace("metarig", "rig")
72 elif "META" in self
.metarig
.name
:
73 rig_new_name
= self
.metarig
.name
.replace("META", "RIG")
75 rig_new_name
= "RIG-" + self
.metarig
.name
77 target_rig
= bpy
.data
.objects
.new(rig_new_name
, bpy
.data
.armatures
.new(rig_new_name
))
78 target_rig
.display_type
= 'WIRE'
80 # If the object is already added to the scene, switch to its collection
81 if target_rig
in list(self
.context
.scene
.collection
.all_objects
):
82 self
.__switch
_to
_usable
_collection
(target_rig
)
84 # Otherwise, add to the selected collection or the metarig collection if unusable
85 if (self
.layer_collection
not in self
.usable_collections
86 or self
.layer_collection
== self
.view_layer
.layer_collection
):
87 self
.__switch
_to
_usable
_collection
(self
.metarig
, True)
89 self
.collection
.objects
.link(target_rig
)
91 # Configure and remember the object
92 meta_data
.rigify_target_rig
= target_rig
93 target_rig
.data
.pose_position
= 'POSE'
98 def __unhide_rig_object(self
, obj
):
99 # Ensure the object is visible and selectable
100 obj
.hide_set(False, view_layer
=self
.view_layer
)
101 obj
.hide_viewport
= False
103 if not obj
.visible_get(view_layer
=self
.view_layer
):
104 raise Exception('Could not generate: Target rig is not visible')
106 obj
.select_set(True, view_layer
=self
.view_layer
)
108 if not obj
.select_get(view_layer
=self
.view_layer
):
109 raise Exception('Could not generate: Cannot select target rig')
111 if self
.layer_collection
not in self
.usable_collections
:
112 raise Exception('Could not generate: Could not find a usable collection.')
115 def __find_legacy_collection(self
) -> bpy
.types
.Collection
:
116 """For backwards comp, matching by name to find a legacy collection.
117 (For before there was a Widget Collection PointerProperty)
119 wgts_group_name
= "WGTS_" + self
.obj
.name
120 old_collection
= bpy
.data
.collections
.get(wgts_group_name
)
122 if old_collection
and old_collection
.library
:
123 old_collection
= None
125 if not old_collection
:
126 # Update the old 'Widgets' collection
127 legacy_collection
= bpy
.data
.collections
.get('Widgets')
129 if legacy_collection
and wgts_group_name
in legacy_collection
.objects
and not legacy_collection
.library
:
130 legacy_collection
.name
= wgts_group_name
131 old_collection
= legacy_collection
134 # Rename the collection
135 old_collection
.name
= wgts_group_name
137 return old_collection
139 def ensure_widget_collection(self
):
140 # Create/find widget collection
141 self
.widget_collection
= self
.metarig
.data
.rigify_widgets_collection
142 if not self
.widget_collection
:
143 self
.widget_collection
= self
.__find
_legacy
_collection
()
144 if not self
.widget_collection
:
145 wgts_group_name
= "WGTS_" + self
.obj
.name
.replace("RIG-", "")
146 self
.widget_collection
= ensure_collection(self
.context
, wgts_group_name
, hidden
=True)
148 self
.metarig
.data
.rigify_widgets_collection
= self
.widget_collection
150 self
.use_mirror_widgets
= self
.metarig
.data
.rigify_mirror_widgets
152 # Build tables for existing widgets
153 self
.old_widget_table
= {}
154 self
.new_widget_table
= {}
155 self
.widget_mirror_mesh
= {}
157 if self
.metarig
.data
.rigify_force_widget_update
:
158 # Remove widgets if force update is set
159 for obj
in list(self
.widget_collection
.objects
):
160 bpy
.data
.objects
.remove(obj
)
162 # Find all widgets from the collection referenced by the old rig
163 known_widgets
= set(obj
.name
for obj
in self
.widget_collection
.objects
)
165 for bone
in self
.obj
.pose
.bones
:
166 if bone
.custom_shape
and bone
.custom_shape
.name
in known_widgets
:
167 self
.old_widget_table
[bone
.name
] = bone
.custom_shape
169 # Rename widgets in case the rig was renamed
170 name_prefix
= WGT_PREFIX
+ self
.obj
.name
+ "_"
172 for bone_name
, widget
in self
.old_widget_table
.items():
173 old_data_name
= change_name_side(widget
.name
, get_name_side(widget
.data
.name
))
175 widget
.name
= name_prefix
+ bone_name
177 # If the mesh name is the same as the object, rename it too
178 if widget
.data
.name
== old_data_name
:
179 widget
.data
.name
= change_name_side(widget
.name
, get_name_side(widget
.data
.name
))
181 # Find meshes for mirroring
182 if self
.use_mirror_widgets
:
183 for bone_name
, widget
in self
.old_widget_table
.items():
184 mid_name
= change_name_side(bone_name
, Side
.MIDDLE
)
185 if bone_name
!= mid_name
:
186 self
.widget_mirror_mesh
[mid_name
] = widget
.data
189 def __duplicate_rig(self
):
191 metarig
= self
.metarig
192 context
= self
.context
194 # Remove all bones from the generated rig armature.
195 bpy
.ops
.object.mode_set(mode
='EDIT')
196 for bone
in obj
.data
.edit_bones
:
197 obj
.data
.edit_bones
.remove(bone
)
198 bpy
.ops
.object.mode_set(mode
='OBJECT')
200 # Select and duplicate metarig
201 select_object(context
, metarig
, deselect_all
=True)
203 bpy
.ops
.object.duplicate()
205 # Rename org bones in the temporary object
206 temp_obj
= context
.view_layer
.objects
.active
208 assert temp_obj
and temp_obj
!= metarig
210 self
.__freeze
_driver
_vars
(temp_obj
)
211 self
.__rename
_org
_bones
(temp_obj
)
213 # Select the target rig and join
214 select_object(context
, obj
)
216 saved_matrix
= obj
.matrix_world
.copy()
217 obj
.matrix_world
= metarig
.matrix_world
219 bpy
.ops
.object.join()
221 obj
.matrix_world
= saved_matrix
223 # Select the generated rig
224 select_object(context
, obj
, deselect_all
=True)
226 # Clean up animation data
227 if obj
.animation_data
:
228 obj
.animation_data
.action
= None
230 for track
in obj
.animation_data
.nla_tracks
:
231 obj
.animation_data
.nla_tracks
.remove(track
)
234 def __freeze_driver_vars(self
, obj
):
235 if obj
.animation_data
:
236 # Freeze drivers referring to custom properties
237 for d
in obj
.animation_data
.drivers
:
238 for var
in d
.driver
.variables
:
239 for tar
in var
.targets
:
240 # If a custom property
241 if var
.type == 'SINGLE_PROP' \
242 and re
.match(r
'^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar
.data_path
):
243 tar
.data_path
= "RIGIFY-" + tar
.data_path
246 def __rename_org_bones(self
, obj
):
247 #----------------------------------
248 # Make a list of the original bones so we can keep track of them.
249 original_bones
= [bone
.name
for bone
in obj
.data
.bones
]
251 # Add the ORG_PREFIX to the original bones.
252 for i
in range(0, len(original_bones
)):
253 bone
= obj
.pose
.bones
[original_bones
[i
]]
255 # Preserve the root bone as is if present
256 if bone
.name
== ROOT_NAME
:
258 raise MetarigError('Root bone must have no parent')
259 if get_rigify_type(bone
) not in ('', 'basic.raw_copy'):
260 raise MetarigError('Root bone must have no rig, or use basic.raw_copy')
263 # This rig type is special in that it preserves the name of the bone.
264 if get_rigify_type(bone
) != 'basic.raw_copy':
265 bone
.name
= make_original_name(original_bones
[i
])
266 original_bones
[i
] = bone
.name
268 self
.original_bones
= original_bones
271 def __create_root_bone(self
):
273 metarig
= self
.metarig
275 if ROOT_NAME
in obj
.data
.bones
:
276 # Use the existing root bone
277 root_bone
= ROOT_NAME
279 # Create the root bone.
280 root_bone
= new_bone(obj
, ROOT_NAME
)
281 spread
= get_xy_spread(metarig
.data
.bones
) or metarig
.data
.bones
[0].length
282 spread
= float('%.3g' % spread
)
284 obj
.data
.edit_bones
[root_bone
].head
= (0, 0, 0)
285 obj
.data
.edit_bones
[root_bone
].tail
= (0, scale
, 0)
286 obj
.data
.edit_bones
[root_bone
].roll
= 0
288 self
.root_bone
= root_bone
289 self
.bone_owners
[root_bone
] = None
290 self
.noparent_bones
.add(root_bone
)
293 def __parent_bones_to_root(self
):
294 eb
= self
.obj
.data
.edit_bones
296 # Parent loose bones to root
298 if bone
.name
in self
.noparent_bones
:
300 elif bone
.parent
is None:
301 bone
.use_connect
= False
302 bone
.parent
= eb
[self
.root_bone
]
305 def __lock_transforms(self
):
306 # Lock transforms on all non-control bones
307 r
= re
.compile("[A-Z][A-Z][A-Z]-")
308 for pb
in self
.obj
.pose
.bones
:
310 pb
.lock_location
= (True, True, True)
311 pb
.lock_rotation
= (True, True, True)
312 pb
.lock_rotation_w
= True
313 pb
.lock_scale
= (True, True, True)
316 def __assign_layers(self
):
317 pbones
= self
.obj
.pose
.bones
319 pbones
[self
.root_bone
].bone
.layers
= ROOT_LAYER
321 # Every bone that has a name starting with "DEF-" make deforming. All the
322 # others make non-deforming.
328 bone
.use_deform
= name
.startswith(DEF_PREFIX
)
330 # Move all the original bones to their layer.
331 if name
.startswith(ORG_PREFIX
):
333 # Move all the bones with names starting with "MCH-" to their layer.
334 elif name
.startswith(MCH_PREFIX
):
336 # Move all the bones with names starting with "DEF-" to their layer.
337 elif name
.startswith(DEF_PREFIX
):
340 if layers
is not None:
343 # Remove custom shapes from non-control bones
344 pbone
.custom_shape
= None
346 bone
.bbone_x
= bone
.bbone_z
= bone
.length
* 0.05
349 def __restore_driver_vars(self
):
352 # Alter marked driver targets
353 if obj
.animation_data
:
354 for d
in obj
.animation_data
.drivers
:
355 for v
in d
.driver
.variables
:
356 for tar
in v
.targets
:
357 if tar
.data_path
.startswith("RIGIFY-"):
358 temp
, bone
, prop
= tuple([x
.strip('"]') for x
in tar
.data_path
.split('["')])
359 if bone
in obj
.data
.bones \
360 and prop
in obj
.pose
.bones
[bone
].keys():
361 tar
.data_path
= tar
.data_path
[7:]
363 org_name
= make_original_name(bone
)
364 org_name
= self
.org_rename_table
.get(org_name
, org_name
)
365 tar
.data_path
= 'pose.bones["%s"]["%s"]' % (org_name
, prop
)
368 def __assign_widgets(self
):
369 obj_table
= {obj
.name
: obj
for obj
in self
.scene
.objects
}
371 # Assign shapes to bones
372 # Object's with name WGT-<bone_name> get used as that bone's shape.
373 for bone
in self
.obj
.pose
.bones
:
374 # First check the table built by create_widget
375 if bone
.name
in self
.new_widget_table
:
376 bone
.custom_shape
= self
.new_widget_table
[bone
.name
]
379 # Object names are limited to 63 characters... arg
380 wgt_name
= (WGT_PREFIX
+ self
.obj
.name
+ '_' + bone
.name
)[:63]
382 if wgt_name
in obj_table
:
383 bone
.custom_shape
= obj_table
[wgt_name
]
386 def __compute_visible_layers(self
):
387 # Reveal all the layers with control bones on them
388 vis_layers
= [False for n
in range(0, 32)]
390 for bone
in self
.obj
.data
.bones
:
391 for i
in range(0, 32):
392 vis_layers
[i
] = vis_layers
[i
] or bone
.layers
[i
]
394 for i
in range(0, 32):
395 vis_layers
[i
] = vis_layers
[i
] and not (ORG_LAYER
[i
] or MCH_LAYER
[i
] or DEF_LAYER
[i
])
397 self
.obj
.data
.layers
= vis_layers
401 context
= self
.context
402 metarig
= self
.metarig
404 id_store
= self
.id_store
405 view_layer
= self
.view_layer
408 self
.usable_collections
= list_layer_collections(view_layer
.layer_collection
, selectable
=True)
410 bpy
.ops
.object.mode_set(mode
='OBJECT')
412 #------------------------------------------
413 # Create/find the rig object and set it up
414 self
.obj
= obj
= self
.ensure_rig_object()
416 self
.__unhide
_rig
_object
(obj
)
418 # Select the chosen working collection in case it changed
419 self
.view_layer
.active_layer_collection
= self
.layer_collection
421 # Get rid of anim data in case the rig already existed
422 print("Clear rig animation data.")
424 obj
.animation_data_clear()
425 obj
.data
.animation_data_clear()
427 select_object(context
, obj
, deselect_all
=True)
429 #------------------------------------------
430 # Create Widget Collection
431 self
.ensure_widget_collection()
433 t
.tick("Create main WGTS: ")
435 #------------------------------------------
436 # Get parented objects to restore later
437 childs
= {} # {object: bone}
438 for child
in obj
.children
:
439 childs
[child
] = child
.parent_bone
441 #------------------------------------------
442 # Copy bones from metarig to obj (adds ORG_PREFIX)
443 self
.__duplicate
_rig
()
445 obj
.data
.use_mirror_x
= False
447 t
.tick("Duplicate rig: ")
449 #------------------------------------------
450 # Put the rig_name in the armature custom properties
451 obj
.data
["rig_id"] = self
.rig_id
453 self
.script
= rig_ui_template
.ScriptGenerator(self
)
455 #------------------------------------------
456 bpy
.ops
.object.mode_set(mode
='OBJECT')
458 self
.instantiate_rig_tree()
460 t
.tick("Instantiate rigs: ")
462 #------------------------------------------
463 bpy
.ops
.object.mode_set(mode
='OBJECT')
465 self
.invoke_initialize()
467 t
.tick("Initialize rigs: ")
469 #------------------------------------------
470 bpy
.ops
.object.mode_set(mode
='EDIT')
472 self
.invoke_prepare_bones()
474 t
.tick("Prepare bones: ")
476 #------------------------------------------
477 bpy
.ops
.object.mode_set(mode
='OBJECT')
478 bpy
.ops
.object.mode_set(mode
='EDIT')
480 self
.__create
_root
_bone
()
482 self
.invoke_generate_bones()
484 t
.tick("Generate bones: ")
486 #------------------------------------------
487 bpy
.ops
.object.mode_set(mode
='OBJECT')
488 bpy
.ops
.object.mode_set(mode
='EDIT')
490 self
.invoke_parent_bones()
492 self
.__parent
_bones
_to
_root
()
494 t
.tick("Parent bones: ")
496 #------------------------------------------
497 bpy
.ops
.object.mode_set(mode
='OBJECT')
499 self
.invoke_configure_bones()
501 t
.tick("Configure bones: ")
503 #------------------------------------------
504 bpy
.ops
.object.mode_set(mode
='OBJECT')
506 self
.invoke_preapply_bones()
508 t
.tick("Preapply bones: ")
510 #------------------------------------------
511 bpy
.ops
.object.mode_set(mode
='EDIT')
513 self
.invoke_apply_bones()
515 t
.tick("Apply bones: ")
517 #------------------------------------------
518 bpy
.ops
.object.mode_set(mode
='OBJECT')
520 self
.invoke_rig_bones()
522 t
.tick("Rig bones: ")
524 #------------------------------------------
525 bpy
.ops
.object.mode_set(mode
='OBJECT')
527 self
.invoke_generate_widgets()
529 # Generate the default root widget last in case it's rigged with raw_copy
530 create_root_widget(obj
, self
.root_bone
)
532 t
.tick("Generate widgets: ")
534 #------------------------------------------
535 bpy
.ops
.object.mode_set(mode
='OBJECT')
537 self
.__lock
_transforms
()
538 self
.__assign
_layers
()
539 self
.__compute
_visible
_layers
()
540 self
.__restore
_driver
_vars
()
542 t
.tick("Assign layers: ")
544 #------------------------------------------
545 bpy
.ops
.object.mode_set(mode
='OBJECT')
547 self
.invoke_finalize()
551 #------------------------------------------
552 bpy
.ops
.object.mode_set(mode
='OBJECT')
554 self
.__assign
_widgets
()
556 # Create Selection Sets
557 create_selection_sets(obj
, metarig
)
560 create_bone_groups(obj
, metarig
, self
.layer_group_priorities
)
564 #----------------------------------
566 bpy
.ops
.object.mode_set(mode
='OBJECT')
567 obj
.data
.pose_position
= 'POSE'
569 # Restore parent to bones
570 for child
, sub_parent
in childs
.items():
571 if sub_parent
in obj
.pose
.bones
:
572 mat
= child
.matrix_world
.copy()
573 child
.parent_bone
= sub_parent
574 child
.matrix_world
= mat
576 # Clear any transient errors in drivers
577 refresh_all_drivers()
579 #----------------------------------
580 # Execute the finalize script
582 if metarig
.data
.rigify_finalize_script
:
583 bpy
.ops
.object.mode_set(mode
='OBJECT')
584 exec(metarig
.data
.rigify_finalize_script
.as_string(), {})
585 bpy
.ops
.object.mode_set(mode
='OBJECT')
587 #----------------------------------
588 # Restore active collection
589 view_layer
.active_layer_collection
= self
.layer_collection
592 def generate_rig(context
, metarig
):
593 """ Generates a rig from a metarig.
596 # Initial configuration
597 rest_backup
= metarig
.data
.pose_position
598 metarig
.data
.pose_position
= 'REST'
601 generator
= Generator(context
, metarig
)
603 base_generate
.BaseGenerator
.instance
= generator
607 metarig
.data
.pose_position
= rest_backup
609 except Exception as e
:
610 # Cleanup if something goes wrong
611 print("Rigify: failed to generate rig.")
613 bpy
.ops
.object.mode_set(mode
='OBJECT')
614 metarig
.data
.pose_position
= rest_backup
616 # Continue the exception
620 base_generate
.BaseGenerator
.instance
= None
623 def create_selection_set_for_rig_layer(
624 rig
: bpy
.types
.Object
,
628 """Create a single selection set on a rig.
630 The set will contain all bones on the rig layer with the given index.
632 selset
= rig
.selection_sets
.add()
633 selset
.name
= set_name
635 for b
in rig
.pose
.bones
:
636 if not b
.bone
.layers
[layer_idx
] or b
.name
in selset
.bone_ids
:
639 bone_id
= selset
.bone_ids
.add()
640 bone_id
.name
= b
.name
642 def create_selection_sets(obj
, metarig
):
643 """Create selection sets if the Selection Sets addon is enabled.
645 Whether a selection set for a rig layer is created is controlled in the
646 Rigify Layer Names panel.
648 # Check if selection sets addon is installed
649 if 'bone_selection_groups' not in bpy
.context
.preferences
.addons \
650 and 'bone_selection_sets' not in bpy
.context
.preferences
.addons
:
653 obj
.selection_sets
.clear()
655 for i
, name
in enumerate(metarig
.data
.rigify_layers
.keys()):
656 if name
== '' or not metarig
.data
.rigify_layers
[i
].selset
:
659 create_selection_set_for_rig_layer(obj
, name
, i
)
662 def create_bone_groups(obj
, metarig
, priorities
={}):
664 bpy
.ops
.object.mode_set(mode
='OBJECT')
666 layers
= metarig
.data
.rigify_layers
667 groups
= metarig
.data
.rigify_colors
675 name
= groups
[g_id
].name
676 if name
not in obj
.pose
.bone_groups
.keys():
677 bg
= obj
.pose
.bone_groups
.new(name
=name
)
678 bg
.color_set
= 'CUSTOM'
679 bg
.colors
.normal
= gamma_correct(groups
[g_id
].normal
)
680 bg
.colors
.select
= gamma_correct(groups
[g_id
].select
)
681 bg
.colors
.active
= gamma_correct(groups
[g_id
].active
)
685 prios
= priorities
.get(b
.name
, dummy
)
686 enabled
= [ i
for i
, v
in enumerate(b
.bone
.layers
) if v
]
687 layer_index
= max(enabled
, key
=lambda i
: prios
.get(i
, 0))
690 if layer_index
> len(layers
) - 1: # bone is on reserved layers
692 g_id
= layers
[layer_index
].group
- 1
694 name
= groups
[g_id
].name
695 b
.bone_group
= obj
.pose
.bone_groups
[name
]
698 def get_xy_spread(bones
):
702 x_max
= max((x_max
, abs(b
.head
[0]), abs(b
.tail
[0])))
703 y_max
= max((y_max
, abs(b
.head
[1]), abs(b
.tail
[1])))
705 return max((x_max
, y_max
))
708 def param_matches_type(param_name
, rig_type
):
709 """ Returns True if the parameter name is consistent with the rig type.
711 if param_name
.rsplit(".", 1)[0] == rig_type
:
717 def param_name(param_name
, rig_type
):
718 """ Get the actual parameter name, sans-rig-type.
720 return param_name
[len(rig_type
) + 1:]