Update for changes in Blender's API
[blender-addons.git] / rigify / generate.py
blobc804cb69319f6c529996acfc072a01c0dd5ac4e1
1 #====================== BEGIN GPL LICENSE BLOCK ======================
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 #======================= END GPL LICENSE BLOCK ========================
19 # <pep8 compliant>
21 import bpy
22 import re
23 import time
24 import traceback
25 import sys
26 from rna_prop_ui import rna_idprop_ui_prop_get
28 from .utils import MetarigError, new_bone, get_rig_type
29 from .utils import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, WGT_PREFIX, ROOT_NAME, make_original_name
30 from .utils import RIG_DIR
31 from .utils import create_root_widget
32 from .utils import ensure_widget_collection
33 from .utils import random_id
34 from .utils import copy_attributes
35 from .utils import gamma_correct
36 from .rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER
39 RIG_MODULE = "rigs"
40 ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to.
41 MCH_LAYER = [n == 30 for n in range(0, 32)] # Armature layer that mechanism bones should be moved to.
42 DEF_LAYER = [n == 29 for n in range(0, 32)] # Armature layer that deformation bones should be moved to.
43 ROOT_LAYER = [n == 28 for n in range(0, 32)] # Armature layer that root bone should be moved to.
46 class Timer:
47 def __init__(self):
48 self.timez = time.time()
50 def tick(self, string):
51 t = time.time()
52 print(string + "%.3f" % (t - self.timez))
53 self.timez = t
56 # TODO: generalize to take a group as input instead of an armature.
57 def generate_rig(context, metarig):
58 """ Generates a rig from a metarig.
60 """
61 t = Timer()
63 # Random string with time appended so that
64 # different rigs don't collide id's
65 rig_id = random_id(16)
67 # Initial configuration
68 # mode_orig = context.mode # UNUSED
69 rest_backup = metarig.data.pose_position
70 metarig.data.pose_position = 'REST'
72 bpy.ops.object.mode_set(mode='OBJECT')
74 scene = context.scene
75 view_layer = context.view_layer
76 collection = context.collection
77 layer_collection = context.layer_collection
78 id_store = context.window_manager
80 #------------------------------------------
81 # Create/find the rig object and set it up
83 # Check if the generated rig already exists, so we can
84 # regenerate in the same object. If not, create a new
85 # object to generate the rig in.
86 print("Fetch rig.")
88 rig_new_name = ""
89 rig_old_name = ""
90 if id_store.rigify_rig_basename:
91 rig_new_name = id_store.rigify_rig_basename + "_rig"
93 if id_store.rigify_generate_mode == 'overwrite':
94 name = id_store.rigify_target_rig or "rig"
95 try:
96 obj = scene.objects[name]
97 rig_old_name = name
98 obj.name = rig_new_name or name
99 except KeyError:
100 rig_old_name = name
101 name = rig_new_name or name
102 obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
103 obj.display_type = 'WIRE'
104 collection.objects.link(obj)
105 else:
106 name = rig_new_name or "rig"
107 obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) # in case name 'rig' exists it will be rig.001
108 obj.display_type = 'WIRE'
109 collection.objects.link(obj)
111 id_store.rigify_target_rig = obj.name
112 obj.data.pose_position = 'POSE'
114 # Get rid of anim data in case the rig already existed
115 print("Clear rig animation data.")
116 obj.animation_data_clear()
118 # Select generated rig object
119 metarig.select_set(False)
120 obj.select_set(True)
121 view_layer.objects.active = obj
123 # Remove wgts if force update is set
124 wgts_group_name = "WGTS_" + (rig_old_name or obj.name)
125 if wgts_group_name in scene.objects and id_store.rigify_force_widget_update:
126 bpy.ops.object.select_all(action='DESELECT')
127 for wgt in bpy.data.objects[wgts_group_name].children:
128 wgt.select_set(True)
129 bpy.ops.object.delete(use_global=False)
130 if rig_old_name:
131 bpy.data.objects[wgts_group_name].name = "WGTS_" + obj.name
133 wgts_group_name = "WGTS_" + obj.name
135 # Get parented objects to restore later
136 childs = {} # {object: bone}
137 for child in obj.children:
138 childs[child] = child.parent_bone
140 # Remove all bones from the generated rig armature.
141 bpy.ops.object.mode_set(mode='EDIT')
142 for bone in obj.data.edit_bones:
143 obj.data.edit_bones.remove(bone)
144 bpy.ops.object.mode_set(mode='OBJECT')
146 # Create temporary duplicates for merging
147 temp_rig_1 = metarig.copy()
148 temp_rig_1.data = metarig.data.copy()
149 collection.objects.link(temp_rig_1)
151 temp_rig_2 = metarig.copy()
152 temp_rig_2.data = obj.data
153 collection.objects.link(temp_rig_2)
155 # Select the temp rigs for merging
156 for objt in scene.objects:
157 objt.select_set(False) # deselect all objects
158 temp_rig_1.select_set(True)
159 temp_rig_2.select_set(True)
160 view_layer.objects.active = temp_rig_2
162 # Merge the temporary rigs
163 bpy.ops.object.join()
165 # Delete the second temp rig
166 bpy.ops.object.delete()
168 # Select the generated rig
169 for objt in scene.objects:
170 objt.select_set(False) # deselect all objects
171 obj.select_set(True)
172 view_layer.objects.active = obj
174 # Copy over bone properties
175 for bone in metarig.data.bones:
176 bone_gen = obj.data.bones[bone.name]
178 # B-bone stuff
179 bone_gen.bbone_segments = bone.bbone_segments
180 bone_gen.bbone_easein = bone.bbone_easein
181 bone_gen.bbone_easeout = bone.bbone_easeout
183 # Copy over the pose_bone properties
184 for bone in metarig.pose.bones:
185 bone_gen = obj.pose.bones[bone.name]
187 # Rotation mode and transform locks
188 bone_gen.rotation_mode = bone.rotation_mode
189 bone_gen.lock_rotation = tuple(bone.lock_rotation)
190 bone_gen.lock_rotation_w = bone.lock_rotation_w
191 bone_gen.lock_rotations_4d = bone.lock_rotations_4d
192 bone_gen.lock_location = tuple(bone.lock_location)
193 bone_gen.lock_scale = tuple(bone.lock_scale)
195 # rigify_type and rigify_parameters
196 bone_gen.rigify_type = bone.rigify_type
197 for prop in dir(bone_gen.rigify_parameters):
198 if (not prop.startswith("_")) \
199 and (not prop.startswith("bl_")) \
200 and (prop != "rna_type"):
201 try:
202 setattr(bone_gen.rigify_parameters, prop, \
203 getattr(bone.rigify_parameters, prop))
204 except AttributeError:
205 print("FAILED TO COPY PARAMETER: " + str(prop))
207 # Custom properties
208 for prop in bone.keys():
209 try:
210 bone_gen[prop] = bone[prop]
211 except KeyError:
212 pass
214 # Constraints
215 for con1 in bone.constraints:
216 con2 = bone_gen.constraints.new(type=con1.type)
217 copy_attributes(con1, con2)
219 # Set metarig target to rig target
220 if "target" in dir(con2):
221 if con2.target == metarig:
222 con2.target = obj
224 # Copy drivers
225 if metarig.animation_data:
226 for d1 in metarig.animation_data.drivers:
227 d2 = obj.driver_add(d1.data_path)
228 copy_attributes(d1, d2)
229 copy_attributes(d1.driver, d2.driver)
231 # Remove default modifiers, variables, etc.
232 for m in d2.modifiers:
233 d2.modifiers.remove(m)
234 for v in d2.driver.variables:
235 d2.driver.variables.remove(v)
237 # Copy modifiers
238 for m1 in d1.modifiers:
239 m2 = d2.modifiers.new(type=m1.type)
240 copy_attributes(m1, m2)
242 # Copy variables
243 for v1 in d1.driver.variables:
244 v2 = d2.driver.variables.new()
245 copy_attributes(v1, v2)
246 for i in range(len(v1.targets)):
247 copy_attributes(v1.targets[i], v2.targets[i])
248 # Switch metarig targets to rig targets
249 if v2.targets[i].id == metarig:
250 v2.targets[i].id = obj
252 # Mark targets that may need to be altered after rig generation
253 tar = v2.targets[i]
254 # If a custom property
255 if v2.type == 'SINGLE_PROP' \
256 and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path):
257 tar.data_path = "RIGIFY-" + tar.data_path
259 # Copy key frames
260 for i in range(len(d1.keyframe_points)):
261 d2.keyframe_points.add()
262 k1 = d1.keyframe_points[i]
263 k2 = d2.keyframe_points[i]
264 copy_attributes(k1, k2)
266 t.tick("Duplicate rig: ")
267 #----------------------------------
268 # Make a list of the original bones so we can keep track of them.
269 original_bones = [bone.name for bone in obj.data.bones]
271 # Add the ORG_PREFIX to the original bones.
272 bpy.ops.object.mode_set(mode='OBJECT')
273 for i in range(0, len(original_bones)):
274 obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i])
275 original_bones[i] = make_original_name(original_bones[i])
277 # Create a sorted list of the original bones, sorted in the order we're
278 # going to traverse them for rigging.
279 # (root-most -> leaf-most, alphabetical)
280 bones_sorted = []
281 for name in original_bones:
282 bones_sorted += [name]
283 bones_sorted.sort() # first sort by names
284 bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive)) # then parents before children
286 t.tick("Make list of org bones: ")
287 #----------------------------------
288 # Create the root bone.
289 bpy.ops.object.mode_set(mode='EDIT')
290 root_bone = new_bone(obj, ROOT_NAME)
291 spread = get_xy_spread(metarig.data.bones) or metarig.data.bones[0].length
292 spread = float('%.3g' % spread)
293 scale = spread/0.589
294 obj.data.edit_bones[root_bone].head = (0, 0, 0)
295 obj.data.edit_bones[root_bone].tail = (0, scale, 0)
296 obj.data.edit_bones[root_bone].roll = 0
297 bpy.ops.object.mode_set(mode='OBJECT')
298 obj.data.bones[root_bone].layers = ROOT_LAYER
300 # Put the rig_name in the armature custom properties
301 rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
302 obj.data["rig_id"] = rig_id
304 # Create/find widge collection
305 widget_collection = ensure_widget_collection(context)
307 # Create Group widget
308 # wgts_group_name = "WGTS"
309 if wgts_group_name not in scene.objects:
310 if wgts_group_name in bpy.data.objects:
311 bpy.data.objects[wgts_group_name].user_clear()
312 bpy.data.objects.remove(bpy.data.objects[wgts_group_name])
313 mesh = bpy.data.meshes.new(wgts_group_name)
314 wgts_obj = bpy.data.objects.new(wgts_group_name, mesh)
315 widget_collection.objects.link(wgts_obj)
316 t.tick("Create main WGTS: ")
318 # if id_store.rigify_generate_mode == 'new':
319 # bpy.ops.object.select_all(action='DESELECT')
320 # for wgt in bpy.data.objects[wgts_group_name].children:
321 # wgt.select_set(True)
322 # bpy.ops.object.make_single_user(obdata=True)
324 #----------------------------------
325 try:
326 # Collect/initialize all the rigs.
327 rigs = []
328 for bone in bones_sorted:
329 bpy.ops.object.mode_set(mode='EDIT')
330 rigs += get_bone_rigs(obj, bone)
331 t.tick("Initialize rigs: ")
333 # Generate all the rigs.
334 ui_scripts = []
335 for rig in rigs:
336 # Go into editmode in the rig armature
337 bpy.ops.object.mode_set(mode='OBJECT')
338 context.view_layer.objects.active = obj
339 obj.select_set(True)
340 bpy.ops.object.mode_set(mode='EDIT')
341 scripts = rig.generate()
342 if scripts is not None:
343 ui_scripts += [scripts[0]]
344 t.tick("Generate rigs: ")
345 except Exception as e:
346 # Cleanup if something goes wrong
347 print("Rigify: failed to generate rig.")
348 metarig.data.pose_position = rest_backup
349 obj.data.pose_position = 'POSE'
350 bpy.ops.object.mode_set(mode='OBJECT')
352 # Continue the exception
353 raise e
355 #----------------------------------
356 bpy.ops.object.mode_set(mode='OBJECT')
358 # Get a list of all the bones in the armature
359 bones = [bone.name for bone in obj.data.bones]
361 # Parent any free-floating bones to the root excluding bones with child of constraint.
362 pbones = obj.pose.bones
365 ik_follow_drivers = []
367 if obj.animation_data:
368 for drv in obj.animation_data.drivers:
369 for var in drv.driver.variables:
370 if 'IK_follow' == var.name:
371 ik_follow_drivers.append(drv.data_path)
373 noparent_bones = []
374 for bone in bones:
375 # if 'IK_follow' in pbones[bone].keys():
376 # noparent_bones += [bone]
377 for d in ik_follow_drivers:
378 if bone in d:
379 noparent_bones += [bone]
381 bpy.ops.object.mode_set(mode='EDIT')
382 for bone in bones:
383 if bone in noparent_bones:
384 continue
385 elif obj.data.edit_bones[bone].parent is None:
386 obj.data.edit_bones[bone].use_connect = False
387 obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
389 bpy.ops.object.mode_set(mode='OBJECT')
391 # Lock transforms on all non-control bones
392 r = re.compile("[A-Z][A-Z][A-Z]-")
393 for bone in bones:
394 if r.match(bone):
395 pb = obj.pose.bones[bone]
396 pb.lock_location = (True, True, True)
397 pb.lock_rotation = (True, True, True)
398 pb.lock_rotation_w = True
399 pb.lock_scale = (True, True, True)
401 # Every bone that has a name starting with "DEF-" make deforming. All the
402 # others make non-deforming.
403 for bone in bones:
404 if obj.data.bones[bone].name.startswith(DEF_PREFIX):
405 obj.data.bones[bone].use_deform = True
406 else:
407 obj.data.bones[bone].use_deform = False
409 # Alter marked driver targets
410 if obj.animation_data:
411 for d in obj.animation_data.drivers:
412 for v in d.driver.variables:
413 for tar in v.targets:
414 if tar.data_path.startswith("RIGIFY-"):
415 temp, bone, prop = tuple([x.strip('"]') for x in tar.data_path.split('["')])
416 if bone in obj.data.bones \
417 and prop in obj.pose.bones[bone].keys():
418 tar.data_path = tar.data_path[7:]
419 else:
420 tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop)
422 # Move all the original bones to their layer.
423 for bone in original_bones:
424 obj.data.bones[bone].layers = ORG_LAYER
426 # Move all the bones with names starting with "MCH-" to their layer.
427 for bone in bones:
428 if obj.data.bones[bone].name.startswith(MCH_PREFIX):
429 obj.data.bones[bone].layers = MCH_LAYER
431 # Move all the bones with names starting with "DEF-" to their layer.
432 for bone in bones:
433 if obj.data.bones[bone].name.startswith(DEF_PREFIX):
434 obj.data.bones[bone].layers = DEF_LAYER
436 # Create root bone widget
437 create_root_widget(obj, "root")
439 # Assign shapes to bones
440 # Object's with name WGT-<bone_name> get used as that bone's shape.
441 for bone in bones:
442 wgt_name = (WGT_PREFIX + obj.name + '_' + obj.data.bones[bone].name)[:63] # Object names are limited to 63 characters... arg
443 if wgt_name in context.scene.objects:
444 # Weird temp thing because it won't let me index by object name
445 for ob in context.scene.objects:
446 if ob.name == wgt_name:
447 obj.pose.bones[bone].custom_shape = ob
448 break
449 # This is what it should do:
450 # obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name]
451 # Reveal all the layers with control bones on them
452 vis_layers = [False for n in range(0, 32)]
453 for bone in bones:
454 for i in range(0, 32):
455 vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i]
456 for i in range(0, 32):
457 vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i])
458 obj.data.layers = vis_layers
460 # Ensure the collection of layer names exists
461 for i in range(1 + len(metarig.data.rigify_layers), 29):
462 metarig.data.rigify_layers.add()
464 # Create list of layer name/row pairs
465 layer_layout = []
466 for l in metarig.data.rigify_layers:
467 print(l.name)
468 layer_layout += [(l.name, l.row)]
470 # Generate the UI script
471 if id_store.rigify_generate_mode == 'overwrite':
472 rig_ui_name = id_store.rigify_rig_ui or 'rig_ui.py'
473 else:
474 rig_ui_name = 'rig_ui.py'
476 if id_store.rigify_generate_mode == 'overwrite' and rig_ui_name in bpy.data.texts.keys():
477 script = bpy.data.texts[rig_ui_name]
478 script.clear()
479 else:
480 script = bpy.data.texts.new("rig_ui.py")
482 rig_ui_old_name = ""
483 if id_store.rigify_rig_basename:
484 rig_ui_old_name = script.name
485 script.name = id_store.rigify_rig_basename + "_rig_ui.py"
487 id_store.rigify_rig_ui = script.name
489 script.write(UI_SLIDERS % rig_id)
490 for s in ui_scripts:
491 script.write("\n " + s.replace("\n", "\n ") + "\n")
492 script.write(layers_ui(vis_layers, layer_layout))
493 script.write(UI_REGISTER)
494 script.use_module = True
496 # Run UI script
497 exec(script.as_string(), {})
499 # Create Selection Sets
500 create_selection_sets(obj, metarig)
502 # Create Bone Groups
503 create_bone_groups(obj, metarig)
505 # Add rig_ui to logic
506 create_persistent_rig_ui(obj, script)
508 t.tick("The rest: ")
509 #----------------------------------
510 # Deconfigure
511 bpy.ops.object.mode_set(mode='OBJECT')
512 metarig.data.pose_position = rest_backup
513 obj.data.pose_position = 'POSE'
515 # Restore parent to bones
516 for child, sub_parent in childs.items():
517 if sub_parent in obj.pose.bones:
518 mat = child.matrix_world.copy()
519 child.parent_bone = sub_parent
520 child.matrix_world = mat
522 #----------------------------------
523 # Restore active collection
524 view_layer.active_layer_collection = layer_collection
527 def create_selection_sets(obj, metarig):
529 # Check if selection sets addon is installed
530 if 'bone_selection_groups' not in bpy.context.user_preferences.addons \
531 and 'bone_selection_sets' not in bpy.context.user_preferences.addons:
532 return
534 bpy.ops.object.mode_set(mode='POSE')
536 bpy.context.view_layer.objects.active = obj
537 obj.select_set(True)
538 metarig.select_set(False)
539 pbones = obj.pose.bones
541 for i, name in enumerate(metarig.data.rigify_layers.keys()):
542 if name == '' or not metarig.data.rigify_layers[i].selset:
543 continue
545 bpy.ops.pose.select_all(action='DESELECT')
546 for b in pbones:
547 if b.bone.layers[i]:
548 b.bone.select = True
550 #bpy.ops.pose.selection_set_add()
551 obj.selection_sets.add()
552 obj.selection_sets[-1].name = name
553 if 'bone_selection_sets' in bpy.context.user_preferences.addons:
554 act_sel_set = obj.selection_sets[-1]
556 # iterate only the selected bones in current pose that are not hidden
557 for bone in bpy.context.selected_pose_bones:
558 if bone.name not in act_sel_set.bone_ids:
559 bone_id = act_sel_set.bone_ids.add()
560 bone_id.name = bone.name
563 def create_bone_groups(obj, metarig):
565 bpy.ops.object.mode_set(mode='OBJECT')
566 pb = obj.pose.bones
567 layers = metarig.data.rigify_layers
568 groups = metarig.data.rigify_colors
570 # Create BGs
571 for l in layers:
572 if l.group == 0:
573 continue
574 g_id = l.group - 1
575 name = groups[g_id].name
576 if name not in obj.pose.bone_groups.keys():
577 bg = obj.pose.bone_groups.new(name=name)
578 bg.color_set = 'CUSTOM'
579 bg.colors.normal = gamma_correct(groups[g_id].normal)
580 bg.colors.select = gamma_correct(groups[g_id].select)
581 bg.colors.active = gamma_correct(groups[g_id].active)
583 for b in pb:
584 try:
585 layer_index = b.bone.layers[:].index(True)
586 except ValueError:
587 continue
588 if layer_index > len(layers) - 1: # bone is on reserved layers
589 continue
590 g_id = layers[layer_index].group - 1
591 if g_id >= 0:
592 name = groups[g_id].name
593 b.bone_group = obj.pose.bone_groups[name]
596 def create_persistent_rig_ui(obj, script):
597 """Make sure the ui script always follows the rig around"""
598 skip = False
599 driver = None
601 for fcurve in obj.animation_data.drivers:
602 if fcurve.data_path == 'pass_index':
603 driver = fcurve.driver
604 for variable in driver.variables:
605 if variable.name == script.name:
606 skip = True
607 break
608 break
610 if not skip:
611 if not driver:
612 fcurve = obj.driver_add("pass_index")
613 driver = fcurve.driver
615 variable = driver.variables.new()
616 variable.name = script.name
617 variable.targets[0].id_type = 'TEXT'
618 variable.targets[0].id = script
621 def get_bone_rigs(obj, bone_name, halt_on_missing=False):
622 """ Fetch all the rigs specified on a bone.
624 rigs = []
625 rig_type = obj.pose.bones[bone_name].rigify_type
626 rig_type = rig_type.replace(" ", "")
628 if rig_type == "":
629 pass
630 else:
631 # Gather parameters
632 params = obj.pose.bones[bone_name].rigify_parameters
634 # Get the rig
635 try:
636 rig = get_rig_type(rig_type).Rig(obj, bone_name, params)
637 except ImportError:
638 message = "Rig Type Missing: python module for type '%s' not found (bone: %s)" % (rig_type, bone_name)
639 if halt_on_missing:
640 raise MetarigError(message)
641 else:
642 print(message)
643 print('print_exc():')
644 traceback.print_exc(file=sys.stdout)
645 else:
646 rigs += [rig]
647 return rigs
650 def get_xy_spread(bones):
651 x_max = 0
652 y_max = 0
653 for b in bones:
654 x_max = max((x_max, abs(b.head[0]), abs(b.tail[0])))
655 y_max = max((y_max, abs(b.head[1]), abs(b.tail[1])))
657 return max((x_max, y_max))
660 def param_matches_type(param_name, rig_type):
661 """ Returns True if the parameter name is consistent with the rig type.
663 if param_name.rsplit(".", 1)[0] == rig_type:
664 return True
665 else:
666 return False
669 def param_name(param_name, rig_type):
670 """ Get the actual parameter name, sans-rig-type.
672 return param_name[len(rig_type) + 1:]