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 ========================
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
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.
48 self
.timez
= time
.time()
50 def tick(self
, string
):
52 print(string
+ "%.3f" % (t
- self
.timez
))
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.
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')
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.
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"
96 obj
= scene
.objects
[name
]
98 obj
.name
= rig_new_name
or 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
)
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)
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
:
129 bpy
.ops
.object.delete(use_global
=False)
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
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
]
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"):
202 setattr(bone_gen
.rigify_parameters
, prop
, \
203 getattr(bone
.rigify_parameters
, prop
))
204 except AttributeError:
205 print("FAILED TO COPY PARAMETER: " + str(prop
))
208 for prop
in bone
.keys():
210 bone_gen
[prop
] = bone
[prop
]
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
:
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
)
238 for m1
in d1
.modifiers
:
239 m2
= d2
.modifiers
.new(type=m1
.type)
240 copy_attributes(m1
, m2
)
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
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
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)
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
)
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 #----------------------------------
326 # Collect/initialize all the 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.
336 # Go into editmode in the rig armature
337 bpy
.ops
.object.mode_set(mode
='OBJECT')
338 context
.view_layer
.objects
.active
= obj
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
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
)
375 # if 'IK_follow' in pbones[bone].keys():
376 # noparent_bones += [bone]
377 for d
in ik_follow_drivers
:
379 noparent_bones
+= [bone
]
381 bpy
.ops
.object.mode_set(mode
='EDIT')
383 if bone
in noparent_bones
:
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]-")
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.
404 if obj
.data
.bones
[bone
].name
.startswith(DEF_PREFIX
):
405 obj
.data
.bones
[bone
].use_deform
= True
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:]
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.
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.
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.
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
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)]
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
466 for l
in metarig
.data
.rigify_layers
:
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'
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
]
480 script
= bpy
.data
.texts
.new("rig_ui.py")
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
)
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
497 exec(script
.as_string(), {})
499 # Create Selection Sets
500 create_selection_sets(obj
, metarig
)
503 create_bone_groups(obj
, metarig
)
505 # Add rig_ui to logic
506 create_persistent_rig_ui(obj
, script
)
509 #----------------------------------
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
:
534 bpy
.ops
.object.mode_set(mode
='POSE')
536 bpy
.context
.view_layer
.objects
.active
= obj
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
:
545 bpy
.ops
.pose
.select_all(action
='DESELECT')
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')
567 layers
= metarig
.data
.rigify_layers
568 groups
= metarig
.data
.rigify_colors
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
)
585 layer_index
= b
.bone
.layers
[:].index(True)
588 if layer_index
> len(layers
) - 1: # bone is on reserved layers
590 g_id
= layers
[layer_index
].group
- 1
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"""
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
:
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.
625 rig_type
= obj
.pose
.bones
[bone_name
].rigify_type
626 rig_type
= rig_type
.replace(" ", "")
632 params
= obj
.pose
.bones
[bone_name
].rigify_parameters
636 rig
= get_rig_type(rig_type
).Rig(obj
, bone_name
, params
)
638 message
= "Rig Type Missing: python module for type '%s' not found (bone: %s)" % (rig_type
, bone_name
)
640 raise MetarigError(message
)
643 print('print_exc():')
644 traceback
.print_exc(file=sys
.stdout
)
650 def get_xy_spread(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
:
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:]