EasyWeight: Major update #317
@ -1,9 +1,9 @@
|
||||
from . import (
|
||||
force_apply_mirror,
|
||||
toggle_weight_paint,
|
||||
weight_paint_context_menu,
|
||||
vertex_group_operators,
|
||||
weight_cleaner,
|
||||
weight_pie,
|
||||
vertex_group_menu,
|
||||
rogue_weights,
|
||||
prefs,
|
||||
@ -27,9 +27,9 @@ bl_info = {
|
||||
modules = [
|
||||
force_apply_mirror,
|
||||
toggle_weight_paint,
|
||||
weight_paint_context_menu,
|
||||
vertex_group_operators,
|
||||
weight_cleaner,
|
||||
weight_pie,
|
||||
vertex_group_menu,
|
||||
rogue_weights,
|
||||
prefs,
|
||||
|
@ -38,6 +38,44 @@ class EASYWEIGHT_addon_preferences(bpy.types.AddonPreferences):
|
||||
default=True,
|
||||
)
|
||||
|
||||
def update_front_faces(self, context):
|
||||
for brush in bpy.data.brushes:
|
||||
if not brush.use_paint_weight:
|
||||
continue
|
||||
brush.use_frontface = self.global_front_faces_only
|
||||
|
||||
def update_accumulate(self, context):
|
||||
for brush in bpy.data.brushes:
|
||||
if not brush.use_paint_weight:
|
||||
continue
|
||||
brush.use_accumulate = self.global_accumulate
|
||||
|
||||
def update_falloff_shape(self, context):
|
||||
for brush in bpy.data.brushes:
|
||||
if not brush.use_paint_weight:
|
||||
continue
|
||||
brush.falloff_shape = 'SPHERE' if self.global_falloff_shape_sphere else 'PROJECTED'
|
||||
for i, val in enumerate(brush.cursor_color_add):
|
||||
if val > 0:
|
||||
brush.cursor_color_add[i] = (
|
||||
0.5 if self.global_falloff_shape_sphere else 2.0)
|
||||
|
||||
global_front_faces_only: BoolProperty(
|
||||
name="Front Faces Only",
|
||||
description="All weight brushes are able to paint on geometry that is facing away from the viewport",
|
||||
update=update_front_faces
|
||||
)
|
||||
global_accumulate: BoolProperty(
|
||||
name="Accumulate",
|
||||
description="All weight paint brushes will accumulate their effect within a single stroke as you move the mouse",
|
||||
update=update_accumulate
|
||||
)
|
||||
global_falloff_shape_sphere: BoolProperty(
|
||||
name="Falloff Shape",
|
||||
description="All weight paint brushes switch between a 3D spherical or a 2D projected circular falloff shape",
|
||||
update=update_falloff_shape,
|
||||
)
|
||||
|
||||
show_hotkeys: BoolProperty(
|
||||
name="Show Hotkeys",
|
||||
description="Reveal the hotkey list. You may customize or disable these hotkeys",
|
||||
@ -171,6 +209,12 @@ class EASYWEIGHT_addon_preferences(bpy.types.AddonPreferences):
|
||||
return kmi
|
||||
|
||||
|
||||
def set_brush_prefs_on_file_load(scene):
|
||||
prefs = get_addon_prefs()
|
||||
prefs.global_front_faces_only = prefs.global_front_faces_only
|
||||
prefs.global_accumulate = prefs.global_accumulate
|
||||
prefs.global_falloff_shape_sphere = prefs.global_falloff_shape_sphere
|
||||
|
||||
# NOTE: This function is copied from CloudRig's cloudrig.py.
|
||||
def register_hotkey(
|
||||
bl_idname, hotkey_kwargs, *, key_cat='Window', space_type='EMPTY', op_kwargs={}
|
||||
@ -234,15 +278,29 @@ registry = [EASYWEIGHT_addon_preferences]
|
||||
|
||||
def register():
|
||||
register_hotkey(
|
||||
bl_idname='object.custom_weight_paint_context_menu',
|
||||
hotkey_kwargs={
|
||||
'type': 'W',
|
||||
'value': 'PRESS',
|
||||
},
|
||||
'wm.call_menu_pie',
|
||||
hotkey_kwargs={'type': "W", 'value': "PRESS"},
|
||||
key_cat='Weight Paint',
|
||||
op_kwargs={'name': 'EASYWEIGHT_MT_PIE_easy_weight'},
|
||||
)
|
||||
register_hotkey(
|
||||
bl_idname='object.weight_paint_toggle',
|
||||
hotkey_kwargs={'type': 'TAB', 'value': 'PRESS', 'ctrl': True},
|
||||
key_cat='3D View',
|
||||
)
|
||||
|
||||
bpy.app.handlers.load_post.append(set_brush_prefs_on_file_load)
|
||||
|
||||
def unregister_hotkeys():
|
||||
prefs_class = bpy.types.AddonPreferences.bl_rna_get_subclass_py('EASYWEIGHT_addon_preferences')
|
||||
if not prefs_class:
|
||||
return
|
||||
for kmi_hash, kmi_tup in prefs_class.easyweight_keymap_items.items():
|
||||
kc, km, kmi = kmi_tup
|
||||
km.keymap_items.remove(kmi)
|
||||
prefs_class.easyweight_keymap_items = {}
|
||||
|
||||
def unregister():
|
||||
unregister_hotkeys()
|
||||
|
||||
bpy.app.handlers.load_post.remove(set_brush_prefs_on_file_load)
|
||||
|
@ -1,36 +1,28 @@
|
||||
import bpy
|
||||
from bpy.types import Menu
|
||||
|
||||
from .vertex_group_operators import (
|
||||
EASYWEIGHTS_OT_delete_empty_deform_groups,
|
||||
EASYWEIGHTS_OT_focus_deform_bones,
|
||||
EASYWEIGHTS_OT_delete_unselected_deform_groups,
|
||||
EASYWEIGHTS_OT_delete_unused_vertex_groups,
|
||||
)
|
||||
|
||||
|
||||
class MESH_MT_vertex_group_batch_delete(Menu):
|
||||
bl_label = "Batch Delete"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("object.vertex_group_remove", text="All Groups", icon='TRASH').all = True
|
||||
layout.operator(
|
||||
"object.vertex_group_remove",
|
||||
text="All Groups",
|
||||
icon='TRASH'
|
||||
).all = True
|
||||
layout.operator(
|
||||
"object.vertex_group_remove",
|
||||
text="All Unlocked Groups",
|
||||
icon='UNLOCKED'
|
||||
"object.vertex_group_remove", text="All Unlocked Groups", icon='UNLOCKED'
|
||||
).all_unlocked = True
|
||||
layout.separator()
|
||||
layout.operator(EASYWEIGHTS_OT_delete_empty_deform_groups.bl_idname,
|
||||
text="Empty Deform Groups", icon='GROUP_BONE')
|
||||
layout.operator(EASYWEIGHTS_OT_delete_unused_vertex_groups.bl_idname,
|
||||
text="Unused Non-Deform Groups", icon='BRUSH_DATA')
|
||||
layout.operator(EASYWEIGHTS_OT_delete_unselected_deform_groups.bl_idname,
|
||||
text="Unselected Deform Groups", icon='RESTRICT_SELECT_ON')
|
||||
layout.operator(
|
||||
'object.delete_empty_deform_vgroups', text="Empty Deform Groups", icon='GROUP_BONE'
|
||||
)
|
||||
layout.operator(
|
||||
'object.delete_unused_vgroups', text="Unused Non-Deform Groups", icon='BRUSH_DATA'
|
||||
)
|
||||
layout.operator(
|
||||
'object.delete_unselected_deform_vgroups',
|
||||
text="Unselected Deform Groups",
|
||||
icon='RESTRICT_SELECT_ON',
|
||||
)
|
||||
|
||||
|
||||
class MESH_MT_vertex_group_symmetry(Menu):
|
||||
@ -41,37 +33,29 @@ class MESH_MT_vertex_group_symmetry(Menu):
|
||||
layout.operator(
|
||||
"object.vertex_group_mirror",
|
||||
text="Mirror Active Group (Proximity)",
|
||||
icon='AUTOMERGE_OFF'
|
||||
icon='AUTOMERGE_OFF',
|
||||
).use_topology = False
|
||||
layout.operator(
|
||||
"object.vertex_group_mirror",
|
||||
text="Mirror Active Group (Topology)",
|
||||
icon='AUTOMERGE_ON'
|
||||
"object.vertex_group_mirror", text="Mirror Active Group (Topology)", icon='AUTOMERGE_ON'
|
||||
).use_topology = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator(
|
||||
"object.symmetrize_vertex_weights",
|
||||
text="Symmetrize Active Group",
|
||||
icon='MOD_MIRROR'
|
||||
"object.symmetrize_vertex_weights", text="Symmetrize Active Group", icon='MOD_MIRROR'
|
||||
).groups = 'ACTIVE'
|
||||
layout.operator(
|
||||
"object.symmetrize_vertex_weights",
|
||||
text="Symmetrize Selected Bones' Groups",
|
||||
icon='MOD_MIRROR'
|
||||
icon='MOD_MIRROR',
|
||||
).groups = 'BONES'
|
||||
op = layout.operator(
|
||||
"object.symmetrize_vertex_weights",
|
||||
text="Symmetrize All Left->Right",
|
||||
icon='MOD_MIRROR'
|
||||
"object.symmetrize_vertex_weights", text="Symmetrize All Left->Right", icon='MOD_MIRROR'
|
||||
)
|
||||
op.groups = 'ALL'
|
||||
op.direction = 'LEFT_TO_RIGHT'
|
||||
op = layout.operator(
|
||||
"object.symmetrize_vertex_weights",
|
||||
text="Symmetrize All Right->Left",
|
||||
icon='MOD_MIRROR'
|
||||
"object.symmetrize_vertex_weights", text="Symmetrize All Right->Left", icon='MOD_MIRROR'
|
||||
)
|
||||
op.groups = 'ALL'
|
||||
op.direction = 'RIGHT_TO_LEFT'
|
||||
@ -100,15 +84,19 @@ class MESH_MT_vertex_group_copy(Menu):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# TODO: This isn't grayed out when there's no active group.
|
||||
# TODO: Maybe for things that use the active group, we should put the name of the group in the button text? Makes it harder to search tho perhaps. Not even sure if menu search supports dynamic menu text?
|
||||
layout.operator("object.vertex_group_copy",
|
||||
icon='DUPLICATE', text="Duplicate Group")
|
||||
obj = context.object
|
||||
if obj and obj.vertex_groups and obj.vertex_groups.active:
|
||||
layout.operator(
|
||||
"object.vertex_group_copy",
|
||||
icon='DUPLICATE',
|
||||
text=f'Duplicate "{obj.vertex_groups.active.name}"',
|
||||
)
|
||||
layout.separator()
|
||||
layout.operator("object.vertex_group_copy_to_linked",
|
||||
text="Synchronize Groups on All Instances", icon='LINKED')
|
||||
layout.operator("object.vertex_group_copy_to_selected",
|
||||
text="Synchronize Groups on Selected", icon='RESTRICT_SELECT_OFF')
|
||||
layout.operator(
|
||||
"object.vertex_group_copy_to_selected",
|
||||
text="Synchronize Groups on Selected",
|
||||
icon='RESTRICT_SELECT_OFF',
|
||||
)
|
||||
|
||||
|
||||
class MESH_MT_vertex_group_lock(Menu):
|
||||
@ -117,14 +105,13 @@ class MESH_MT_vertex_group_lock(Menu):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
props = layout.operator(
|
||||
"object.vertex_group_lock", icon='LOCKED', text="Lock All")
|
||||
props = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All")
|
||||
props.action, props.mask = 'LOCK', 'ALL'
|
||||
props = layout.operator(
|
||||
"object.vertex_group_lock", icon='UNLOCKED', text="Unlock All")
|
||||
props = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All")
|
||||
props.action, props.mask = 'UNLOCK', 'ALL'
|
||||
props = layout.operator(
|
||||
"object.vertex_group_lock", icon='UV_SYNC_SELECT', text="Invert All Locks")
|
||||
"object.vertex_group_lock", icon='UV_SYNC_SELECT', text="Invert All Locks"
|
||||
)
|
||||
props.action, props.mask = 'INVERT', 'ALL'
|
||||
|
||||
|
||||
@ -140,9 +127,7 @@ class MESH_MT_vertex_group_weight(Menu):
|
||||
text="Remove Selected Verts from All Groups",
|
||||
).use_all_groups = True
|
||||
layout.operator(
|
||||
"object.vertex_group_clean",
|
||||
icon='BRUSH_DATA',
|
||||
text="Clean 0 Weights from All Groups"
|
||||
"object.vertex_group_clean", icon='BRUSH_DATA', text="Clean 0 Weights from All Groups"
|
||||
).group_select_mode = 'ALL'
|
||||
|
||||
layout.separator()
|
||||
@ -150,20 +135,16 @@ class MESH_MT_vertex_group_weight(Menu):
|
||||
layout.operator(
|
||||
"object.vertex_group_remove_from",
|
||||
icon='TRASH',
|
||||
text="Remove All Verts from Selected Group"
|
||||
text="Remove All Verts from Selected Group",
|
||||
).use_all_verts = True
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator(
|
||||
'paint.weight_from_bones',
|
||||
text="Assign Automatic from Bones",
|
||||
icon='BONE_DATA'
|
||||
'paint.weight_from_bones', text="Assign Automatic from Bones", icon='BONE_DATA'
|
||||
).type = 'AUTOMATIC'
|
||||
op = layout.operator(
|
||||
'object.vertex_group_normalize_all',
|
||||
text="Normalize Deform",
|
||||
icon='IPO_SINE'
|
||||
'object.vertex_group_normalize_all', text="Normalize Deform", icon='IPO_SINE'
|
||||
)
|
||||
op.group_select_mode = 'BONE_DEFORM'
|
||||
op.lock_active = False
|
||||
@ -171,12 +152,7 @@ class MESH_MT_vertex_group_weight(Menu):
|
||||
|
||||
def draw_misc(self, context):
|
||||
layout = self.layout
|
||||
layout.operator(EASYWEIGHTS_OT_focus_deform_bones.bl_idname, icon='ZOOM_IN')
|
||||
|
||||
# TODO: Add an operator called "Smart Cleanup" that creates missing mirror groups,
|
||||
# Cleans 0 weights,
|
||||
# Deletes unused deforming groups,
|
||||
# and deletes unused non-deforming groups.
|
||||
layout.operator('object.focus_deform_vgroups', icon='ZOOM_IN')
|
||||
|
||||
|
||||
def draw_vertex_group_menu(self, context):
|
||||
@ -195,23 +171,27 @@ registry = [
|
||||
MESH_MT_vertex_group_sort,
|
||||
MESH_MT_vertex_group_copy,
|
||||
MESH_MT_vertex_group_lock,
|
||||
MESH_MT_vertex_group_weight
|
||||
MESH_MT_vertex_group_weight,
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.old_draw = bpy.types.MESH_MT_vertex_group_context_menu.draw
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.old_draw = (
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.draw
|
||||
)
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.remove(
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.draw)
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.draw
|
||||
)
|
||||
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.append(draw_vertex_group_menu)
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.append(draw_misc)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.draw = bpy.types.MESH_MT_vertex_group_context_menu.old_draw
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.draw = (
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.old_draw
|
||||
)
|
||||
del bpy.types.MESH_MT_vertex_group_context_menu.old_draw
|
||||
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.remove(draw_vertex_group_menu)
|
||||
bpy.types.MESH_MT_vertex_group_context_menu.remove(draw_misc)
|
||||
|
||||
|
@ -1,203 +0,0 @@
|
||||
import bpy
|
||||
from bpy.props import BoolProperty, EnumProperty
|
||||
from .vertex_group_operators import EASYWEIGHTS_OT_delete_empty_deform_groups, EASYWEIGHTS_OT_delete_unused_vertex_groups
|
||||
from .prefs import get_addon_prefs
|
||||
|
||||
class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
||||
""" Custom Weight Paint context menu """
|
||||
bl_idname = "object.custom_weight_paint_context_menu"
|
||||
bl_label = "Custom Weight Paint Context Menu"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
def update_front_faces(self, context):
|
||||
for b in bpy.data.brushes:
|
||||
if not b.use_paint_weight:
|
||||
continue
|
||||
b.use_frontface = self.front_faces
|
||||
|
||||
def update_accumulate(self, context):
|
||||
for b in bpy.data.brushes:
|
||||
if not b.use_paint_weight:
|
||||
continue
|
||||
b.use_accumulate = self.accumulate
|
||||
|
||||
def update_falloff_shape(self, context):
|
||||
for b in bpy.data.brushes:
|
||||
if not b.use_paint_weight:
|
||||
continue
|
||||
b.falloff_shape = self.falloff_shape
|
||||
for i, val in enumerate(b.cursor_color_add):
|
||||
if val > 0:
|
||||
b.cursor_color_add[i] = (
|
||||
0.5 if self.falloff_shape == 'SPHERE' else 2.0)
|
||||
|
||||
front_faces: BoolProperty(
|
||||
name="Front Faces Only", description="Toggle the Front Faces Only setting for all weight brushes", update=update_front_faces)
|
||||
accumulate: BoolProperty(
|
||||
name="Accumulate", description="Toggle the Accumulate setting for all weight brushes", update=update_accumulate)
|
||||
falloff_shape: EnumProperty(name="Falloff Type", description="Select the Falloff Shape setting for all weight brushes", update=update_falloff_shape,
|
||||
items=[
|
||||
('SPHERE', 'Sphere', "The brush influence falls off along a sphere whose center is the mesh under the cursor's pointer"),
|
||||
('PROJECTED', 'Projected', "The brush influence falls off in a tube around the cursor. This is useful for painting backfaces, as long as Front Faces Only is off.")
|
||||
]
|
||||
)
|
||||
|
||||
# @classmethod
|
||||
# def poll(cls, context):
|
||||
# return context.mode == 'PAINT_WEIGHT'
|
||||
|
||||
def draw_operators(self, layout, context):
|
||||
layout.label(text="Operators")
|
||||
|
||||
op = layout.operator(
|
||||
'object.vertex_group_normalize_all',
|
||||
text="Normalize Deform",
|
||||
icon='IPO_SINE'
|
||||
)
|
||||
op.group_select_mode = 'BONE_DEFORM'
|
||||
op.lock_active = False
|
||||
|
||||
row = layout.row()
|
||||
row.operator("object.vertex_group_clean", icon='BRUSH_DATA',
|
||||
text="Clean 0").group_select_mode = 'ALL'
|
||||
row.operator(EASYWEIGHTS_OT_delete_empty_deform_groups.bl_idname,
|
||||
text="Wipe Empty", icon='GROUP_BONE')
|
||||
row.operator(EASYWEIGHTS_OT_delete_unused_vertex_groups.bl_idname,
|
||||
text="Wipe Unused", icon='BRUSH_DATA')
|
||||
|
||||
def draw_minimal(self, layout, context):
|
||||
overlay = context.space_data.overlay
|
||||
row = layout.row(heading="Symmetry: ")
|
||||
# Compatibility for versions between rB5502517c3c12086c111a and rBfa9b05149c2ca3915a4fb26.
|
||||
if hasattr(context.weight_paint_object.data, "use_mirror_vertex_group_x"):
|
||||
row.prop(context.weight_paint_object.data,
|
||||
"use_mirror_vertex_group_x", text="X-Mirror", toggle=True)
|
||||
else:
|
||||
row.prop(context.weight_paint_object.data,
|
||||
"use_mirror_x", text="X-Mirror", toggle=True)
|
||||
if hasattr(context.weight_paint_object.data, 'use_mirror_vertex_groups'):
|
||||
row.prop(context.weight_paint_object.data,
|
||||
'use_mirror_vertex_groups', text="Flip Groups", toggle=True)
|
||||
|
||||
row = layout.row(heading="Mesh Display: ")
|
||||
row.prop(overlay, "show_wpaint_contours",
|
||||
text="Weight Contours", toggle=True)
|
||||
row.prop(overlay, "show_paint_wire", text="Wireframe", toggle=True)
|
||||
|
||||
row = layout.row(heading="Bone Display: ")
|
||||
row.prop(overlay, "show_bones", text="Bones", toggle=True)
|
||||
if context.pose_object:
|
||||
row.prop(context.pose_object, "show_in_front", toggle=True)
|
||||
|
||||
self.draw_operators(layout, context)
|
||||
|
||||
def draw_overlay_settings(self, layout, context):
|
||||
overlay = context.space_data.overlay
|
||||
tool_settings = context.tool_settings
|
||||
layout.label(text="Overlay")
|
||||
row = layout.row()
|
||||
row.use_property_split = True
|
||||
row.prop(tool_settings, "vertex_group_user",
|
||||
text="Zero Weights Display", expand=True)
|
||||
if hasattr(context.space_data, "overlay"):
|
||||
row = layout.row()
|
||||
row.prop(overlay, "show_wpaint_contours",
|
||||
text="Weight Contours", toggle=True)
|
||||
row.prop(overlay, "show_paint_wire", text="Wireframe", toggle=True)
|
||||
row.prop(overlay, "show_bones", text="Bones", toggle=True)
|
||||
|
||||
if context.pose_object:
|
||||
layout.label(text="Armature Display")
|
||||
layout.prop(context.pose_object.data, "display_type", expand=True)
|
||||
layout.prop(context.pose_object, "show_in_front", toggle=True)
|
||||
|
||||
def draw_weight_paint_settings(self, layout, context):
|
||||
tool_settings = context.tool_settings
|
||||
layout.label(text="Weight Paint settings")
|
||||
|
||||
prefs = get_addon_prefs(context)
|
||||
|
||||
row = layout.row()
|
||||
row.prop(tool_settings, "use_auto_normalize",
|
||||
text="Auto Normalize", toggle=True)
|
||||
row.prop(prefs, "auto_clean_weights", toggle=True)
|
||||
row.prop(tool_settings, "use_multipaint",
|
||||
text="Multi-Paint", toggle=True)
|
||||
row = layout.row()
|
||||
# Compatibility for versions between rB5502517c3c12086c111a and rBfa9b05149c2ca3915a4fb26.
|
||||
if hasattr(context.weight_paint_object.data, "use_mirror_vertex_group_x"):
|
||||
row.prop(context.weight_paint_object.data,
|
||||
"use_mirror_vertex_group_x", text="X-Mirror", toggle=True)
|
||||
else:
|
||||
row.prop(context.weight_paint_object.data,
|
||||
"use_mirror_x", text="X-Mirror", toggle=True)
|
||||
if hasattr(context.weight_paint_object.data, 'use_mirror_vertex_groups'):
|
||||
row.prop(context.weight_paint_object.data,
|
||||
'use_mirror_vertex_groups', text="Flip Groups", toggle=True)
|
||||
|
||||
def draw_brush_settings(self, layout, context):
|
||||
row = layout.row()
|
||||
row.label(text="Brush Settings (Global)")
|
||||
icon = 'HIDE_ON' if context.scene.easyweight_minimal else 'HIDE_OFF'
|
||||
row.prop(context.scene, "easyweight_minimal",
|
||||
icon=icon, toggle=False, text="", emboss=False)
|
||||
layout.prop(self, "accumulate", toggle=True)
|
||||
layout.prop(self, "front_faces", toggle=True)
|
||||
row = layout.row(heading="Falloff Shape: ")
|
||||
row.prop(self, "falloff_shape", expand=True)
|
||||
layout.separator()
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
self.draw_brush_settings(layout, context)
|
||||
layout.separator()
|
||||
|
||||
if context.scene.easyweight_minimal:
|
||||
self.draw_minimal(layout, context)
|
||||
return
|
||||
|
||||
self.draw_weight_paint_settings(layout, context)
|
||||
layout.separator()
|
||||
self.draw_overlay_settings(layout, context)
|
||||
layout.separator()
|
||||
self.draw_operators(layout, context)
|
||||
|
||||
def invoke(self, context, event):
|
||||
active_brush = context.tool_settings.weight_paint.brush
|
||||
self.front_faces = active_brush.use_frontface
|
||||
self.falloff_shape = active_brush.falloff_shape
|
||||
self.accumulate = active_brush.use_accumulate
|
||||
if 'clean_weights' not in context.scene:
|
||||
context.scene['clean_weights'] = False
|
||||
self.clean_weights = context.scene['clean_weights']
|
||||
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def draw_menu_entry(self, context):
|
||||
self.layout.operator(EASYWEIGHT_OT_wp_context_menu.bl_idname)
|
||||
|
||||
|
||||
registry = [
|
||||
EASYWEIGHT_OT_wp_context_menu
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.easyweight_minimal = BoolProperty(
|
||||
name="Minimal",
|
||||
description="Hide options that are less frequently used",
|
||||
default=False
|
||||
)
|
||||
|
||||
bpy.types.VIEW3D_MT_paint_weight.append(draw_menu_entry)
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.easyweight_minimal
|
||||
|
||||
bpy.types.VIEW3D_MT_paint_weight.remove(draw_menu_entry)
|
124
scripts-blender/addons/easy_weights/weight_pie.py
Normal file
124
scripts-blender/addons/easy_weights/weight_pie.py
Normal file
@ -0,0 +1,124 @@
|
||||
from bpy.types import Menu
|
||||
from .prefs import get_addon_prefs
|
||||
from .vertex_group_operators import get_deforming_armature
|
||||
|
||||
|
||||
class EASYWEIGHT_MT_PIE_easy_weight(Menu):
|
||||
bl_label = "Easy Weight"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
prefs = get_addon_prefs(context)
|
||||
|
||||
# 1) < Operators
|
||||
self.draw_operators(pie.column().box(), context)
|
||||
|
||||
# 2) > Front Faces Only
|
||||
pie.prop(
|
||||
prefs,
|
||||
'global_front_faces_only',
|
||||
icon='OVERLAY',
|
||||
text="Paint Through Mesh",
|
||||
invert_checkbox=True,
|
||||
)
|
||||
|
||||
# 3) V Overlay & Armature Display settings
|
||||
self.draw_overlay_settings(pie.column().box(), context)
|
||||
|
||||
# 4) ^ Accumulate
|
||||
pie.prop(prefs, 'global_accumulate', icon='GP_SELECT_STROKES')
|
||||
|
||||
# 5) <^ Empty
|
||||
pie.separator()
|
||||
|
||||
# 6) ^> Toggle Falloff Shape
|
||||
icon = 'SPHERE' if prefs.global_falloff_shape_sphere else 'MESH_CIRCLE'
|
||||
text = "Spherical" if prefs.global_falloff_shape_sphere else "Projected Circle"
|
||||
pie.prop(
|
||||
prefs,
|
||||
'global_falloff_shape_sphere',
|
||||
text="Falloff Shape: " + text,
|
||||
icon=icon,
|
||||
invert_checkbox=prefs.global_falloff_shape_sphere,
|
||||
)
|
||||
|
||||
# 7) <v Empty
|
||||
pie.separator()
|
||||
|
||||
# 8) v>Empty
|
||||
pie.separator()
|
||||
|
||||
def draw_operators(self, layout, context):
|
||||
layout.label(text="Operators")
|
||||
|
||||
prefs = get_addon_prefs(context)
|
||||
|
||||
deform_rig = get_deforming_armature(context.active_object)
|
||||
if deform_rig:
|
||||
layout.operator('object.focus_deform_vgroups', icon='ZOOM_IN')
|
||||
|
||||
op = layout.operator(
|
||||
'object.vertex_group_normalize_all', text="Normalize Deform Groups", icon='IPO_SINE'
|
||||
)
|
||||
op.group_select_mode = 'BONE_DEFORM'
|
||||
op.lock_active = False
|
||||
|
||||
layout.operator(
|
||||
'object.delete_empty_deform_vgroups',
|
||||
text="Clear Empty Deform Groups",
|
||||
icon='GROUP_BONE',
|
||||
)
|
||||
if not prefs.auto_clean_weights:
|
||||
layout.operator(
|
||||
"object.vertex_group_clean", icon='BRUSH_DATA', text="Clean Zero-Weights"
|
||||
).group_select_mode = 'ALL'
|
||||
layout.operator(
|
||||
'object.delete_unused_vgroups', text="Clear Unused Groups", icon='BRUSH_DATA'
|
||||
)
|
||||
|
||||
layout.operator(
|
||||
'paint.weight_from_bones', text="Assign Automatic from Bones", icon='BONE_DATA'
|
||||
).type = 'AUTOMATIC'
|
||||
op = layout.operator(
|
||||
'object.vertex_group_normalize_all', text="Normalize Deform", icon='IPO_SINE'
|
||||
)
|
||||
op.group_select_mode = 'BONE_DEFORM'
|
||||
op.lock_active = False
|
||||
|
||||
def draw_overlay_settings(self, layout, context):
|
||||
overlay = context.space_data.overlay
|
||||
tool_settings = context.tool_settings
|
||||
prefs = get_addon_prefs(context)
|
||||
layout.label(text="Overlay")
|
||||
if not prefs.always_show_zero_weights:
|
||||
row = layout.row()
|
||||
row.prop(tool_settings, "vertex_group_user", text="Zero Weights Display", expand=True)
|
||||
if hasattr(context.space_data, "overlay"):
|
||||
row = layout.row()
|
||||
row.prop(
|
||||
overlay,
|
||||
"show_wpaint_contours",
|
||||
text="Weight Contours",
|
||||
toggle=True,
|
||||
icon='MOD_INSTANCE',
|
||||
)
|
||||
row.prop(overlay, "show_paint_wire", text="Wireframe", toggle=True, icon='SHADING_WIRE')
|
||||
icon = 'HIDE_OFF' if overlay.show_bones else 'HIDE_ON'
|
||||
row.prop(overlay, "show_bones", text="Bones", toggle=True, icon=icon)
|
||||
|
||||
if context.pose_object:
|
||||
col = layout.column()
|
||||
col.label(text="Armature Display")
|
||||
row = col.row(align=True)
|
||||
row.prop(context.pose_object.data, "display_type", expand=True)
|
||||
x_row = col.row()
|
||||
x_row.prop(context.pose_object, "show_in_front", toggle=True, icon='XRAY')
|
||||
if overlay.show_xray_bone:
|
||||
x_row.prop(overlay, 'show_xray_bone', text="X-Ray Overlay")
|
||||
|
||||
|
||||
registry = [
|
||||
EASYWEIGHT_MT_PIE_easy_weight,
|
||||
]
|
Loading…
Reference in New Issue
Block a user