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 #####
20 "name": "Skinify Rig",
21 "author": "Albert Makac (karab44)",
22 "version": (0, 11, 0),
24 "location": "Properties > Bone > Skinify Rig (visible on pose mode only)",
25 "description": "Creates a mesh object from selected bones",
27 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
28 "Py/Scripts/Object/Skinify",
32 from bpy
.props
import (
38 from bpy
.types
import (
43 from mathutils
import (
47 from bpy
.app
.handlers
import persistent
50 # can the armature data properties group_prop and row be fetched directly from the rigify script?
52 (1, 5), (2, 4), (3, 0), (4, 3), (5, 4), (1, 0), (1, 0), (7, 2), (8, 5), (9, 4), \
53 (7, 2), (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), \
54 (13, 6), (1, 4), (14, 6), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
57 (1, 5), (2, 4), (1, 0), (3, 3), (4, 4), (5, 6), (6, 5), (7, 4), (6, 5), (7, 4), \
58 (8, 3), (9, 4), (1, 0), (1, 6), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), \
59 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
62 (1, 6), (2, 4), (1, 0), (3, 3), (4, 4), (1, 0), (1, 0), (6, 5), (8, 0), (7, 4), (6, 5), \
63 (8, 0), (7, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
64 (13, 6), (14, 4), (1, 0), (8, 6), (1, 0), (1, 0), (1, 0), (14, 1),
67 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
68 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 3), (14, 4), \
69 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (16, 1),
72 (1, 0), (1, 0), (1, 0), (3, 3), (4, 4), (1, 0), (1, 0), (7, 2), (8, 5), (9, 4), (7, 2), \
73 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
74 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
77 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
78 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
79 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
82 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
83 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 6), (1, 0), \
84 (13, 0), (13, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
87 (1, 0), (2, 0), (2, 0), (3, 3), (4, 4), (5, 0), (6, 0), (7, 2), (8, 5), (9, 4), \
88 (7, 2), (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 6), \
89 (1, 0), (13, 0), (13, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
92 (1, None), (1, None), (2, None), (1, None), (3, None), (3, None), (4, None), (5, None), \
93 (6, None), (4, None), (5, None), (6, None), (7, None), (8, None), (9, None), (7, None), \
94 (8, None), (9, None), (1, None), (1, None), (1, None), (1, None), (1, None), (1, None), \
95 (1, None), (1, None), (1, None), (1, None),
98 (1, None), (2, None), (2, None), (3, None), (4, None), (5, None), (6, None), (7, None), \
99 (8, None), (9, None), (7, None), (8, None), (9, None), (10, None), (11, None), (12, None), \
100 (10, None), (11, None), (12, None), (1, None), (1, None), (1, None), (1, None), (1, None), \
101 (1, None), (1, None), (1, None), (1, None),
103 rigify_data
= horse_data
, shark_data
, bird_data
, cat_data
, biped_data
, human_data
, \
104 wolf_data
, quadruped_data
, human_legacy_data
, pitchipoy_data
107 class Rig_type(Enum
):
121 rig_type
= Rig_type
.OTHER
124 class Idx_Store(object):
125 def __init__(self
, rig_type
):
126 self
.rig_type
= rig_type
127 self
.hand_r_merge
= []
128 self
.hand_l_merge
= []
129 self
.hands_pretty
= []
132 if not self
.rig_type
== Rig_type
.LEGACY
and \
133 not self
.rig_type
== Rig_type
.HUMAN
and \
134 not self
.rig_type
== Rig_type
.PITCHIPOY
:
137 if self
.rig_type
== Rig_type
.LEGACY
:
138 self
.hand_l_merge
= [7, 12, 16, 21, 26, 27]
139 self
.hand_r_merge
= [30, 31, 36, 40, 45, 50]
140 self
.hands_pretty
= [6, 29]
143 if self
.rig_type
== Rig_type
.HUMAN
or self
.rig_type
== Rig_type
.PITCHIPOY
:
144 self
.hand_l_merge
= [9, 10, 15, 19, 24, 29]
145 self
.hand_r_merge
= [32, 33, 37, 42, 47, 52]
146 self
.hands_pretty
= [8, 31]
149 def get_all_idx(self
):
150 return self
.hand_l_merge
, self
.hand_r_merge
, self
.hands_pretty
, self
.root
152 def get_hand_l_merge_idx(self
):
153 return self
.hand_l_merge
155 def get_hand_r_merge_idx(self
):
156 return self
.hand_r_merge
158 def get_hands_pretty_idx(self
):
159 return self
.hands_pretty
161 def get_root_idx(self
):
165 # initialize properties
167 # additional check - this should be a rare case if the handler
168 # wasn't removed for some reason and the add-on is not toggled on/off
169 if hasattr(bpy
.types
.Scene
, "skinify"):
170 scn
= bpy
.context
.scene
.skinify
172 scn
.connect_mesh
= False
173 scn
.connect_parents
= False
174 scn
.generate_all
= False
176 scn
.finger_thickness
= 0.25
178 scn
.parent_armature
= True
183 def select_vertices(mesh_obj
, idx
):
184 bpy
.context
.scene
.objects
.active
= mesh_obj
186 bpy
.ops
.object.mode_set(mode
='EDIT')
187 bpy
.ops
.mesh
.select_all(action
='DESELECT')
188 bpy
.ops
.object.mode_set(mode
='OBJECT')
191 mesh_obj
.data
.vertices
[i
].select
= True
193 selectedVerts
= [v
.index
for v
in mesh_obj
.data
.vertices
if v
.select
]
195 bpy
.ops
.object.mode_set(mode
=mode
)
200 if 'rigify_layers' not in bpy
.context
.object.data
:
201 return Rig_type
.OTHER
# non recognized
203 LEGACY_LAYERS_SIZE
= 28
204 layers
= bpy
.context
.object.data
['rigify_layers']
206 for type, rig
in enumerate(rigify_data
):
210 if len(layers
) == LEGACY_LAYERS_SIZE
and 'group_prop' not in props
:
212 if props
['row'] != rig
[index
][0] or rig
[index
][1] is not None:
215 elif (props
['row'] != rig
[index
][0]) or (props
['group_prop'] != rig
[index
][1]):
218 # SUCCESS if reach the end
219 if index
== len(layers
) - 1:
220 return Rig_type(type)
224 return Rig_type
.OTHER
227 def prepare_ignore_list(rig_type
, bones
):
228 # detect the head, face, hands, breast, heels or other exceptionary bones to exclusion or customization
229 common_ignore_list
= ['eye', 'heel', 'breast', 'root']
231 # edit these lists to suits your taste
233 horse_ignore_list
= ['chest', 'belly', 'pelvis', 'jaw', 'nose', 'skull', 'ear.']
235 shark_ignore_list
= ['jaw']
238 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
239 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue', 'beak'
242 'face', 'belly' 'pelvis.C', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
243 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
245 biped_ignore_list
= ['pelvis']
247 human_ignore_list
= [
248 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
249 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
252 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
253 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
256 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
257 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
259 rigify_legacy_ignore_list
= []
261 pitchipoy_ignore_list
= [
262 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
263 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
266 other_ignore_list
= []
268 ignore_list
= common_ignore_list
270 if rig_type
== Rig_type
.HORSE
:
271 ignore_list
= ignore_list
+ horse_ignore_list
272 print("RIDER OF THE APOCALYPSE")
273 elif rig_type
== Rig_type
.SHARK
:
274 ignore_list
= ignore_list
+ shark_ignore_list
276 elif rig_type
== Rig_type
.BIRD
:
277 ignore_list
= ignore_list
+ bird_ignore_list
278 print("WINGS OF LIBERTY")
279 elif rig_type
== Rig_type
.CAT
:
280 ignore_list
= ignore_list
+ cat_ignore_list
282 elif rig_type
== Rig_type
.BIPED
:
283 ignore_list
= ignore_list
+ biped_ignore_list
285 elif rig_type
== Rig_type
.HUMAN
:
286 ignore_list
= ignore_list
+ human_ignore_list
287 print("JUST A HUMAN AFTER ALL")
288 elif rig_type
== Rig_type
.WOLF
:
289 ignore_list
= ignore_list
+ wolf_ignore_list
291 elif rig_type
== Rig_type
.QUAD
:
292 ignore_list
= ignore_list
+ quad_ignore_list
293 print("MYSTERIOUS CREATURE")
294 elif rig_type
== Rig_type
.LEGACY
:
295 ignore_list
= ignore_list
+ rigify_legacy_ignore_list
296 print("LEGACY RIGIFY")
297 elif rig_type
== Rig_type
.PITCHIPOY
:
298 ignore_list
= ignore_list
+ pitchipoy_ignore_list
300 elif rig_type
== Rig_type
.OTHER
:
301 ignore_list
= ignore_list
+ other_ignore_list
302 print("rig non recognized...")
307 # generates edges from vertices used by skin modifier
308 def generate_edges(mesh
, shape_object
, bones
, scale
, connect_mesh
=False, connect_parents
=False,
309 head_ornaments
=False, generate_all
=False):
311 This function adds vertices for all heads and tails
315 alternate_scale_list
= []
321 alternate_scale_idx_list
= list()
323 rig_type
= identify_rig()
324 ignore_list
= prepare_ignore_list(rig_type
, bones
)
326 # edge generator loop
328 # look for rig's hands and their childs
329 if 'hand' in b
.name
.lower():
331 for c
in b
.children_recursive
:
332 alternate_scale_list
.append(c
.name
)
336 for i
in ignore_list
:
337 if i
in b
.name
.lower():
341 if found
and generate_all
is False:
344 # fix for drawing rootbone and relationship lines
345 if 'root' in b
.name
.lower() and generate_all
is False:
348 # ignore any head ornaments
349 if head_ornaments
is False:
350 if b
.parent
is not None:
352 if 'head' in b
.parent
.name
.lower() and not rig_type
== Rig_type
.HUMAN
:
355 if 'face' in b
.parent
.name
.lower() and rig_type
== Rig_type
.HUMAN
:
359 if b
.parent
is not None and b
.parent
.bone
.select
is True and b
.bone
.use_connect
is False:
360 if 'root' in b
.parent
.name
.lower() and generate_all
is False:
363 if 'shoulder' in b
.name
.lower() and connect_mesh
is True:
365 # connect the upper arm directly with chest ommiting shoulders
366 if 'shoulder' in b
.parent
.name
.lower() and connect_mesh
is True:
368 vert2
= b
.parent
.parent
.tail
372 vert2
= b
.parent
.tail
376 edges
.append([idx
, idx
+ 1])
378 # also make list of edges made of gaps between the bones
379 for a
in alternate_scale_list
:
381 alternate_scale_idx_list
.append(idx
)
382 alternate_scale_idx_list
.append(idx
+ 1)
385 # for bvh free floating hips and hips correction for rigify and pitchipoy
386 if ((generate_all
is False and 'hip' in b
.name
.lower()) or
387 (generate_all
is False and (b
.name
== 'hips' and rig_type
== Rig_type
.LEGACY
) or
388 (b
.name
== 'spine' and rig_type
== Rig_type
.PITCHIPOY
) or (b
.name
== 'spine' and
389 rig_type
== Rig_type
.HUMAN
) or (b
.name
== 'spine' and rig_type
== Rig_type
.BIPED
))):
397 edges
.append([idx
, idx
+ 1])
399 for a
in alternate_scale_list
:
401 alternate_scale_idx_list
.append(idx
)
402 alternate_scale_idx_list
.append(idx
+ 1)
406 # Create mesh from given verts, faces
407 me
.from_pydata(verts
, edges
, [])
408 # Update mesh with new data
411 # set object scale exact as armature's scale
412 shape_object
.scale
= scale
414 return alternate_scale_idx_list
, rig_type
417 def generate_mesh(shape_object
, size
, thickness
=0.8, finger_thickness
=0.25, sub_level
=1,
418 connect_mesh
=False, connect_parents
=False, generate_all
=False, apply_mod
=True,
419 alternate_scale_idx_list
=[], rig_type
=0, bones
=[]):
421 This function adds modifiers for generated edges
423 total_bones_num
= len(bpy
.context
.object.pose
.bones
.keys())
424 selected_bones_num
= len(bones
)
426 bpy
.ops
.object.mode_set(mode
='EDIT')
427 bpy
.ops
.mesh
.select_all(action
='DESELECT')
430 shape_object
.modifiers
.new("Skin", 'SKIN')
431 bpy
.ops
.mesh
.select_all(action
='SELECT')
433 override
= bpy
.context
.copy()
434 for area
in bpy
.context
.screen
.areas
:
435 if area
.type == 'VIEW_3D':
436 for region
in area
.regions
:
437 if region
.type == 'WINDOW':
438 override
['area'] = area
439 override
['region'] = region
440 override
['edit_object'] = bpy
.context
.edit_object
441 override
['scene'] = bpy
.context
.scene
442 override
['active_object'] = shape_object
443 override
['object'] = shape_object
444 override
['modifier'] = bpy
.context
.object.modifiers
447 # calculate optimal thickness for defaults
448 bpy
.ops
.object.skin_root_mark(override
)
449 bpy
.ops
.transform
.skin_resize(override
,
450 value
=(1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10)),
451 constraint_axis
=(False, False, False), constraint_orientation
='GLOBAL',
452 mirror
=False, proportional
='DISABLED', proportional_edit_falloff
='SMOOTH',
455 shape_object
.modifiers
["Skin"].use_smooth_shade
= True
456 shape_object
.modifiers
["Skin"].use_x_symmetry
= True
458 # select finger vertices and calculate optimal thickness for fingers to fix proportions
459 if len(alternate_scale_idx_list
) > 0:
460 select_vertices(shape_object
, alternate_scale_idx_list
)
462 bpy
.ops
.object.skin_loose_mark_clear(override
, action
='MARK')
463 # by default set fingers thickness to 25 percent of body thickness
464 bpy
.ops
.transform
.skin_resize(override
,
465 value
=(finger_thickness
, finger_thickness
, finger_thickness
),
466 constraint_axis
=(False, False, False), constraint_orientation
='GLOBAL',
467 mirror
=False, proportional
='DISABLED', proportional_edit_falloff
='SMOOTH',
470 # make loose hands only for better topology
472 # bpy.ops.mesh.select_all(action='DESELECT')
475 bpy
.ops
.object.mode_set(mode
='EDIT')
476 bpy
.ops
.mesh
.select_all(action
='DESELECT')
477 bpy
.ops
.mesh
.select_all(action
='SELECT')
478 bpy
.ops
.mesh
.remove_doubles()
480 idx_store
= Idx_Store(rig_type
)
482 # fix rigify and pitchipoy hands topology
483 if connect_mesh
and connect_parents
and generate_all
is False and \
484 (rig_type
== Rig_type
.LEGACY
or rig_type
== Rig_type
.PITCHIPOY
or rig_type
== Rig_type
.HUMAN
) and \
485 selected_bones_num
== total_bones_num
:
486 # thickness will set palm vertex for both hands look pretty
487 corrective_thickness
= 2.5
489 merge_idx
= idx_store
.get_hand_l_merge_idx()
491 select_vertices(shape_object
, merge_idx
)
492 bpy
.ops
.mesh
.merge(type='CENTER')
493 bpy
.ops
.transform
.skin_resize(override
,
494 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
495 constraint_axis
=(False, False, False), constraint_orientation
='GLOBAL',
496 mirror
=False, proportional
='DISABLED', proportional_edit_falloff
='SMOOTH',
499 bpy
.ops
.mesh
.select_all(action
='DESELECT')
502 merge_idx
= idx_store
.get_hand_r_merge_idx()
504 select_vertices(shape_object
, merge_idx
)
505 bpy
.ops
.mesh
.merge(type='CENTER')
506 bpy
.ops
.transform
.skin_resize(override
,
507 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
508 constraint_axis
=(False, False, False), constraint_orientation
='GLOBAL',
509 mirror
=False, proportional
='DISABLED', proportional_edit_falloff
='SMOOTH',
513 # making hands even more pretty
514 bpy
.ops
.mesh
.select_all(action
='DESELECT')
515 hands_idx
= idx_store
.get_hands_pretty_idx()
517 select_vertices(shape_object
, hands_idx
)
518 # change the thickness to make hands look less blocky and more sexy
519 corrective_thickness
= 0.7
520 bpy
.ops
.transform
.skin_resize(override
,
521 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
522 constraint_axis
=(False, False, False), constraint_orientation
='GLOBAL',
523 mirror
=False, proportional
='DISABLED', proportional_edit_falloff
='SMOOTH',
526 bpy
.ops
.mesh
.select_all(action
='DESELECT')
528 # todo optionally take root from rig's hip tail or head depending on scenario
530 root_idx
= idx_store
.get_root_idx()
532 if selected_bones_num
== total_bones_num
:
535 if len(root_idx
) > 0:
536 select_vertices(shape_object
, root_idx
)
537 bpy
.ops
.object.skin_root_mark(override
)
539 # add Subsurf modifier
540 shape_object
.modifiers
.new("Subsurf", 'SUBSURF')
541 shape_object
.modifiers
["Subsurf"].levels
= sub_level
542 shape_object
.modifiers
["Subsurf"].render_levels
= sub_level
544 bpy
.ops
.object.mode_set(mode
='OBJECT')
546 # object mode apply all modifiers
548 bpy
.ops
.object.modifier_apply(override
, apply_as
='DATA', modifier
="Skin")
549 bpy
.ops
.object.modifier_apply(override
, apply_as
='DATA', modifier
="Subsurf")
556 This script will create a custome shape
559 # ### Check if selection is OK ###
560 if len(context
.selected_pose_bones
) == 0 or \
561 len(context
.selected_objects
) == 0 or \
562 context
.selected_objects
[0].type != 'ARMATURE':
563 return {'CANCELLED'}, "No bone selected or the Armature is hidden"
565 scn
= bpy
.context
.scene
568 # initialize the mesh object
569 mesh_name
= context
.selected_objects
[0].name
+ "_mesh"
570 obj_name
= context
.selected_objects
[0].name
+ "_object"
571 armature_object
= context
.object
573 origin
= context
.object.location
574 bone_selection
= context
.selected_pose_bones
578 armature_object
= scn
.objects
.active
579 armature_object
.select
= True
581 old_pose_pos
= armature_object
.data
.pose_position
582 bpy
.ops
.object.mode_set(mode
='OBJECT')
583 oldLocation
= Vector(armature_object
.location
)
584 oldRotation
= Euler(armature_object
.rotation_euler
)
585 oldScale
= Vector(armature_object
.scale
)
587 bpy
.ops
.object.rotation_clear(clear_delta
=False)
588 bpy
.ops
.object.location_clear(clear_delta
=False)
589 bpy
.ops
.object.scale_clear(clear_delta
=False)
590 if sknfy
.apply_mod
and sknfy
.parent_armature
:
591 armature_object
.data
.pose_position
= 'REST'
593 scale
= bpy
.context
.object.scale
594 size
= bpy
.context
.object.dimensions
[2]
596 bpy
.ops
.object.mode_set(mode
='OBJECT')
597 bpy
.ops
.object.select_all(action
='DESELECT')
599 bpy
.ops
.object.add(type='MESH', enter_editmode
=False, location
=origin
)
601 # get the mesh object
602 ob
= scn
.objects
.active
607 # this way we fit mesh and bvh with armature modifier correctly
609 alternate_scale_idx_list
, rig_type
= generate_edges(
610 me
, ob
, bone_selection
, scale
, sknfy
.connect_mesh
,
611 sknfy
.connect_parents
, sknfy
.head_ornaments
,
615 generate_mesh(ob
, size
, sknfy
.thickness
, sknfy
.finger_thickness
, sknfy
.sub_level
,
616 sknfy
.connect_mesh
, sknfy
.connect_parents
, sknfy
.generate_all
,
617 sknfy
.apply_mod
, alternate_scale_idx_list
, rig_type
, bone_selection
)
619 # parent mesh with armature only if modifiers are applied
620 if sknfy
.apply_mod
and sknfy
.parent_armature
:
621 bpy
.ops
.object.mode_set(mode
='OBJECT')
622 bpy
.ops
.object.select_all(action
='DESELECT')
624 armature_object
.select
= True
625 scn
.objects
.active
= armature_object
627 bpy
.ops
.object.parent_set(type='ARMATURE_AUTO')
628 armature_object
.data
.pose_position
= old_pose_pos
629 armature_object
.select
= False
631 bpy
.ops
.object.mode_set(mode
='OBJECT')
632 ob
.location
= oldLocation
633 ob
.rotation_euler
= oldRotation
636 armature_object
.select
= True
637 scn
.objects
.active
= armature_object
639 armature_object
.location
= oldLocation
640 armature_object
.rotation_euler
= oldRotation
641 armature_object
.scale
= oldScale
642 bpy
.ops
.object.mode_set(mode
='POSE')
644 return {'FINISHED'}, me
647 class BONE_OT_custom_shape(Operator
):
648 bl_idname
= "object.skinify_rig"
649 bl_label
= "Skinify Rig"
650 bl_description
= "Creates a mesh object at the selected bones positions"
651 bl_options
= {'UNDO', 'INTERNAL'}
654 def poll(cls
, context
):
655 return context
.active_object
is not None
657 def execute(self
, context
):
659 if Mesh
[0] == {'CANCELLED'}:
660 self
.report({'WARNING'}, Mesh
[1])
663 self
.report({'INFO'}, Mesh
[1].name
+ " has been created")
668 class BONE_PT_custom_shape(Panel
):
669 bl_space_type
= 'PROPERTIES'
670 bl_region_type
= 'WINDOW'
672 bl_label
= "Skinify Rig"
675 def poll(cls
, context
):
677 return ob
and ob
.mode
== 'POSE' and context
.bone
679 def draw(self
, context
):
681 scn
= context
.scene
.skinify
684 row
.operator("object.skinify_rig", text
="Add Shape", icon
='BONE_DATA')
686 split
= layout
.split(percentage
=0.3)
687 split
.label("Thickness:")
688 split
.prop(scn
, "thickness", text
="Body", icon
='MOD_SKIN')
689 split
.prop(scn
, "finger_thickness", text
="Fingers", icon
='HAND')
691 split
= layout
.split(percentage
=0.3)
692 split
.label("Mesh Density:")
693 split
.prop(scn
, "sub_level", icon
='MESH_ICOSPHERE')
696 row
.prop(scn
, "connect_mesh", icon
='EDITMODE_HLT')
697 row
.prop(scn
, "connect_parents", icon
='CONSTRAINT_BONE')
699 row
.prop(scn
, "head_ornaments", icon
='GROUP_BONE')
700 row
.prop(scn
, "generate_all", icon
='GROUP_BONE')
702 row
.prop(scn
, "apply_mod", icon
='FILE_TICK')
705 row
.prop(scn
, "parent_armature", icon
='POSE_HLT')
708 # define the scene properties in a group - call them with context.scene.skinify
709 class Skinify_Properties(PropertyGroup
):
710 sub_level
= IntProperty(
714 description
="Mesh density"
716 thickness
= FloatProperty(
720 description
="Adjust shape thickness"
722 finger_thickness
= FloatProperty(
723 name
="Finger Thickness",
726 description
="Adjust finger thickness relative to body"
728 connect_mesh
= BoolProperty(
731 description
="Makes solid shape from bone chains"
733 connect_parents
= BoolProperty(
736 description
="Fills the gaps between parented bones"
738 generate_all
= BoolProperty(
741 description
="Generates shapes from all bones"
743 head_ornaments
= BoolProperty(
744 name
="Head Ornaments",
746 description
="Includes head ornaments"
748 apply_mod
= BoolProperty(
749 name
="Apply Modifiers",
751 description
="Applies Modifiers to mesh"
753 parent_armature
= BoolProperty(
754 name
="Parent Armature",
756 description
="Applies mesh to Armature"
763 def startup_init(dummy
):
768 bpy
.utils
.register_class(BONE_OT_custom_shape
)
769 bpy
.utils
.register_class(BONE_PT_custom_shape
)
770 bpy
.utils
.register_class(Skinify_Properties
)
772 bpy
.types
.Scene
.skinify
= PointerProperty(
773 type=Skinify_Properties
776 bpy
.app
.handlers
.load_post
.append(startup_init
)
780 bpy
.utils
.unregister_class(BONE_OT_custom_shape
)
781 bpy
.utils
.unregister_class(BONE_PT_custom_shape
)
782 bpy
.utils
.unregister_class(Skinify_Properties
)
784 # cleanup the handler
785 bpy
.app
.handlers
.load_post
.remove(startup_init
)
787 del bpy
.types
.Scene
.skinify
790 if __name__
== "__main__":