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 bpy
.types
import bpy_struct
, bpy_prop_array
, Constraint
28 RIG_DIR
= "rigs" # Name of the directory where rig types are kept
29 METARIG_DIR
= "metarigs" # Name of the directory where metarigs are kept
30 TEMPLATE_DIR
= "ui_templates" # Name of the directory where ui templates are kept
33 outdated_types
= {"pitchipoy.limbs.super_limb": "limbs.super_limb",
34 "pitchipoy.limbs.super_arm": "limbs.super_limb",
35 "pitchipoy.limbs.super_leg": "limbs.super_limb",
36 "pitchipoy.limbs.super_front_paw": "limbs.super_limb",
37 "pitchipoy.limbs.super_rear_paw": "limbs.super_limb",
38 "pitchipoy.limbs.super_finger": "limbs.super_finger",
39 "pitchipoy.super_torso_turbo": "spines.super_spine",
40 "pitchipoy.simple_tentacle": "limbs.simple_tentacle",
41 "pitchipoy.super_face": "faces.super_face",
42 "pitchipoy.super_palm": "limbs.super_palm",
43 "pitchipoy.super_copy": "basic.super_copy",
44 "pitchipoy.tentacle": "",
45 "palm": "limbs.super_palm",
46 "basic.copy": "basic.super_copy",
55 def get_rigify_type(pose_bone
):
56 return pose_bone
.rigify_type
.replace(" ", "")
58 def is_rig_base_bone(obj
, name
):
59 return bool(get_rigify_type(obj
.pose
.bones
[name
]))
61 def upgradeMetarigTypes(metarig
, revert
=False):
62 """Replaces rigify_type properties from old versions with their current names
64 :param revert: revert types to previous version (if old type available)
68 vals
= list(outdated_types
.values())
69 rig_defs
= {v
: k
for k
, v
in outdated_types
.items() if vals
.count(v
) == 1}
71 rig_defs
= outdated_types
73 for bone
in metarig
.pose
.bones
:
74 rig_type
= bone
.rigify_type
75 if rig_type
in rig_defs
:
76 bone
.rigify_type
= rig_defs
[rig_type
]
78 bone
.rigfy_parameters
.limb_type
= 'leg'
80 bone
.rigfy_parameters
.limb_type
= 'arm'
82 bone
.rigfy_parameters
.limb_type
= 'paw'
83 if rig_type
== "basic.copy":
84 bone
.rigify_parameters
.make_widget
= False
87 #=============================================
89 #=============================================
91 def get_resource(resource_name
):
92 """ Fetches a rig module by name, and returns it.
95 module
= importlib
.import_module(resource_name
)
96 importlib
.reload(module
)
100 def connected_children_names(obj
, bone_name
):
101 """ Returns a list of bone names (in order) of the bones that form a single
102 connected chain starting with the given bone as a parent.
103 If there is a connected branch, the list stops there.
105 bone
= obj
.data
.bones
[bone_name
]
112 for child
in bone
.children
:
113 if child
.use_connect
:
115 con_name
= child
.name
119 bone
= obj
.data
.bones
[con_name
]
126 def has_connected_children(bone
):
127 """ Returns true/false whether a bone has connected children or not.
130 for b
in bone
.children
:
131 t
= t
or b
.use_connect
135 def _list_bone_names_depth_first_sorted_rec(result_list
, bone
):
136 result_list
.append(bone
.name
)
138 for child
in sorted(list(bone
.children
), key
=lambda b
: b
.name
):
139 _list_bone_names_depth_first_sorted_rec(result_list
, child
)
141 def list_bone_names_depth_first_sorted(obj
):
142 """Returns a list of bone names in depth first name sorted order."""
145 for bone
in sorted(list(obj
.data
.bones
), key
=lambda b
: b
.name
):
146 if bone
.parent
is None:
147 _list_bone_names_depth_first_sorted_rec(result_list
, bone
)
152 def _get_property_value(obj
, name
):
153 value
= getattr(obj
, name
, None)
154 if isinstance(value
, bpy_prop_array
):
158 def _generate_properties(lines
, prefix
, obj
, base_class
, *, defaults
={}, objects
={}):
159 block_props
= set(prop
.identifier
for prop
in base_class
.bl_rna
.properties
) - set(defaults
.keys())
161 for prop
in type(obj
).bl_rna
.properties
:
162 if prop
.identifier
not in block_props
and not prop
.is_readonly
:
163 cur_value
= _get_property_value(obj
, prop
.identifier
)
165 if prop
.identifier
in defaults
:
166 if cur_value
== defaults
[prop
.identifier
]:
169 if isinstance(cur_value
, bpy_struct
):
170 if cur_value
in objects
:
171 lines
.append('%s.%s = %s' % (prefix
, prop
.identifier
, objects
[cur_value
]))
173 lines
.append('%s.%s = %r' % (prefix
, prop
.identifier
, cur_value
))
176 def write_metarig(obj
, layers
=False, func_name
="create", groups
=False):
178 Write a metarig as a python script, this rig is to have all info needed for
179 generating the real rig with rigify.
183 code
.append("import bpy\n\n")
184 code
.append("from mathutils import Color\n\n")
186 code
.append("def %s(obj):" % func_name
)
187 code
.append(" # generated by rigify.utils.write_metarig")
188 bpy
.ops
.object.mode_set(mode
='EDIT')
189 code
.append(" bpy.ops.object.mode_set(mode='EDIT')")
190 code
.append(" arm = obj.data")
194 # Rigify bone group colors info
195 if groups
and len(arm
.rigify_colors
) > 0:
196 code
.append("\n for i in range(" + str(len(arm
.rigify_colors
)) + "):")
197 code
.append(" arm.rigify_colors.add()\n")
199 for i
in range(len(arm
.rigify_colors
)):
200 name
= arm
.rigify_colors
[i
].name
201 active
= arm
.rigify_colors
[i
].active
202 normal
= arm
.rigify_colors
[i
].normal
203 select
= arm
.rigify_colors
[i
].select
204 standard_colors_lock
= arm
.rigify_colors
[i
].standard_colors_lock
205 code
.append(' arm.rigify_colors[' + str(i
) + '].name = "' + name
+ '"')
206 code
.append(' arm.rigify_colors[' + str(i
) + '].active = Color(' + str(active
[:]) + ')')
207 code
.append(' arm.rigify_colors[' + str(i
) + '].normal = Color(' + str(normal
[:]) + ')')
208 code
.append(' arm.rigify_colors[' + str(i
) + '].select = Color(' + str(select
[:]) + ')')
209 code
.append(' arm.rigify_colors[' + str(i
) + '].standard_colors_lock = ' + str(standard_colors_lock
))
211 # Rigify layer layout info
212 if layers
and len(arm
.rigify_layers
) > 0:
213 code
.append("\n for i in range(" + str(len(arm
.rigify_layers
)) + "):")
214 code
.append(" arm.rigify_layers.add()\n")
216 for i
in range(len(arm
.rigify_layers
)):
217 name
= arm
.rigify_layers
[i
].name
218 row
= arm
.rigify_layers
[i
].row
219 selset
= arm
.rigify_layers
[i
].selset
220 group
= arm
.rigify_layers
[i
].group
221 code
.append(' arm.rigify_layers[' + str(i
) + '].name = "' + name
+ '"')
222 code
.append(' arm.rigify_layers[' + str(i
) + '].row = ' + str(row
))
223 code
.append(' arm.rigify_layers[' + str(i
) + '].selset = ' + str(selset
))
224 code
.append(' arm.rigify_layers[' + str(i
) + '].group = ' + str(group
))
226 # write parents first
227 bones
= [(len(bone
.parent_recursive
), bone
.name
) for bone
in arm
.edit_bones
]
228 bones
.sort(key
=lambda item
: item
[0])
229 bones
= [item
[1] for item
in bones
]
231 code
.append("\n bones = {}\n")
233 for bone_name
in bones
:
234 bone
= arm
.edit_bones
[bone_name
]
235 code
.append(" bone = arm.edit_bones.new(%r)" % bone
.name
)
236 code
.append(" bone.head = %.4f, %.4f, %.4f" % bone
.head
.to_tuple(4))
237 code
.append(" bone.tail = %.4f, %.4f, %.4f" % bone
.tail
.to_tuple(4))
238 code
.append(" bone.roll = %.4f" % bone
.roll
)
239 code
.append(" bone.use_connect = %s" % str(bone
.use_connect
))
240 if bone
.inherit_scale
!= 'FULL':
241 code
.append(" bone.inherit_scale = %r" % str(bone
.inherit_scale
))
243 code
.append(" bone.parent = arm.edit_bones[bones[%r]]" % bone
.parent
.name
)
244 code
.append(" bones[%r] = bone.name" % bone
.name
)
246 bpy
.ops
.object.mode_set(mode
='OBJECT')
248 code
.append(" bpy.ops.object.mode_set(mode='OBJECT')")
250 # Rig type and other pose properties
251 for bone_name
in bones
:
252 pbone
= obj
.pose
.bones
[bone_name
]
254 code
.append(" pbone = obj.pose.bones[bones[%r]]" % bone_name
)
255 code
.append(" pbone.rigify_type = %r" % pbone
.rigify_type
)
256 code
.append(" pbone.lock_location = %s" % str(tuple(pbone
.lock_location
)))
257 code
.append(" pbone.lock_rotation = %s" % str(tuple(pbone
.lock_rotation
)))
258 code
.append(" pbone.lock_rotation_w = %s" % str(pbone
.lock_rotation_w
))
259 code
.append(" pbone.lock_scale = %s" % str(tuple(pbone
.lock_scale
)))
260 code
.append(" pbone.rotation_mode = %r" % pbone
.rotation_mode
)
262 code
.append(" pbone.bone.layers = %s" % str(list(pbone
.bone
.layers
)))
263 # Rig type parameters
264 for param_name
in pbone
.rigify_parameters
.keys():
265 param
= getattr(pbone
.rigify_parameters
, param_name
, '')
266 if str(type(param
)) == "<class 'bpy_prop_array'>":
268 if type(param
) == str:
269 param
= '"' + param
+ '"'
271 code
.append(" pbone.rigify_parameters.%s = %s" % (param_name
, str(param
)))
272 code
.append(" except AttributeError:")
275 for con
in pbone
.constraints
:
276 code
.append(" con = pbone.constraints.new(%r)" % (con
.type))
277 code
.append(" con.name = %r" % (con
.name
))
278 # Add target first because of target_space handling
279 if con
.type == 'ARMATURE':
280 for tgt
in con
.targets
:
281 code
.append(" tgt = con.targets.new()")
282 code
.append(" tgt.target = obj")
283 code
.append(" tgt.subtarget = %r" % (tgt
.subtarget
))
284 code
.append(" tgt.weight = %.3f" % (tgt
.weight
))
285 elif getattr(con
, 'target', None) == obj
:
286 code
.append(" con.target = obj")
288 _generate_properties(
289 code
, " con", con
, Constraint
,
291 'owner_space': 'WORLD', 'target_space': 'WORLD',
292 'mute': False, 'influence': 1.0,
295 objects
={obj
: 'obj'},
298 code
.append("\n bpy.ops.object.mode_set(mode='EDIT')")
299 code
.append(" for bone in arm.edit_bones:")
300 code
.append(" bone.select = False")
301 code
.append(" bone.select_head = False")
302 code
.append(" bone.select_tail = False")
304 code
.append(" for b in bones:")
305 code
.append(" bone = arm.edit_bones[bones[b]]")
306 code
.append(" bone.select = True")
307 code
.append(" bone.select_head = True")
308 code
.append(" bone.select_tail = True")
309 code
.append(" bone.bbone_x = bone.bbone_z = bone.length * 0.05")
310 code
.append(" arm.edit_bones.active = bone")
312 # Set appropriate layers visible
314 # Find what layers have bones on them
316 for bone_name
in bones
:
317 bone
= obj
.data
.bones
[bone_name
]
318 for i
in range(len(bone
.layers
)):
320 if i
not in active_layers
:
321 active_layers
.append(i
)
324 code
.append("\n arm.layers = [(x in " + str(active_layers
) + ") for x in range(" + str(len(arm
.layers
)) + ")]")
326 code
.append("\n return bones")
328 code
.append('\nif __name__ == "__main__":')
329 code
.append(" " + func_name
+ "(bpy.context.active_object)\n")
331 return "\n".join(code
)