EasyWeight: Major update #317
@ -1,24 +1,11 @@
|
|||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
rogue_weights,
|
|
||||||
vertex_group_menu,
|
|
||||||
vertex_group_operators,
|
|
||||||
weight_paint_context_menu,
|
|
||||||
toggle_weight_paint,
|
|
||||||
force_apply_mirror,
|
force_apply_mirror,
|
||||||
|
toggle_weight_paint,
|
||||||
|
weight_paint_context_menu,
|
||||||
|
vertex_group_operators,
|
||||||
|
weight_cleaner,
|
||||||
|
vertex_group_menu,
|
||||||
|
rogue_weights,
|
||||||
prefs,
|
prefs,
|
||||||
)
|
)
|
||||||
import bpy
|
import bpy
|
||||||
@ -42,6 +29,7 @@ modules = [
|
|||||||
toggle_weight_paint,
|
toggle_weight_paint,
|
||||||
weight_paint_context_menu,
|
weight_paint_context_menu,
|
||||||
vertex_group_operators,
|
vertex_group_operators,
|
||||||
|
weight_cleaner,
|
||||||
vertex_group_menu,
|
vertex_group_menu,
|
||||||
rogue_weights,
|
rogue_weights,
|
||||||
prefs,
|
prefs,
|
||||||
|
@ -1,15 +1,31 @@
|
|||||||
import bpy, json
|
import bpy, json
|
||||||
from bpy.props import BoolProperty
|
from bpy.props import BoolProperty
|
||||||
|
from . import __package__ as base_package
|
||||||
|
|
||||||
|
|
||||||
|
def get_addon_prefs(context=None):
|
||||||
|
if not context:
|
||||||
|
context = bpy.context
|
||||||
|
if base_package.startswith('bl_ext'):
|
||||||
|
# 4.2
|
||||||
|
return context.preferences.addons[base_package].preferences
|
||||||
|
else:
|
||||||
|
return context.preferences.addons[base_package.split(".")[0]].preferences
|
||||||
|
|
||||||
|
|
||||||
class EASYWEIGHT_addon_preferences(bpy.types.AddonPreferences):
|
class EASYWEIGHT_addon_preferences(bpy.types.AddonPreferences):
|
||||||
bl_idname = __package__
|
bl_idname = __package__
|
||||||
easyweight_keymap_items = {}
|
easyweight_keymap_items = {}
|
||||||
|
|
||||||
|
auto_clean_weights: BoolProperty(
|
||||||
|
name="Auto Clean",
|
||||||
|
description="While this is enabled, zero-weights will be removed automatically after every brush stroke",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
show_hotkeys: BoolProperty(
|
show_hotkeys: BoolProperty(
|
||||||
name="Show Hotkeys",
|
name="Show Hotkeys",
|
||||||
default=False,
|
|
||||||
description="Reveal the hotkey list. You may customize or disable these hotkeys",
|
description="Reveal the hotkey list. You may customize or disable these hotkeys",
|
||||||
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@ -205,12 +221,6 @@ def register():
|
|||||||
)
|
)
|
||||||
register_hotkey(
|
register_hotkey(
|
||||||
bl_idname='object.weight_paint_toggle',
|
bl_idname='object.weight_paint_toggle',
|
||||||
hotkey_kwargs={
|
hotkey_kwargs={'type': 'TAB', 'value': 'PRESS', 'ctrl': True},
|
||||||
'type': 'TAB',
|
|
||||||
'value': 'PRESS',
|
|
||||||
'ctrl': True
|
|
||||||
},
|
|
||||||
key_cat='3D View',
|
key_cat='3D View',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
55
scripts-blender/addons/easy_weights/weight_cleaner.py
Normal file
55
scripts-blender/addons/easy_weights/weight_cleaner.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.app.handlers import persistent
|
||||||
|
from .prefs import get_addon_prefs
|
||||||
|
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def start_cleaner(scene, depsgraph):
|
||||||
|
bpy.app.handlers.depsgraph_update_pre.append(WeightCleaner.clean_weights)
|
||||||
|
bpy.app.handlers.depsgraph_update_post.append(WeightCleaner.reset_flag)
|
||||||
|
|
||||||
|
|
||||||
|
class WeightCleaner:
|
||||||
|
"""Run bpy.ops.object.vertex_group_clean on every depsgraph update while in weight paint mode (ie. every brush stroke)."""
|
||||||
|
|
||||||
|
# Flag set in post_depsgraph_update, to indicate to pre_depsgraph_update that the depsgraph update has indeed completed.
|
||||||
|
can_clean = True
|
||||||
|
# Flag set by pre_depsgraph_update to indicate to post_depsgraph_update that the cleanup operator is still running (in a different thread).
|
||||||
|
cleaning_in_progress = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clean_weights(cls, scene, depsgraph):
|
||||||
|
context = bpy.context
|
||||||
|
prefs = get_addon_prefs(context)
|
||||||
|
if context.mode != 'PAINT_WEIGHT':
|
||||||
|
return
|
||||||
|
if not context or not hasattr(context, 'object') or not context.object:
|
||||||
|
return
|
||||||
|
if not prefs.auto_clean_weights:
|
||||||
|
return
|
||||||
|
if cls.can_clean:
|
||||||
|
cls.can_clean = False
|
||||||
|
cls.cleaning_in_progress = True
|
||||||
|
# This will trigger a depsgraph update, and therefore clean_weights, again.
|
||||||
|
bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.001)
|
||||||
|
cls.cleaning_in_progress = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset_flag(cls, scene, depsgraph):
|
||||||
|
context = bpy.context
|
||||||
|
if context.mode != 'PAINT_WEIGHT':
|
||||||
|
return
|
||||||
|
if not context or not hasattr(context, 'object') or not context.object:
|
||||||
|
return
|
||||||
|
if cls.cleaning_in_progress:
|
||||||
|
return
|
||||||
|
cls.can_clean = True
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
start_cleaner(None, None)
|
||||||
|
bpy.app.handlers.load_post.append(start_cleaner)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.app.handlers.load_post.remove(start_cleaner)
|
@ -1,7 +1,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.props import BoolProperty, EnumProperty
|
from bpy.props import BoolProperty, EnumProperty
|
||||||
from bpy.app.handlers import persistent
|
|
||||||
from .vertex_group_operators import EASYWEIGHTS_OT_delete_empty_deform_groups, EASYWEIGHTS_OT_delete_unused_vertex_groups
|
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):
|
class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
||||||
""" Custom Weight Paint context menu """
|
""" Custom Weight Paint context menu """
|
||||||
@ -9,10 +9,6 @@ class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
|||||||
bl_label = "Custom Weight Paint Context Menu"
|
bl_label = "Custom Weight Paint Context Menu"
|
||||||
bl_options = {'REGISTER'}
|
bl_options = {'REGISTER'}
|
||||||
|
|
||||||
def update_clean_weights(self, context):
|
|
||||||
context.scene['clean_weights'] = self.clean_weights
|
|
||||||
WeightCleaner.cleaner_active = context.scene['clean_weights']
|
|
||||||
|
|
||||||
def update_front_faces(self, context):
|
def update_front_faces(self, context):
|
||||||
for b in bpy.data.brushes:
|
for b in bpy.data.brushes:
|
||||||
if not b.use_paint_weight:
|
if not b.use_paint_weight:
|
||||||
@ -35,8 +31,6 @@ class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
|||||||
b.cursor_color_add[i] = (
|
b.cursor_color_add[i] = (
|
||||||
0.5 if self.falloff_shape == 'SPHERE' else 2.0)
|
0.5 if self.falloff_shape == 'SPHERE' else 2.0)
|
||||||
|
|
||||||
clean_weights: BoolProperty(
|
|
||||||
name="Clean Weights", description="Run the Clean Vertex Groups operator after every weight brush stroke", update=update_clean_weights)
|
|
||||||
front_faces: BoolProperty(
|
front_faces: BoolProperty(
|
||||||
name="Front Faces Only", description="Toggle the Front Faces Only setting for all weight brushes", update=update_front_faces)
|
name="Front Faces Only", description="Toggle the Front Faces Only setting for all weight brushes", update=update_front_faces)
|
||||||
accumulate: BoolProperty(
|
accumulate: BoolProperty(
|
||||||
@ -121,10 +115,12 @@ class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
|||||||
tool_settings = context.tool_settings
|
tool_settings = context.tool_settings
|
||||||
layout.label(text="Weight Paint settings")
|
layout.label(text="Weight Paint settings")
|
||||||
|
|
||||||
|
prefs = get_addon_prefs(context)
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(tool_settings, "use_auto_normalize",
|
row.prop(tool_settings, "use_auto_normalize",
|
||||||
text="Auto Normalize", toggle=True)
|
text="Auto Normalize", toggle=True)
|
||||||
row.prop(self, "clean_weights", toggle=True)
|
row.prop(prefs, "auto_clean_weights", toggle=True)
|
||||||
row.prop(tool_settings, "use_multipaint",
|
row.prop(tool_settings, "use_multipaint",
|
||||||
text="Multi-Paint", toggle=True)
|
text="Multi-Paint", toggle=True)
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@ -171,6 +167,7 @@ class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
|||||||
active_brush = context.tool_settings.weight_paint.brush
|
active_brush = context.tool_settings.weight_paint.brush
|
||||||
self.front_faces = active_brush.use_frontface
|
self.front_faces = active_brush.use_frontface
|
||||||
self.falloff_shape = active_brush.falloff_shape
|
self.falloff_shape = active_brush.falloff_shape
|
||||||
|
self.accumulate = active_brush.use_accumulate
|
||||||
if 'clean_weights' not in context.scene:
|
if 'clean_weights' not in context.scene:
|
||||||
context.scene['clean_weights'] = False
|
context.scene['clean_weights'] = False
|
||||||
self.clean_weights = context.scene['clean_weights']
|
self.clean_weights = context.scene['clean_weights']
|
||||||
@ -183,55 +180,10 @@ class EASYWEIGHT_OT_wp_context_menu(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class WeightCleaner:
|
|
||||||
"""Run bpy.ops.object.vertex_group_clean on every depsgraph update while in weight paint mode (ie. every brush stroke)."""
|
|
||||||
# Most of the code is simply responsible for avoiding infinite looping depsgraph updates.
|
|
||||||
# Flag set by the user via the custom WP context menu.
|
|
||||||
cleaner_active = False
|
|
||||||
|
|
||||||
# Flag set in post_depsgraph_update, to indicate to pre_depsgraph_update that the depsgraph update has indeed completed.
|
|
||||||
can_clean = True
|
|
||||||
# Flag set by pre_depsgraph_update to indicate to post_depsgraph_update that the cleanup operator is still running (in a different thread).
|
|
||||||
cleaning_in_progress = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean_weights(cls, scene, depsgraph):
|
|
||||||
if bpy.context.mode != 'PAINT_WEIGHT':
|
|
||||||
return
|
|
||||||
if not bpy.context or not hasattr(bpy.context, 'object') or not bpy.context.object:
|
|
||||||
return
|
|
||||||
if not cls.cleaner_active:
|
|
||||||
return
|
|
||||||
if cls.can_clean:
|
|
||||||
cls.can_clean = False
|
|
||||||
cls.cleaning_in_progress = True
|
|
||||||
# This will trigger a depsgraph update, and therefore clean_weights, again.
|
|
||||||
bpy.ops.object.vertex_group_clean(
|
|
||||||
group_select_mode='ALL', limit=0.001)
|
|
||||||
cls.cleaning_in_progress = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def reset_flag(cls, scene, depsgraph):
|
|
||||||
if bpy.context.mode != 'PAINT_WEIGHT':
|
|
||||||
return
|
|
||||||
if not bpy.context or not hasattr(bpy.context, 'object') or not bpy.context.object:
|
|
||||||
return
|
|
||||||
if cls.cleaning_in_progress:
|
|
||||||
return
|
|
||||||
if not cls.cleaner_active:
|
|
||||||
return
|
|
||||||
cls.can_clean = True
|
|
||||||
|
|
||||||
|
|
||||||
def draw_menu_entry(self, context):
|
def draw_menu_entry(self, context):
|
||||||
self.layout.operator(EASYWEIGHT_OT_wp_context_menu.bl_idname)
|
self.layout.operator(EASYWEIGHT_OT_wp_context_menu.bl_idname)
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
|
||||||
def start_cleaner(scene, depsgraph):
|
|
||||||
bpy.app.handlers.depsgraph_update_pre.append(WeightCleaner.clean_weights)
|
|
||||||
bpy.app.handlers.depsgraph_update_post.append(WeightCleaner.reset_flag)
|
|
||||||
|
|
||||||
registry = [
|
registry = [
|
||||||
EASYWEIGHT_OT_wp_context_menu
|
EASYWEIGHT_OT_wp_context_menu
|
||||||
]
|
]
|
||||||
@ -243,13 +195,10 @@ def register():
|
|||||||
description="Hide options that are less frequently used",
|
description="Hide options that are less frequently used",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
start_cleaner(None, None)
|
|
||||||
bpy.app.handlers.load_post.append(start_cleaner)
|
|
||||||
|
|
||||||
bpy.types.VIEW3D_MT_paint_weight.append(draw_menu_entry)
|
bpy.types.VIEW3D_MT_paint_weight.append(draw_menu_entry)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Scene.easyweight_minimal
|
del bpy.types.Scene.easyweight_minimal
|
||||||
bpy.app.handlers.load_post.remove(start_cleaner)
|
|
||||||
|
|
||||||
bpy.types.VIEW3D_MT_paint_weight.remove(draw_menu_entry)
|
bpy.types.VIEW3D_MT_paint_weight.remove(draw_menu_entry)
|
||||||
|
Loading…
Reference in New Issue
Block a user