Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py1
-rw-r--r--source/blender/blenkernel/BKE_deform.h15
-rw-r--r--source/blender/blenkernel/BKE_object_deform.h10
-rw-r--r--source/blender/blenkernel/intern/deform.c100
-rw-r--r--source/blender/blenkernel/intern/object_deform.c66
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h5
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c20
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c49
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c162
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c38
-rw-r--r--source/blender/makesdna/DNA_scene_types.h5
-rw-r--r--source/blender/makesrna/intern/rna_scene.c9
12 files changed, 429 insertions, 51 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 9ad339b418c..758f0686bb4 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -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")
diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h
index cdfbd45bb5f..c86b877b575 100644
--- a/source/blender/blenkernel/BKE_deform.h
+++ b/source/blender/blenkernel/BKE_deform.h
@@ -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,
diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h
index f5283ef8405..410cb862aa7 100644
--- a/source/blender/blenkernel/BKE_object_deform.h
+++ b/source/blender/blenkernel/BKE_object_deform.h
@@ -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,
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index 18064fd95d4..3b7162b122e 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -835,26 +835,21 @@ bool BKE_defvert_is_weight_zero(const struct MDeformVert *dvert, const int defgr
}
/**
- * \return The representative weight of a multipaint group, used for
- * viewport colors and actual painting.
- *
- * Result equal to sum of weights with auto normalize, and average otherwise.
- * Value is not clamped, since painting relies on multiplication being always
- * commutative with the collective weight function.
+ * \return The total weight in all groups marked in the selection mask.
*/
-float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
- int defbase_tot,
- const bool *defbase_sel,
- int defbase_tot_sel,
- bool do_autonormalize)
+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++) {
- /* 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;
@@ -862,13 +857,90 @@ float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
}
}
- if (do_autonormalize == false) {
+ return total;
+}
+
+/**
+ * \return The representative weight of a multipaint group, used for
+ * viewport colors and actual painting.
+ *
+ * Result equal to sum of weights with auto normalize, and average otherwise.
+ * Value is not clamped, since painting relies on multiplication being always
+ * commutative with the collective weight function.
+ */
+float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
+ int defbase_tot,
+ const bool *defbase_sel,
+ int defbase_tot_sel,
+ bool is_normalized)
+{
+ float total = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_sel);
+
+ /* 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
* \{ */
diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c
index 00be25d5210..573573f20ed 100644
--- a/source/blender/blenkernel/intern/object_deform.c
+++ b/source/blender/blenkernel/intern/object_deform.c
@@ -694,6 +694,72 @@ bool *BKE_object_defgroup_selected_get(Object *ob, int defbase_tot, int *r_dg_fl
}
/**
+ * 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.
* Designed to be usable after BKE_object_defgroup_selected_get to extend selection to mirror.
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index 6226b5dcf7f..0e02b07e95b 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -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 {
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index b385facd9aa..274a5492533 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -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;
}
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 2e423c4dfe2..c89b6baa921 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -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);
+ }
+ }
}
/** \} */
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index f957f2287f0..a00296a911f 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -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);
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 4b8990c1b5d..5c408933559 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -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;
}
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index aac976fbabd..236f9fa4eb2 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -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
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index f974d5c563f..e96db76bd3a 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -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);