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
27
from rigify.utils import MetarigError, new_bone, get_rig_type
28
from rigify.utils import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, WGT_PREFIX, ROOT_NAME, make_original_name
29
from rigify.utils import RIG_DIR
30
from rigify.utils import create_root_widget
31
from rigify.utils import random_id
32
from rigify.utils import copy_attributes
33
from rigify.rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER
34
from rigify import rigs
37
ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to.
38
MCH_LAYER = [n == 30 for n in range(0, 32)] # Armature layer that mechanism bones should be moved to.
39
DEF_LAYER = [n == 29 for n in range(0, 32)] # Armature layer that deformation bones should be moved to.
40
ROOT_LAYER = [n == 28 for n in range(0, 32)] # Armature layer that root bone should be moved to.
45
self.timez = time.time()
47
def tick(self, string):
49
print(string + "%.3f" % (t - self.timez))
53
# TODO: generalize to take a group as input instead of an armature.
54
def generate_rig(context, metarig):
55
""" Generates a rig from a metarig.
60
# Random string with time appended so that
61
# different rigs don't collide id's
62
rig_id = random_id(16)
64
# Initial configuration
65
# mode_orig = context.mode # UNUSED
66
rest_backup = metarig.data.pose_position
67
metarig.data.pose_position = 'REST'
69
bpy.ops.object.mode_set(mode='OBJECT')
73
#------------------------------------------
74
# Create/find the rig object and set it up
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.
81
name = metarig["rig_object_name"]
86
obj = scene.objects[name]
88
obj = bpy.data.objects.new(name, bpy.data.armatures.new(name))
89
obj.draw_type = 'WIRE'
90
scene.objects.link(obj)
92
obj.data.pose_position = 'POSE'
94
# Get rid of anim data in case the rig already existed
95
print("Clear rig animation data.")
96
obj.animation_data_clear()
98
# Select generated rig object
99
metarig.select = False
101
scene.objects.active = obj
103
# Remove all bones from the generated rig armature.
104
bpy.ops.object.mode_set(mode='EDIT')
105
for bone in obj.data.edit_bones:
106
obj.data.edit_bones.remove(bone)
107
bpy.ops.object.mode_set(mode='OBJECT')
109
# Create temporary duplicates for merging
110
temp_rig_1 = metarig.copy()
111
temp_rig_1.data = metarig.data.copy()
112
scene.objects.link(temp_rig_1)
114
temp_rig_2 = metarig.copy()
115
temp_rig_2.data = obj.data
116
scene.objects.link(temp_rig_2)
118
# Select the temp rigs for merging
119
for objt in scene.objects:
120
objt.select = False # deselect all objects
121
temp_rig_1.select = True
122
temp_rig_2.select = True
123
scene.objects.active = temp_rig_2
125
# Merge the temporary rigs
126
bpy.ops.object.join()
128
# Delete the second temp rig
129
bpy.ops.object.delete()
131
# Select the generated rig
132
for objt in scene.objects:
133
objt.select = False # deselect all objects
135
scene.objects.active = obj
137
# Copy over bone properties
138
for bone in metarig.data.bones:
139
bone_gen = obj.data.bones[bone.name]
142
bone_gen.bbone_segments = bone.bbone_segments
143
bone_gen.bbone_in = bone.bbone_in
144
bone_gen.bbone_out = bone.bbone_out
146
# Copy over the pose_bone properties
147
for bone in metarig.pose.bones:
148
bone_gen = obj.pose.bones[bone.name]
150
# Rotation mode and transform locks
151
bone_gen.rotation_mode = bone.rotation_mode
152
bone_gen.lock_rotation = tuple(bone.lock_rotation)
153
bone_gen.lock_rotation_w = bone.lock_rotation_w
154
bone_gen.lock_rotations_4d = bone.lock_rotations_4d
155
bone_gen.lock_location = tuple(bone.lock_location)
156
bone_gen.lock_scale = tuple(bone.lock_scale)
158
# rigify_type and rigify_parameters
159
bone_gen.rigify_type = bone.rigify_type
160
if len(bone.rigify_parameters) > 0:
161
bone_gen.rigify_parameters.add()
162
for prop in dir(bone_gen.rigify_parameters[0]):
163
if (not prop.startswith("_")) \
164
and (not prop.startswith("bl_")) \
165
and (prop != "rna_type"):
167
setattr(bone_gen.rigify_parameters[0], prop, \
168
getattr(bone.rigify_parameters[0], prop))
169
except AttributeError:
170
print("FAILED TO COPY PARAMETER: " + str(prop))
173
for prop in bone.keys():
175
bone_gen[prop] = bone[prop]
180
for con1 in bone.constraints:
181
con2 = bone_gen.constraints.new(type=con1.type)
182
copy_attributes(con1, con2)
184
# Set metarig target to rig target
185
if "target" in dir(con2):
186
if con2.target == metarig:
190
if metarig.animation_data:
191
for d1 in metarig.animation_data.drivers:
192
d2 = obj.driver_add(d1.data_path)
193
copy_attributes(d1, d2)
194
copy_attributes(d1.driver, d2.driver)
196
# Remove default modifiers, variables, etc.
197
for m in d2.modifiers:
198
d2.modifiers.remove(m)
199
for v in d2.driver.variables:
200
d2.driver.variables.remove(v)
203
for m1 in d1.modifiers:
204
m2 = d2.modifiers.new(type=m1.type)
205
copy_attributes(m1, m2)
208
for v1 in d1.driver.variables:
209
v2 = d2.driver.variables.new()
210
copy_attributes(v1, v2)
211
for i in range(len(v1.targets)):
212
copy_attributes(v1.targets[i], v2.targets[i])
213
# Switch metarig targets to rig targets
214
if v2.targets[i].id == metarig:
215
v2.targets[i].id = obj
217
# Mark targets that may need to be altered after rig generation
219
# If a custom property
220
if v2.type == 'SINGLE_PROP' \
221
and re.match('^pose.bones\["[^"\]]*"\]\["[^"\]]*"\]$', tar.data_path):
222
tar.data_path = "RIGIFY-" + tar.data_path
225
for i in range(len(d1.keyframe_points)):
226
d2.keyframe_points.add()
227
k1 = d1.keyframe_points[i]
228
k2 = d2.keyframe_points[i]
229
copy_attributes(k1, k2)
231
t.tick("Duplicate rig: ")
232
#----------------------------------
233
# Make a list of the original bones so we can keep track of them.
234
original_bones = [bone.name for bone in obj.data.bones]
236
# Add the ORG_PREFIX to the original bones.
237
bpy.ops.object.mode_set(mode='OBJECT')
238
for i in range(0, len(original_bones)):
239
obj.data.bones[original_bones[i]].name = make_original_name(original_bones[i])
240
original_bones[i] = make_original_name(original_bones[i])
242
# Create a sorted list of the original bones, sorted in the order we're
243
# going to traverse them for rigging.
244
# (root-most -> leaf-most, alphabetical)
246
for name in original_bones:
247
bones_sorted += [name]
248
bones_sorted.sort() # first sort by names
249
bones_sorted.sort(key=lambda bone: len(obj.pose.bones[bone].parent_recursive)) # then parents before children
251
t.tick("Make list of org bones: ")
252
#----------------------------------
253
# Create the root bone.
254
bpy.ops.object.mode_set(mode='EDIT')
255
root_bone = new_bone(obj, ROOT_NAME)
256
obj.data.edit_bones[root_bone].head = (0, 0, 0)
257
obj.data.edit_bones[root_bone].tail = (0, 1, 0)
258
obj.data.edit_bones[root_bone].roll = 0
259
bpy.ops.object.mode_set(mode='OBJECT')
260
obj.data.bones[root_bone].layers = ROOT_LAYER
261
# Put the rig_name in the armature custom properties
262
rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
263
obj.data["rig_id"] = rig_id
265
t.tick("Create root bone: ")
266
#----------------------------------
268
# Collect/initialize all the rigs.
270
deformation_rigs = []
271
for bone in bones_sorted:
272
bpy.ops.object.mode_set(mode='EDIT')
273
rigs += get_bone_rigs(obj, bone)
274
t.tick("Initialize rigs: ")
276
# Generate all the rigs.
279
# Go into editmode in the rig armature
280
bpy.ops.object.mode_set(mode='OBJECT')
281
context.scene.objects.active = obj
283
bpy.ops.object.mode_set(mode='EDIT')
284
scripts = rig.generate()
286
ui_scripts += [scripts[0]]
287
t.tick("Generate rigs: ")
288
except Exception as e:
289
# Cleanup if something goes wrong
290
print("Rigify: failed to generate rig.")
291
metarig.data.pose_position = rest_backup
292
obj.data.pose_position = 'POSE'
293
bpy.ops.object.mode_set(mode='OBJECT')
295
# Continue the exception
298
#----------------------------------
299
bpy.ops.object.mode_set(mode='OBJECT')
301
# Get a list of all the bones in the armature
302
bones = [bone.name for bone in obj.data.bones]
304
# Parent any free-floating bones to the root.
305
bpy.ops.object.mode_set(mode='EDIT')
307
if obj.data.edit_bones[bone].parent is None:
308
obj.data.edit_bones[bone].use_connect = False
309
obj.data.edit_bones[bone].parent = obj.data.edit_bones[root_bone]
310
bpy.ops.object.mode_set(mode='OBJECT')
312
# Every bone that has a name starting with "DEF-" make deforming. All the
313
# others make non-deforming.
315
if obj.data.bones[bone].name.startswith(DEF_PREFIX):
316
obj.data.bones[bone].use_deform = True
318
obj.data.bones[bone].use_deform = False
320
# Alter marked driver targets
321
if obj.animation_data:
322
for d in obj.animation_data.drivers:
323
for v in d.driver.variables:
324
for tar in v.targets:
325
if tar.data_path.startswith("RIGIFY-"):
326
temp, bone, prop = tuple([x.strip('"]') for x in tar.data_path.split('["')])
327
if bone in obj.data.bones \
328
and prop in obj.pose.bones[bone].keys():
329
tar.data_path = tar.data_path[7:]
331
tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop)
333
# Move all the original bones to their layer.
334
for bone in original_bones:
335
obj.data.bones[bone].layers = ORG_LAYER
337
# Move all the bones with names starting with "MCH-" to their layer.
339
if obj.data.bones[bone].name.startswith(MCH_PREFIX):
340
obj.data.bones[bone].layers = MCH_LAYER
342
# Move all the bones with names starting with "DEF-" to their layer.
344
if obj.data.bones[bone].name.startswith(DEF_PREFIX):
345
obj.data.bones[bone].layers = DEF_LAYER
347
# Create root bone widget
348
create_root_widget(obj, "root")
350
# Assign shapes to bones
351
# Object's with name WGT-<bone_name> get used as that bone's shape.
353
wgt_name = (WGT_PREFIX + obj.data.bones[bone].name)[:21] # Object names are limited to 21 characters... arg
354
if wgt_name in context.scene.objects:
355
# Weird temp thing because it won't let me index by object name
356
for ob in context.scene.objects:
357
if ob.name == wgt_name:
358
obj.pose.bones[bone].custom_shape = ob
360
# This is what it should do:
361
# obj.pose.bones[bone].custom_shape = context.scene.objects[wgt_name]
362
# Reveal all the layers with control bones on them
363
vis_layers = [False for n in range(0, 32)]
365
for i in range(0, 32):
366
vis_layers[i] = vis_layers[i] or obj.data.bones[bone].layers[i]
367
for i in range(0, 32):
368
vis_layers[i] = vis_layers[i] and not (ORG_LAYER[i] or MCH_LAYER[i] or DEF_LAYER[i])
369
obj.data.layers = vis_layers
371
# Ensure the collection of layer names exists
372
for i in range(1 + len(metarig.data.rigify_layers), 29):
373
metarig.data.rigify_layers.add()
375
# Create list of layer name/row pairs
377
for l in metarig.data.rigify_layers:
378
layer_layout += [(l.name, l.row)]
380
# Generate the UI script
381
if "rig_ui.py" in bpy.data.texts:
382
script = bpy.data.texts["rig_ui.py"]
385
script = bpy.data.texts.new("rig_ui.py")
386
script.write(UI_SLIDERS % rig_id)
388
script.write("\n " + s.replace("\n", "\n ") + "\n")
389
script.write(layers_ui(vis_layers, layer_layout))
390
script.write(UI_REGISTER)
391
script.use_module = True
394
exec(script.as_string(), {})
397
#----------------------------------
399
bpy.ops.object.mode_set(mode='OBJECT')
400
metarig.data.pose_position = rest_backup
401
obj.data.pose_position = 'POSE'
404
def get_bone_rigs(obj, bone_name, halt_on_missing=False):
405
""" Fetch all the rigs specified on a bone.
408
rig_type = obj.pose.bones[bone_name].rigify_type
409
rig_type = rig_type.replace(" ", "")
416
params = obj.pose.bones[bone_name].rigify_parameters[0]
417
except (KeyError, IndexError):
422
rig = get_rig_type(rig_type).Rig(obj, bone_name, params)
424
message = "Rig Type Missing: python module for type '%s' not found (bone: %s)" % (rig_type, bone_name)
426
raise MetarigError(message)
429
print('print_exc():')
430
traceback.print_exc(file=sys.stdout)
436
def param_matches_type(param_name, rig_type):
437
""" Returns True if the parameter name is consistent with the rig type.
439
if param_name.rsplit(".", 1)[0] == rig_type:
445
def param_name(param_name, rig_type):
446
""" Get the actual parameter name, sans-rig-type.
448
return param_name[len(rig_type) + 1:]