Vertex Paint: fix feedback loop w/ smear tool

Was reading and writing from same array,
this also makes smear thread-safe.
This commit is contained in:
Campbell Barton 2017-10-03 23:04:22 +11:00
parent 314d93838e
commit 6a7dc3348a

@ -1198,6 +1198,12 @@ struct WPaintData {
bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
int defbase_tot;
/* Special storage for smear brush, avoid feedback loop - update each step and swap. */
struct {
float *weight_prev;
float *weight_curr;
} smear;
};
/* Initialize the stroke cache invariants from operator properties */
@ -1422,6 +1428,26 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
wpd->mirror.lock = tmpflags;
}
if (vp->paint.brush->vertexpaint_tool == PAINT_BLEND_SMEAR) {
wpd->smear.weight_prev = MEM_mallocN(sizeof(float) * me->totvert, __func__);
const MDeformVert *dv = me->dvert;
if (wpd->do_multipaint) {
const bool do_auto_normalize = ((ts->auto_normalize != 0) && (wpd->vgroup_validmap != NULL));
for (int i = 0; i < me->totvert; i++, dv++) {
float weight = BKE_defvert_multipaint_collective_weight(
dv, wpd->defbase_tot, wpd->defbase_sel, wpd->defbase_tot_sel, do_auto_normalize);
CLAMP(weight, 0.0f, 1.0f);
wpd->smear.weight_prev[i] = weight;
}
}
else {
for (int i = 0; i < me->totvert; i++, dv++) {
wpd->smear.weight_prev[i] = defvert_find_weight(dv, wpd->active.index);
}
}
wpd->smear.weight_curr = MEM_dupallocN(wpd->smear.weight_prev);
}
/* painting on subsurfs should give correct points too, this returns me->totvert amount */
ob->sculpt->building_vp_handle = true;
wpd->vp_handle = ED_vpaint_proj_handle_create(scene, ob, &wpd->vertexcosnos);
@ -1663,8 +1689,7 @@ static void do_wpaint_brush_smear_task_cb_ex(
if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot;
MDeformVert *dv = &data->me->dvert[v_other_index];
weight_final = wpaint_get_active_weight(dv, data->wpi);
weight_final = data->wpd->smear.weight_prev[v_other_index];
do_color = true;
}
}
@ -1679,6 +1704,9 @@ static void do_wpaint_brush_smear_task_cb_ex(
do_weight_paint_vertex(
data->vp, data->ob, data->wpi,
v_index, final_alpha, (float)weight_final);
/* Access the weight again because it might not have been applied completely. */
data->wpd->smear.weight_curr[v_index] =
wpaint_get_active_weight(&data->me->dvert[v_index], data->wpi);
}
}
}
@ -2044,6 +2072,10 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
swap_m4m4(vc->rv3d->persmat, mat);
if (wp->paint.brush->vertexpaint_tool == PAINT_BLEND_SMEAR) {
SWAP(float *, wpd->smear.weight_curr, wpd->smear.weight_prev);
}
/* calculate pivot for rotation around seletion if needed */
/* also needed for "View Selected" on last stroke */
paint_last_stroke_update(scene, vc->ar, mval);
@ -2093,6 +2125,10 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
MEM_freeN((void *)wpd->active.lock);
if (wpd->mirror.lock)
MEM_freeN((void *)wpd->mirror.lock);
if (wpd->smear.weight_prev)
MEM_freeN(wpd->smear.weight_prev);
if (wpd->smear.weight_curr)
MEM_freeN(wpd->smear.weight_curr);
MEM_freeN(wpd);
}
@ -2323,6 +2359,12 @@ struct VPaintData {
bool *mlooptag;
bool is_texbrush;
/* Special storage for smear brush, avoid feedback loop - update each step and swap. */
struct {
uint *color_prev;
uint *color_curr;
} smear;
};
static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
@ -2374,6 +2416,12 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
}
if (brush->vertexpaint_tool == PAINT_BLEND_SMEAR) {
vpd->smear.color_prev = MEM_mallocN(sizeof(uint) * me->totloop, __func__);
memcpy(vpd->smear.color_prev, me->mloopcol, sizeof(uint) * me->totloop);
vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev);
}
/* Create projection handle */
if (vpd->is_texbrush) {
ob->sculpt->building_vp_handle = true;
@ -2747,7 +2795,7 @@ static void do_vpaint_brush_smear_task_cb_ex(
if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot;
color_final = lcol[mp->loopstart + k];
color_final = data->vpd->smear.color_prev[mp->loopstart + k];
do_color = true;
}
}
@ -2780,6 +2828,8 @@ static void do_vpaint_brush_smear_task_cb_ex(
lcol[l_index] = vpaint_blend(
data->vp, lcol[l_index], color_orig, color_final,
final_alpha, 255 * brush_strength);
data->vpd->smear.color_curr[l_index] = lcol[l_index];
}
}
}
@ -2955,6 +3005,10 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
swap_m4m4(vc->rv3d->persmat, mat);
if (vp->paint.brush->vertexpaint_tool == PAINT_BLEND_SMEAR) {
SWAP(uint *, vpd->smear.color_curr, vpd->smear.color_prev);
}
/* calculate pivot for rotation around seletion if needed */
/* also needed for "View Selected" on last stroke */
paint_last_stroke_update(scene, vc->ar, mval);
@ -2980,6 +3034,10 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
if (vpd->mlooptag)
MEM_freeN(vpd->mlooptag);
if (vpd->smear.color_prev)
MEM_freeN(vpd->smear.color_prev);
if (vpd->smear.color_curr)
MEM_freeN(vpd->smear.color_curr);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);