diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_deform.h | 15 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object_deform.h | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/deform.c | 100 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object_deform.c | 66 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_extract.h | 5 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_extract_mesh.c | 20 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_impl_mesh.c | 49 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_vertex.c | 162 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c | 38 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_scene.c | 9 |
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); |