Modeling: Add Auto Normalize when Assigning vertex weights

Part of #135062.

This PR adds an Auto Normalize option in the Vertex Group panel that
when checked will normalize the other deform-bone vertex groups' weights
when assigning the weight of a deform-bone vertex group. For example if
a selected vertex has the following weights:

| Name    | Weight |
|---------|--------|
| Group 1 | 0.2    |
| Group 2 | 0.2    |
| Group 3 | 0.6    |

And the user selects Group 3, checks the Auto Normalize checkbox, sets
the weight to 0.8, and clicks Assign, the new weights will be:

| Name    | Weight |
|---------|--------|
| Group 1 | 0.1    |
| Group 2 | 0.1    |
| Group 3 | 0.8    |

Co-authored-by: Andy Beers <acbeers1@gmail.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/138133
This commit is contained in:
Nathan Vegdahl
2025-05-01 16:08:10 +02:00
committed by Nathan Vegdahl
parent 41a69b76a6
commit 4e7dfcbe3f
3 changed files with 54 additions and 12 deletions

View File

@@ -272,7 +272,11 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
sub.operator("object.vertex_group_select", text="Select")
sub.operator("object.vertex_group_deselect", text="Deselect")
layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
col = layout.column(align=True)
col.use_property_split = True
col.separator()
col.prop(context.tool_settings, "vertex_group_weight", text="Weight")
col.prop(context.tool_settings, "use_auto_normalize", text="Auto Normalize")
draw_attribute_warnings(context, layout, None)

View File

@@ -1341,21 +1341,14 @@ static void vgroup_levels_subset(Object *ob,
static bool vgroup_normalize_all(Object *ob,
const bool *vgroup_validmap,
const int vgroup_tot,
const int subset_count,
const bool lock_active,
ReportList *reports)
{
MDeformVert *dv, **dvert_array = nullptr;
int i, dvert_tot = 0;
const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1;
const bool use_vert_sel = vertex_group_use_vert_sel(ob);
if (subset_count == 0) {
BKE_report(reports, RPT_ERROR, "No vertex groups to operate on");
return false;
}
vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
@@ -1411,6 +1404,35 @@ static bool vgroup_normalize_all(Object *ob,
return false;
}
/**
* If the currently active vertex group is for a deform bone, normalize all
* vertex groups that are for deform bones.
*
* \param lock_active: If true, the active vertex group will be left untouched,
* and the remaining deform groups will be normalized to occupy the remaining
* weight not used by it.
*/
static void vgroup_normalize_all_deform_if_active_is_deform(Object *ob,
const bool lock_active,
ReportList *reports)
{
int r_defgroup_tot = BKE_object_defgroup_count(ob);
bool *defgroup_validmap = BKE_object_defgroup_validmap_get(ob, r_defgroup_tot);
const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1;
/* Only auto-normalize if the active group is bone-deforming. */
if (defgroup_validmap[def_nr] == true) {
int subset_count, vgroup_tot;
const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
ob, WT_VGROUP_BONE_DEFORM, &vgroup_tot, &subset_count);
vgroup_normalize_all(ob, vgroup_validmap, vgroup_tot, lock_active, reports);
MEM_SAFE_FREE(vgroup_validmap);
}
MEM_SAFE_FREE(defgroup_validmap);
}
enum {
VGROUP_TOGGLE,
VGROUP_LOCK,
@@ -2714,13 +2736,18 @@ void OBJECT_OT_vertex_group_remove(wmOperatorType *ot)
/** \name Vertex Group Assign Operator
* \{ */
static wmOperatorStatus vertex_group_assign_exec(bContext *C, wmOperator * /*op*/)
static wmOperatorStatus vertex_group_assign_exec(bContext *C, wmOperator *op)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = context_object(C);
Scene &scene = *CTX_data_scene(C);
vgroup_assign_verts(ob, scene, ts->vgroup_weight);
if (ts->auto_normalize) {
vgroup_normalize_all_deform_if_active_is_deform(ob, true, op->reports);
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
@@ -2818,6 +2845,11 @@ static wmOperatorStatus vertex_group_remove_from_exec(bContext *C, wmOperator *o
}
}
ToolSettings *ts = CTX_data_tool_settings(C);
if (ts->auto_normalize) {
vgroup_normalize_all_deform_if_active_is_deform(ob, false, op->reports);
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
@@ -3096,8 +3128,14 @@ static wmOperatorStatus vertex_group_normalize_all_exec(bContext *C, wmOperator
const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
ob, subset_type, &vgroup_tot, &subset_count);
changed = vgroup_normalize_all(
ob, vgroup_validmap, vgroup_tot, subset_count, lock_active, op->reports);
if (subset_count == 0) {
BKE_report(op->reports, RPT_ERROR, "No vertex groups to operate on");
changed = false;
}
else {
changed = vgroup_normalize_all(ob, vgroup_validmap, vgroup_tot, lock_active, op->reports);
}
MEM_freeN(vgroup_validmap);
if (changed) {

View File

@@ -3272,7 +3272,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Weight Paint Auto-Normalize",
"Ensure all bone-deforming vertex groups add up "
"to 1.0 while weight painting");
"to 1.0 while weight painting or assigning to vertices");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
prop = RNA_def_property(srna, "use_lock_relative", PROP_BOOLEAN, PROP_NONE);