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 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
28 "object/skinify.html",
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
.view_layer
.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 omitting 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
= bpy
.context
.selected_pose_bones_from_active_object
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(
451 value
=(1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10), 1 * thickness
* (size
/ 10)),
452 constraint_axis
=(False, False, False),
453 orient_type
='GLOBAL',
455 use_proportional_edit
=False,
457 shape_object
.modifiers
["Skin"].use_smooth_shade
= True
458 shape_object
.modifiers
["Skin"].use_x_symmetry
= True
460 # select finger vertices and calculate optimal thickness for fingers to fix proportions
461 if len(alternate_scale_idx_list
) > 0:
462 select_vertices(shape_object
, alternate_scale_idx_list
)
464 bpy
.ops
.object.skin_loose_mark_clear(override
, action
='MARK')
465 # by default set fingers thickness to 25 percent of body thickness
466 bpy
.ops
.transform
.skin_resize(
468 value
=(finger_thickness
, finger_thickness
, finger_thickness
),
469 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
471 use_proportional_edit
=False,
473 # make loose hands only for better topology
475 # bpy.ops.mesh.select_all(action='DESELECT')
478 bpy
.ops
.object.mode_set(mode
='EDIT')
479 bpy
.ops
.mesh
.select_all(action
='DESELECT')
480 bpy
.ops
.mesh
.select_all(action
='SELECT')
481 bpy
.ops
.mesh
.remove_doubles()
483 idx_store
= Idx_Store(rig_type
)
485 # fix rigify and pitchipoy hands topology
486 if connect_mesh
and connect_parents
and generate_all
is False and \
487 (rig_type
== Rig_type
.LEGACY
or rig_type
== Rig_type
.PITCHIPOY
or rig_type
== Rig_type
.HUMAN
) and \
488 selected_bones_num
== total_bones_num
:
489 # thickness will set palm vertex for both hands look pretty
490 corrective_thickness
= 2.5
492 merge_idx
= idx_store
.get_hand_l_merge_idx()
494 select_vertices(shape_object
, merge_idx
)
495 bpy
.ops
.mesh
.merge(type='CENTER')
496 bpy
.ops
.transform
.skin_resize(
498 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
499 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
501 use_proportional_edit
=False,
503 bpy
.ops
.mesh
.select_all(action
='DESELECT')
506 merge_idx
= idx_store
.get_hand_r_merge_idx()
508 select_vertices(shape_object
, merge_idx
)
509 bpy
.ops
.mesh
.merge(type='CENTER')
510 bpy
.ops
.transform
.skin_resize(
512 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
513 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
515 use_proportional_edit
=False,
518 # making hands even more pretty
519 bpy
.ops
.mesh
.select_all(action
='DESELECT')
520 hands_idx
= idx_store
.get_hands_pretty_idx()
522 select_vertices(shape_object
, hands_idx
)
523 # change the thickness to make hands look less blocky and more sexy
524 corrective_thickness
= 0.7
525 bpy
.ops
.transform
.skin_resize(
527 value
=(corrective_thickness
, corrective_thickness
, corrective_thickness
),
528 constraint_axis
=(False, False, False), orient_type
='GLOBAL',
530 use_proportional_edit
=False,
532 bpy
.ops
.mesh
.select_all(action
='DESELECT')
534 # todo optionally take root from rig's hip tail or head depending on scenario
536 root_idx
= idx_store
.get_root_idx()
538 if selected_bones_num
== total_bones_num
:
541 if len(root_idx
) > 0:
542 select_vertices(shape_object
, root_idx
)
543 bpy
.ops
.object.skin_root_mark(override
)
545 # add Subsurf modifier
546 shape_object
.modifiers
.new("Subsurf", 'SUBSURF')
547 shape_object
.modifiers
["Subsurf"].levels
= sub_level
548 shape_object
.modifiers
["Subsurf"].render_levels
= sub_level
550 bpy
.ops
.object.mode_set(mode
='OBJECT')
552 # object mode apply all modifiers
554 bpy
.ops
.object.modifier_apply(override
, apply_as
='DATA', modifier
="Skin")
555 bpy
.ops
.object.modifier_apply(override
, apply_as
='DATA', modifier
="Subsurf")
562 This script will create a custom shape
565 # ### Check if selection is OK ###
566 if len(context
.selected_pose_bones
) == 0 or \
567 len(context
.selected_objects
) == 0 or \
568 context
.selected_objects
[0].type != 'ARMATURE':
569 return {'CANCELLED'}, "No bone selected or the Armature is hidden"
571 scn
= bpy
.context
.scene
574 # initialize the mesh object
575 mesh_name
= context
.selected_objects
[0].name
+ "_mesh"
576 obj_name
= context
.selected_objects
[0].name
+ "_object"
577 armature_object
= context
.object
579 origin
= context
.object.location
580 bone_selection
= context
.selected_pose_bones
584 armature_object
= context
.view_layer
.objects
.active
585 armature_object
.select_set(True)
587 old_pose_pos
= armature_object
.data
.pose_position
588 bpy
.ops
.object.mode_set(mode
='OBJECT')
589 oldLocation
= Vector(armature_object
.location
)
590 oldRotation
= Euler(armature_object
.rotation_euler
)
591 oldScale
= Vector(armature_object
.scale
)
593 bpy
.ops
.object.rotation_clear(clear_delta
=False)
594 bpy
.ops
.object.location_clear(clear_delta
=False)
595 bpy
.ops
.object.scale_clear(clear_delta
=False)
596 if sknfy
.apply_mod
and sknfy
.parent_armature
:
597 armature_object
.data
.pose_position
= 'REST'
599 scale
= bpy
.context
.object.scale
600 size
= bpy
.context
.object.dimensions
[2]
602 bpy
.ops
.object.mode_set(mode
='OBJECT')
603 bpy
.ops
.object.select_all(action
='DESELECT')
605 bpy
.ops
.object.add(type='MESH', enter_editmode
=False, location
=origin
)
607 # get the mesh object
608 ob
= context
.view_layer
.objects
.active
613 # this way we fit mesh and bvh with armature modifier correctly
615 alternate_scale_idx_list
, rig_type
= generate_edges(
616 me
, ob
, bone_selection
, scale
, sknfy
.connect_mesh
,
617 sknfy
.connect_parents
, sknfy
.head_ornaments
,
621 generate_mesh(ob
, size
, sknfy
.thickness
, sknfy
.finger_thickness
, sknfy
.sub_level
,
622 sknfy
.connect_mesh
, sknfy
.connect_parents
, sknfy
.generate_all
,
623 sknfy
.apply_mod
, alternate_scale_idx_list
, rig_type
, bone_selection
)
625 # parent mesh with armature only if modifiers are applied
626 if sknfy
.apply_mod
and sknfy
.parent_armature
:
627 bpy
.ops
.object.mode_set(mode
='OBJECT')
628 bpy
.ops
.object.select_all(action
='DESELECT')
630 armature_object
.select_set(True)
631 bpy
.context
.view_layer
.objects
.active
= armature_object
633 bpy
.ops
.object.parent_set(type='ARMATURE_AUTO')
634 armature_object
.data
.pose_position
= old_pose_pos
635 armature_object
.select_set(False)
637 bpy
.ops
.object.mode_set(mode
='OBJECT')
638 ob
.location
= oldLocation
639 ob
.rotation_euler
= oldRotation
642 armature_object
.select_set(True)
643 scn
.objects
.active
= armature_object
645 armature_object
.location
= oldLocation
646 armature_object
.rotation_euler
= oldRotation
647 armature_object
.scale
= oldScale
648 bpy
.ops
.object.mode_set(mode
='POSE')
650 return {'FINISHED'}, me
653 class BONE_OT_custom_shape(Operator
):
654 bl_idname
= "object.skinify_rig"
655 bl_label
= "Skinify Rig"
656 bl_description
= "Creates a mesh object at the selected bones positions"
657 bl_options
= {'UNDO', 'INTERNAL'}
660 def poll(cls
, context
):
661 return context
.active_object
is not None
663 def execute(self
, context
):
665 if Mesh
[0] == {'CANCELLED'}:
666 self
.report({'WARNING'}, Mesh
[1])
669 self
.report({'INFO'}, Mesh
[1].name
+ " has been created")
674 class BONE_PT_custom_shape(Panel
):
675 bl_space_type
= "VIEW_3D"
676 bl_region_type
= "UI"
677 bl_category
= "Create"
678 # bl_context = "bone"
679 bl_label
= "Skinify Rig"
680 bl_options
= {'DEFAULT_CLOSED'}
683 def poll(cls
, context
):
685 return ob
and ob
.mode
== 'POSE' #and context.bone
687 def draw(self
, context
):
689 scn
= context
.scene
.skinify
692 row
.operator("object.skinify_rig", text
="Add Shape", icon
='BONE_DATA')
694 split
= layout
.split(factor
=0.3)
695 split
.label(text
="Thickness:")
696 split
.prop(scn
, "thickness", text
="Body", icon
='MOD_SKIN')
697 split
.prop(scn
, "finger_thickness", text
="Fingers", icon
='HAND')
699 split
= layout
.split(factor
=0.3)
700 split
.label(text
="Mesh Density:")
701 split
.prop(scn
, "sub_level", icon
='MESH_ICOSPHERE')
704 row
.prop(scn
, "connect_mesh", icon
='EDITMODE_HLT')
705 row
.prop(scn
, "connect_parents", icon
='CONSTRAINT_BONE')
707 row
.prop(scn
, "head_ornaments", icon
='GROUP_BONE')
708 row
.prop(scn
, "generate_all", icon
='GROUP_BONE')
710 row
.prop(scn
, "apply_mod", icon
='FILE_TICK')
713 row
.prop(scn
, "parent_armature", icon
='POSE_HLT')
716 # define the scene properties in a group - call them with context.scene.skinify
717 class Skinify_Properties(PropertyGroup
):
718 sub_level
: IntProperty(
722 description
="Mesh density"
724 thickness
: FloatProperty(
728 description
="Adjust shape thickness"
730 finger_thickness
: FloatProperty(
731 name
="Finger Thickness",
734 description
="Adjust finger thickness relative to body"
736 connect_mesh
: BoolProperty(
739 description
="Makes solid shape from bone chains"
741 connect_parents
: BoolProperty(
744 description
="Fills the gaps between parented bones"
746 generate_all
: BoolProperty(
749 description
="Generates shapes from all bones"
751 head_ornaments
: BoolProperty(
752 name
="Head Ornaments",
754 description
="Includes head ornaments"
756 apply_mod
: BoolProperty(
757 name
="Apply Modifiers",
759 description
="Applies Modifiers to mesh"
761 parent_armature
: BoolProperty(
762 name
="Parent Armature",
764 description
="Applies mesh to Armature"
771 def startup_init(dummy
):
776 bpy
.utils
.register_class(BONE_OT_custom_shape
)
777 bpy
.utils
.register_class(BONE_PT_custom_shape
)
778 bpy
.utils
.register_class(Skinify_Properties
)
780 bpy
.types
.Scene
.skinify
= PointerProperty(
781 type=Skinify_Properties
784 bpy
.app
.handlers
.load_post
.append(startup_init
)
788 bpy
.utils
.unregister_class(BONE_OT_custom_shape
)
789 bpy
.utils
.unregister_class(BONE_PT_custom_shape
)
790 bpy
.utils
.unregister_class(Skinify_Properties
)
792 # cleanup the handler
793 bpy
.app
.handlers
.load_post
.remove(startup_init
)
795 del bpy
.types
.Scene
.skinify
798 if __name__
== "__main__":