Weight Paint: add a pie menu for locking and unlocking vertex groups.

Provide different options for locking and unlocking vertex groups
using bone selection, accessible via a pie menu triggered via the
'K' hotkey. To implement a variety of operations, extend the old
operator with a new option to mask it by bone selection. If the
X Mirror option is enabled, selection is automatically mirrored.

This follows D6533 as the next step in improving accessibility of
vertex group locking during weight painting.

Differential Revision: https://developer.blender.org/D6618
This commit is contained in:
Alexander Gavrilov 2019-12-23 12:46:05 +03:00
parent cd57c9e310
commit a1e50cfe6b
Notes: blender-bot 2023-02-14 08:06:35 +01:00
Referenced by commit a22cd6b6a9, UI: Weight Paint: add a menu for locking and unlocking vertex groups.
4 changed files with 199 additions and 8 deletions

View File

@ -3869,6 +3869,7 @@ def km_weight_paint(params):
{"properties": [("data_path", 'weight_paint_object.data.use_paint_mask_vertex')]}),
("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}),
op_menu_pie("VIEW3D_MT_wpaint_vgroup_lock_pie", {"type" : 'K', "value": 'PRESS'}),
*_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", params.context_menu_event),
])

View File

@ -55,9 +55,12 @@ class MESH_MT_vertex_group_context_menu(Menu):
layout.operator("object.vertex_group_remove", text="Delete All Unlocked Groups").all_unlocked = True
layout.operator("object.vertex_group_remove", text="Delete All Groups").all = True
layout.separator()
layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All").action = 'LOCK'
layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="UnLock All").action = 'UNLOCK'
layout.operator("object.vertex_group_lock", text="Lock Invert All").action = 'INVERT'
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.action, props.mask = 'UNLOCK', 'ALL'
props = layout.operator("object.vertex_group_lock", text="Lock Invert All")
props.action, props.mask = 'INVERT', 'ALL'
class MESH_MT_shape_key_context_menu(Menu):

View File

@ -5020,6 +5020,39 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu):
op.auto_iteration_count = False
class VIEW3D_MT_wpaint_vgroup_lock_pie(Menu):
bl_label = "Vertex Group Locks"
def draw(self, _context):
layout = self.layout
pie = layout.menu_pie()
# 1: Left
op = pie.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All")
op.action, op.mask = 'LOCK', 'ALL'
# 2: Right
op = pie.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All")
op.action, op.mask = 'UNLOCK', 'ALL'
# 3: Down
op = pie.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock Selected")
op.action, op.mask = 'UNLOCK', 'SELECTED'
# 4: Up
op = pie.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Selected")
op.action, op.mask = 'LOCK', 'SELECTED'
# 5: Up/Left
op = pie.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Unselected")
op.action, op.mask = 'LOCK', 'UNSELECTED'
# 6: Up/Right
op = pie.operator("object.vertex_group_lock", text="Lock Only Selected")
op.action, op.mask = 'LOCK', 'INVERT_UNSELECTED'
# 7: Down/Left
op = pie.operator("object.vertex_group_lock", text="Lock Only Unselected")
op.action, op.mask = 'UNLOCK', 'INVERT_UNSELECTED'
# 8: Down/Right
op = pie.operator("object.vertex_group_lock", text="Invert Locks")
op.action, op.mask = 'INVERT', 'ALL'
# ********** Panel **********
@ -7090,6 +7123,7 @@ classes = (
VIEW3D_MT_orientations_pie,
VIEW3D_MT_proportional_editing_falloff_pie,
VIEW3D_MT_sculpt_mask_edit_pie,
VIEW3D_MT_wpaint_vgroup_lock_pie,
VIEW3D_PT_active_tool,
VIEW3D_PT_active_tool_duplicate,
VIEW3D_PT_view3d_properties,

View File

@ -47,6 +47,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
#include "BKE_editmesh.h"
@ -1687,13 +1688,78 @@ static const EnumPropertyItem vgroup_lock_actions[] = {
{0, NULL, 0, NULL, NULL},
};
static void vgroup_lock_all(Object *ob, int action)
enum {
VGROUP_MASK_ALL,
VGROUP_MASK_SELECTED,
VGROUP_MASK_UNSELECTED,
VGROUP_MASK_INVERT_UNSELECTED,
};
static const EnumPropertyItem vgroup_lock_mask[] = {
{VGROUP_MASK_ALL, "ALL", 0, "All", "Apply action to all vertex groups"},
{VGROUP_MASK_SELECTED, "SELECTED", 0, "Selected", "Apply to selected vertex groups"},
{VGROUP_MASK_UNSELECTED, "UNSELECTED", 0, "Unselected", "Apply to unselected vertex groups"},
{VGROUP_MASK_INVERT_UNSELECTED,
"INVERT_UNSELECTED",
0,
"Invert Unselected",
"Apply the opposite of Lock/Unlock to unselected vertex groups"},
{0, NULL, 0, NULL, NULL},
};
static bool *vgroup_selected_get(Object *ob)
{
int sel_count = 0, defbase_tot = BLI_listbase_count(&ob->defbase);
bool *mask;
if (ob->mode & OB_MODE_WEIGHT_PAINT) {
mask = BKE_object_defgroup_selected_get(ob, defbase_tot, &sel_count);
/* Mirror the selection if X Mirror is enabled. */
Mesh *me = BKE_mesh_from_object(ob);
if (me && (me->editflag & ME_EDIT_MIRROR_X) != 0) {
BKE_object_defgroup_mirror_selection(ob, defbase_tot, mask, mask, &sel_count);
}
}
else {
mask = MEM_callocN(defbase_tot * sizeof(bool), __func__);
}
if (sel_count == 0 && ob->actdef >= 1 && ob->actdef <= defbase_tot) {
mask[ob->actdef - 1] = true;
}
return mask;
}
static void vgroup_lock_all(Object *ob, int action, int mask)
{
bDeformGroup *dg;
bool *selected = NULL;
int i;
if (mask != VGROUP_MASK_ALL) {
selected = vgroup_selected_get(ob);
}
if (action == VGROUP_TOGGLE) {
action = VGROUP_LOCK;
for (dg = ob->defbase.first; dg; dg = dg->next) {
for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) {
switch (mask) {
case VGROUP_MASK_INVERT_UNSELECTED:
case VGROUP_MASK_SELECTED:
if (!selected[i])
continue;
break;
case VGROUP_MASK_UNSELECTED:
if (selected[i])
continue;
break;
default:;
}
if (dg->flag & DG_LOCK_WEIGHT) {
action = VGROUP_UNLOCK;
break;
@ -1701,7 +1767,19 @@ static void vgroup_lock_all(Object *ob, int action)
}
}
for (dg = ob->defbase.first; dg; dg = dg->next) {
for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) {
switch (mask) {
case VGROUP_MASK_SELECTED:
if (!selected[i])
continue;
break;
case VGROUP_MASK_UNSELECTED:
if (selected[i])
continue;
break;
default:;
}
switch (action) {
case VGROUP_LOCK:
dg->flag |= DG_LOCK_WEIGHT;
@ -1713,6 +1791,14 @@ static void vgroup_lock_all(Object *ob, int action)
dg->flag ^= DG_LOCK_WEIGHT;
break;
}
if (mask == VGROUP_MASK_INVERT_UNSELECTED && !selected[i]) {
dg->flag ^= DG_LOCK_WEIGHT;
}
}
if (selected) {
MEM_freeN(selected);
}
}
@ -3176,24 +3262,84 @@ static int vertex_group_lock_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
int action = RNA_enum_get(op->ptr, "action");
int mask = RNA_enum_get(op->ptr, "mask");
vgroup_lock_all(ob, action);
vgroup_lock_all(ob, action, mask);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
return OPERATOR_FINISHED;
}
static char *vertex_group_lock_description(struct bContext *UNUSED(C),
struct wmOperatorType *UNUSED(op),
struct PointerRNA *params)
{
int action = RNA_enum_get(params, "action");
int mask = RNA_enum_get(params, "mask");
const char *action_str, *target_str;
switch (action) {
case VGROUP_LOCK:
action_str = "Lock";
break;
case VGROUP_UNLOCK:
action_str = "Unlock";
break;
case VGROUP_TOGGLE:
action_str = "Toggle locks of";
break;
case VGROUP_INVERT:
action_str = "Invert locks of";
break;
default:
return NULL;
}
switch (mask) {
case VGROUP_MASK_ALL:
target_str = "all";
break;
case VGROUP_MASK_SELECTED:
target_str = "selected";
break;
case VGROUP_MASK_UNSELECTED:
target_str = "unselected";
break;
case VGROUP_MASK_INVERT_UNSELECTED:
switch (action) {
case VGROUP_INVERT:
target_str = "selected";
break;
case VGROUP_LOCK:
target_str = "selected and unlock unselected";
break;
case VGROUP_UNLOCK:
target_str = "selected and lock unselected";
break;
default:
target_str = "all and invert unselected";
}
break;
default:
return NULL;
}
return BLI_sprintfN("%s %s vertex groups of the active object", action_str, target_str);
}
void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Change the Lock On Vertex Groups";
ot->idname = "OBJECT_OT_vertex_group_lock";
ot->description = "Change the lock state of all vertex groups of active object";
ot->description = "Change the lock state of all or some vertex groups of active object";
/* api callbacks */
ot->poll = vertex_group_poll;
ot->exec = vertex_group_lock_exec;
ot->get_description = vertex_group_lock_description;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@ -3204,6 +3350,13 @@ void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)
VGROUP_TOGGLE,
"Action",
"Lock action to execute on vertex groups");
RNA_def_enum(ot->srna,
"mask",
vgroup_lock_mask,
VGROUP_MASK_ALL,
"Mask",
"Apply the action based on vertex group selection");
}
static int vertex_group_invert_exec(bContext *C, wmOperator *op)