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 skin_modifier
= shape_object
.modifiers
.new("Skin", 'SKIN')
415 bpy
.ops
.mesh
.select_all(action
='SELECT')
417 # calculate optimal thickness for defaults
418 bpy
.ops
.object.skin_root_mark()
419 bpy
.ops
.transform
.skin_resize(
420 value
=(1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10)),
421 constraint_axis
=(False, False, False),
422 orient_type
='GLOBAL',
424 use_proportional_edit
=False,
426 skin_modifier
.use_smooth_shade
= True
427 skin_modifier
.use_x_symmetry
= True
429 # select finger vertices and calculate optimal thickness for fingers to fix proportions
430 if len(alternate_scale_idx_list
) > 0:
431 select_vertices(shape_object
, alternate_scale_idx_list
)
433 bpy
.ops
.object.skin_loose_mark_clear(action
='MARK')
434 # by default set fingers thickness to 25 percent of body thickness
435 bpy
.ops
.transform
.skin_resize(
436 value
=(finger_thickness
, finger_thickness
, finger_thickness
),
437 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
439 use_proportional_edit
=False,
441 # make loose hands only for better topology
443 # bpy.ops.mesh.select_all(action='DESELECT')
446 bpy
.ops
.object.mode_set(mode
='EDIT')
447 bpy
.ops
.mesh
.select_all(action
='DESELECT')
448 bpy
.ops
.mesh
.select_all(action
='SELECT')
449 bpy
.ops
.mesh
.remove_doubles()
451 idx_store
= Idx_Store(rig_type
)
453 # fix rigify and pitchipoy hands topology
454 if connect_mesh
and connect_parents
and generate_all
is False and \
455 (rig_type
== Rig_type
.LEGACY
or rig_type
== Rig_type
.PITCHIPOY
or rig_type
== Rig_type
.HUMAN
) and \
456 selected_bones_num
== total_bones_num
:
457 # thickness will set palm vertex for both hands look pretty
458 corrective_thickness
= 2.5
460 merge_idx
= idx_store
.get_hand_l_merge_idx()
462 select_vertices(shape_object
, merge_idx
)
463 bpy
.ops
.mesh
.merge(type='CENTER')
464 bpy
.ops
.transform
.skin_resize(
465 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
466 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
468 use_proportional_edit
=False,
470 bpy
.ops
.mesh
.select_all(action
='DESELECT')
473 merge_idx
= idx_store
.get_hand_r_merge_idx()
475 select_vertices(shape_object
, merge_idx
)
476 bpy
.ops
.mesh
.merge(type='CENTER')
477 bpy
.ops
.transform
.skin_resize(
478 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
479 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
481 use_proportional_edit
=False,
484 # making hands even more pretty
485 bpy
.ops
.mesh
.select_all(action
='DESELECT')
486 hands_idx
= idx_store
.get_hands_pretty_idx()
488 select_vertices(shape_object
, hands_idx
)
489 # change the thickness to make hands look less blocky and more sexy
490 corrective_thickness
= 0.7
491 bpy
.ops
.transform
.skin_resize(
492 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
493 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
495 use_proportional_edit
=False,
497 bpy
.ops
.mesh
.select_all(action
='DESELECT')
499 # todo optionally take root from rig's hip tail or head depending on scenario
501 root_idx
= idx_store
.get_root_idx()
503 if selected_bones_num
== total_bones_num
:
506 if len(root_idx
) > 0:
507 select_vertices(shape_object
, root_idx
)
508 bpy
.ops
.object.skin_root_mark()
510 # add Subsurf modifier
511 subsurf_modifier
= shape_object
.modifiers
.new("Subsurf", 'SUBSURF')
512 subsurf_modifier
.levels
= sub_level
513 subsurf_modifier
.render_levels
= sub_level
515 bpy
.ops
.object.mode_set(mode
='OBJECT')
516 bpy
.context
.view_layer
.update()
518 # object mode apply all modifiers
520 bpy
.ops
.object.modifier_apply(modifier
=skin_modifier
.name
)
521 bpy
.ops
.object.modifier_apply(modifier
=subsurf_modifier
.name
)
528 This script will create a custom shape
531 # ### Check if selection is OK ###
532 if len(context
.selected_pose_bones
) == 0 or \
533 len(context
.selected_objects
) == 0 or \
534 context
.selected_objects
[0].type != 'ARMATURE':
535 return {'CANCELLED'}, "No bone selected or the Armature is hidden"
537 scn
= bpy
.context
.scene
540 # initialize the mesh object
541 mesh_name
= context
.selected_objects
[0].name
+ "_mesh"
542 obj_name
= context
.selected_objects
[0].name
+ "_object"
543 armature_object
= context
.object
545 origin
= context
.object.location
546 bone_selection
= context
.selected_pose_bones
550 armature_object
= context
.view_layer
.objects
.active
551 armature_object
.select_set(True)
553 old_pose_pos
= armature_object
.data
.pose_position
554 bpy
.ops
.object.mode_set(mode
='OBJECT')
555 oldLocation
= Vector(armature_object
.location
)
556 oldRotation
= Euler(armature_object
.rotation_euler
)
557 oldScale
= Vector(armature_object
.scale
)
559 bpy
.ops
.object.rotation_clear(clear_delta
=False)
560 bpy
.ops
.object.location_clear(clear_delta
=False)
561 bpy
.ops
.object.scale_clear(clear_delta
=False)
562 if sknfy
.apply_mod
and sknfy
.parent_armature
:
563 armature_object
.data
.pose_position
= 'REST'
565 scale
= bpy
.context
.object.scale
566 size
= bpy
.context
.object.dimensions
[2]
568 bpy
.ops
.object.mode_set(mode
='OBJECT')
569 bpy
.ops
.object.select_all(action
='DESELECT')
571 bpy
.ops
.object.add(type='MESH', enter_editmode
=False, location
=origin
)
573 # get the mesh object
574 ob
= context
.view_layer
.objects
.active
579 # this way we fit mesh and bvh with armature modifier correctly
581 alternate_scale_idx_list
, rig_type
= generate_edges(
582 me
, ob
, bone_selection
, scale
, sknfy
.connect_mesh
,
583 sknfy
.connect_parents
, sknfy
.head_ornaments
,
587 generate_mesh(ob
, size
, sknfy
.thickness
, sknfy
.finger_thickness
, sknfy
.sub_level
,
588 sknfy
.connect_mesh
, sknfy
.connect_parents
, sknfy
.generate_all
,
589 sknfy
.apply_mod
, alternate_scale_idx_list
, rig_type
, bone_selection
)
591 # parent mesh with armature only if modifiers are applied
592 if sknfy
.apply_mod
and sknfy
.parent_armature
:
593 bpy
.ops
.object.mode_set(mode
='OBJECT')
594 bpy
.ops
.object.select_all(action
='DESELECT')
596 armature_object
.select_set(True)
597 bpy
.context
.view_layer
.objects
.active
= armature_object
599 bpy
.ops
.object.parent_set(type='ARMATURE_AUTO')
600 armature_object
.data
.pose_position
= old_pose_pos
601 armature_object
.select_set(False)
603 bpy
.ops
.object.mode_set(mode
='OBJECT')
604 ob
.location
= oldLocation
605 ob
.rotation_euler
= oldRotation
608 armature_object
.select_set(True)
609 context
.view_layer
.objects
.active
= armature_object
611 armature_object
.location
= oldLocation
612 armature_object
.rotation_euler
= oldRotation
613 armature_object
.scale
= oldScale
614 bpy
.ops
.object.mode_set(mode
='POSE')
616 return {'FINISHED'}, me
619 class BONE_OT_custom_shape(Operator
):
620 bl_idname
= "object.skinify_rig"
621 bl_label
= "Skinify Rig"
622 bl_description
= "Creates a mesh object at the selected bones positions"
623 bl_options
= {'UNDO', 'INTERNAL'}
626 def poll(cls
, context
):
627 return context
.active_object
is not None
629 def execute(self
, context
):
631 if Mesh
[0] == {'CANCELLED'}:
632 self
.report({'WARNING'}, Mesh
[1])
635 self
.report({'INFO'}, Mesh
[1].name
+ " has been created")
640 class BONE_PT_custom_shape(Panel
):
641 bl_space_type
= "VIEW_3D"
642 bl_region_type
= "UI"
643 bl_category
= "Create"
644 # bl_context = "bone"
645 bl_label
= "Skinify Rig"
646 bl_options
= {'DEFAULT_CLOSED'}
649 def poll(cls
, context
):
651 return ob
and ob
.mode
== 'POSE' #and context.bone
653 def draw(self
, context
):
655 scn
= context
.scene
.skinify
658 row
.operator("object.skinify_rig", text
="Add Shape", icon
='BONE_DATA')
660 split
= layout
.split(factor
=0.3)
661 split
.label(text
="Thickness:")
662 split
.prop(scn
, "thickness", text
="Body", icon
='MOD_SKIN')
663 split
.prop(scn
, "finger_thickness", text
="Fingers", icon
='HAND')
665 split
= layout
.split(factor
=0.3)
666 split
.label(text
="Mesh Density:")
667 split
.prop(scn
, "sub_level", icon
='MESH_ICOSPHERE')
670 row
.prop(scn
, "connect_mesh", icon
='EDITMODE_HLT')
671 row
.prop(scn
, "connect_parents", icon
='CONSTRAINT_BONE')
673 row
.prop(scn
, "head_ornaments", icon
='GROUP_BONE')
674 row
.prop(scn
, "generate_all", icon
='GROUP_BONE')
676 row
.prop(scn
, "apply_mod", icon
='FILE_TICK')
679 row
.prop(scn
, "parent_armature", icon
='POSE_HLT')
682 # define the scene properties in a group - call them with context.scene.skinify
683 class Skinify_Properties(PropertyGroup
):
684 sub_level
: IntProperty(
688 description
="Mesh density"
690 thickness
: FloatProperty(
694 description
="Adjust shape thickness"
696 finger_thickness
: FloatProperty(
697 name
="Finger Thickness",
700 description
="Adjust finger thickness relative to body"
702 connect_mesh
: BoolProperty(
705 description
="Makes solid shape from bone chains"
707 connect_parents
: BoolProperty(
710 description
="Fills the gaps between parented bones"
712 generate_all
: BoolProperty(
715 description
="Generates shapes from all bones"
717 head_ornaments
: BoolProperty(
718 name
="Head Ornaments",
720 description
="Includes head ornaments"
722 apply_mod
: BoolProperty(
723 name
="Apply Modifiers",
725 description
="Applies Modifiers to mesh"
727 parent_armature
: BoolProperty(
728 name
="Parent Armature",
730 description
="Applies mesh to Armature"
737 def startup_init(dummy
):
742 bpy
.utils
.register_class(BONE_OT_custom_shape
)
743 bpy
.utils
.register_class(BONE_PT_custom_shape
)
744 bpy
.utils
.register_class(Skinify_Properties
)
746 bpy
.types
.Scene
.skinify
= PointerProperty(
747 type=Skinify_Properties
750 bpy
.app
.handlers
.load_post
.append(startup_init
)
754 bpy
.utils
.unregister_class(BONE_OT_custom_shape
)
755 bpy
.utils
.unregister_class(BONE_PT_custom_shape
)
756 bpy
.utils
.unregister_class(Skinify_Properties
)
758 # cleanup the handler
759 bpy
.app
.handlers
.load_post
.remove(startup_init
)
761 del bpy
.types
.Scene
.skinify
764 if __name__
== "__main__":