1 # SPDX-License-Identifier: GPL-2.0-or-later
5 "author": "Albert Makac (karab44)",
8 "location": "Pose Mode > Sidebar > Create Tab",
9 "description": "Creates a mesh object from selected bones",
10 "warning": "Work in progress",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/object/skinify.html",
16 from bpy
.props
import (
22 from bpy
.types
import (
27 from mathutils
import (
31 from bpy
.app
.handlers
import persistent
34 # can the armature data properties group_prop and row be fetched directly from the rigify script?
36 (1, 5), (2, 4), (3, 0), (4, 3), (5, 4), (1, 0), (1, 0), (7, 2), (8, 5), (9, 4), \
37 (7, 2), (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), \
38 (13, 6), (1, 4), (14, 6), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
41 (1, 5), (2, 4), (1, 0), (3, 3), (4, 4), (5, 6), (6, 5), (7, 4), (6, 5), (7, 4), \
42 (8, 3), (9, 4), (1, 0), (1, 6), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), \
43 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
46 (1, 6), (2, 4), (1, 0), (3, 3), (4, 4), (1, 0), (1, 0), (6, 5), (8, 0), (7, 4), (6, 5), \
47 (8, 0), (7, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
48 (13, 6), (14, 4), (1, 0), (8, 6), (1, 0), (1, 0), (1, 0), (14, 1),
51 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
52 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 3), (14, 4), \
53 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (16, 1),
56 (1, 0), (1, 0), (1, 0), (3, 3), (4, 4), (1, 0), (1, 0), (7, 2), (8, 5), (9, 4), (7, 2), \
57 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
58 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
61 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
62 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (1, 0), (1, 0), \
63 (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
66 (1, 5), (2, 2), (2, 3), (3, 3), (4, 4), (5, 6), (6, 4), (7, 2), (8, 5), (9, 4), (7, 2), \
67 (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 6), (1, 0), \
68 (13, 0), (13, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
71 (1, 0), (2, 0), (2, 0), (3, 3), (4, 4), (5, 0), (6, 0), (7, 2), (8, 5), (9, 4), \
72 (7, 2), (8, 5), (9, 4), (10, 2), (11, 5), (12, 4), (10, 2), (11, 5), (12, 4), (13, 6), \
73 (1, 0), (13, 0), (13, 0), (1, 0), (1, 0), (1, 0), (1, 0), (1, 0), (14, 1),
76 (1, None), (1, None), (2, None), (1, None), (3, None), (3, None), (4, None), (5, None), \
77 (6, None), (4, None), (5, None), (6, None), (7, None), (8, None), (9, None), (7, None), \
78 (8, None), (9, None), (1, None), (1, None), (1, None), (1, None), (1, None), (1, None), \
79 (1, None), (1, None), (1, None), (1, None),
82 (1, None), (2, None), (2, None), (3, None), (4, None), (5, None), (6, None), (7, None), \
83 (8, None), (9, None), (7, None), (8, None), (9, None), (10, None), (11, None), (12, None), \
84 (10, None), (11, None), (12, None), (1, None), (1, None), (1, None), (1, None), (1, None), \
85 (1, None), (1, None), (1, None), (1, None),
87 rigify_data
= horse_data
, shark_data
, bird_data
, cat_data
, biped_data
, human_data
, \
88 wolf_data
, quadruped_data
, human_legacy_data
, pitchipoy_data
105 rig_type
= Rig_type
.OTHER
108 class Idx_Store(object):
109 def __init__(self
, rig_type
):
110 self
.rig_type
= rig_type
111 self
.hand_r_merge
= []
112 self
.hand_l_merge
= []
113 self
.hands_pretty
= []
116 if not self
.rig_type
== Rig_type
.LEGACY
and \
117 not self
.rig_type
== Rig_type
.HUMAN
and \
118 not self
.rig_type
== Rig_type
.PITCHIPOY
:
121 if self
.rig_type
== Rig_type
.LEGACY
:
122 self
.hand_l_merge
= [7, 12, 16, 21, 26, 27]
123 self
.hand_r_merge
= [30, 31, 36, 40, 45, 50]
124 self
.hands_pretty
= [6, 29]
127 if self
.rig_type
== Rig_type
.HUMAN
or self
.rig_type
== Rig_type
.PITCHIPOY
:
128 self
.hand_l_merge
= [9, 10, 15, 19, 24, 29]
129 self
.hand_r_merge
= [32, 33, 37, 42, 47, 52]
130 self
.hands_pretty
= [8, 31]
133 def get_all_idx(self
):
134 return self
.hand_l_merge
, self
.hand_r_merge
, self
.hands_pretty
, self
.root
136 def get_hand_l_merge_idx(self
):
137 return self
.hand_l_merge
139 def get_hand_r_merge_idx(self
):
140 return self
.hand_r_merge
142 def get_hands_pretty_idx(self
):
143 return self
.hands_pretty
145 def get_root_idx(self
):
149 # initialize properties
151 # additional check - this should be a rare case if the handler
152 # wasn't removed for some reason and the add-on is not toggled on/off
153 if hasattr(bpy
.types
.Scene
, "skinify"):
154 scn
= bpy
.context
.scene
.skinify
156 scn
.connect_mesh
= False
157 scn
.connect_parents
= False
158 scn
.generate_all
= False
160 scn
.finger_thickness
= 0.25
162 scn
.parent_armature
= True
167 def select_vertices(mesh_obj
, idx
):
168 bpy
.context
.view_layer
.objects
.active
= mesh_obj
170 bpy
.ops
.object.mode_set(mode
='EDIT')
171 bpy
.ops
.mesh
.select_all(action
='DESELECT')
172 bpy
.ops
.object.mode_set(mode
='OBJECT')
175 mesh_obj
.data
.vertices
[i
].select
= True
177 selectedVerts
= [v
.index
for v
in mesh_obj
.data
.vertices
if v
.select
]
179 bpy
.ops
.object.mode_set(mode
=mode
)
184 if 'rigify_layers' not in bpy
.context
.object.data
:
185 return Rig_type
.OTHER
# non recognized
187 LEGACY_LAYERS_SIZE
= 28
188 layers
= bpy
.context
.object.data
['rigify_layers']
190 for type, rig
in enumerate(rigify_data
):
194 if len(layers
) == LEGACY_LAYERS_SIZE
and 'group_prop' not in props
:
196 if props
['row'] != rig
[index
][0] or rig
[index
][1] is not None:
199 elif (props
['row'] != rig
[index
][0]) or (props
['group_prop'] != rig
[index
][1]):
202 # SUCCESS if reach the end
203 if index
== len(layers
) - 1:
204 return Rig_type(type)
208 return Rig_type
.OTHER
211 def prepare_ignore_list(rig_type
, bones
):
212 # detect the head, face, hands, breast, heels or other exceptionary bones to exclusion or customization
213 common_ignore_list
= ['eye', 'heel', 'breast', 'root']
215 # edit these lists to suits your taste
217 horse_ignore_list
= ['chest', 'belly', 'pelvis', 'jaw', 'nose', 'skull', 'ear.']
219 shark_ignore_list
= ['jaw']
222 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
223 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue', 'beak'
226 'face', 'belly' 'pelvis.C', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
227 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
229 biped_ignore_list
= ['pelvis']
231 human_ignore_list
= [
232 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
233 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
236 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
237 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
240 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
241 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
243 rigify_legacy_ignore_list
= []
245 pitchipoy_ignore_list
= [
246 'face', 'pelvis', 'nose', 'lip', 'jaw', 'chin', 'ear.', 'brow',
247 'lid', 'forehead', 'temple', 'cheek', 'teeth', 'tongue'
250 other_ignore_list
= []
252 ignore_list
= common_ignore_list
254 if rig_type
== Rig_type
.HORSE
:
255 ignore_list
= ignore_list
+ horse_ignore_list
256 print("RIDER OF THE APOCALYPSE")
257 elif rig_type
== Rig_type
.SHARK
:
258 ignore_list
= ignore_list
+ shark_ignore_list
260 elif rig_type
== Rig_type
.BIRD
:
261 ignore_list
= ignore_list
+ bird_ignore_list
262 print("WINGS OF LIBERTY")
263 elif rig_type
== Rig_type
.CAT
:
264 ignore_list
= ignore_list
+ cat_ignore_list
266 elif rig_type
== Rig_type
.BIPED
:
267 ignore_list
= ignore_list
+ biped_ignore_list
269 elif rig_type
== Rig_type
.HUMAN
:
270 ignore_list
= ignore_list
+ human_ignore_list
271 print("JUST A HUMAN AFTER ALL")
272 elif rig_type
== Rig_type
.WOLF
:
273 ignore_list
= ignore_list
+ wolf_ignore_list
275 elif rig_type
== Rig_type
.QUAD
:
276 ignore_list
= ignore_list
+ quad_ignore_list
277 print("MYSTERIOUS CREATURE")
278 elif rig_type
== Rig_type
.LEGACY
:
279 ignore_list
= ignore_list
+ rigify_legacy_ignore_list
280 print("LEGACY RIGIFY")
281 elif rig_type
== Rig_type
.PITCHIPOY
:
282 ignore_list
= ignore_list
+ pitchipoy_ignore_list
284 elif rig_type
== Rig_type
.OTHER
:
285 ignore_list
= ignore_list
+ other_ignore_list
286 print("rig non recognized...")
291 # generates edges from vertices used by skin modifier
292 def generate_edges(mesh
, shape_object
, bones
, scale
, connect_mesh
=False, connect_parents
=False,
293 head_ornaments
=False, generate_all
=False):
295 This function adds vertices for all heads and tails
299 alternate_scale_list
= []
305 alternate_scale_idx_list
= list()
307 rig_type
= identify_rig()
308 ignore_list
= prepare_ignore_list(rig_type
, bones
)
310 # edge generator loop
312 # look for rig's hands and their childs
313 if 'hand' in b
.name
.lower():
315 for c
in b
.children_recursive
:
316 alternate_scale_list
.append(c
.name
)
320 for i
in ignore_list
:
321 if i
in b
.name
.lower():
325 if found
and generate_all
is False:
328 # fix for drawing rootbone and relationship lines
329 if 'root' in b
.name
.lower() and generate_all
is False:
332 # ignore any head ornaments
333 if head_ornaments
is False:
334 if b
.parent
is not None:
336 if 'head' in b
.parent
.name
.lower() and not rig_type
== Rig_type
.HUMAN
:
339 if 'face' in b
.parent
.name
.lower() and rig_type
== Rig_type
.HUMAN
:
343 if b
.parent
is not None and b
.parent
.bone
.select
is True and b
.bone
.use_connect
is False:
344 if 'root' in b
.parent
.name
.lower() and generate_all
is False:
347 if 'shoulder' in b
.name
.lower() and connect_mesh
is True:
349 # connect the upper arm directly with chest omitting shoulders
350 if 'shoulder' in b
.parent
.name
.lower() and connect_mesh
is True:
352 vert2
= b
.parent
.parent
.tail
356 vert2
= b
.parent
.tail
360 edges
.append([idx
, idx
+ 1])
362 # also make list of edges made of gaps between the bones
363 for a
in alternate_scale_list
:
365 alternate_scale_idx_list
.append(idx
)
366 alternate_scale_idx_list
.append(idx
+ 1)
369 # for bvh free floating hips and hips correction for rigify and pitchipoy
370 if ((generate_all
is False and 'hip' in b
.name
.lower()) or
371 (generate_all
is False and (b
.name
== 'hips' and rig_type
== Rig_type
.LEGACY
) or
372 (b
.name
== 'spine' and rig_type
== Rig_type
.PITCHIPOY
) or (b
.name
== 'spine' and
373 rig_type
== Rig_type
.HUMAN
) or (b
.name
== 'spine' and rig_type
== Rig_type
.BIPED
))):
381 edges
.append([idx
, idx
+ 1])
383 for a
in alternate_scale_list
:
385 alternate_scale_idx_list
.append(idx
)
386 alternate_scale_idx_list
.append(idx
+ 1)
390 # Create mesh from given verts, faces
391 me
.from_pydata(verts
, edges
, [])
392 # Update mesh with new data
395 # set object scale exact as armature's scale
396 shape_object
.scale
= scale
398 return alternate_scale_idx_list
, rig_type
401 def generate_mesh(shape_object
, size
, thickness
=0.8, finger_thickness
=0.25, sub_level
=1,
402 connect_mesh
=False, connect_parents
=False, generate_all
=False, apply_mod
=True,
403 alternate_scale_idx_list
=[], rig_type
=0, bones
=[]):
405 This function adds modifiers for generated edges
407 total_bones_num
= bpy
.context
.selected_pose_bones_from_active_object
408 selected_bones_num
= len(bones
)
410 bpy
.ops
.object.mode_set(mode
='EDIT')
411 bpy
.ops
.mesh
.select_all(action
='DESELECT')
414 shape_object
.modifiers
.new("Skin", 'SKIN')
415 bpy
.ops
.mesh
.select_all(action
='SELECT')
417 override
= bpy
.context
.copy()
418 for area
in bpy
.context
.screen
.areas
:
419 if area
.type == 'VIEW_3D':
420 for region
in area
.regions
:
421 if region
.type == 'WINDOW':
422 override
['area'] = area
423 override
['region'] = region
424 override
['edit_object'] = bpy
.context
.edit_object
425 override
['scene'] = bpy
.context
.scene
426 override
['active_object'] = shape_object
427 override
['object'] = shape_object
428 override
['modifier'] = bpy
.context
.object.modifiers
431 # calculate optimal thickness for defaults
432 bpy
.ops
.object.skin_root_mark(override
)
433 bpy
.ops
.transform
.skin_resize(
435 value
=(1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10)),
436 constraint_axis
=(False, False, False),
437 orient_type
='GLOBAL',
439 use_proportional_edit
=False,
441 shape_object
.modifiers
["Skin"].use_smooth_shade
= True
442 shape_object
.modifiers
["Skin"].use_x_symmetry
= True
444 # select finger vertices and calculate optimal thickness for fingers to fix proportions
445 if len(alternate_scale_idx_list
) > 0:
446 select_vertices(shape_object
, alternate_scale_idx_list
)
448 bpy
.ops
.object.skin_loose_mark_clear(override
, action
='MARK')
449 # by default set fingers thickness to 25 percent of body thickness
450 bpy
.ops
.transform
.skin_resize(
452 value
=(finger_thickness
, finger_thickness
, finger_thickness
),
453 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
455 use_proportional_edit
=False,
457 # make loose hands only for better topology
459 # bpy.ops.mesh.select_all(action='DESELECT')
462 bpy
.ops
.object.mode_set(mode
='EDIT')
463 bpy
.ops
.mesh
.select_all(action
='DESELECT')
464 bpy
.ops
.mesh
.select_all(action
='SELECT')
465 bpy
.ops
.mesh
.remove_doubles()
467 idx_store
= Idx_Store(rig_type
)
469 # fix rigify and pitchipoy hands topology
470 if connect_mesh
and connect_parents
and generate_all
is False and \
471 (rig_type
== Rig_type
.LEGACY
or rig_type
== Rig_type
.PITCHIPOY
or rig_type
== Rig_type
.HUMAN
) and \
472 selected_bones_num
== total_bones_num
:
473 # thickness will set palm vertex for both hands look pretty
474 corrective_thickness
= 2.5
476 merge_idx
= idx_store
.get_hand_l_merge_idx()
478 select_vertices(shape_object
, merge_idx
)
479 bpy
.ops
.mesh
.merge(type='CENTER')
480 bpy
.ops
.transform
.skin_resize(
482 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
483 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
485 use_proportional_edit
=False,
487 bpy
.ops
.mesh
.select_all(action
='DESELECT')
490 merge_idx
= idx_store
.get_hand_r_merge_idx()
492 select_vertices(shape_object
, merge_idx
)
493 bpy
.ops
.mesh
.merge(type='CENTER')
494 bpy
.ops
.transform
.skin_resize(
496 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
497 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
499 use_proportional_edit
=False,
502 # making hands even more pretty
503 bpy
.ops
.mesh
.select_all(action
='DESELECT')
504 hands_idx
= idx_store
.get_hands_pretty_idx()
506 select_vertices(shape_object
, hands_idx
)
507 # change the thickness to make hands look less blocky and more sexy
508 corrective_thickness
= 0.7
509 bpy
.ops
.transform
.skin_resize(
511 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
512 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
514 use_proportional_edit
=False,
516 bpy
.ops
.mesh
.select_all(action
='DESELECT')
518 # todo optionally take root from rig's hip tail or head depending on scenario
520 root_idx
= idx_store
.get_root_idx()
522 if selected_bones_num
== total_bones_num
:
525 if len(root_idx
) > 0:
526 select_vertices(shape_object
, root_idx
)
527 bpy
.ops
.object.skin_root_mark(override
)
529 # add Subsurf modifier
530 shape_object
.modifiers
.new("Subsurf", 'SUBSURF')
531 shape_object
.modifiers
["Subsurf"].levels
= sub_level
532 shape_object
.modifiers
["Subsurf"].render_levels
= sub_level
534 bpy
.ops
.object.mode_set(mode
='OBJECT')
536 # object mode apply all modifiers
538 bpy
.ops
.object.modifier_apply(override
, modifier
="Skin")
539 bpy
.ops
.object.modifier_apply(override
, modifier
="Subsurf")
546 This script will create a custom shape
549 # ### Check if selection is OK ###
550 if len(context
.selected_pose_bones
) == 0 or \
551 len(context
.selected_objects
) == 0 or \
552 context
.selected_objects
[0].type != 'ARMATURE':
553 return {'CANCELLED'}, "No bone selected or the Armature is hidden"
555 scn
= bpy
.context
.scene
558 # initialize the mesh object
559 mesh_name
= context
.selected_objects
[0].name
+ "_mesh"
560 obj_name
= context
.selected_objects
[0].name
+ "_object"
561 armature_object
= context
.object
563 origin
= context
.object.location
564 bone_selection
= context
.selected_pose_bones
568 armature_object
= context
.view_layer
.objects
.active
569 armature_object
.select_set(True)
571 old_pose_pos
= armature_object
.data
.pose_position
572 bpy
.ops
.object.mode_set(mode
='OBJECT')
573 oldLocation
= Vector(armature_object
.location
)
574 oldRotation
= Euler(armature_object
.rotation_euler
)
575 oldScale
= Vector(armature_object
.scale
)
577 bpy
.ops
.object.rotation_clear(clear_delta
=False)
578 bpy
.ops
.object.location_clear(clear_delta
=False)
579 bpy
.ops
.object.scale_clear(clear_delta
=False)
580 if sknfy
.apply_mod
and sknfy
.parent_armature
:
581 armature_object
.data
.pose_position
= 'REST'
583 scale
= bpy
.context
.object.scale
584 size
= bpy
.context
.object.dimensions
[2]
586 bpy
.ops
.object.mode_set(mode
='OBJECT')
587 bpy
.ops
.object.select_all(action
='DESELECT')
589 bpy
.ops
.object.add(type='MESH', enter_editmode
=False, location
=origin
)
591 # get the mesh object
592 ob
= context
.view_layer
.objects
.active
597 # this way we fit mesh and bvh with armature modifier correctly
599 alternate_scale_idx_list
, rig_type
= generate_edges(
600 me
, ob
, bone_selection
, scale
, sknfy
.connect_mesh
,
601 sknfy
.connect_parents
, sknfy
.head_ornaments
,
605 generate_mesh(ob
, size
, sknfy
.thickness
, sknfy
.finger_thickness
, sknfy
.sub_level
,
606 sknfy
.connect_mesh
, sknfy
.connect_parents
, sknfy
.generate_all
,
607 sknfy
.apply_mod
, alternate_scale_idx_list
, rig_type
, bone_selection
)
609 # parent mesh with armature only if modifiers are applied
610 if sknfy
.apply_mod
and sknfy
.parent_armature
:
611 bpy
.ops
.object.mode_set(mode
='OBJECT')
612 bpy
.ops
.object.select_all(action
='DESELECT')
614 armature_object
.select_set(True)
615 bpy
.context
.view_layer
.objects
.active
= armature_object
617 bpy
.ops
.object.parent_set(type='ARMATURE_AUTO')
618 armature_object
.data
.pose_position
= old_pose_pos
619 armature_object
.select_set(False)
621 bpy
.ops
.object.mode_set(mode
='OBJECT')
622 ob
.location
= oldLocation
623 ob
.rotation_euler
= oldRotation
626 armature_object
.select_set(True)
627 scn
.objects
.active
= armature_object
629 armature_object
.location
= oldLocation
630 armature_object
.rotation_euler
= oldRotation
631 armature_object
.scale
= oldScale
632 bpy
.ops
.object.mode_set(mode
='POSE')
634 return {'FINISHED'}, me
637 class BONE_OT_custom_shape(Operator
):
638 bl_idname
= "object.skinify_rig"
639 bl_label
= "Skinify Rig"
640 bl_description
= "Creates a mesh object at the selected bones positions"
641 bl_options
= {'UNDO', 'INTERNAL'}
644 def poll(cls
, context
):
645 return context
.active_object
is not None
647 def execute(self
, context
):
649 if Mesh
[0] == {'CANCELLED'}:
650 self
.report({'WARNING'}, Mesh
[1])
653 self
.report({'INFO'}, Mesh
[1].name
+ " has been created")
658 class BONE_PT_custom_shape(Panel
):
659 bl_space_type
= "VIEW_3D"
660 bl_region_type
= "UI"
661 bl_category
= "Create"
662 # bl_context = "bone"
663 bl_label
= "Skinify Rig"
664 bl_options
= {'DEFAULT_CLOSED'}
667 def poll(cls
, context
):
669 return ob
and ob
.mode
== 'POSE' #and context.bone
671 def draw(self
, context
):
673 scn
= context
.scene
.skinify
676 row
.operator("object.skinify_rig", text
="Add Shape", icon
='BONE_DATA')
678 split
= layout
.split(factor
=0.3)
679 split
.label(text
="Thickness:")
680 split
.prop(scn
, "thickness", text
="Body", icon
='MOD_SKIN')
681 split
.prop(scn
, "finger_thickness", text
="Fingers", icon
='HAND')
683 split
= layout
.split(factor
=0.3)
684 split
.label(text
="Mesh Density:")
685 split
.prop(scn
, "sub_level", icon
='MESH_ICOSPHERE')
688 row
.prop(scn
, "connect_mesh", icon
='EDITMODE_HLT')
689 row
.prop(scn
, "connect_parents", icon
='CONSTRAINT_BONE')
691 row
.prop(scn
, "head_ornaments", icon
='GROUP_BONE')
692 row
.prop(scn
, "generate_all", icon
='GROUP_BONE')
694 row
.prop(scn
, "apply_mod", icon
='FILE_TICK')
697 row
.prop(scn
, "parent_armature", icon
='POSE_HLT')
700 # define the scene properties in a group - call them with context.scene.skinify
701 class Skinify_Properties(PropertyGroup
):
702 sub_level
: IntProperty(
706 description
="Mesh density"
708 thickness
: FloatProperty(
712 description
="Adjust shape thickness"
714 finger_thickness
: FloatProperty(
715 name
="Finger Thickness",
718 description
="Adjust finger thickness relative to body"
720 connect_mesh
: BoolProperty(
723 description
="Makes solid shape from bone chains"
725 connect_parents
: BoolProperty(
728 description
="Fills the gaps between parented bones"
730 generate_all
: BoolProperty(
733 description
="Generates shapes from all bones"
735 head_ornaments
: BoolProperty(
736 name
="Head Ornaments",
738 description
="Includes head ornaments"
740 apply_mod
: BoolProperty(
741 name
="Apply Modifiers",
743 description
="Applies Modifiers to mesh"
745 parent_armature
: BoolProperty(
746 name
="Parent Armature",
748 description
="Applies mesh to Armature"
755 def startup_init(dummy
):
760 bpy
.utils
.register_class(BONE_OT_custom_shape
)
761 bpy
.utils
.register_class(BONE_PT_custom_shape
)
762 bpy
.utils
.register_class(Skinify_Properties
)
764 bpy
.types
.Scene
.skinify
= PointerProperty(
765 type=Skinify_Properties
768 bpy
.app
.handlers
.load_post
.append(startup_init
)
772 bpy
.utils
.unregister_class(BONE_OT_custom_shape
)
773 bpy
.utils
.unregister_class(BONE_PT_custom_shape
)
774 bpy
.utils
.unregister_class(Skinify_Properties
)
776 # cleanup the handler
777 bpy
.app
.handlers
.load_post
.remove(startup_init
)
779 del bpy
.types
.Scene
.skinify
782 if __name__
== "__main__":