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),
23 "blender": (2, 80, 0),
24 "location": "Pose Mode > Sidebar > Create Tab",
25 "description": "Creates a mesh object from selected bones",
26 "warning": "Work in progress",
27 "doc_url": "https://docs.blender.org/manual/en/dev/addons/"
28 "object/skinify.html",
33 from bpy
.props
import (
39 from bpy
.types
import (
44 from mathutils
import (
48 from bpy
.app
.handlers
import persistent
51 # can the armature data properties group_prop and row be fetched directly from the rigify script?
53 (1, 5), (2, 4), (3, 0), (4, 3), (5, 4), (1, 0), (1, 0), (7, 2), (8, 5), (9, 4), \
54 (7, 2), (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), \
55 (13, 6), (1, 4), (14, 6), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
58 (1, 5), (2, 4), (1, 0), (3, 3), (4, 4), (5, 6), (6, 5), (7, 4), (6, 5), (7, 4), \
59 (8, 3), (9, 4), (1, 0), (1, 6), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), \
60 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
63 (1, 6), (2, 4), (1, 0), (3, 3), (4, 4), (1, 0), (1, 0), (6, 5), (8, 0), (7, 4), (6, 5), \
64 (8, 0), (7, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
65 (13, 6), (14, 4), (1, 0), (8, 6), (1, 0), (1, 0), (1, 0), (14, 1),
68 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
69 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 3), (14, 4), \
70 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (16, 1),
73 (1, 0), (1, 0), (1, 0), (3, 3), (4, 4), (1, 0), (1, 0), (7, 2), (8, 5), (9, 4), (7, 2), \
74 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
75 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
78 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
79 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
80 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
83 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
84 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 6), (1, 0), \
85 (13, 0), (13, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
88 (1, 0), (2, 0), (2, 0), (3, 3), (4, 4), (5, 0), (6, 0), (7, 2), (8, 5), (9, 4), \
89 (7, 2), (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 6), \
90 (1, 0), (13, 0), (13, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
93 (1, None), (1, None), (2, None), (1, None), (3, None), (3, None), (4, None), (5, None), \
94 (6, None), (4, None), (5, None), (6, None), (7, None), (8, None), (9, None), (7, None), \
95 (8, None), (9, None), (1, None), (1, None), (1, None), (1, None), (1, None), (1, None), \
96 (1, None), (1, None), (1, None), (1, None),
99 (1, None), (2, None), (2, None), (3, None), (4, None), (5, None), (6, None), (7, None), \
100 (8, None), (9, None), (7, None), (8, None), (9, None), (10, None), (11, None), (12, None), \
101 (10, None), (11, None), (12, None), (1, None), (1, None), (1, None), (1, None), (1, None), \
102 (1, None), (1, None), (1, None), (1, None),
104 rigify_data
= horse_data
, shark_data
, bird_data
, cat_data
, biped_data
, human_data
, \
105 wolf_data
, quadruped_data
, human_legacy_data
, pitchipoy_data
108 class Rig_type(Enum
):
122 rig_type
= Rig_type
.OTHER
125 class Idx_Store(object):
126 def __init__(self
, rig_type
):
127 self
.rig_type
= rig_type
128 self
.hand_r_merge
= []
129 self
.hand_l_merge
= []
130 self
.hands_pretty
= []
133 if not self
.rig_type
== Rig_type
.LEGACY
and \
134 not self
.rig_type
== Rig_type
.HUMAN
and \
135 not self
.rig_type
== Rig_type
.PITCHIPOY
:
138 if self
.rig_type
== Rig_type
.LEGACY
:
139 self
.hand_l_merge
= [7, 12, 16, 21, 26, 27]
140 self
.hand_r_merge
= [30, 31, 36, 40, 45, 50]
141 self
.hands_pretty
= [6, 29]
144 if self
.rig_type
== Rig_type
.HUMAN
or self
.rig_type
== Rig_type
.PITCHIPOY
:
145 self
.hand_l_merge
= [9, 10, 15, 19, 24, 29]
146 self
.hand_r_merge
= [32, 33, 37, 42, 47, 52]
147 self
.hands_pretty
= [8, 31]
150 def get_all_idx(self
):
151 return self
.hand_l_merge
, self
.hand_r_merge
, self
.hands_pretty
, self
.root
153 def get_hand_l_merge_idx(self
):
154 return self
.hand_l_merge
156 def get_hand_r_merge_idx(self
):
157 return self
.hand_r_merge
159 def get_hands_pretty_idx(self
):
160 return self
.hands_pretty
162 def get_root_idx(self
):
166 # initialize properties
168 # additional check - this should be a rare case if the handler
169 # wasn't removed for some reason and the add-on is not toggled on/off
170 if hasattr(bpy
.types
.Scene
, "skinify"):
171 scn
= bpy
.context
.scene
.skinify
173 scn
.connect_mesh
= False
174 scn
.connect_parents
= False
175 scn
.generate_all
= False
177 scn
.finger_thickness
= 0.25
179 scn
.parent_armature
= True
184 def select_vertices(mesh_obj
, idx
):
185 bpy
.context
.view_layer
.objects
.active
= mesh_obj
187 bpy
.ops
.object.mode_set(mode
='EDIT')
188 bpy
.ops
.mesh
.select_all(action
='DESELECT')
189 bpy
.ops
.object.mode_set(mode
='OBJECT')
192 mesh_obj
.data
.vertices
[i
].select
= True
194 selectedVerts
= [v
.index
for v
in mesh_obj
.data
.vertices
if v
.select
]
196 bpy
.ops
.object.mode_set(mode
=mode
)
201 if 'rigify_layers' not in bpy
.context
.object.data
:
202 return Rig_type
.OTHER
# non recognized
204 LEGACY_LAYERS_SIZE
= 28
205 layers
= bpy
.context
.object.data
['rigify_layers']
207 for type, rig
in enumerate(rigify_data
):
211 if len(layers
) == LEGACY_LAYERS_SIZE
and 'group_prop' not in props
:
213 if props
['row'] != rig
[index
][0] or rig
[index
][1] is not None:
216 elif (props
['row'] != rig
[index
][0]) or (props
['group_prop'] != rig
[index
][1]):
219 # SUCCESS if reach the end
220 if index
== len(layers
) - 1:
221 return Rig_type(type)
225 return Rig_type
.OTHER
228 def prepare_ignore_list(rig_type
, bones
):
229 # detect the head, face, hands, breast, heels or other exceptionary bones to exclusion or customization
230 common_ignore_list
= ['eye', 'heel', 'breast', 'root']
232 # edit these lists to suits your taste
234 horse_ignore_list
= ['chest', 'belly', 'pelvis', 'jaw', 'nose', 'skull', 'ear.']
236 shark_ignore_list
= ['jaw']
239 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
240 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue', 'beak'
243 'face', 'belly' 'pelvis.C', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
244 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
246 biped_ignore_list
= ['pelvis']
248 human_ignore_list
= [
249 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
250 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
253 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
254 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
257 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
258 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
260 rigify_legacy_ignore_list
= []
262 pitchipoy_ignore_list
= [
263 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
264 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
267 other_ignore_list
= []
269 ignore_list
= common_ignore_list
271 if rig_type
== Rig_type
.HORSE
:
272 ignore_list
= ignore_list
+ horse_ignore_list
273 print("RIDER OF THE APOCALYPSE")
274 elif rig_type
== Rig_type
.SHARK
:
275 ignore_list
= ignore_list
+ shark_ignore_list
277 elif rig_type
== Rig_type
.BIRD
:
278 ignore_list
= ignore_list
+ bird_ignore_list
279 print("WINGS OF LIBERTY")
280 elif rig_type
== Rig_type
.CAT
:
281 ignore_list
= ignore_list
+ cat_ignore_list
283 elif rig_type
== Rig_type
.BIPED
:
284 ignore_list
= ignore_list
+ biped_ignore_list
286 elif rig_type
== Rig_type
.HUMAN
:
287 ignore_list
= ignore_list
+ human_ignore_list
288 print("JUST A HUMAN AFTER ALL")
289 elif rig_type
== Rig_type
.WOLF
:
290 ignore_list
= ignore_list
+ wolf_ignore_list
292 elif rig_type
== Rig_type
.QUAD
:
293 ignore_list
= ignore_list
+ quad_ignore_list
294 print("MYSTERIOUS CREATURE")
295 elif rig_type
== Rig_type
.LEGACY
:
296 ignore_list
= ignore_list
+ rigify_legacy_ignore_list
297 print("LEGACY RIGIFY")
298 elif rig_type
== Rig_type
.PITCHIPOY
:
299 ignore_list
= ignore_list
+ pitchipoy_ignore_list
301 elif rig_type
== Rig_type
.OTHER
:
302 ignore_list
= ignore_list
+ other_ignore_list
303 print("rig non recognized...")
308 # generates edges from vertices used by skin modifier
309 def generate_edges(mesh
, shape_object
, bones
, scale
, connect_mesh
=False, connect_parents
=False,
310 head_ornaments
=False, generate_all
=False):
312 This function adds vertices for all heads and tails
316 alternate_scale_list
= []
322 alternate_scale_idx_list
= list()
324 rig_type
= identify_rig()
325 ignore_list
= prepare_ignore_list(rig_type
, bones
)
327 # edge generator loop
329 # look for rig's hands and their childs
330 if 'hand' in b
.name
.lower():
332 for c
in b
.children_recursive
:
333 alternate_scale_list
.append(c
.name
)
337 for i
in ignore_list
:
338 if i
in b
.name
.lower():
342 if found
and generate_all
is False:
345 # fix for drawing rootbone and relationship lines
346 if 'root' in b
.name
.lower() and generate_all
is False:
349 # ignore any head ornaments
350 if head_ornaments
is False:
351 if b
.parent
is not None:
353 if 'head' in b
.parent
.name
.lower() and not rig_type
== Rig_type
.HUMAN
:
356 if 'face' in b
.parent
.name
.lower() and rig_type
== Rig_type
.HUMAN
:
360 if b
.parent
is not None and b
.parent
.bone
.select
is True and b
.bone
.use_connect
is False:
361 if 'root' in b
.parent
.name
.lower() and generate_all
is False:
364 if 'shoulder' in b
.name
.lower() and connect_mesh
is True:
366 # connect the upper arm directly with chest omitting shoulders
367 if 'shoulder' in b
.parent
.name
.lower() and connect_mesh
is True:
369 vert2
= b
.parent
.parent
.tail
373 vert2
= b
.parent
.tail
377 edges
.append([idx
, idx
+ 1])
379 # also make list of edges made of gaps between the bones
380 for a
in alternate_scale_list
:
382 alternate_scale_idx_list
.append(idx
)
383 alternate_scale_idx_list
.append(idx
+ 1)
386 # for bvh free floating hips and hips correction for rigify and pitchipoy
387 if ((generate_all
is False and 'hip' in b
.name
.lower()) or
388 (generate_all
is False and (b
.name
== 'hips' and rig_type
== Rig_type
.LEGACY
) or
389 (b
.name
== 'spine' and rig_type
== Rig_type
.PITCHIPOY
) or (b
.name
== 'spine' and
390 rig_type
== Rig_type
.HUMAN
) or (b
.name
== 'spine' and rig_type
== Rig_type
.BIPED
))):
398 edges
.append([idx
, idx
+ 1])
400 for a
in alternate_scale_list
:
402 alternate_scale_idx_list
.append(idx
)
403 alternate_scale_idx_list
.append(idx
+ 1)
407 # Create mesh from given verts, faces
408 me
.from_pydata(verts
, edges
, [])
409 # Update mesh with new data
412 # set object scale exact as armature's scale
413 shape_object
.scale
= scale
415 return alternate_scale_idx_list
, rig_type
418 def generate_mesh(shape_object
, size
, thickness
=0.8, finger_thickness
=0.25, sub_level
=1,
419 connect_mesh
=False, connect_parents
=False, generate_all
=False, apply_mod
=True,
420 alternate_scale_idx_list
=[], rig_type
=0, bones
=[]):
422 This function adds modifiers for generated edges
424 total_bones_num
= bpy
.context
.selected_pose_bones_from_active_object
425 selected_bones_num
= len(bones
)
427 bpy
.ops
.object.mode_set(mode
='EDIT')
428 bpy
.ops
.mesh
.select_all(action
='DESELECT')
431 shape_object
.modifiers
.new("Skin", 'SKIN')
432 bpy
.ops
.mesh
.select_all(action
='SELECT')
434 override
= bpy
.context
.copy()
435 for area
in bpy
.context
.screen
.areas
:
436 if area
.type == 'VIEW_3D':
437 for region
in area
.regions
:
438 if region
.type == 'WINDOW':
439 override
['area'] = area
440 override
['region'] = region
441 override
['edit_object'] = bpy
.context
.edit_object
442 override
['scene'] = bpy
.context
.scene
443 override
['active_object'] = shape_object
444 override
['object'] = shape_object
445 override
['modifier'] = bpy
.context
.object.modifiers
448 # calculate optimal thickness for defaults
449 bpy
.ops
.object.skin_root_mark(override
)
450 bpy
.ops
.transform
.skin_resize(
452 value
=(1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10)),
453 constraint_axis
=(False, False, False),
454 orient_type
='GLOBAL',
456 use_proportional_edit
=False,
458 shape_object
.modifiers
["Skin"].use_smooth_shade
= True
459 shape_object
.modifiers
["Skin"].use_x_symmetry
= True
461 # select finger vertices and calculate optimal thickness for fingers to fix proportions
462 if len(alternate_scale_idx_list
) > 0:
463 select_vertices(shape_object
, alternate_scale_idx_list
)
465 bpy
.ops
.object.skin_loose_mark_clear(override
, action
='MARK')
466 # by default set fingers thickness to 25 percent of body thickness
467 bpy
.ops
.transform
.skin_resize(
469 value
=(finger_thickness
, finger_thickness
, finger_thickness
),
470 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
472 use_proportional_edit
=False,
474 # make loose hands only for better topology
476 # bpy.ops.mesh.select_all(action='DESELECT')
479 bpy
.ops
.object.mode_set(mode
='EDIT')
480 bpy
.ops
.mesh
.select_all(action
='DESELECT')
481 bpy
.ops
.mesh
.select_all(action
='SELECT')
482 bpy
.ops
.mesh
.remove_doubles()
484 idx_store
= Idx_Store(rig_type
)
486 # fix rigify and pitchipoy hands topology
487 if connect_mesh
and connect_parents
and generate_all
is False and \
488 (rig_type
== Rig_type
.LEGACY
or rig_type
== Rig_type
.PITCHIPOY
or rig_type
== Rig_type
.HUMAN
) and \
489 selected_bones_num
== total_bones_num
:
490 # thickness will set palm vertex for both hands look pretty
491 corrective_thickness
= 2.5
493 merge_idx
= idx_store
.get_hand_l_merge_idx()
495 select_vertices(shape_object
, merge_idx
)
496 bpy
.ops
.mesh
.merge(type='CENTER')
497 bpy
.ops
.transform
.skin_resize(
499 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
500 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
502 use_proportional_edit
=False,
504 bpy
.ops
.mesh
.select_all(action
='DESELECT')
507 merge_idx
= idx_store
.get_hand_r_merge_idx()
509 select_vertices(shape_object
, merge_idx
)
510 bpy
.ops
.mesh
.merge(type='CENTER')
511 bpy
.ops
.transform
.skin_resize(
513 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
514 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
516 use_proportional_edit
=False,
519 # making hands even more pretty
520 bpy
.ops
.mesh
.select_all(action
='DESELECT')
521 hands_idx
= idx_store
.get_hands_pretty_idx()
523 select_vertices(shape_object
, hands_idx
)
524 # change the thickness to make hands look less blocky and more sexy
525 corrective_thickness
= 0.7
526 bpy
.ops
.transform
.skin_resize(
528 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
529 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
531 use_proportional_edit
=False,
533 bpy
.ops
.mesh
.select_all(action
='DESELECT')
535 # todo optionally take root from rig's hip tail or head depending on scenario
537 root_idx
= idx_store
.get_root_idx()
539 if selected_bones_num
== total_bones_num
:
542 if len(root_idx
) > 0:
543 select_vertices(shape_object
, root_idx
)
544 bpy
.ops
.object.skin_root_mark(override
)
546 # add Subsurf modifier
547 shape_object
.modifiers
.new("Subsurf", 'SUBSURF')
548 shape_object
.modifiers
["Subsurf"].levels
= sub_level
549 shape_object
.modifiers
["Subsurf"].render_levels
= sub_level
551 bpy
.ops
.object.mode_set(mode
='OBJECT')
553 # object mode apply all modifiers
555 bpy
.ops
.object.modifier_apply(override
, apply_as
='DATA', modifier
="Skin")
556 bpy
.ops
.object.modifier_apply(override
, apply_as
='DATA', modifier
="Subsurf")
563 This script will create a custom shape
566 # ### Check if selection is OK ###
567 if len(context
.selected_pose_bones
) == 0 or \
568 len(context
.selected_objects
) == 0 or \
569 context
.selected_objects
[0].type != 'ARMATURE':
570 return {'CANCELLED'}, "No bone selected or the Armature is hidden"
572 scn
= bpy
.context
.scene
575 # initialize the mesh object
576 mesh_name
= context
.selected_objects
[0].name
+ "_mesh"
577 obj_name
= context
.selected_objects
[0].name
+ "_object"
578 armature_object
= context
.object
580 origin
= context
.object.location
581 bone_selection
= context
.selected_pose_bones
585 armature_object
= context
.view_layer
.objects
.active
586 armature_object
.select_set(True)
588 old_pose_pos
= armature_object
.data
.pose_position
589 bpy
.ops
.object.mode_set(mode
='OBJECT')
590 oldLocation
= Vector(armature_object
.location
)
591 oldRotation
= Euler(armature_object
.rotation_euler
)
592 oldScale
= Vector(armature_object
.scale
)
594 bpy
.ops
.object.rotation_clear(clear_delta
=False)
595 bpy
.ops
.object.location_clear(clear_delta
=False)
596 bpy
.ops
.object.scale_clear(clear_delta
=False)
597 if sknfy
.apply_mod
and sknfy
.parent_armature
:
598 armature_object
.data
.pose_position
= 'REST'
600 scale
= bpy
.context
.object.scale
601 size
= bpy
.context
.object.dimensions
[2]
603 bpy
.ops
.object.mode_set(mode
='OBJECT')
604 bpy
.ops
.object.select_all(action
='DESELECT')
606 bpy
.ops
.object.add(type='MESH', enter_editmode
=False, location
=origin
)
608 # get the mesh object
609 ob
= context
.view_layer
.objects
.active
614 # this way we fit mesh and bvh with armature modifier correctly
616 alternate_scale_idx_list
, rig_type
= generate_edges(
617 me
, ob
, bone_selection
, scale
, sknfy
.connect_mesh
,
618 sknfy
.connect_parents
, sknfy
.head_ornaments
,
622 generate_mesh(ob
, size
, sknfy
.thickness
, sknfy
.finger_thickness
, sknfy
.sub_level
,
623 sknfy
.connect_mesh
, sknfy
.connect_parents
, sknfy
.generate_all
,
624 sknfy
.apply_mod
, alternate_scale_idx_list
, rig_type
, bone_selection
)
626 # parent mesh with armature only if modifiers are applied
627 if sknfy
.apply_mod
and sknfy
.parent_armature
:
628 bpy
.ops
.object.mode_set(mode
='OBJECT')
629 bpy
.ops
.object.select_all(action
='DESELECT')
631 armature_object
.select_set(True)
632 bpy
.context
.view_layer
.objects
.active
= armature_object
634 bpy
.ops
.object.parent_set(type='ARMATURE_AUTO')
635 armature_object
.data
.pose_position
= old_pose_pos
636 armature_object
.select_set(False)
638 bpy
.ops
.object.mode_set(mode
='OBJECT')
639 ob
.location
= oldLocation
640 ob
.rotation_euler
= oldRotation
643 armature_object
.select_set(True)
644 scn
.objects
.active
= armature_object
646 armature_object
.location
= oldLocation
647 armature_object
.rotation_euler
= oldRotation
648 armature_object
.scale
= oldScale
649 bpy
.ops
.object.mode_set(mode
='POSE')
651 return {'FINISHED'}, me
654 class BONE_OT_custom_shape(Operator
):
655 bl_idname
= "object.skinify_rig"
656 bl_label
= "Skinify Rig"
657 bl_description
= "Creates a mesh object at the selected bones positions"
658 bl_options
= {'UNDO', 'INTERNAL'}
661 def poll(cls
, context
):
662 return context
.active_object
is not None
664 def execute(self
, context
):
666 if Mesh
[0] == {'CANCELLED'}:
667 self
.report({'WARNING'}, Mesh
[1])
670 self
.report({'INFO'}, Mesh
[1].name
+ " has been created")
675 class BONE_PT_custom_shape(Panel
):
676 bl_space_type
= "VIEW_3D"
677 bl_region_type
= "UI"
678 bl_category
= "Create"
679 # bl_context = "bone"
680 bl_label
= "Skinify Rig"
681 bl_options
= {'DEFAULT_CLOSED'}
684 def poll(cls
, context
):
686 return ob
and ob
.mode
== 'POSE' #and context.bone
688 def draw(self
, context
):
690 scn
= context
.scene
.skinify
693 row
.operator("object.skinify_rig", text
="Add Shape", icon
='BONE_DATA')
695 split
= layout
.split(factor
=0.3)
696 split
.label(text
="Thickness:")
697 split
.prop(scn
, "thickness", text
="Body", icon
='MOD_SKIN')
698 split
.prop(scn
, "finger_thickness", text
="Fingers", icon
='HAND')
700 split
= layout
.split(factor
=0.3)
701 split
.label(text
="Mesh Density:")
702 split
.prop(scn
, "sub_level", icon
='MESH_ICOSPHERE')
705 row
.prop(scn
, "connect_mesh", icon
='EDITMODE_HLT')
706 row
.prop(scn
, "connect_parents", icon
='CONSTRAINT_BONE')
708 row
.prop(scn
, "head_ornaments", icon
='GROUP_BONE')
709 row
.prop(scn
, "generate_all", icon
='GROUP_BONE')
711 row
.prop(scn
, "apply_mod", icon
='FILE_TICK')
714 row
.prop(scn
, "parent_armature", icon
='POSE_HLT')
717 # define the scene properties in a group - call them with context.scene.skinify
718 class Skinify_Properties(PropertyGroup
):
719 sub_level
: IntProperty(
723 description
="Mesh density"
725 thickness
: FloatProperty(
729 description
="Adjust shape thickness"
731 finger_thickness
: FloatProperty(
732 name
="Finger Thickness",
735 description
="Adjust finger thickness relative to body"
737 connect_mesh
: BoolProperty(
740 description
="Makes solid shape from bone chains"
742 connect_parents
: BoolProperty(
745 description
="Fills the gaps between parented bones"
747 generate_all
: BoolProperty(
750 description
="Generates shapes from all bones"
752 head_ornaments
: BoolProperty(
753 name
="Head Ornaments",
755 description
="Includes head ornaments"
757 apply_mod
: BoolProperty(
758 name
="Apply Modifiers",
760 description
="Applies Modifiers to mesh"
762 parent_armature
: BoolProperty(
763 name
="Parent Armature",
765 description
="Applies mesh to Armature"
772 def startup_init(dummy
):
777 bpy
.utils
.register_class(BONE_OT_custom_shape
)
778 bpy
.utils
.register_class(BONE_PT_custom_shape
)
779 bpy
.utils
.register_class(Skinify_Properties
)
781 bpy
.types
.Scene
.skinify
= PointerProperty(
782 type=Skinify_Properties
785 bpy
.app
.handlers
.load_post
.append(startup_init
)
789 bpy
.utils
.unregister_class(BONE_OT_custom_shape
)
790 bpy
.utils
.unregister_class(BONE_PT_custom_shape
)
791 bpy
.utils
.unregister_class(Skinify_Properties
)
793 # cleanup the handler
794 bpy
.app
.handlers
.load_post
.remove(startup_init
)
796 del bpy
.types
.Scene
.skinify
799 if __name__
== "__main__":