Weight Paint: Implement a new Lock-Relative mode.

This check box alters how weights are displayed and painted,
similar to Multi Paint, but in a different way. Specifically,
weights are presented as if all locked vertex groups were
deleted, and the remaining deform groups normalized.

The new feature is intended for use when balancing weights within
a group of bones while all others are locked. Enabling the option
presents weight as if the locked bones didn't exist, and their
weight was proportionally redistributed to the editable bones.

Conversely, the Multi-Paint feature allows balancing a group of
bones as a whole against all unselected bones, while ignoring
weight distribution within the selected group.

This mode also allows temporarily viewing non-normalized weights
as if they were normalized, without actually changing the values.

Differential Revision: https://developer.blender.org/D3837
This commit is contained in:
Alexander Gavrilov 2018-10-07 18:25:51 +03:00
parent 82c51d0edb
commit 084bf7daee
12 changed files with 430 additions and 52 deletions

View File

@ -1058,6 +1058,7 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):
col = layout.column()
col.prop(tool_settings, "use_auto_normalize", text="Auto Normalize")
col.prop(tool_settings, "use_lock_relative", text="Lock-Relative")
col.prop(tool_settings, "use_multipaint", text="Multi-Paint")
col.prop(wpaint, "use_group_restrict")

View File

@ -67,11 +67,24 @@ float BKE_defvert_array_find_weight_safe(const struct MDeformVert *dvert,
const int index,
const int defgroup);
float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
int defbase_tot,
const bool *defbase_sel);
float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
int defbase_tot,
const bool *defbase_sel,
int defbase_tot_sel,
bool do_autonormalize);
bool is_normalized);
float BKE_defvert_calc_lock_relative_weight(float weight,
float locked_weight,
float unlocked_weight);
float BKE_defvert_lock_relative_weight(float weight,
const struct MDeformVert *dv,
int defbase_tot,
const bool *defbase_locked,
const bool *defbase_unlocked);
void BKE_defvert_copy(struct MDeformVert *dvert_dst, const struct MDeformVert *dvert_src);
void BKE_defvert_copy_subset(struct MDeformVert *dvert_dst,

View File

@ -76,6 +76,16 @@ bool *BKE_object_defgroup_selected_get(struct Object *ob,
int defbase_tot,
int *r_dg_flags_sel_tot);
bool BKE_object_defgroup_check_lock_relative(const bool *lock_flags,
const bool *validmap,
int index);
bool BKE_object_defgroup_check_lock_relative_multi(int defbase_tot,
const bool *lock_flags,
const bool *selected,
int sel_tot);
void BKE_object_defgroup_split_locked_validmap(
int defbase_tot, const bool *locked, const bool *deform, bool *r_locked, bool *r_unlocked);
void BKE_object_defgroup_mirror_selection(struct Object *ob,
int defbase_tot,
const bool *selection,

View File

@ -834,6 +834,32 @@ bool BKE_defvert_is_weight_zero(const struct MDeformVert *dvert, const int defgr
return true;
}
/**
* \return The total weight in all groups marked in the selection mask.
*/
float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
int defbase_tot,
const bool *defbase_sel)
{
int i;
float total = 0.0f;
const MDeformWeight *dw = dv->dw;
if (defbase_sel == NULL) {
return total;
}
for (i = dv->totweight; i != 0; i--, dw++) {
if (dw->def_nr < defbase_tot) {
if (defbase_sel[dw->def_nr]) {
total += dw->weight;
}
}
}
return total;
}
/**
* \return The representative weight of a multipaint group, used for
* viewport colors and actual painting.
@ -846,29 +872,75 @@ float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
int defbase_tot,
const bool *defbase_sel,
int defbase_tot_sel,
bool do_autonormalize)
bool is_normalized)
{
int i;
float total = 0.0f;
const MDeformWeight *dw = dv->dw;
float total = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_sel);
for (i = dv->totweight; i != 0; i--, dw++) {
/* in multipaint, get the average if auto normalize is inactive
* get the sum if it is active */
if (dw->def_nr < defbase_tot) {
if (defbase_sel[dw->def_nr]) {
total += dw->weight;
}
}
}
if (do_autonormalize == false) {
/* in multipaint, get the average if auto normalize is inactive
* get the sum if it is active */
if (!is_normalized) {
total /= defbase_tot_sel;
}
return total;
}
/**
* Computes the display weight for the lock relative weight paint mode.
*
* @return weight divided by 1-locked_weight with division by zero check
*/
float BKE_defvert_calc_lock_relative_weight(float weight,
float locked_weight,
float unlocked_weight)
{
/* First try normalizing unlocked weights. */
if (unlocked_weight > 0.0f) {
return weight / unlocked_weight;
}
/* If no unlocked weight exists, take locked into account. */
if (locked_weight <= 0.0f) {
return weight;
}
/* handle division by zero */
if (locked_weight >= 1.0f) {
if (weight != 0.0f) {
return 1.0f;
}
else {
/* resolve 0/0 to 0 */
return 0.0f;
}
}
/* non-degenerate division */
return weight / (1.0f - locked_weight);
}
/**
* Computes the display weight for the lock relative weight paint mode, using weight data.
*
* @return weight divided by unlocked, or 1-locked_weight with division by zero check
*/
float BKE_defvert_lock_relative_weight(float weight,
const struct MDeformVert *dv,
int defbase_tot,
const bool *defbase_locked,
const bool *defbase_unlocked)
{
float unlocked = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_unlocked);
if (unlocked > 0.0f) {
return weight / unlocked;
}
float locked = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_locked);
return BKE_defvert_calc_lock_relative_weight(weight, locked, unlocked);
}
/* -------------------------------------------------------------------- */
/** \name Defvert Array functions
* \{ */

View File

@ -693,6 +693,72 @@ bool *BKE_object_defgroup_selected_get(Object *ob, int defbase_tot, int *r_dg_fl
return dg_selection;
}
/**
* Checks if the lock relative mode is applicable.
*
* \return true if an unlocked deform group is active.
*/
bool BKE_object_defgroup_check_lock_relative(const bool *lock_flags,
const bool *validmap,
int index)
{
return validmap && validmap[index] && !(lock_flags && lock_flags[index]);
}
/**
* Additional check for whether the lock relative mode is applicable in multipaint mode.
*
* @return true if none of the selected groups are locked.
*/
bool BKE_object_defgroup_check_lock_relative_multi(int defbase_tot,
const bool *lock_flags,
const bool *selected,
int sel_tot)
{
if (lock_flags == NULL) {
return true;
}
if (selected == NULL || sel_tot <= 1) {
return true;
}
for (int i = 0; i < defbase_tot; i++) {
if (selected[i] && lock_flags[i]) {
return false;
}
}
return true;
}
/**
* Takes a pair of boolean masks of all locked and all deform groups, and computes
* a pair of masks for locked deform and unlocked deform groups. Output buffers may
* reuse the input ones.
*/
void BKE_object_defgroup_split_locked_validmap(
int defbase_tot, const bool *locked, const bool *deform, bool *r_locked, bool *r_unlocked)
{
if (!locked) {
if (r_unlocked != deform) {
memcpy(r_unlocked, deform, sizeof(bool) * defbase_tot);
}
if (r_locked) {
memset(r_locked, 0, sizeof(bool) * defbase_tot);
}
return;
}
for (int i = 0; i < defbase_tot; i++) {
bool is_locked = locked[i];
bool is_deform = deform[i];
r_locked[i] = is_deform && is_locked;
r_unlocked[i] = is_deform && !is_locked;
}
}
/**
* Marks mirror vgroups in output and counts them.
* Output and counter assumed to be already initialized.

View File

@ -34,12 +34,17 @@ typedef struct DRW_MeshWeightState {
/* Set of all selected bones for Multipaint. */
bool *defgroup_sel; /* [defgroup_len] */
int defgroup_sel_count;
/* Set of all locked and unlocked deform bones for Lock Relative mode. */
bool *defgroup_locked; /* [defgroup_len] */
bool *defgroup_unlocked; /* [defgroup_len] */
} DRW_MeshWeightState;
/* DRW_MeshWeightState.flags */
enum {
DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0),
DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1),
DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE = (1 << 2),
};
typedef struct DRW_MeshCDMask {

View File

@ -2407,12 +2407,13 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig
float input = 0.0f;
if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
/* Multi-Paint feature */
input = BKE_defvert_multipaint_collective_weight(
dvert,
wstate->defgroup_len,
wstate->defgroup_sel,
wstate->defgroup_sel_count,
(wstate->flags & DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE) != 0);
bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE |
DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE));
input = BKE_defvert_multipaint_collective_weight(dvert,
wstate->defgroup_len,
wstate->defgroup_sel,
wstate->defgroup_sel_count,
is_normalized);
/* make it black if the selected groups have no weight on a vertex */
if (input == 0.0f) {
return -1.0f;
@ -2435,6 +2436,13 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig
}
}
}
/* Lock-Relative: display the fraction of current weight vs total unlocked weight. */
if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) {
input = BKE_defvert_lock_relative_weight(
input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked);
}
CLAMP(input, 0.0f, 1.0f);
return input;
}

View File

@ -239,6 +239,8 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
static void drw_mesh_weight_state_clear(struct DRW_MeshWeightState *wstate)
{
MEM_SAFE_FREE(wstate->defgroup_sel);
MEM_SAFE_FREE(wstate->defgroup_locked);
MEM_SAFE_FREE(wstate->defgroup_unlocked);
memset(wstate, 0, sizeof(*wstate));
@ -250,12 +252,26 @@ static void drw_mesh_weight_state_copy(struct DRW_MeshWeightState *wstate_dst,
const struct DRW_MeshWeightState *wstate_src)
{
MEM_SAFE_FREE(wstate_dst->defgroup_sel);
MEM_SAFE_FREE(wstate_dst->defgroup_locked);
MEM_SAFE_FREE(wstate_dst->defgroup_unlocked);
memcpy(wstate_dst, wstate_src, sizeof(*wstate_dst));
if (wstate_src->defgroup_sel) {
wstate_dst->defgroup_sel = MEM_dupallocN(wstate_src->defgroup_sel);
}
if (wstate_src->defgroup_locked) {
wstate_dst->defgroup_locked = MEM_dupallocN(wstate_src->defgroup_locked);
}
if (wstate_src->defgroup_unlocked) {
wstate_dst->defgroup_unlocked = MEM_dupallocN(wstate_src->defgroup_unlocked);
}
}
static bool drw_mesh_flags_equal(const bool *array1, const bool *array2, int size)
{
return ((!array1 && !array2) ||
(array1 && array2 && memcmp(array1, array2, size * sizeof(bool)) == 0));
}
/** Compare two selection structures. */
@ -265,9 +281,9 @@ static bool drw_mesh_weight_state_compare(const struct DRW_MeshWeightState *a,
return a->defgroup_active == b->defgroup_active && a->defgroup_len == b->defgroup_len &&
a->flags == b->flags && a->alert_mode == b->alert_mode &&
a->defgroup_sel_count == b->defgroup_sel_count &&
((!a->defgroup_sel && !b->defgroup_sel) ||
(a->defgroup_sel && b->defgroup_sel &&
memcmp(a->defgroup_sel, b->defgroup_sel, a->defgroup_len * sizeof(bool)) == 0));
drw_mesh_flags_equal(a->defgroup_sel, b->defgroup_sel, a->defgroup_len) &&
drw_mesh_flags_equal(a->defgroup_locked, b->defgroup_locked, a->defgroup_len) &&
drw_mesh_flags_equal(a->defgroup_unlocked, b->defgroup_unlocked, a->defgroup_len);
}
static void drw_mesh_weight_state_extract(Object *ob,
@ -308,6 +324,33 @@ static void drw_mesh_weight_state_extract(Object *ob,
MEM_SAFE_FREE(wstate->defgroup_sel);
}
}
if (paint_mode && ts->wpaint_lock_relative) {
/* Set of locked vertex groups for the lock relative mode. */
wstate->defgroup_locked = BKE_object_defgroup_lock_flags_get(ob, wstate->defgroup_len);
wstate->defgroup_unlocked = BKE_object_defgroup_validmap_get(ob, wstate->defgroup_len);
/* Check that a deform group is active, and none of selected groups are locked. */
if (BKE_object_defgroup_check_lock_relative(
wstate->defgroup_locked, wstate->defgroup_unlocked, wstate->defgroup_active) &&
BKE_object_defgroup_check_lock_relative_multi(wstate->defgroup_len,
wstate->defgroup_locked,
wstate->defgroup_sel,
wstate->defgroup_sel_count)) {
wstate->flags |= DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE;
/* Compute the set of locked and unlocked deform vertex groups. */
BKE_object_defgroup_split_locked_validmap(wstate->defgroup_len,
wstate->defgroup_locked,
wstate->defgroup_unlocked,
wstate->defgroup_locked, /* out */
wstate->defgroup_unlocked);
}
else {
MEM_SAFE_FREE(wstate->defgroup_unlocked);
MEM_SAFE_FREE(wstate->defgroup_locked);
}
}
}
/** \} */

View File

@ -413,6 +413,31 @@ static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
}
}
static float wpaint_undo_lock_relative(
float weight, float old_weight, float locked_weight, float free_weight, bool auto_normalize)
{
/* In auto-normalize mode, or when there is no unlocked weight,
* compute based on locked weight. */
if (auto_normalize || free_weight <= 0.0f) {
weight *= (1.0f - locked_weight);
}
else {
/* When dealing with full unlocked weight, don't paint, as it is always displayed as 1. */
if (old_weight >= free_weight) {
weight = old_weight;
}
/* Try to compute a weight value that would produce the desired effect if normalized. */
else if (weight < 1.0f) {
weight = weight * (free_weight - old_weight) / (1 - weight);
}
else {
weight = 1.0f;
}
}
return weight;
}
/* ----------------------------------------------------- */
static void do_weight_paint_normalize_all(MDeformVert *dvert,
@ -704,10 +729,16 @@ typedef struct WeightPaintInfo {
/* same as WeightPaintData.vgroup_validmap,
* only added here for convenience */
const bool *vgroup_validmap;
/* same as WeightPaintData.vgroup_locked/unlocked,
* only added here for convenience */
const bool *vgroup_locked;
const bool *vgroup_unlocked;
bool do_flip;
bool do_multipaint;
bool do_auto_normalize;
bool do_lock_relative;
bool is_normalized;
float brush_alpha_value; /* result of BKE_brush_alpha_get() */
} WeightPaintInfo;
@ -727,7 +758,8 @@ static void do_weight_paint_vertex_single(
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
MDeformWeight *dw;
float weight_prev;
float weight_prev, weight_cur;
float dw_rel_locked = 0.0f, dw_rel_free = 1.0f;
/* mirror vars */
int index_mirr;
@ -795,6 +827,17 @@ static void do_weight_paint_vertex_single(
dw_mirr = NULL;
}
weight_cur = dw->weight;
/* Handle weight caught up in locked defgroups for Lock Relative. */
if (wpi->do_lock_relative) {
dw_rel_free = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_unlocked);
dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_locked);
CLAMP(dw_rel_locked, 0.0f, 1.0f);
weight_cur = BKE_defvert_calc_lock_relative_weight(weight_cur, dw_rel_locked, dw_rel_free);
}
if (!brush_use_accumulate(wp)) {
MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index);
@ -803,9 +846,14 @@ static void do_weight_paint_vertex_single(
}
weight_prev = BKE_defvert_find_weight(dv_prev, wpi->active.index);
if (wpi->do_lock_relative) {
weight_prev = BKE_defvert_lock_relative_weight(
weight_prev, dv_prev, wpi->defbase_tot, wpi->vgroup_locked, wpi->vgroup_unlocked);
}
}
else {
weight_prev = dw->weight;
weight_prev = weight_cur;
}
/* If there are no normalize-locks or multipaint,
@ -815,7 +863,28 @@ static void do_weight_paint_vertex_single(
float new_weight = wpaint_blend(
wp, weight_prev, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
dw->weight = wpaint_clamp_monotonic(weight_prev, dw->weight, new_weight);
float weight = wpaint_clamp_monotonic(weight_prev, weight_cur, new_weight);
/* Undo the lock relative weight correction. */
if (wpi->do_lock_relative) {
if (index_mirr == index) {
/* When painting a center vertex with X Mirror and L/R pair,
* handle both groups together. This avoids weird fighting
* in the non-normalized weight mode. */
float orig_weight = dw->weight + dw_mirr->weight;
weight = 0.5f *
wpaint_undo_lock_relative(
weight * 2, orig_weight, dw_rel_locked, dw_rel_free, wpi->do_auto_normalize);
}
else {
weight = wpaint_undo_lock_relative(
weight, dw->weight, dw_rel_locked, dw_rel_free, wpi->do_auto_normalize);
}
CLAMP(weight, 0.0f, 1.0f);
}
dw->weight = weight;
/* WATCH IT: take care of the ordering of applying mirror -> normalize,
* can give wrong results [#26193], least confusing if normalize is done last */
@ -892,7 +961,8 @@ static void do_weight_paint_vertex_multi(
MDeformVert *dv_mirr = NULL;
/* weights */
float curw, oldw, neww, change, curw_mirr, change_mirr;
float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr;
float dw_rel_free, dw_rel_locked;
/* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
if (me->editflag & ME_EDIT_MIRROR_X) {
@ -907,14 +977,23 @@ static void do_weight_paint_vertex_multi(
}
/* compute weight change by applying the brush to average or sum of group weights */
curw = BKE_defvert_multipaint_collective_weight(
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
curw = curw_real = BKE_defvert_multipaint_collective_weight(
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->is_normalized);
if (curw == 0.0f) {
/* note: no weight to assign to this vertex, could add all groups? */
return;
}
/* Handle weight caught up in locked defgroups for Lock Relative. */
if (wpi->do_lock_relative) {
dw_rel_free = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_unlocked);
dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_locked);
CLAMP(dw_rel_locked, 0.0f, 1.0f);
curw = BKE_defvert_calc_lock_relative_weight(curw, dw_rel_locked, dw_rel_free);
}
if (!brush_use_accumulate(wp)) {
MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index);
@ -923,7 +1002,12 @@ static void do_weight_paint_vertex_multi(
}
oldw = BKE_defvert_multipaint_collective_weight(
dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->is_normalized);
if (wpi->do_lock_relative) {
oldw = BKE_defvert_lock_relative_weight(
oldw, dv_prev, wpi->defbase_tot, wpi->vgroup_locked, wpi->vgroup_unlocked);
}
}
else {
oldw = curw;
@ -932,14 +1016,19 @@ static void do_weight_paint_vertex_multi(
neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
neww = wpaint_clamp_monotonic(oldw, curw, neww);
change = neww / curw;
if (wpi->do_lock_relative) {
neww = wpaint_undo_lock_relative(
neww, curw_real, dw_rel_locked, dw_rel_free, wpi->do_auto_normalize);
}
change = neww / curw_real;
/* verify for all groups that 0 < result <= 1 */
multipaint_clamp_change(dv, wpi->defbase_tot, wpi->defbase_sel, &change);
if (dv_mirr != NULL) {
curw_mirr = BKE_defvert_multipaint_collective_weight(
dv_mirr, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
dv_mirr, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->is_normalized);
if (curw_mirr == 0.0f) {
/* can't mirror into a zero weight vertex */
@ -947,7 +1036,7 @@ static void do_weight_paint_vertex_multi(
}
else {
/* mirror is changed to achieve the same collective weight value */
float orig = change_mirr = curw * change / curw_mirr;
float orig = change_mirr = curw_real * change / curw_mirr;
multipaint_clamp_change(dv_mirr, wpi->defbase_tot, wpi->defbase_sel, &change_mirr);
@ -1408,11 +1497,14 @@ struct WPaintData {
/* variables for auto normalize */
const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */
const bool *lock_flags;
const bool *vgroup_locked; /* mask of locked defbones */
const bool *vgroup_unlocked; /* mask of unlocked defbones */
/* variables for multipaint */
const bool *defbase_sel; /* set of selected groups */
int defbase_tot_sel; /* number of selected groups */
bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
bool do_lock_relative;
int defbase_tot;
@ -1615,10 +1707,29 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
/* set up auto-normalize, and generate map for detecting which
* vgroups affect deform bones */
wpd->lock_flags = BKE_object_defgroup_lock_flags_get(ob, wpd->defbase_tot);
if (ts->auto_normalize || ts->multipaint || wpd->lock_flags) {
if (ts->auto_normalize || ts->multipaint || wpd->lock_flags || ts->wpaint_lock_relative) {
wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(ob, wpd->defbase_tot);
}
/* Compute the set of all locked deform groups when Lock Relative is active. */
if (ts->wpaint_lock_relative &&
BKE_object_defgroup_check_lock_relative(
wpd->lock_flags, wpd->vgroup_validmap, wpd->active.index) &&
(!wpd->do_multipaint || BKE_object_defgroup_check_lock_relative_multi(
defbase_tot, wpd->lock_flags, defbase_sel, defbase_tot_sel))) {
bool *unlocked = MEM_dupallocN(wpd->vgroup_validmap);
if (wpd->lock_flags) {
bool *locked = MEM_mallocN(sizeof(bool) * wpd->defbase_tot, __func__);
BKE_object_defgroup_split_locked_validmap(
wpd->defbase_tot, wpd->lock_flags, wpd->vgroup_validmap, locked, unlocked);
wpd->vgroup_locked = locked;
}
wpd->vgroup_unlocked = unlocked;
wpd->do_lock_relative = true;
}
if (wpd->do_multipaint && ts->auto_normalize) {
bool *tmpflags;
tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__);
@ -1686,16 +1797,23 @@ static void get_brush_alpha_data(const Scene *scene,
static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintInfo *wpi)
{
if (wpi->do_multipaint) {
float weight = BKE_defvert_multipaint_collective_weight(
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
float weight;
CLAMP(weight, 0.0f, 1.0f);
return weight;
if (wpi->do_multipaint) {
weight = BKE_defvert_multipaint_collective_weight(
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->is_normalized);
}
else {
return BKE_defvert_find_weight(dv, wpi->active.index);
weight = BKE_defvert_find_weight(dv, wpi->active.index);
}
if (wpi->do_lock_relative) {
weight = BKE_defvert_lock_relative_weight(
weight, dv, wpi->defbase_tot, wpi->vgroup_locked, wpi->vgroup_unlocked);
}
CLAMP(weight, 0.0f, 1.0f);
return weight;
}
static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata,
@ -2318,9 +2436,13 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
wpi.mirror = wpd->mirror;
wpi.lock_flags = wpd->lock_flags;
wpi.vgroup_validmap = wpd->vgroup_validmap;
wpi.vgroup_locked = wpd->vgroup_locked;
wpi.vgroup_unlocked = wpd->vgroup_unlocked;
wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip");
wpi.do_multipaint = wpd->do_multipaint;
wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL));
wpi.do_lock_relative = wpd->do_lock_relative;
wpi.is_normalized = wpi.do_auto_normalize || wpi.do_lock_relative;
wpi.brush_alpha_value = brush_alpha_value;
/* *** done setting up WeightPaintInfo *** */
@ -2378,6 +2500,12 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
if (wpd->vgroup_validmap) {
MEM_freeN((void *)wpd->vgroup_validmap);
}
if (wpd->vgroup_locked) {
MEM_freeN((void *)wpd->vgroup_locked);
}
if (wpd->vgroup_unlocked) {
MEM_freeN((void *)wpd->vgroup_unlocked);
}
if (wpd->lock_flags) {
MEM_freeN((void *)wpd->lock_flags);
}

View File

@ -212,12 +212,22 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
const int vgroup_active = vc.obact->actdef - 1;
float vgroup_weight = BKE_defvert_find_weight(&me->dvert[v_idx_best], vgroup_active);
const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
bool use_lock_relative = ts->wpaint_lock_relative;
bool *defbase_locked = NULL, *defbase_unlocked = NULL;
if (use_lock_relative) {
defbase_locked = BKE_object_defgroup_lock_flags_get(vc.obact, defbase_tot);
defbase_unlocked = BKE_object_defgroup_validmap_get(vc.obact, defbase_tot);
use_lock_relative = BKE_object_defgroup_check_lock_relative(
defbase_locked, defbase_unlocked, vgroup_active);
}
/* use combined weight in multipaint mode,
* since that's what is displayed to the user in the colors */
if (ts->multipaint) {
int defbase_tot_sel;
const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
bool *defbase_sel = BKE_object_defgroup_selected_get(
vc.obact, defbase_tot, &defbase_tot_sel);
@ -227,20 +237,30 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
}
vgroup_weight = BKE_defvert_multipaint_collective_weight(&me->dvert[v_idx_best],
defbase_tot,
defbase_sel,
defbase_tot_sel,
ts->auto_normalize);
use_lock_relative = use_lock_relative &&
BKE_object_defgroup_check_lock_relative_multi(
defbase_tot, defbase_locked, defbase_sel, defbase_tot_sel);
/* If auto-normalize is enabled, but weights are not normalized,
* the value can exceed 1. */
CLAMP(vgroup_weight, 0.0f, 1.0f);
bool is_normalized = ts->auto_normalize || use_lock_relative;
vgroup_weight = BKE_defvert_multipaint_collective_weight(
&me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, is_normalized);
}
MEM_freeN(defbase_sel);
}
if (use_lock_relative) {
BKE_object_defgroup_split_locked_validmap(
defbase_tot, defbase_locked, defbase_unlocked, defbase_locked, defbase_unlocked);
vgroup_weight = BKE_defvert_lock_relative_weight(
vgroup_weight, &me->dvert[v_idx_best], defbase_tot, defbase_locked, defbase_unlocked);
}
MEM_SAFE_FREE(defbase_locked);
MEM_SAFE_FREE(defbase_unlocked);
CLAMP(vgroup_weight, 0.0f, 1.0f);
BKE_brush_weight_set(vc.scene, brush, vgroup_weight);
changed = true;
}

View File

@ -1470,6 +1470,9 @@ typedef struct ToolSettings {
/** Auto normalizing mode in wpaint. */
char auto_normalize;
/** Present weights as if all locked vertex groups were
* deleted, and the remaining deform groups normalized. */
char wpaint_lock_relative;
/** Paint multiple bones in wpaint. */
char multipaint;
char weightuser;
@ -1480,7 +1483,7 @@ typedef struct ToolSettings {
char gpencil_selectmode_vertex;
/* UV painting */
char _pad2[2];
char _pad2[1];
char uv_sculpt_settings;
char uv_relax_method;
/* XXX: these sculpt_paint_* fields are deprecated, use the

View File

@ -2863,6 +2863,15 @@ static void rna_def_tool_settings(BlenderRNA *brna)
"to 1.0 while weight painting");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
prop = RNA_def_property(srna, "use_lock_relative", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "wpaint_lock_relative", 1);
RNA_def_property_ui_text(prop,
"WPaint Lock-Relative",
"Display bone-deforming groups as if all locked deform groups "
"were deleted, and the remaining ones were re-normalized");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
prop = RNA_def_property(srna, "use_multipaint", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "multipaint", 1);