From e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Apr 2019 06:17:24 +0200 Subject: ClangFormat: apply to source, most of intern Apply clang format as proposed in T53211. For details on usage and instructions for migrating branches without conflicts, see: https://wiki.blender.org/wiki/Tools/ClangFormat --- source/blender/editors/sculpt_paint/paint_vertex.c | 5580 ++++++++++---------- 1 file changed, 2832 insertions(+), 2748 deletions(-) (limited to 'source/blender/editors/sculpt_paint/paint_vertex.c') diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 85bdba683cd..70783ff004e 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -77,130 +77,130 @@ #include "BKE_ccg.h" #include "sculpt_intern.h" -#include "paint_intern.h" /* own include */ +#include "paint_intern.h" /* own include */ /* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */ struct VPaintAverageAccum { - uint len; - uint value[3]; + uint len; + uint value[3]; }; struct WPaintAverageAccum { - uint len; - double value; + uint len; + double value; }; struct NormalAnglePrecalc { - bool do_mask_normal; - /* what angle to mask at */ - float angle; - /* cos(angle), faster to compare */ - float angle__cos; - float angle_inner; - float angle_inner__cos; - /* difference between angle and angle_inner, for easy access */ - float angle_range; + bool do_mask_normal; + /* what angle to mask at */ + float angle; + /* cos(angle), faster to compare */ + float angle__cos; + float angle_inner; + float angle_inner__cos; + /* difference between angle and angle_inner, for easy access */ + float angle_range; }; - -static void view_angle_limits_init( - struct NormalAnglePrecalc *a, float angle, bool do_mask_normal) +static void view_angle_limits_init(struct NormalAnglePrecalc *a, float angle, bool do_mask_normal) { - angle = RAD2DEGF(angle); - a->do_mask_normal = do_mask_normal; - if (do_mask_normal) { - a->angle_inner = angle; - a->angle = (a->angle_inner + 90.0f) * 0.5f; - } - else { - a->angle_inner = a->angle = angle; - } + angle = RAD2DEGF(angle); + a->do_mask_normal = do_mask_normal; + if (do_mask_normal) { + a->angle_inner = angle; + a->angle = (a->angle_inner + 90.0f) * 0.5f; + } + else { + a->angle_inner = a->angle = angle; + } - a->angle_inner *= (float)(M_PI_2 / 90); - a->angle *= (float)(M_PI_2 / 90); - a->angle_range = a->angle - a->angle_inner; + a->angle_inner *= (float)(M_PI_2 / 90); + a->angle *= (float)(M_PI_2 / 90); + a->angle_range = a->angle - a->angle_inner; - if (a->angle_range <= 0.0f) { - a->do_mask_normal = false; /* no need to do blending */ - } + if (a->angle_range <= 0.0f) { + a->do_mask_normal = false; /* no need to do blending */ + } - a->angle__cos = cosf(a->angle); - a->angle_inner__cos = cosf(a->angle_inner); + a->angle__cos = cosf(a->angle); + a->angle_inner__cos = cosf(a->angle_inner); } -static float view_angle_limits_apply_falloff( - const struct NormalAnglePrecalc *a, float angle_cos, float *mask_p) +static float view_angle_limits_apply_falloff(const struct NormalAnglePrecalc *a, + float angle_cos, + float *mask_p) { - if (angle_cos <= a->angle__cos) { - /* outsize the normal limit */ - return false; - } - else if (angle_cos < a->angle_inner__cos) { - *mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range; - return true; - } - else { - return true; - } + if (angle_cos <= a->angle__cos) { + /* outsize the normal limit */ + return false; + } + else if (angle_cos < a->angle_inner__cos) { + *mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range; + return true; + } + else { + return true; + } } static bool vwpaint_use_normal(const VPaint *vp) { - return ((vp->paint.brush->flag & BRUSH_FRONTFACE) != 0) || - ((vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + return ((vp->paint.brush->flag & BRUSH_FRONTFACE) != 0) || + ((vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); } static bool brush_use_accumulate_ex(const Brush *brush, const int ob_mode) { - return ((brush->flag & BRUSH_ACCUMULATE) != 0 || - (ob_mode == OB_MODE_VERTEX_PAINT ? - (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) : - (brush->weightpaint_tool == WPAINT_TOOL_SMEAR))); + return ((brush->flag & BRUSH_ACCUMULATE) != 0 || + (ob_mode == OB_MODE_VERTEX_PAINT ? (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) : + (brush->weightpaint_tool == WPAINT_TOOL_SMEAR))); } static bool brush_use_accumulate(const VPaint *vp) { - return brush_use_accumulate_ex(vp->paint.brush, vp->paint.runtime.ob_mode); + return brush_use_accumulate_ex(vp->paint.brush, vp->paint.runtime.ob_mode); } -static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index) +static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev, + MDeformVert *dvert_curr, + int index) { - MDeformVert *dv_curr = &dvert_curr[index]; - MDeformVert *dv_prev = &dvert_prev[index]; - if (dv_prev->flag == 1) { - dv_prev->flag = 0; - defvert_copy(dv_prev, dv_curr); - } - return dv_prev; + MDeformVert *dv_curr = &dvert_curr[index]; + MDeformVert *dv_prev = &dvert_prev[index]; + if (dv_prev->flag == 1) { + dv_prev->flag = 0; + defvert_copy(dv_prev, dv_curr); + } + return dv_prev; } /* check if we can do partial updates and have them draw realtime * (without evaluating modifiers) */ static bool vertex_paint_use_fast_update_check(Object *ob) { - Mesh *me_eval = ob->runtime.mesh_eval; + Mesh *me_eval = ob->runtime.mesh_eval; - if (me_eval != NULL) { - Mesh *me = BKE_mesh_from_object(ob); - if (me && me->mloopcol) { - return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_MLOOPCOL)); - } - } + if (me_eval != NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me && me->mloopcol) { + return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_MLOOPCOL)); + } + } - return false; + return false; } static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval[2]) { - const int mval_i[2] = {mval[0], mval[1]}; - float world[3]; + const int mval_i[2] = {mval[0], mval[1]}; + float world[3]; - if (ED_view3d_autodist_simple(ar, mval_i, world, 0, NULL)) { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->average_stroke_counter++; - add_v3_v3(ups->average_stroke_accum, world); - ups->last_stroke_valid = true; - } + if (ED_view3d_autodist_simple(ar, mval_i, world, 0, NULL)) { + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + ups->average_stroke_counter++; + add_v3_v3(ups->average_stroke_accum, world); + ups->last_stroke_valid = true; + } } /* polling - retrieve whether cursor should be set or operator should be done */ @@ -208,349 +208,360 @@ static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval /* Returns true if vertex paint mode is active */ bool vertex_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly; + return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly; } static bool vertex_paint_poll_ex(bContext *C, bool check_tool) { - if (vertex_paint_mode_poll(C) && - BKE_paint_brush(&CTX_data_tool_settings(C)->vpaint->paint)) - { - ScrArea *sa = CTX_wm_area(C); - if (sa && sa->spacetype == SPACE_VIEW3D) { - ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { - if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { - return 1; - } - } - } - } - return 0; + if (vertex_paint_mode_poll(C) && BKE_paint_brush(&CTX_data_tool_settings(C)->vpaint->paint)) { + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype == SPACE_VIEW3D) { + ARegion *ar = CTX_wm_region(C); + if (ar->regiontype == RGN_TYPE_WINDOW) { + if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { + return 1; + } + } + } + } + return 0; } bool vertex_paint_poll(bContext *C) { - return vertex_paint_poll_ex(C, true); + return vertex_paint_poll_ex(C, true); } bool vertex_paint_poll_ignore_tool(bContext *C) { - return vertex_paint_poll_ex(C, false); + return vertex_paint_poll_ex(C, false); } bool weight_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly; + return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly; } static bool weight_paint_poll_ex(bContext *C, bool check_tool) { - Object *ob = CTX_data_active_object(C); - ScrArea *sa; - - if ((ob != NULL) && - (ob->mode & OB_MODE_WEIGHT_PAINT) && - (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && - (sa = CTX_wm_area(C)) && - (sa->spacetype == SPACE_VIEW3D)) - { - ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { - if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { - return 1; - } - } - } - return 0; + Object *ob = CTX_data_active_object(C); + ScrArea *sa; + + if ((ob != NULL) && (ob->mode & OB_MODE_WEIGHT_PAINT) && + (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && + (sa = CTX_wm_area(C)) && (sa->spacetype == SPACE_VIEW3D)) { + ARegion *ar = CTX_wm_region(C); + if (ar->regiontype == RGN_TYPE_WINDOW) { + if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { + return 1; + } + } + } + return 0; } bool weight_paint_poll(bContext *C) { - return weight_paint_poll_ex(C, true); + return weight_paint_poll_ex(C, true); } bool weight_paint_poll_ignore_tool(bContext *C) { - return weight_paint_poll_ex(C, false); + return weight_paint_poll_ex(C, false); } uint vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary) { - Brush *brush = BKE_paint_brush(&vp->paint); - uchar col[4]; - rgb_float_to_uchar(col, secondary ? BKE_brush_secondary_color_get(scene, brush) : BKE_brush_color_get(scene, brush)); - col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ - return *(uint *)col; + Brush *brush = BKE_paint_brush(&vp->paint); + uchar col[4]; + rgb_float_to_uchar(col, + secondary ? BKE_brush_secondary_color_get(scene, brush) : + BKE_brush_color_get(scene, brush)); + col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ + return *(uint *)col; } /* wpaint has 'wpaint_blend' */ -static uint vpaint_blend( - const VPaint *vp, uint color_curr, uint color_orig, - uint color_paint, const int alpha_i, - /* pre scaled from [0-1] --> [0-255] */ - const int brush_alpha_value_i) -{ - const Brush *brush = vp->paint.brush; - const IMB_BlendMode blend = brush->blend; - - uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); - - /* if no accumulate, clip color adding with colorig & orig alpha */ - if (!brush_use_accumulate(vp)) { - uint color_test, a; - char *cp, *ct, *co; - - color_test = ED_vpaint_blend_tool(blend, color_orig, color_paint, brush_alpha_value_i); - - cp = (char *)&color_blend; - ct = (char *)&color_test; - co = (char *)&color_orig; - - for (a = 0; a < 4; a++) { - if (ct[a] < co[a]) { - if (cp[a] < ct[a]) cp[a] = ct[a]; - else if (cp[a] > co[a]) cp[a] = co[a]; - } - else { - if (cp[a] < co[a]) cp[a] = co[a]; - else if (cp[a] > ct[a]) cp[a] = ct[a]; - } - } - } - - if ((brush->flag & BRUSH_LOCK_ALPHA) && - !ELEM(blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA)) - { - char *cp, *cc; - cp = (char *)&color_blend; - cc = (char *)&color_curr; - cp[3] = cc[3]; - } - - return color_blend; -} - -static void tex_color_alpha( - VPaint *vp, const ViewContext *vc, const float co[3], - float r_rgba[4]) -{ - const Brush *brush = BKE_paint_brush(&vp->paint); - BLI_assert(brush->mtex.tex != NULL); - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL); - } - else { - float co_ss[2]; /* screenspace */ - if (ED_view3d_project_float_object( - vc->ar, - co, co_ss, - V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) - { - const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ - BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL); - } - else { - zero_v4(r_rgba); - } - } +static uint vpaint_blend(const VPaint *vp, + uint color_curr, + uint color_orig, + uint color_paint, + const int alpha_i, + /* pre scaled from [0-1] --> [0-255] */ + const int brush_alpha_value_i) +{ + const Brush *brush = vp->paint.brush; + const IMB_BlendMode blend = brush->blend; + + uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); + + /* if no accumulate, clip color adding with colorig & orig alpha */ + if (!brush_use_accumulate(vp)) { + uint color_test, a; + char *cp, *ct, *co; + + color_test = ED_vpaint_blend_tool(blend, color_orig, color_paint, brush_alpha_value_i); + + cp = (char *)&color_blend; + ct = (char *)&color_test; + co = (char *)&color_orig; + + for (a = 0; a < 4; a++) { + if (ct[a] < co[a]) { + if (cp[a] < ct[a]) + cp[a] = ct[a]; + else if (cp[a] > co[a]) + cp[a] = co[a]; + } + else { + if (cp[a] < co[a]) + cp[a] = co[a]; + else if (cp[a] > ct[a]) + cp[a] = ct[a]; + } + } + } + + if ((brush->flag & BRUSH_LOCK_ALPHA) && + !ELEM(blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA)) { + char *cp, *cc; + cp = (char *)&color_blend; + cc = (char *)&color_curr; + cp[3] = cc[3]; + } + + return color_blend; +} + +static void tex_color_alpha(VPaint *vp, const ViewContext *vc, const float co[3], float r_rgba[4]) +{ + const Brush *brush = BKE_paint_brush(&vp->paint); + BLI_assert(brush->mtex.tex != NULL); + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { + BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL); + } + else { + float co_ss[2]; /* screenspace */ + if (ED_view3d_project_float_object( + vc->ar, co, co_ss, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == + V3D_PROJ_RET_OK) { + const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ + BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL); + } + else { + zero_v4(r_rgba); + } + } } /* vpaint has 'vpaint_blend' */ -static float wpaint_blend( - const VPaint *wp, float weight, - const float alpha, float paintval, - const float UNUSED(brush_alpha_value), - const short do_flip) -{ - const Brush *brush = wp->paint.brush; - IMB_BlendMode blend = brush->blend; - - if (do_flip) { - switch (blend) { - case IMB_BLEND_MIX: - paintval = 1.f - paintval; break; - case IMB_BLEND_ADD: - blend = IMB_BLEND_SUB; break; - case IMB_BLEND_SUB: - blend = IMB_BLEND_ADD; break; - case IMB_BLEND_LIGHTEN: - blend = IMB_BLEND_DARKEN; break; - case IMB_BLEND_DARKEN: - blend = IMB_BLEND_LIGHTEN; break; - default: break; - } - } - - weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha); - - CLAMP(weight, 0.0f, 1.0f); - - return weight; +static float wpaint_blend(const VPaint *wp, + float weight, + const float alpha, + float paintval, + const float UNUSED(brush_alpha_value), + const short do_flip) +{ + const Brush *brush = wp->paint.brush; + IMB_BlendMode blend = brush->blend; + + if (do_flip) { + switch (blend) { + case IMB_BLEND_MIX: + paintval = 1.f - paintval; + break; + case IMB_BLEND_ADD: + blend = IMB_BLEND_SUB; + break; + case IMB_BLEND_SUB: + blend = IMB_BLEND_ADD; + break; + case IMB_BLEND_LIGHTEN: + blend = IMB_BLEND_DARKEN; + break; + case IMB_BLEND_DARKEN: + blend = IMB_BLEND_LIGHTEN; + break; + default: + break; + } + } + + weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha); + + CLAMP(weight, 0.0f, 1.0f); + + return weight; } static float wpaint_clamp_monotonic(float oldval, float curval, float newval) { - if (newval < oldval) - return MIN2(newval, curval); - else if (newval > oldval) - return MAX2(newval, curval); - else - return newval; + if (newval < oldval) + return MIN2(newval, curval); + else if (newval > oldval) + return MAX2(newval, curval); + else + return newval; } /* ----------------------------------------------------- */ -static void do_weight_paint_normalize_all(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap) -{ - float sum = 0.0f, fac; - uint i, tot = 0; - MDeformWeight *dw; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - tot++; - sum += dw->weight; - } - } - - if ((tot == 0) || (sum == 1.0f)) { - return; - } - - if (sum != 0.0f) { - fac = 1.0f / sum; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - dw->weight *= fac; - } - } - } - else { - /* hrmf, not a factor in this case */ - fac = 1.0f / tot; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - dw->weight = fac; - } - } - } +static void do_weight_paint_normalize_all(MDeformVert *dvert, + const int defbase_tot, + const bool *vgroup_validmap) +{ + float sum = 0.0f, fac; + uint i, tot = 0; + MDeformWeight *dw; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + tot++; + sum += dw->weight; + } + } + + if ((tot == 0) || (sum == 1.0f)) { + return; + } + + if (sum != 0.0f) { + fac = 1.0f / sum; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + dw->weight *= fac; + } + } + } + else { + /* hrmf, not a factor in this case */ + fac = 1.0f / tot; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + dw->weight = fac; + } + } + } } /** * A version of #do_weight_paint_normalize_all that includes locked weights * but only changes unlocked weights. */ -static bool do_weight_paint_normalize_all_locked( - MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap, - const bool *lock_flags) -{ - float sum = 0.0f, fac; - float sum_unlock = 0.0f; - float lock_weight = 0.0f; - uint i, tot = 0; - MDeformWeight *dw; - - if (lock_flags == NULL) { - do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap); - return true; - } - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - sum += dw->weight; - - if (lock_flags[dw->def_nr]) { - lock_weight += dw->weight; - } - else { - tot++; - sum_unlock += dw->weight; - } - } - } - - if (sum == 1.0f) { - return true; - } - - if (tot == 0) { - return false; - } - - if (lock_weight >= 1.0f) { - /* locked groups make it impossible to fully normalize, - * zero out what we can and return false */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - if (lock_flags[dw->def_nr] == false) { - dw->weight = 0.0f; - } - } - } - - return (lock_weight == 1.0f); - } - else if (sum_unlock != 0.0f) { - fac = (1.0f - lock_weight) / sum_unlock; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - if (lock_flags[dw->def_nr] == false) { - dw->weight *= fac; - /* paranoid but possibly with float error */ - CLAMP(dw->weight, 0.0f, 1.0f); - } - } - } - } - else { - /* hrmf, not a factor in this case */ - fac = (1.0f - lock_weight) / tot; - /* paranoid but possibly with float error */ - CLAMP(fac, 0.0f, 1.0f); - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - if (lock_flags[dw->def_nr] == false) { - dw->weight = fac; - } - } - } - } - - return true; +static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert, + const int defbase_tot, + const bool *vgroup_validmap, + const bool *lock_flags) +{ + float sum = 0.0f, fac; + float sum_unlock = 0.0f; + float lock_weight = 0.0f; + uint i, tot = 0; + MDeformWeight *dw; + + if (lock_flags == NULL) { + do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap); + return true; + } + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + sum += dw->weight; + + if (lock_flags[dw->def_nr]) { + lock_weight += dw->weight; + } + else { + tot++; + sum_unlock += dw->weight; + } + } + } + + if (sum == 1.0f) { + return true; + } + + if (tot == 0) { + return false; + } + + if (lock_weight >= 1.0f) { + /* locked groups make it impossible to fully normalize, + * zero out what we can and return false */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + if (lock_flags[dw->def_nr] == false) { + dw->weight = 0.0f; + } + } + } + + return (lock_weight == 1.0f); + } + else if (sum_unlock != 0.0f) { + fac = (1.0f - lock_weight) / sum_unlock; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + if (lock_flags[dw->def_nr] == false) { + dw->weight *= fac; + /* paranoid but possibly with float error */ + CLAMP(dw->weight, 0.0f, 1.0f); + } + } + } + } + else { + /* hrmf, not a factor in this case */ + fac = (1.0f - lock_weight) / tot; + /* paranoid but possibly with float error */ + CLAMP(fac, 0.0f, 1.0f); + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + if (lock_flags[dw->def_nr] == false) { + dw->weight = fac; + } + } + } + } + + return true; } /** * \note same as function above except it does a second pass without active group * if normalize fails with it. */ -static void do_weight_paint_normalize_all_locked_try_active( - MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap, - const bool *lock_flags, const bool *lock_with_active) -{ - /* first pass with both active and explicitly locked groups restricted from change */ - - bool success = do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_with_active); - - if (!success) { - /** - * Locks prevented the first pass from full completion, so remove restriction on active group; e.g: - * - * - With 1.0 weight painted into active: - * nonzero locked weight; first pass zeroed out unlocked weight; scale 1 down to fit. - * - With 0.0 weight painted into active: - * no unlocked groups; first pass did nothing; increase 0 to fit. - */ - do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags); - } +static void do_weight_paint_normalize_all_locked_try_active(MDeformVert *dvert, + const int defbase_tot, + const bool *vgroup_validmap, + const bool *lock_flags, + const bool *lock_with_active) +{ + /* first pass with both active and explicitly locked groups restricted from change */ + + bool success = do_weight_paint_normalize_all_locked( + dvert, defbase_tot, vgroup_validmap, lock_with_active); + + if (!success) { + /** + * Locks prevented the first pass from full completion, so remove restriction on active group; e.g: + * + * - With 1.0 weight painted into active: + * nonzero locked weight; first pass zeroed out unlocked weight; scale 1 down to fit. + * - With 0.0 weight painted into active: + * no unlocked groups; first pass did nothing; increase 0 to fit. + */ + do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags); + } } #if 0 /* UNUSED */ @@ -558,101 +569,108 @@ static bool has_unselected_unlocked_bone_group( int defbase_tot, bool *defbase_sel, int selected, const bool *lock_flags, const bool *vgroup_validmap) { - int i; - if (defbase_tot == selected) { - return false; - } - for (i = 0; i < defbase_tot; i++) { - if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) { - return true; - } - } - return false; + int i; + if (defbase_tot == selected) { + return false; + } + for (i = 0; i < defbase_tot; i++) { + if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) { + return true; + } + } + return false; } #endif -static void multipaint_clamp_change( - MDeformVert *dvert, const int defbase_tot, const bool *defbase_sel, - float *change_p) -{ - int i; - MDeformWeight *dw; - float val; - float change = *change_p; - - /* verify that the change does not cause values exceeding 1 and clamp it */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { - if (dw->weight) { - val = dw->weight * change; - if (val > 1) { - change = 1.0f / dw->weight; - } - } - } - } - - *change_p = change; -} - -static bool multipaint_verify_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel) -{ - int i; - MDeformWeight *dw; - float val; - - /* in case the change is reduced, you need to recheck - * the earlier values to make sure they are not 0 - * (precision error) */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { - if (dw->weight) { - val = dw->weight * change; - /* the value should never reach zero while multi-painting if it - * was nonzero beforehand */ - if (val <= 0) { - return false; - } - } - } - } - - return true; -} - -static void multipaint_apply_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel) -{ - int i; - MDeformWeight *dw; - - /* apply the valid change */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { - if (dw->weight) { - dw->weight = dw->weight * change; - CLAMP(dw->weight, 0.0f, 1.0f); - } - } - } +static void multipaint_clamp_change(MDeformVert *dvert, + const int defbase_tot, + const bool *defbase_sel, + float *change_p) +{ + int i; + MDeformWeight *dw; + float val; + float change = *change_p; + + /* verify that the change does not cause values exceeding 1 and clamp it */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { + if (dw->weight) { + val = dw->weight * change; + if (val > 1) { + change = 1.0f / dw->weight; + } + } + } + } + + *change_p = change; +} + +static bool multipaint_verify_change(MDeformVert *dvert, + const int defbase_tot, + float change, + const bool *defbase_sel) +{ + int i; + MDeformWeight *dw; + float val; + + /* in case the change is reduced, you need to recheck + * the earlier values to make sure they are not 0 + * (precision error) */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { + if (dw->weight) { + val = dw->weight * change; + /* the value should never reach zero while multi-painting if it + * was nonzero beforehand */ + if (val <= 0) { + return false; + } + } + } + } + + return true; +} + +static void multipaint_apply_change(MDeformVert *dvert, + const int defbase_tot, + float change, + const bool *defbase_sel) +{ + int i; + MDeformWeight *dw; + + /* apply the valid change */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { + if (dw->weight) { + dw->weight = dw->weight * change; + CLAMP(dw->weight, 0.0f, 1.0f); + } + } + } } /** * Variables stored both for 'active' and 'mirror' sides. */ struct WeightPaintGroupData { - /** index of active group or its mirror - * - * - 'active' is always `ob->actdef`. - * - 'mirror' is -1 when 'ME_EDIT_MIRROR_X' flag id disabled, - * otherwise this will be set to the mirror or the active group (if the group isn't mirrored). - */ - int index; - /** lock that includes the 'index' as locked too - * - * - 'active' is set of locked or active/selected groups - * - 'mirror' is set of locked or mirror groups - */ - const bool *lock; + /** index of active group or its mirror + * + * - 'active' is always `ob->actdef`. + * - 'mirror' is -1 when 'ME_EDIT_MIRROR_X' flag id disabled, + * otherwise this will be set to the mirror or the active group (if the group isn't mirrored). + */ + int index; + /** lock that includes the 'index' as locked too + * + * - 'active' is set of locked or active/selected groups + * - 'mirror' is set of locked or mirror groups + */ + const bool *lock; }; /* struct to avoid passing many args each call to do_weight_paint_vertex() @@ -661,490 +679,504 @@ struct WeightPaintGroupData { * paint stroke update - campbell */ typedef struct WeightPaintInfo { - int defbase_tot; + int defbase_tot; - /* both must add up to 'defbase_tot' */ - int defbase_tot_sel; - int defbase_tot_unsel; + /* both must add up to 'defbase_tot' */ + int defbase_tot_sel; + int defbase_tot_unsel; - struct WeightPaintGroupData active, mirror; + struct WeightPaintGroupData active, mirror; - /* boolean array for locked bones, - * length of defbase_tot */ - const bool *lock_flags; - /* boolean array for selected bones, - * length of defbase_tot, cant be const because of how its passed */ - const bool *defbase_sel; - /* same as WeightPaintData.vgroup_validmap, - * only added here for convenience */ - const bool *vgroup_validmap; + /* boolean array for locked bones, + * length of defbase_tot */ + const bool *lock_flags; + /* boolean array for selected bones, + * length of defbase_tot, cant be const because of how its passed */ + const bool *defbase_sel; + /* same as WeightPaintData.vgroup_validmap, + * only added here for convenience */ + const bool *vgroup_validmap; - bool do_flip; - bool do_multipaint; - bool do_auto_normalize; + bool do_flip; + bool do_multipaint; + bool do_auto_normalize; - float brush_alpha_value; /* result of BKE_brush_alpha_get() */ + float brush_alpha_value; /* result of BKE_brush_alpha_get() */ } WeightPaintInfo; static void do_weight_paint_vertex_single( - /* vars which remain the same for every vert */ - const VPaint *wp, Object *ob, const WeightPaintInfo *wpi, - /* vars which change on each stroke */ - const uint index, float alpha, float paintweight) -{ - Mesh *me = ob->data; - MDeformVert *dv = &me->dvert[index]; - bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - - MDeformWeight *dw; - float weight_prev; - - /* mirror vars */ - int index_mirr; - int vgroup_mirr; - - MDeformVert *dv_mirr; - MDeformWeight *dw_mirr; - - /* 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) { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); - vgroup_mirr = wpi->mirror.index; - - /* another possible error - mirror group _and_ active group are the same (which is fine), - * but we also are painting onto a center vertex - this would paint the same weight twice */ - if (index_mirr == index && vgroup_mirr == wpi->active.index) { - index_mirr = vgroup_mirr = -1; - } - } - else { - index_mirr = vgroup_mirr = -1; - } - - if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { - dw = defvert_find_index(dv, wpi->active.index); - } - else { - dw = defvert_verify_index(dv, wpi->active.index); - } - - if (dw == NULL) { - return; - } - - /* get the mirror def vars */ - if (index_mirr != -1) { - dv_mirr = &me->dvert[index_mirr]; - if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { - dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr); - - if (dw_mirr == NULL) { - index_mirr = vgroup_mirr = -1; - dv_mirr = NULL; - } - } - else { - if (index != index_mirr) { - dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); - } - else { - /* dv and dv_mirr are the same */ - int totweight_prev = dv_mirr->totweight; - int dw_offset = (int)(dw - dv_mirr->dw); - dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); - - /* if we added another, get our old one back */ - if (totweight_prev != dv_mirr->totweight) { - dw = &dv_mirr->dw[dw_offset]; - } - } - } - } - else { - dv_mirr = NULL; - dw_mirr = NULL; - } - - 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); - if (index_mirr != -1) { - defweight_prev_init(dvert_prev, me->dvert, index_mirr); - } - - weight_prev = defvert_find_weight(dv_prev, wpi->active.index); - } - else { - weight_prev = dw->weight; - } - - /* If there are no normalize-locks or multipaint, - * then there is no need to run the more complicated checks */ - - { - 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); - - /* WATCH IT: take care of the ordering of applying mirror -> normalize, - * can give wrong results [#26193], least confusing if normalize is done last */ - - /* apply mirror */ - if (index_mirr != -1) { - /* copy, not paint again */ - dw_mirr->weight = dw->weight; - } - - /* apply normalize */ - if (wpi->do_auto_normalize) { - /* note on normalize - this used to be applied after painting and normalize all weights, - * in some ways this is good because there is feedback where the more weights involved would - * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active. - * - * However this gave a problem since applying mirror, then normalize both verts - * the resulting weight wont match on both sides. - * - * If this 'resisting', slower normalize is nicer, we could call - * do_weight_paint_normalize_all() and only use... - * do_weight_paint_normalize_all_active() when normalizing the mirror vertex. - * - campbell - */ - do_weight_paint_normalize_all_locked_try_active( - dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - - if (index_mirr != -1) { - /* only normalize if this is not a center vertex, - * else we get a conflict, normalizing twice */ - if (index != index_mirr) { - do_weight_paint_normalize_all_locked_try_active( - dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->mirror.lock); - } - else { - /* this case accounts for... - * - painting onto a center vertex of a mesh - * - x mirror is enabled - * - auto normalize is enabled - * - the group you are painting onto has a L / R version - * - * We want L/R vgroups to have the same weight but this cant be if both are over 0.5, - * We _could_ have special check for that, but this would need its own normalize function which - * holds 2 groups from changing at once. - * - * So! just balance out the 2 weights, it keeps them equal and everything normalized. - * - * While it wont hit the desired weight immediately as the user waggles their mouse, - * constant painting and re-normalizing will get there. this is also just simpler logic. - * - campbell */ - dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f; - } - } - } - } + /* vars which remain the same for every vert */ + const VPaint *wp, + Object *ob, + const WeightPaintInfo *wpi, + /* vars which change on each stroke */ + const uint index, + float alpha, + float paintweight) +{ + Mesh *me = ob->data; + MDeformVert *dv = &me->dvert[index]; + bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + MDeformWeight *dw; + float weight_prev; + + /* mirror vars */ + int index_mirr; + int vgroup_mirr; + + MDeformVert *dv_mirr; + MDeformWeight *dw_mirr; + + /* 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) { + index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); + vgroup_mirr = wpi->mirror.index; + + /* another possible error - mirror group _and_ active group are the same (which is fine), + * but we also are painting onto a center vertex - this would paint the same weight twice */ + if (index_mirr == index && vgroup_mirr == wpi->active.index) { + index_mirr = vgroup_mirr = -1; + } + } + else { + index_mirr = vgroup_mirr = -1; + } + + if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { + dw = defvert_find_index(dv, wpi->active.index); + } + else { + dw = defvert_verify_index(dv, wpi->active.index); + } + + if (dw == NULL) { + return; + } + + /* get the mirror def vars */ + if (index_mirr != -1) { + dv_mirr = &me->dvert[index_mirr]; + if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { + dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr); + + if (dw_mirr == NULL) { + index_mirr = vgroup_mirr = -1; + dv_mirr = NULL; + } + } + else { + if (index != index_mirr) { + dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); + } + else { + /* dv and dv_mirr are the same */ + int totweight_prev = dv_mirr->totweight; + int dw_offset = (int)(dw - dv_mirr->dw); + dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); + + /* if we added another, get our old one back */ + if (totweight_prev != dv_mirr->totweight) { + dw = &dv_mirr->dw[dw_offset]; + } + } + } + } + else { + dv_mirr = NULL; + dw_mirr = NULL; + } + + 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); + if (index_mirr != -1) { + defweight_prev_init(dvert_prev, me->dvert, index_mirr); + } + + weight_prev = defvert_find_weight(dv_prev, wpi->active.index); + } + else { + weight_prev = dw->weight; + } + + /* If there are no normalize-locks or multipaint, + * then there is no need to run the more complicated checks */ + + { + 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); + + /* WATCH IT: take care of the ordering of applying mirror -> normalize, + * can give wrong results [#26193], least confusing if normalize is done last */ + + /* apply mirror */ + if (index_mirr != -1) { + /* copy, not paint again */ + dw_mirr->weight = dw->weight; + } + + /* apply normalize */ + if (wpi->do_auto_normalize) { + /* note on normalize - this used to be applied after painting and normalize all weights, + * in some ways this is good because there is feedback where the more weights involved would + * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active. + * + * However this gave a problem since applying mirror, then normalize both verts + * the resulting weight wont match on both sides. + * + * If this 'resisting', slower normalize is nicer, we could call + * do_weight_paint_normalize_all() and only use... + * do_weight_paint_normalize_all_active() when normalizing the mirror vertex. + * - campbell + */ + do_weight_paint_normalize_all_locked_try_active( + dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); + + if (index_mirr != -1) { + /* only normalize if this is not a center vertex, + * else we get a conflict, normalizing twice */ + if (index != index_mirr) { + do_weight_paint_normalize_all_locked_try_active( + dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->mirror.lock); + } + else { + /* this case accounts for... + * - painting onto a center vertex of a mesh + * - x mirror is enabled + * - auto normalize is enabled + * - the group you are painting onto has a L / R version + * + * We want L/R vgroups to have the same weight but this cant be if both are over 0.5, + * We _could_ have special check for that, but this would need its own normalize function which + * holds 2 groups from changing at once. + * + * So! just balance out the 2 weights, it keeps them equal and everything normalized. + * + * While it wont hit the desired weight immediately as the user waggles their mouse, + * constant painting and re-normalizing will get there. this is also just simpler logic. + * - campbell */ + dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f; + } + } + } + } } static void do_weight_paint_vertex_multi( - /* vars which remain the same for every vert */ - const VPaint *wp, Object *ob, const WeightPaintInfo *wpi, - /* vars which change on each stroke */ - const uint index, float alpha, float paintweight) -{ - Mesh *me = ob->data; - MDeformVert *dv = &me->dvert[index]; - bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - - /* mirror vars */ - int index_mirr = -1; - MDeformVert *dv_mirr = NULL; - - /* weights */ - float curw, oldw, neww, change, curw_mirr, change_mirr; - - /* 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) { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); - - if (index_mirr != -1 && index_mirr != index) { - dv_mirr = &me->dvert[index_mirr]; - } - else { - index_mirr = -1; - } - } - - /* 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); - - if (curw == 0.0f) { - /* note: no weight to assign to this vertex, could add all groups? */ - return; - } - - 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); - if (index_mirr != -1) { - defweight_prev_init(dvert_prev, me->dvert, index_mirr); - } - - oldw = BKE_defvert_multipaint_collective_weight( - dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); - } - else { - oldw = curw; - } - - neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip); - neww = wpaint_clamp_monotonic(oldw, curw, neww); - - change = neww / curw; - - /* 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); - - if (curw_mirr == 0.0f) { - /* can't mirror into a zero weight vertex */ - dv_mirr = NULL; - } - else { - /* mirror is changed to achieve the same collective weight value */ - float orig = change_mirr = curw * change / curw_mirr; - - multipaint_clamp_change(dv_mirr, wpi->defbase_tot, wpi->defbase_sel, &change_mirr); - - if (!multipaint_verify_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel)) { - return; - } - - change *= change_mirr / orig; - } - } - - if (!multipaint_verify_change(dv, wpi->defbase_tot, change, wpi->defbase_sel)) { - return; - } - - /* apply validated change to vertex and mirror */ - multipaint_apply_change(dv, wpi->defbase_tot, change, wpi->defbase_sel); - - if (dv_mirr != NULL) { - multipaint_apply_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel); - } - - /* normalize */ - if (wpi->do_auto_normalize) { - do_weight_paint_normalize_all_locked_try_active( - dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - - if (dv_mirr != NULL) { - do_weight_paint_normalize_all_locked_try_active( - dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - } - } + /* vars which remain the same for every vert */ + const VPaint *wp, + Object *ob, + const WeightPaintInfo *wpi, + /* vars which change on each stroke */ + const uint index, + float alpha, + float paintweight) +{ + Mesh *me = ob->data; + MDeformVert *dv = &me->dvert[index]; + bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + /* mirror vars */ + int index_mirr = -1; + MDeformVert *dv_mirr = NULL; + + /* weights */ + float curw, oldw, neww, change, curw_mirr, change_mirr; + + /* 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) { + index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); + + if (index_mirr != -1 && index_mirr != index) { + dv_mirr = &me->dvert[index_mirr]; + } + else { + index_mirr = -1; + } + } + + /* 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); + + if (curw == 0.0f) { + /* note: no weight to assign to this vertex, could add all groups? */ + return; + } + + 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); + if (index_mirr != -1) { + defweight_prev_init(dvert_prev, me->dvert, index_mirr); + } + + oldw = BKE_defvert_multipaint_collective_weight( + dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); + } + else { + oldw = curw; + } + + neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip); + neww = wpaint_clamp_monotonic(oldw, curw, neww); + + change = neww / curw; + + /* 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); + + if (curw_mirr == 0.0f) { + /* can't mirror into a zero weight vertex */ + dv_mirr = NULL; + } + else { + /* mirror is changed to achieve the same collective weight value */ + float orig = change_mirr = curw * change / curw_mirr; + + multipaint_clamp_change(dv_mirr, wpi->defbase_tot, wpi->defbase_sel, &change_mirr); + + if (!multipaint_verify_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel)) { + return; + } + + change *= change_mirr / orig; + } + } + + if (!multipaint_verify_change(dv, wpi->defbase_tot, change, wpi->defbase_sel)) { + return; + } + + /* apply validated change to vertex and mirror */ + multipaint_apply_change(dv, wpi->defbase_tot, change, wpi->defbase_sel); + + if (dv_mirr != NULL) { + multipaint_apply_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel); + } + + /* normalize */ + if (wpi->do_auto_normalize) { + do_weight_paint_normalize_all_locked_try_active( + dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); + + if (dv_mirr != NULL) { + do_weight_paint_normalize_all_locked_try_active( + dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); + } + } } static void do_weight_paint_vertex( - /* vars which remain the same for every vert */ - const VPaint *wp, Object *ob, const WeightPaintInfo *wpi, - /* vars which change on each stroke */ - const uint index, float alpha, float paintweight) -{ - if (wpi->do_multipaint) { - do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight); - } - else { - do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight); - } + /* vars which remain the same for every vert */ + const VPaint *wp, + Object *ob, + const WeightPaintInfo *wpi, + /* vars which change on each stroke */ + const uint index, + float alpha, + float paintweight) +{ + if (wpi->do_multipaint) { + do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight); + } + else { + do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight); + } } - /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.c) */ -static void vertex_paint_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +static void vertex_paint_init_session(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + eObjectMode object_mode) { - /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + /* Create persistent sculpt mode data */ + BKE_sculpt_toolsettings_data_ensure(scene); - BLI_assert(ob->sculpt == NULL); - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); - ob->sculpt->mode_type = object_mode; - BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); + BLI_assert(ob->sculpt == NULL); + ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->mode_type = object_mode; + BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); } static void vertex_paint_init_stroke(Depsgraph *depsgraph, Scene *scene, Object *ob) { - BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); + BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); } static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) { - /* Create maps */ - struct SculptVertexPaintGeomMap *gmap = NULL; - if (ob->mode == OB_MODE_VERTEX_PAINT) { - gmap = &ob->sculpt->mode.vpaint.gmap; - BLI_assert(ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT); - } - else if (ob->mode == OB_MODE_WEIGHT_PAINT) { - gmap = &ob->sculpt->mode.wpaint.gmap; - BLI_assert(ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT); - } - else { - ob->sculpt->mode_type = 0; - BLI_assert(0); - return; - } - - Mesh *me = ob->data; - - if (gmap->vert_to_loop == NULL) { - gmap->vert_map_mem = NULL; - gmap->vert_to_loop = NULL; - gmap->poly_map_mem = NULL; - gmap->vert_to_poly = NULL; - BKE_mesh_vert_loop_map_create( - &gmap->vert_to_loop, - &gmap->vert_map_mem, - me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); - BKE_mesh_vert_poly_map_create( - &gmap->vert_to_poly, - &gmap->poly_map_mem, - me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); - } - - /* Create average brush arrays */ - if (ob->mode == OB_MODE_VERTEX_PAINT) { - if (!brush_use_accumulate(ts->vpaint)) { - if (ob->sculpt->mode.vpaint.previous_color == NULL) { - ob->sculpt->mode.vpaint.previous_color = - MEM_callocN(me->totloop * sizeof(uint), __func__); - } - } - else { - MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color); - } - } - else if (ob->mode == OB_MODE_WEIGHT_PAINT) { - if (!brush_use_accumulate(ts->wpaint)) { - if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { - ob->sculpt->mode.wpaint.alpha_weight = - MEM_callocN(me->totvert * sizeof(float), __func__); - } - if (ob->sculpt->mode.wpaint.dvert_prev == NULL) { - ob->sculpt->mode.wpaint.dvert_prev = - MEM_callocN(me->totvert * sizeof(MDeformVert), __func__); - MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; - for (int i = 0; i < me->totvert; i++, dv++) { - /* Use to show this isn't initialized, never apply to the mesh data. */ - dv->flag = 1; - } - } - } - else { - MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight); - if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { - BKE_defvert_array_free_elems(ob->sculpt->mode.wpaint.dvert_prev, me->totvert); - MEM_freeN(ob->sculpt->mode.wpaint.dvert_prev); - ob->sculpt->mode.wpaint.dvert_prev = NULL; - } - } - } - + /* Create maps */ + struct SculptVertexPaintGeomMap *gmap = NULL; + if (ob->mode == OB_MODE_VERTEX_PAINT) { + gmap = &ob->sculpt->mode.vpaint.gmap; + BLI_assert(ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT); + } + else if (ob->mode == OB_MODE_WEIGHT_PAINT) { + gmap = &ob->sculpt->mode.wpaint.gmap; + BLI_assert(ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT); + } + else { + ob->sculpt->mode_type = 0; + BLI_assert(0); + return; + } + + Mesh *me = ob->data; + + if (gmap->vert_to_loop == NULL) { + gmap->vert_map_mem = NULL; + gmap->vert_to_loop = NULL; + gmap->poly_map_mem = NULL; + gmap->vert_to_poly = NULL; + BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, + &gmap->vert_map_mem, + me->mpoly, + me->mloop, + me->totvert, + me->totpoly, + me->totloop); + BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly, + &gmap->poly_map_mem, + me->mpoly, + me->mloop, + me->totvert, + me->totpoly, + me->totloop); + } + + /* Create average brush arrays */ + if (ob->mode == OB_MODE_VERTEX_PAINT) { + if (!brush_use_accumulate(ts->vpaint)) { + if (ob->sculpt->mode.vpaint.previous_color == NULL) { + ob->sculpt->mode.vpaint.previous_color = MEM_callocN(me->totloop * sizeof(uint), __func__); + } + } + else { + MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color); + } + } + else if (ob->mode == OB_MODE_WEIGHT_PAINT) { + if (!brush_use_accumulate(ts->wpaint)) { + if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { + ob->sculpt->mode.wpaint.alpha_weight = MEM_callocN(me->totvert * sizeof(float), __func__); + } + if (ob->sculpt->mode.wpaint.dvert_prev == NULL) { + ob->sculpt->mode.wpaint.dvert_prev = MEM_callocN(me->totvert * sizeof(MDeformVert), + __func__); + MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; + for (int i = 0; i < me->totvert; i++, dv++) { + /* Use to show this isn't initialized, never apply to the mesh data. */ + dv->flag = 1; + } + } + } + else { + MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight); + if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { + BKE_defvert_array_free_elems(ob->sculpt->mode.wpaint.dvert_prev, me->totvert); + MEM_freeN(ob->sculpt->mode.wpaint.dvert_prev); + ob->sculpt->mode.wpaint.dvert_prev = NULL; + } + } + } } /* -------------------------------------------------------------------- */ /** \name Enter Vertex/Weight Paint Mode * \{ */ -static void ed_vwpaintmode_enter_generic( - Main *bmain, Depsgraph *depsgraph, - wmWindowManager *wm, Scene *scene, - Object *ob, const eObjectMode mode_flag) -{ - ob->mode |= mode_flag; - Mesh *me = BKE_mesh_from_object(ob); - - /* Same as sculpt mode, make sure we don't have cached derived mesh which - * points to freed arrays. - */ - BKE_object_free_derived_caches(ob); - - if (mode_flag == OB_MODE_VERTEX_PAINT) { - const ePaintMode paint_mode = PAINT_MODE_VERTEX; - ED_mesh_color_ensure(me, NULL); - - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - paint_cursor_start_explicit(paint, wm, vertex_paint_poll); - BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); - } - else if (mode_flag == OB_MODE_WEIGHT_PAINT) { - const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - paint_cursor_start_explicit(paint, wm, weight_paint_poll); - BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); - - /* weight paint specific */ - ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 's'); - ED_vgroup_sync_from_pose(ob); - } - else { - BLI_assert(0); - } - - /* Create vertex/weight paint mode session data */ - if (ob->sculpt) { - if (ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } - BKE_sculptsession_free(ob); - } - - vertex_paint_init_session(depsgraph, scene, ob, mode_flag); - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); +static void ed_vwpaintmode_enter_generic(Main *bmain, + Depsgraph *depsgraph, + wmWindowManager *wm, + Scene *scene, + Object *ob, + const eObjectMode mode_flag) +{ + ob->mode |= mode_flag; + Mesh *me = BKE_mesh_from_object(ob); + + /* Same as sculpt mode, make sure we don't have cached derived mesh which + * points to freed arrays. + */ + BKE_object_free_derived_caches(ob); + + if (mode_flag == OB_MODE_VERTEX_PAINT) { + const ePaintMode paint_mode = PAINT_MODE_VERTEX; + ED_mesh_color_ensure(me, NULL); + + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); + paint_cursor_start_explicit(paint, wm, vertex_paint_poll); + BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); + } + else if (mode_flag == OB_MODE_WEIGHT_PAINT) { + const ePaintMode paint_mode = PAINT_MODE_WEIGHT; + + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); + paint_cursor_start_explicit(paint, wm, weight_paint_poll); + BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); + + /* weight paint specific */ + ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 's'); + ED_vgroup_sync_from_pose(ob); + } + else { + BLI_assert(0); + } + + /* Create vertex/weight paint mode session data */ + if (ob->sculpt) { + if (ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } + BKE_sculptsession_free(ob); + } + + vertex_paint_init_session(depsgraph, scene, ob, mode_flag); + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_vpaintmode_enter_ex( - Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, - Scene *scene, Object *ob) + Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, Scene *scene, Object *ob) { - ed_vwpaintmode_enter_generic( - bmain, depsgraph, wm, scene, ob, OB_MODE_VERTEX_PAINT); + ed_vwpaintmode_enter_generic(bmain, depsgraph, wm, scene, ob, OB_MODE_VERTEX_PAINT); } void ED_object_vpaintmode_enter(struct bContext *C) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - wmWindowManager *wm = CTX_wm_manager(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); } void ED_object_wpaintmode_enter_ex( - Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, - Scene *scene, Object *ob) + Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, Scene *scene, Object *ob) { - ed_vwpaintmode_enter_generic( - bmain, depsgraph, wm, scene, ob, OB_MODE_WEIGHT_PAINT); + ed_vwpaintmode_enter_generic(bmain, depsgraph, wm, scene, ob, OB_MODE_WEIGHT_PAINT); } void ED_object_wpaintmode_enter(struct bContext *C) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - wmWindowManager *wm = CTX_wm_manager(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); } /** \} */ @@ -1153,72 +1185,71 @@ void ED_object_wpaintmode_enter(struct bContext *C) /** \name Exit Vertex/Weight Paint Mode * \{ */ -static void ed_vwpaintmode_exit_generic( - Object *ob, const eObjectMode mode_flag) -{ - Mesh *me = BKE_mesh_from_object(ob); - ob->mode &= ~mode_flag; - - if (mode_flag == OB_MODE_VERTEX_PAINT) { - if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { - BKE_mesh_flush_select_from_polys(me); - } - else if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { - BKE_mesh_flush_select_from_verts(me); - } - } - else if (mode_flag == OB_MODE_WEIGHT_PAINT) { - if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { - BKE_mesh_flush_select_from_verts(me); - } - else if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { - BKE_mesh_flush_select_from_polys(me); - } - } - else { - BLI_assert(0); - } - - /* If the cache is not released by a cancel or a done, free it now. */ - if (ob->sculpt && ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } - - BKE_sculptsession_free(ob); - - paint_cursor_delete_textures(); - - if (mode_flag == OB_MODE_WEIGHT_PAINT) { - ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e'); - ED_mesh_mirror_topo_table(NULL, NULL, 'e'); - } - - /* Never leave derived meshes behind. */ - BKE_object_free_derived_caches(ob); - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); +static void ed_vwpaintmode_exit_generic(Object *ob, const eObjectMode mode_flag) +{ + Mesh *me = BKE_mesh_from_object(ob); + ob->mode &= ~mode_flag; + + if (mode_flag == OB_MODE_VERTEX_PAINT) { + if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { + BKE_mesh_flush_select_from_polys(me); + } + else if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { + BKE_mesh_flush_select_from_verts(me); + } + } + else if (mode_flag == OB_MODE_WEIGHT_PAINT) { + if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { + BKE_mesh_flush_select_from_verts(me); + } + else if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { + BKE_mesh_flush_select_from_polys(me); + } + } + else { + BLI_assert(0); + } + + /* If the cache is not released by a cancel or a done, free it now. */ + if (ob->sculpt && ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } + + BKE_sculptsession_free(ob); + + paint_cursor_delete_textures(); + + if (mode_flag == OB_MODE_WEIGHT_PAINT) { + ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e'); + ED_mesh_mirror_topo_table(NULL, NULL, 'e'); + } + + /* Never leave derived meshes behind. */ + BKE_object_free_derived_caches(ob); + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_vpaintmode_exit_ex(Object *ob) { - ed_vwpaintmode_exit_generic(ob, OB_MODE_VERTEX_PAINT); + ed_vwpaintmode_exit_generic(ob, OB_MODE_VERTEX_PAINT); } void ED_object_vpaintmode_exit(struct bContext *C) { - Object *ob = CTX_data_active_object(C); - ED_object_vpaintmode_exit_ex(ob); + Object *ob = CTX_data_active_object(C); + ED_object_vpaintmode_exit_ex(ob); } void ED_object_wpaintmode_exit_ex(Object *ob) { - ed_vwpaintmode_exit_generic(ob, OB_MODE_WEIGHT_PAINT); + ed_vwpaintmode_exit_generic(ob, OB_MODE_WEIGHT_PAINT); } void ED_object_wpaintmode_exit(struct bContext *C) { - Object *ob = CTX_data_active_object(C); - ED_object_wpaintmode_exit_ex(ob); + Object *ob = CTX_data_active_object(C); + ED_object_wpaintmode_exit_ex(ob); } /** \} */ @@ -1230,1159 +1261,1182 @@ void ED_object_wpaintmode_exit(struct bContext *C) */ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Object *ob = CTX_data_active_object(C); - const int mode_flag = OB_MODE_WEIGHT_PAINT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } - - Mesh *me = BKE_mesh_from_object(ob); - - if (is_mode_set) { - ED_object_wpaintmode_exit_ex(ob); - } - else { - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - wmWindowManager *wm = CTX_wm_manager(C); - ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); - BKE_paint_toolslots_brush_validate(bmain, &ts->wpaint->paint); - } - - /* When locked, it's almost impossible to select the pose then the object to enter weight paint mode. - * In this case move our pose object in/out of pose mode. - * This is in fits with the convention of selecting multiple objects and entering a mode. */ - if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { - Object *ob_arm = modifiers_isDeformedByArmature(ob); - if (ob_arm && (ob_arm->base_flag & BASE_SELECTED)) { - if (ob_arm->mode & OB_MODE_POSE) { - ED_object_posemode_exit_ex(bmain, ob_arm); - } - else { - ED_object_posemode_enter_ex(bmain, ob_arm); - } - } - } - - /* Weightpaint works by overriding colors in mesh, - * so need to make sure we recalc on enter and - * exit (exit needs doing regardless because we - * should redeform). - */ - DEG_id_tag_update(&me->id, 0); - - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - - WM_toolsystem_update_from_context_view3d(C); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Object *ob = CTX_data_active_object(C); + const int mode_flag = OB_MODE_WEIGHT_PAINT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + Mesh *me = BKE_mesh_from_object(ob); + + if (is_mode_set) { + ED_object_wpaintmode_exit_ex(ob); + } + else { + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + wmWindowManager *wm = CTX_wm_manager(C); + ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + BKE_paint_toolslots_brush_validate(bmain, &ts->wpaint->paint); + } + + /* When locked, it's almost impossible to select the pose then the object to enter weight paint mode. + * In this case move our pose object in/out of pose mode. + * This is in fits with the convention of selecting multiple objects and entering a mode. */ + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + Object *ob_arm = modifiers_isDeformedByArmature(ob); + if (ob_arm && (ob_arm->base_flag & BASE_SELECTED)) { + if (ob_arm->mode & OB_MODE_POSE) { + ED_object_posemode_exit_ex(bmain, ob_arm); + } + else { + ED_object_posemode_enter_ex(bmain, ob_arm); + } + } + } + + /* Weightpaint works by overriding colors in mesh, + * so need to make sure we recalc on enter and + * exit (exit needs doing regardless because we + * should redeform). + */ + DEG_id_tag_update(&me->id, 0); + + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + + WM_toolsystem_update_from_context_view3d(C); + + return OPERATOR_FINISHED; } /* for switching to/from mode */ static bool paint_poll_test(bContext *C) { - Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->type != OB_MESH) - return 0; - if (!ob->data || ID_IS_LINKED(ob->data)) - return 0; - if (CTX_data_edit_object(C)) - return 0; - return 1; + Object *ob = CTX_data_active_object(C); + if (ob == NULL || ob->type != OB_MESH) + return 0; + if (!ob->data || ID_IS_LINKED(ob->data)) + return 0; + if (CTX_data_edit_object(C)) + return 0; + return 1; } void PAINT_OT_weight_paint_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Weight Paint Mode"; - ot->idname = "PAINT_OT_weight_paint_toggle"; - ot->description = "Toggle weight paint mode in 3D view"; + /* identifiers */ + ot->name = "Weight Paint Mode"; + ot->idname = "PAINT_OT_weight_paint_toggle"; + ot->description = "Toggle weight paint mode in 3D view"; - /* api callbacks */ - ot->exec = wpaint_mode_toggle_exec; - ot->poll = paint_poll_test; + /* api callbacks */ + ot->exec = wpaint_mode_toggle_exec; + ot->poll = paint_poll_test; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } /* ************ weight paint operator ********** */ struct WPaintData { - ViewContext vc; - struct NormalAnglePrecalc normal_angle_precalc; + ViewContext vc; + struct NormalAnglePrecalc normal_angle_precalc; - struct WeightPaintGroupData active, mirror; + struct WeightPaintGroupData active, mirror; - /* variables for auto normalize */ - const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */ - const bool *lock_flags; + /* variables for auto normalize */ + const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */ + const bool *lock_flags; - /* 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 */ + /* 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 */ - int defbase_tot; + int defbase_tot; - /* original weight values for use in blur/smear */ - float *precomputed_weight; - bool precomputed_weight_ready; + /* original weight values for use in blur/smear */ + float *precomputed_weight; + bool precomputed_weight_ready; }; /* Initialize the stroke cache invariants from operator properties */ static void vwpaint_update_cache_invariants( - bContext *C, const VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2]) -{ - StrokeCache *cache; - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - const Brush *brush = vp->paint.brush; - ViewContext *vc = paint_stroke_view_context(op->customdata); - Object *ob = CTX_data_active_object(C); - float mat[3][3]; - float view_dir[3] = {0.0f, 0.0f, 1.0f}; - int mode; - - /* VW paint needs to allocate stroke cache before update is called. */ - if (!ss->cache) { - cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); - ss->cache = cache; - } - else { - cache = ss->cache; - } - - /* Initial mouse location */ - if (mouse) - copy_v2_v2(cache->initial_mouse, mouse); - else - zero_v2(cache->initial_mouse); - - mode = RNA_enum_get(op->ptr, "mode"); - cache->invert = mode == BRUSH_STROKE_INVERT; - cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; - /* not very nice, but with current events system implementation - * we can't handle brush appearance inversion hotkey separately (sergey) */ - if (cache->invert) ups->draw_inverted = true; - else ups->draw_inverted = false; - - copy_v2_v2(cache->mouse, cache->initial_mouse); - /* Truly temporary data that isn't stored in properties */ - cache->vc = vc; - cache->brush = brush; - cache->first_time = 1; - - /* cache projection matrix */ - ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); - - invert_m4_m4(ob->imat, ob->obmat); - copy_m3_m4(mat, cache->vc->rv3d->viewinv); - mul_m3_v3(mat, view_dir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, view_dir); - normalize_v3_v3(cache->true_view_normal, view_dir); - - copy_v3_v3(cache->view_normal, cache->true_view_normal); - cache->bstrength = BKE_brush_alpha_get(scene, brush); - cache->is_last_valid = false; + bContext *C, const VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2]) +{ + StrokeCache *cache; + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + const Brush *brush = vp->paint.brush; + ViewContext *vc = paint_stroke_view_context(op->customdata); + Object *ob = CTX_data_active_object(C); + float mat[3][3]; + float view_dir[3] = {0.0f, 0.0f, 1.0f}; + int mode; + + /* VW paint needs to allocate stroke cache before update is called. */ + if (!ss->cache) { + cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); + ss->cache = cache; + } + else { + cache = ss->cache; + } + + /* Initial mouse location */ + if (mouse) + copy_v2_v2(cache->initial_mouse, mouse); + else + zero_v2(cache->initial_mouse); + + mode = RNA_enum_get(op->ptr, "mode"); + cache->invert = mode == BRUSH_STROKE_INVERT; + cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; + /* not very nice, but with current events system implementation + * we can't handle brush appearance inversion hotkey separately (sergey) */ + if (cache->invert) + ups->draw_inverted = true; + else + ups->draw_inverted = false; + + copy_v2_v2(cache->mouse, cache->initial_mouse); + /* Truly temporary data that isn't stored in properties */ + cache->vc = vc; + cache->brush = brush; + cache->first_time = 1; + + /* cache projection matrix */ + ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); + + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, cache->vc->rv3d->viewinv); + mul_m3_v3(mat, view_dir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, view_dir); + normalize_v3_v3(cache->true_view_normal, view_dir); + + copy_v3_v3(cache->view_normal, cache->true_view_normal); + cache->bstrength = BKE_brush_alpha_get(scene, brush); + cache->is_last_valid = false; } /* Initialize the stroke cache variants from operator properties */ static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, PointerRNA *ptr) { - Scene *scene = CTX_data_scene(C); - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - Brush *brush = BKE_paint_brush(&vp->paint); - - /* This effects the actual brush radius, so things farther away - * are compared with a larger radius and vice versa. */ - if (cache->first_time) { - RNA_float_get_array(ptr, "location", cache->true_location); - } - - RNA_float_get_array(ptr, "mouse", cache->mouse); - - /* XXX: Use pressure value from first brush step for brushes which don't - * support strokes (grab, thumb). They depends on initial state and - * brush coord/pressure/etc. - * It's more an events design issue, which doesn't split coordinate/pressure/angle - * changing events. We should avoid this after events system re-design */ - if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { - cache->pressure = RNA_float_get(ptr, "pressure"); - } - - /* Truly temporary data that isn't stored in properties */ - if (cache->first_time) { - if (!BKE_brush_use_locked_size(scene, brush)) { - cache->initial_radius = paint_calc_object_space_radius( - cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); - BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); - } - else { - cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); - } - } - - if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { - cache->radius = cache->initial_radius * cache->pressure; - } - else { - cache->radius = cache->initial_radius; - } - - cache->radius_squared = cache->radius * cache->radius; - - if (ss->pbvh) { - BKE_pbvh_update(ss->pbvh, PBVH_UpdateRedraw, NULL); - BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - } + Scene *scene = CTX_data_scene(C); + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + Brush *brush = BKE_paint_brush(&vp->paint); + + /* This effects the actual brush radius, so things farther away + * are compared with a larger radius and vice versa. */ + if (cache->first_time) { + RNA_float_get_array(ptr, "location", cache->true_location); + } + + RNA_float_get_array(ptr, "mouse", cache->mouse); + + /* XXX: Use pressure value from first brush step for brushes which don't + * support strokes (grab, thumb). They depends on initial state and + * brush coord/pressure/etc. + * It's more an events design issue, which doesn't split coordinate/pressure/angle + * changing events. We should avoid this after events system re-design */ + if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { + cache->pressure = RNA_float_get(ptr, "pressure"); + } + + /* Truly temporary data that isn't stored in properties */ + if (cache->first_time) { + if (!BKE_brush_use_locked_size(scene, brush)) { + cache->initial_radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); + } + else { + cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); + } + } + + if (BKE_brush_use_size_pressure(scene, brush) && + paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { + cache->radius = cache->initial_radius * cache->pressure; + } + else { + cache->radius = cache->initial_radius; + } + + cache->radius_squared = cache->radius * cache->radius; + + if (ss->pbvh) { + BKE_pbvh_update(ss->pbvh, PBVH_UpdateRedraw, NULL); + BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); + } } static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { - Scene *scene = CTX_data_scene(C); - struct PaintStroke *stroke = op->customdata; - ToolSettings *ts = scene->toolsettings; - Object *ob = CTX_data_active_object(C); - Mesh *me = BKE_mesh_from_object(ob); - struct WPaintData *wpd; - struct WPaintVGroupIndex vgroup_index; - int defbase_tot, defbase_tot_sel; - bool *defbase_sel; - SculptSession *ss = ob->sculpt; - VPaint *vp = CTX_data_tool_settings(C)->wpaint; - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) { - return false; - } - - { - /* check if we are attempting to paint onto a locked vertex group, - * and other options disallow it from doing anything useful */ - bDeformGroup *dg; - dg = BLI_findlink(&ob->defbase, vgroup_index.active); - if (dg->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); - return false; - } - if (vgroup_index.mirror != -1) { - dg = BLI_findlink(&ob->defbase, vgroup_index.mirror); - if (dg->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); - return false; - } - } - } - - /* check that multipaint groups are unlocked */ - defbase_tot = BLI_listbase_count(&ob->defbase); - defbase_sel = BKE_object_defgroup_selected_get(ob, defbase_tot, &defbase_tot_sel); - - if (ts->multipaint && defbase_tot_sel > 1) { - int i; - bDeformGroup *dg; - - if (me->editflag & ME_EDIT_MIRROR_X) { - BKE_object_defgroup_mirror_selection(ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel); - } - - for (i = 0; i < defbase_tot; i++) { - if (defbase_sel[i]) { - dg = BLI_findlink(&ob->defbase, i); - if (dg->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); - MEM_freeN(defbase_sel); - return false; - } - } - } - } - - /* ALLOCATIONS! no return after this line */ - /* make mode data storage */ - wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); - paint_stroke_set_mode_data(stroke, wpd); - ED_view3d_viewcontext_init(C, &wpd->vc); - view_angle_limits_init( - &wpd->normal_angle_precalc, vp->paint.brush->falloff_angle, - (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); - - wpd->active.index = vgroup_index.active; - wpd->mirror.index = vgroup_index.mirror; - - /* multipaint */ - wpd->defbase_tot = defbase_tot; - wpd->defbase_sel = defbase_sel; - wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1; - wpd->do_multipaint = (ts->multipaint && defbase_tot_sel > 1); - - /* 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) { - wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(ob, wpd->defbase_tot); - } - - if (wpd->do_multipaint && ts->auto_normalize) { - bool *tmpflags; - tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__); - if (wpd->lock_flags) { - BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot); - } - else { - memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot); - } - wpd->active.lock = tmpflags; - } - else if (ts->auto_normalize) { - bool *tmpflags; - - tmpflags = wpd->lock_flags ? - MEM_dupallocN(wpd->lock_flags) : - MEM_callocN(sizeof(bool) * defbase_tot, __func__); - tmpflags[wpd->active.index] = true; - wpd->active.lock = tmpflags; - - tmpflags = wpd->lock_flags ? - MEM_dupallocN(wpd->lock_flags) : - MEM_callocN(sizeof(bool) * defbase_tot, __func__); - tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true; - wpd->mirror.lock = tmpflags; - } - - if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { - wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__); - } - - /* If not previously created, create vertex/weight paint mode session data */ - vertex_paint_init_stroke(depsgraph, scene, ob); - vwpaint_update_cache_invariants(C, vp, ss, op, mouse); - vertex_paint_init_session_data(ts, ob); - - if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { - MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; - for (int i = 0; i < me->totvert; i++, dv++) { - /* Use to show this isn't initialized, never apply to the mesh data. */ - dv->flag = 1; - } - } - - return true; + Scene *scene = CTX_data_scene(C); + struct PaintStroke *stroke = op->customdata; + ToolSettings *ts = scene->toolsettings; + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_mesh_from_object(ob); + struct WPaintData *wpd; + struct WPaintVGroupIndex vgroup_index; + int defbase_tot, defbase_tot_sel; + bool *defbase_sel; + SculptSession *ss = ob->sculpt; + VPaint *vp = CTX_data_tool_settings(C)->wpaint; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) { + return false; + } + + { + /* check if we are attempting to paint onto a locked vertex group, + * and other options disallow it from doing anything useful */ + bDeformGroup *dg; + dg = BLI_findlink(&ob->defbase, vgroup_index.active); + if (dg->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); + return false; + } + if (vgroup_index.mirror != -1) { + dg = BLI_findlink(&ob->defbase, vgroup_index.mirror); + if (dg->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); + return false; + } + } + } + + /* check that multipaint groups are unlocked */ + defbase_tot = BLI_listbase_count(&ob->defbase); + defbase_sel = BKE_object_defgroup_selected_get(ob, defbase_tot, &defbase_tot_sel); + + if (ts->multipaint && defbase_tot_sel > 1) { + int i; + bDeformGroup *dg; + + if (me->editflag & ME_EDIT_MIRROR_X) { + BKE_object_defgroup_mirror_selection( + ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel); + } + + for (i = 0; i < defbase_tot; i++) { + if (defbase_sel[i]) { + dg = BLI_findlink(&ob->defbase, i); + if (dg->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); + MEM_freeN(defbase_sel); + return false; + } + } + } + } + + /* ALLOCATIONS! no return after this line */ + /* make mode data storage */ + wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); + paint_stroke_set_mode_data(stroke, wpd); + ED_view3d_viewcontext_init(C, &wpd->vc); + view_angle_limits_init(&wpd->normal_angle_precalc, + vp->paint.brush->falloff_angle, + (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + + wpd->active.index = vgroup_index.active; + wpd->mirror.index = vgroup_index.mirror; + + /* multipaint */ + wpd->defbase_tot = defbase_tot; + wpd->defbase_sel = defbase_sel; + wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1; + wpd->do_multipaint = (ts->multipaint && defbase_tot_sel > 1); + + /* 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) { + wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(ob, wpd->defbase_tot); + } + + if (wpd->do_multipaint && ts->auto_normalize) { + bool *tmpflags; + tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__); + if (wpd->lock_flags) { + BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot); + } + else { + memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot); + } + wpd->active.lock = tmpflags; + } + else if (ts->auto_normalize) { + bool *tmpflags; + + tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) : + MEM_callocN(sizeof(bool) * defbase_tot, __func__); + tmpflags[wpd->active.index] = true; + wpd->active.lock = tmpflags; + + tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) : + MEM_callocN(sizeof(bool) * defbase_tot, __func__); + tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true; + wpd->mirror.lock = tmpflags; + } + + if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { + wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__); + } + + /* If not previously created, create vertex/weight paint mode session data */ + vertex_paint_init_stroke(depsgraph, scene, ob); + vwpaint_update_cache_invariants(C, vp, ss, op, mouse); + vertex_paint_init_session_data(ts, ob); + + if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { + MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; + for (int i = 0; i < me->totvert; i++, dv++) { + /* Use to show this isn't initialized, never apply to the mesh data. */ + dv->flag = 1; + } + } + + return true; } static float dot_vf3vs3(const float brushNormal[3], const short vertexNormal[3]) { - float normal[3]; - normal_short_to_float_v3(normal, vertexNormal); - return dot_v3v3(brushNormal, normal); + float normal[3]; + normal_short_to_float_v3(normal, vertexNormal); + return dot_v3v3(brushNormal, normal); } -static void get_brush_alpha_data( - const Scene *scene, const SculptSession *ss, const Brush *brush, - float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure) +static void get_brush_alpha_data(const Scene *scene, + const SculptSession *ss, + const Brush *brush, + float *r_brush_size_pressure, + float *r_brush_alpha_value, + float *r_brush_alpha_pressure) { - *r_brush_size_pressure = - BKE_brush_size_get(scene, brush) * - (BKE_brush_use_size_pressure(scene, brush) ? ss->cache->pressure : 1.0f); - *r_brush_alpha_value = - BKE_brush_alpha_get(scene, brush); - *r_brush_alpha_pressure = - (BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : 1.0f); + *r_brush_size_pressure = BKE_brush_size_get(scene, brush) * + (BKE_brush_use_size_pressure(scene, brush) ? ss->cache->pressure : + 1.0f); + *r_brush_alpha_value = BKE_brush_alpha_get(scene, brush); + *r_brush_alpha_pressure = (BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : + 1.0f); } 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); + 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); - CLAMP(weight, 0.0f, 1.0f); - return weight; - } - else { - return defvert_find_weight(dv, wpi->active.index); - } + CLAMP(weight, 0.0f, 1.0f); + return weight; + } + else { + return defvert_find_weight(dv, wpi->active.index); + } } -static void do_wpaint_precompute_weight_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - const MDeformVert *dv = &data->me->dvert[n]; + SculptThreadedTaskData *data = userdata; + const MDeformVert *dv = &data->me->dvert[n]; - data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi); + data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi); } static void precompute_weight_values( - bContext *C, Object *ob, Brush *brush, struct WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) -{ - if (wpd->precomputed_weight_ready && !brush_use_accumulate_ex(brush, ob->mode)) - return; - - /* threaded loop over vertices */ - SculptThreadedTaskData data = { - .C = C, .ob = ob, .wpd = wpd, .wpi = wpi, .me = me, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range( - 0, me->totvert, - &data, - do_wpaint_precompute_weight_cb_ex, - &settings); - - wpd->precomputed_weight_ready = true; -} - -static void do_wpaint_brush_blur_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; - - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - Scene *scene = CTX_data_scene(data->C); - - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const char v_flag = data->me->mvert[v_index].flag; - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { - /* Get the average poly weight */ - int total_hit_loops = 0; - float weight_final = 0.0f; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - - total_hit_loops += mp->totloop; - for (int k = 0; k < mp->totloop; k++) { - const int l_index = mp->loopstart + k; - const MLoop *ml = &data->me->mloop[l_index]; - weight_final += data->wpd->precomputed_weight[ml->v]; - } - } - - /* Apply the weight to the vertex. */ - if (total_hit_loops != 0) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - const float final_alpha = - brush_fade * brush_strength * - grid_alpha * brush_alpha_pressure; - - if ((brush->flag & BRUSH_ACCUMULATE) == 0) { - if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { - ss->mode.wpaint.alpha_weight[v_index] = final_alpha; - } - else { - continue; - } - } - - weight_final /= total_hit_loops; - /* Only paint visible verts */ - do_weight_paint_vertex( - data->vp, data->ob, data->wpi, - v_index, final_alpha, weight_final); - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_wpaint_brush_smear_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; - - const Brush *brush = data->brush; - const Scene *scene = CTX_data_scene(data->C); - const StrokeCache *cache = ss->cache; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - float brush_dir[3]; - - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); - - if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv_curr = &data->me->mvert[v_index]; - - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - bool do_color = false; - /* Minimum dot product between brush direction and current - * to neighbor direction is 0.0, meaning orthogonal. */ - float stroke_dot_max = 0.0f; - - /* Get the color of the loop in the opposite direction of the brush movement - * (this callback is specifically for smear.) */ - float weight_final = 0.0; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - const MLoop *ml_other = &data->me->mloop[mp->loopstart]; - for (int k = 0; k < mp->totloop; k++, ml_other++) { - const uint v_other_index = ml_other->v; - if (v_other_index != v_index) { - const MVert *mv_other = &data->me->mvert[v_other_index]; - - /* Get the direction from the selected vert to the neighbor. */ - float other_dir[3]; - sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); - - normalize_v3(other_dir); - - const float stroke_dot = dot_v3v3(other_dir, brush_dir); - - if (stroke_dot > stroke_dot_max) { - stroke_dot_max = stroke_dot; - weight_final = data->wpd->precomputed_weight[v_other_index]; - do_color = true; - } - } - } - } - /* Apply weight to vertex */ - if (do_color) { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - const float final_alpha = - brush_fade * brush_strength * - grid_alpha * brush_alpha_pressure; - - if (final_alpha <= 0.0f) - continue; - - do_weight_paint_vertex( - data->vp, data->ob, data->wpi, - v_index, final_alpha, (float)weight_final); - } - } - } - } - } - BKE_pbvh_vertex_iter_end; - } -} - -static void do_wpaint_brush_draw_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const Scene *scene = CTX_data_scene(data->C); - - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - /* note: normally `BKE_brush_weight_get(scene, brush)` is used, - * however in this case we calculate a new weight each time. */ - const float paintweight = data->strength; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* Note: grids are 1:1 with corners (aka loops). - * For multires, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - - const char v_flag = data->me->mvert[v_index].flag; - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - const float final_alpha = brush_fade * brush_strength * grid_alpha * brush_alpha_pressure; - - if ((brush->flag & BRUSH_ACCUMULATE) == 0) { - if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { - ss->mode.wpaint.alpha_weight[v_index] = final_alpha; - } - else { - continue; - } - } - - do_weight_paint_vertex( - data->vp, data->ob, data->wpi, - v_index, final_alpha, paintweight); - } - } - } - } - BKE_pbvh_vertex_iter_end; + bContext *C, Object *ob, Brush *brush, struct WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) +{ + if (wpd->precomputed_weight_ready && !brush_use_accumulate_ex(brush, ob->mode)) + return; + + /* threaded loop over vertices */ + SculptThreadedTaskData data = { + .C = C, + .ob = ob, + .wpd = wpd, + .wpi = wpi, + .me = me, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, me->totvert, &data, do_wpaint_precompute_weight_cb_ex, &settings); + + wpd->precomputed_weight_ready = true; +} + +static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; + + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + Scene *scene = CTX_data_scene(data->C); + + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const char v_flag = data->me->mvert[v_index].flag; + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + /* Get the average poly weight */ + int total_hit_loops = 0; + float weight_final = 0.0f; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &data->me->mpoly[p_index]; + + total_hit_loops += mp->totloop; + for (int k = 0; k < mp->totloop; k++) { + const int l_index = mp->loopstart + k; + const MLoop *ml = &data->me->mloop[l_index]; + weight_final += data->wpd->precomputed_weight[ml->v]; + } + } + + /* Apply the weight to the vertex. */ + if (total_hit_loops != 0) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + const float final_alpha = brush_fade * brush_strength * grid_alpha * + brush_alpha_pressure; + + if ((brush->flag & BRUSH_ACCUMULATE) == 0) { + if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { + ss->mode.wpaint.alpha_weight[v_index] = final_alpha; + } + else { + continue; + } + } + + weight_final /= total_hit_loops; + /* Only paint visible verts */ + do_weight_paint_vertex( + data->vp, data->ob, data->wpi, v_index, final_alpha, weight_final); + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; + + const Brush *brush = data->brush; + const Scene *scene = CTX_data_scene(data->C); + const StrokeCache *cache = ss->cache; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + float brush_dir[3]; + + sub_v3_v3v3(brush_dir, cache->location, cache->last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + + if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv_curr = &data->me->mvert[v_index]; + + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) { + bool do_color = false; + /* Minimum dot product between brush direction and current + * to neighbor direction is 0.0, meaning orthogonal. */ + float stroke_dot_max = 0.0f; + + /* Get the color of the loop in the opposite direction of the brush movement + * (this callback is specifically for smear.) */ + float weight_final = 0.0; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &data->me->mpoly[p_index]; + const MLoop *ml_other = &data->me->mloop[mp->loopstart]; + for (int k = 0; k < mp->totloop; k++, ml_other++) { + const uint v_other_index = ml_other->v; + if (v_other_index != v_index) { + const MVert *mv_other = &data->me->mvert[v_other_index]; + + /* Get the direction from the selected vert to the neighbor. */ + float other_dir[3]; + sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); + project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + + normalize_v3(other_dir); + + const float stroke_dot = dot_v3v3(other_dir, brush_dir); + + if (stroke_dot > stroke_dot_max) { + stroke_dot_max = stroke_dot; + weight_final = data->wpd->precomputed_weight[v_other_index]; + do_color = true; + } + } + } + } + /* Apply weight to vertex */ + if (do_color) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + const float final_alpha = brush_fade * brush_strength * grid_alpha * + brush_alpha_pressure; + + if (final_alpha <= 0.0f) + continue; + + do_weight_paint_vertex( + data->vp, data->ob, data->wpi, v_index, final_alpha, (float)weight_final); + } + } + } + } + } + BKE_pbvh_vertex_iter_end; + } +} + +static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const Scene *scene = CTX_data_scene(data->C); + + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + /* note: normally `BKE_brush_weight_get(scene, brush)` is used, + * however in this case we calculate a new weight each time. */ + const float paintweight = data->strength; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* Note: grids are 1:1 with corners (aka loops). + * For multires, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + + const char v_flag = data->me->mvert[v_index].flag; + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + const float final_alpha = brush_fade * brush_strength * grid_alpha * + brush_alpha_pressure; + + if ((brush->flag & BRUSH_ACCUMULATE) == 0) { + if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { + ss->mode.wpaint.alpha_weight[v_index] = final_alpha; + } + else { + continue; + } + } + + do_weight_paint_vertex(data->vp, data->ob, data->wpi, v_index, final_alpha, paintweight); + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_wpaint_brush_calc_average_weight_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - StrokeCache *cache = ss->cache; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - struct WPaintAverageAccum *accum = (struct WPaintAverageAccum *)data->custom_data + n; - accum->len = 0; - accum->value = 0.0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - // const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const char v_flag = data->me->mvert[v_index].flag; - - /* If the vertex is selected. */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { - const MDeformVert *dv = &data->me->dvert[v_index]; - accum->len += 1; - accum->value += wpaint_get_active_weight(dv, data->wpi); - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void calculate_average_weight(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode) -{ - struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); - data->custom_data = accum; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((data->sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - data, - do_wpaint_brush_calc_average_weight_cb_ex, - &settings); - - uint accum_len = 0; - double accum_weight = 0.0; - for (int i = 0; i < totnode; i++) { - accum_len += accum[i].len; - accum_weight += accum[i].value; - } - if (accum_len != 0) { - accum_weight /= accum_len; - data->strength = (float)accum_weight; - } - - MEM_SAFE_FREE(data->custom_data); /* 'accum' */ -} - - -static void wpaint_paint_leaves( - bContext *C, Object *ob, Sculpt *sd, VPaint *vp, struct WPaintData *wpd, WeightPaintInfo *wpi, - Mesh *me, PBVHNode **nodes, int totnode) -{ - Scene *scene = CTX_data_scene(C); - const Brush *brush = ob->sculpt->cache->brush; - - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .C = C, .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .vp = vp, .wpd = wpd, .wpi = wpi, .me = me, - }; - - /* Use this so average can modify its weight without touching the brush. */ - data.strength = BKE_brush_weight_get(scene, brush); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - /* NOTE: current mirroring code cannot be run in parallel */ - settings.use_threading = !(me->editflag & ME_EDIT_MIRROR_X); - - switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { - case WPAINT_TOOL_AVERAGE: - calculate_average_weight(&data, nodes, totnode); - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_draw_task_cb_ex, - &settings); - break; - case WPAINT_TOOL_SMEAR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_smear_task_cb_ex, - &settings); - break; - case WPAINT_TOOL_BLUR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_blur_task_cb_ex, - &settings); - break; - case WPAINT_TOOL_DRAW: - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_draw_task_cb_ex, - &settings); - break; - } + void *__restrict userdata, const int n, const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + StrokeCache *cache = ss->cache; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + struct WPaintAverageAccum *accum = (struct WPaintAverageAccum *)data->custom_data + n; + accum->len = 0; + accum->value = 0.0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float angle_cos = (use_normal && vd.no) ? dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (angle_cos > 0.0 && + BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + // const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const char v_flag = data->me->mvert[v_index].flag; + + /* If the vertex is selected. */ + if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + const MDeformVert *dv = &data->me->dvert[v_index]; + accum->len += 1; + accum->value += wpaint_get_active_weight(dv, data->wpi); + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void calculate_average_weight(SculptThreadedTaskData *data, + PBVHNode **UNUSED(nodes), + int totnode) +{ + struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); + data->custom_data = accum; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((data->sd->flags & SCULPT_USE_OPENMP) && + totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); + + uint accum_len = 0; + double accum_weight = 0.0; + for (int i = 0; i < totnode; i++) { + accum_len += accum[i].len; + accum_weight += accum[i].value; + } + if (accum_len != 0) { + accum_weight /= accum_len; + data->strength = (float)accum_weight; + } + + MEM_SAFE_FREE(data->custom_data); /* 'accum' */ +} + +static void wpaint_paint_leaves(bContext *C, + Object *ob, + Sculpt *sd, + VPaint *vp, + struct WPaintData *wpd, + WeightPaintInfo *wpi, + Mesh *me, + PBVHNode **nodes, + int totnode) +{ + Scene *scene = CTX_data_scene(C); + const Brush *brush = ob->sculpt->cache->brush; + + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .C = C, + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .vp = vp, + .wpd = wpd, + .wpi = wpi, + .me = me, + }; + + /* Use this so average can modify its weight without touching the brush. */ + data.strength = BKE_brush_weight_get(scene, brush); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + /* NOTE: current mirroring code cannot be run in parallel */ + settings.use_threading = !(me->editflag & ME_EDIT_MIRROR_X); + + switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { + case WPAINT_TOOL_AVERAGE: + calculate_average_weight(&data, nodes, totnode); + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + break; + case WPAINT_TOOL_SMEAR: + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_smear_task_cb_ex, &settings); + break; + case WPAINT_TOOL_BLUR: + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_blur_task_cb_ex, &settings); + break; + case WPAINT_TOOL_DRAW: + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + break; + } } static PBVHNode **vwpaint_pbvh_gather_generic( - Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode) -{ - SculptSession *ss = ob->sculpt; - const bool use_normal = vwpaint_use_normal(wp); - PBVHNode **nodes = NULL; - - /* Build a list of all nodes that are potentially within the brush's area of influence */ - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = ss->cache->radius_squared, - .original = true, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); - if (use_normal) { - sculpt_pbvh_calc_area_normal(brush, ob, nodes, *r_totnode, true, ss->cache->sculpt_normal_symm); - } - else { - zero_v3(ss->cache->sculpt_normal_symm); - } - } - else { - struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; - dist_squared_ray_to_aabb_v3_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); - SculptSearchCircleData data = { - .ss = ss, - .sd = sd, - .radius_squared = ss->cache->radius_squared, - .original = true, - .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); - if (use_normal) { - copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal); - } - else { - zero_v3(ss->cache->sculpt_normal_symm); - } - } - return nodes; -} - -static void wpaint_do_paint( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi, - Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle) -{ - SculptSession *ss = ob->sculpt; - ss->cache->radial_symmetry_pass = i; - sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - - int totnode; - PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, wp, sd, brush, &totnode); - - wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode); - - if (nodes) - MEM_freeN(nodes); -} - -static void wpaint_do_radial_symmetry( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi, - Mesh *me, Brush *brush, const char symm, const int axis) -{ - for (int i = 1; i < wp->radial_symm[axis - 'X']; i++) { - const float angle = (2.0 * M_PI) * i / wp->radial_symm[axis - 'X']; - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, symm, axis, i, angle); - } + Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode) +{ + SculptSession *ss = ob->sculpt; + const bool use_normal = vwpaint_use_normal(wp); + PBVHNode **nodes = NULL; + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = ss->cache->radius_squared, + .original = true, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); + if (use_normal) { + sculpt_pbvh_calc_area_normal( + brush, ob, nodes, *r_totnode, true, ss->cache->sculpt_normal_symm); + } + else { + zero_v3(ss->cache->sculpt_normal_symm); + } + } + else { + struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; + dist_squared_ray_to_aabb_v3_precalc( + &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); + SculptSearchCircleData data = { + .ss = ss, + .sd = sd, + .radius_squared = ss->cache->radius_squared, + .original = true, + .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); + if (use_normal) { + copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal); + } + else { + zero_v3(ss->cache->sculpt_normal_symm); + } + } + return nodes; +} + +static void wpaint_do_paint(bContext *C, + Object *ob, + VPaint *wp, + Sculpt *sd, + struct WPaintData *wpd, + WeightPaintInfo *wpi, + Mesh *me, + Brush *brush, + const char symm, + const int axis, + const int i, + const float angle) +{ + SculptSession *ss = ob->sculpt; + ss->cache->radial_symmetry_pass = i; + sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); + + int totnode; + PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, wp, sd, brush, &totnode); + + wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode); + + if (nodes) + MEM_freeN(nodes); +} + +static void wpaint_do_radial_symmetry(bContext *C, + Object *ob, + VPaint *wp, + Sculpt *sd, + struct WPaintData *wpd, + WeightPaintInfo *wpi, + Mesh *me, + Brush *brush, + const char symm, + const int axis) +{ + for (int i = 1; i < wp->radial_symm[axis - 'X']; i++) { + const float angle = (2.0 * M_PI) * i / wp->radial_symm[axis - 'X']; + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, symm, axis, i, angle); + } } /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */ static void wpaint_do_symmetrical_brush_actions( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi) -{ - Brush *brush = BKE_paint_brush(&wp->paint); - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const char symm = wp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - int i = 0; - - /* initial stroke */ - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X'); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Y'); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Z'); - - cache->symmetry = symm; - - /* symm is a bit combination of XYZ - 1 is mirror - * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 1; i <= symm; i++) { - if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - cache->mirror_symmetry_pass = i; - cache->radial_symmetry_pass = 0; - sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); - - if (i & (1 << 0)) { - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X'); - } - if (i & (1 << 1)) { - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y'); - } - if (i & (1 << 2)) { - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z'); - } - } - } - copy_v3_v3(cache->true_last_location, cache->true_location); - cache->is_last_valid = true; + bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi) +{ + Brush *brush = BKE_paint_brush(&wp->paint); + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const char symm = wp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + int i = 0; + + /* initial stroke */ + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X'); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Y'); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Z'); + + cache->symmetry = symm; + + /* symm is a bit combination of XYZ - 1 is mirror + * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for (i = 1; i <= symm; i++) { + if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + cache->mirror_symmetry_pass = i; + cache->radial_symmetry_pass = 0; + sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); + + if (i & (1 << 0)) { + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X'); + } + if (i & (1 << 1)) { + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y'); + } + if (i & (1 << 2)) { + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z'); + } + } + } + copy_v3_v3(cache->true_last_location, cache->true_location); + cache->is_last_valid = true; } static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - VPaint *wp = ts->wpaint; - Brush *brush = BKE_paint_brush(&wp->paint); - struct WPaintData *wpd = paint_stroke_mode_data(stroke); - ViewContext *vc; - Object *ob = CTX_data_active_object(C); - - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + VPaint *wp = ts->wpaint; + Brush *brush = BKE_paint_brush(&wp->paint); + struct WPaintData *wpd = paint_stroke_mode_data(stroke); + ViewContext *vc; + Object *ob = CTX_data_active_object(C); - vwpaint_update_cache_variants(C, wp, ob, itemptr); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - float mat[4][4]; - float mval[2]; + vwpaint_update_cache_variants(C, wp, ob, itemptr); - const float brush_alpha_value = BKE_brush_alpha_get(scene, brush); + float mat[4][4]; + float mval[2]; - /* intentionally don't initialize as NULL, make sure we initialize all members below */ - WeightPaintInfo wpi; + const float brush_alpha_value = BKE_brush_alpha_get(scene, brush); - /* cannot paint if there is no stroke data */ - if (wpd == NULL) { - /* XXX: force a redraw here, since even though we can't paint, - * at least view won't freeze until stroke ends */ - ED_region_tag_redraw(CTX_wm_region(C)); - return; - } + /* intentionally don't initialize as NULL, make sure we initialize all members below */ + WeightPaintInfo wpi; - vc = &wpd->vc; - ob = vc->obact; + /* cannot paint if there is no stroke data */ + if (wpd == NULL) { + /* XXX: force a redraw here, since even though we can't paint, + * at least view won't freeze until stroke ends */ + ED_region_tag_redraw(CTX_wm_region(C)); + return; + } - view3d_operator_needs_opengl(C); - ED_view3d_init_mats_rv3d(ob, vc->rv3d); + vc = &wpd->vc; + ob = vc->obact; - /* load projection matrix */ - mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + view3d_operator_needs_opengl(C); + ED_view3d_init_mats_rv3d(ob, vc->rv3d); + /* load projection matrix */ + mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); - /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */ - wpi.defbase_tot = wpd->defbase_tot; - wpi.defbase_sel = wpd->defbase_sel; - wpi.defbase_tot_sel = wpd->defbase_tot_sel; + /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */ + wpi.defbase_tot = wpd->defbase_tot; + wpi.defbase_sel = wpd->defbase_sel; + wpi.defbase_tot_sel = wpd->defbase_tot_sel; - wpi.defbase_tot_unsel = wpi.defbase_tot - wpi.defbase_tot_sel; - wpi.active = wpd->active; - wpi.mirror = wpd->mirror; - wpi.lock_flags = wpd->lock_flags; - wpi.vgroup_validmap = wpd->vgroup_validmap; - 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.brush_alpha_value = brush_alpha_value; - /* *** done setting up WeightPaintInfo *** */ + wpi.defbase_tot_unsel = wpi.defbase_tot - wpi.defbase_tot_sel; + wpi.active = wpd->active; + wpi.mirror = wpd->mirror; + wpi.lock_flags = wpd->lock_flags; + wpi.vgroup_validmap = wpd->vgroup_validmap; + 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.brush_alpha_value = brush_alpha_value; + /* *** done setting up WeightPaintInfo *** */ - if (wpd->precomputed_weight) { - precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data); - } + if (wpd->precomputed_weight) { + precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data); + } - wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); + wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); - swap_m4m4(vc->rv3d->persmat, mat); + swap_m4m4(vc->rv3d->persmat, mat); - /* calculate pivot for rotation around seletion if needed */ - /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, mval); + /* calculate pivot for rotation around seletion if needed */ + /* also needed for "View Selected" on last stroke */ + paint_last_stroke_update(scene, vc->ar, mval); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - DEG_id_tag_update(ob->data, 0); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - swap_m4m4(wpd->vc.rv3d->persmat, mat); + DEG_id_tag_update(ob->data, 0); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + swap_m4m4(wpd->vc.rv3d->persmat, mat); - rcti r; - if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) { - if (ss->cache) { - ss->cache->current_r = r; - } + rcti r; + if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) { + if (ss->cache) { + ss->cache->current_r = r; + } - /* previous is not set in the current cache else - * the partial rect will always grow */ - if (ss->cache) { - if (!BLI_rcti_is_empty(&ss->cache->previous_r)) - BLI_rcti_union(&r, &ss->cache->previous_r); - } + /* previous is not set in the current cache else + * the partial rect will always grow */ + if (ss->cache) { + if (!BLI_rcti_is_empty(&ss->cache->previous_r)) + BLI_rcti_union(&r, &ss->cache->previous_r); + } - r.xmin += vc->ar->winrct.xmin - 2; - r.xmax += vc->ar->winrct.xmin + 2; - r.ymin += vc->ar->winrct.ymin - 2; - r.ymax += vc->ar->winrct.ymin + 2; + r.xmin += vc->ar->winrct.xmin - 2; + r.xmax += vc->ar->winrct.xmin + 2; + r.ymin += vc->ar->winrct.ymin - 2; + r.ymax += vc->ar->winrct.ymin + 2; - ss->partial_redraw = 1; - } - ED_region_tag_redraw_partial(vc->ar, &r); + ss->partial_redraw = 1; + } + ED_region_tag_redraw_partial(vc->ar, &r); } static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) { - Object *ob = CTX_data_active_object(C); - struct WPaintData *wpd = paint_stroke_mode_data(stroke); - - if (wpd) { - if (wpd->defbase_sel) - MEM_freeN((void *)wpd->defbase_sel); - if (wpd->vgroup_validmap) - MEM_freeN((void *)wpd->vgroup_validmap); - if (wpd->lock_flags) - MEM_freeN((void *)wpd->lock_flags); - if (wpd->active.lock) - MEM_freeN((void *)wpd->active.lock); - if (wpd->mirror.lock) - MEM_freeN((void *)wpd->mirror.lock); - if (wpd->precomputed_weight) - MEM_freeN(wpd->precomputed_weight); - - MEM_freeN(wpd); - } - - /* and particles too */ - if (ob->particlesystem.first) { - ParticleSystem *psys; - int i; - - for (psys = ob->particlesystem.first; psys; psys = psys->next) { - for (i = 0; i < PSYS_TOT_VG; i++) { - if (psys->vgroup[i] == ob->actdef) { - psys->recalc |= ID_RECALC_PSYS_RESET; - break; - } - } - } - } - - DEG_id_tag_update(ob->data, 0); - - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + Object *ob = CTX_data_active_object(C); + struct WPaintData *wpd = paint_stroke_mode_data(stroke); + + if (wpd) { + if (wpd->defbase_sel) + MEM_freeN((void *)wpd->defbase_sel); + if (wpd->vgroup_validmap) + MEM_freeN((void *)wpd->vgroup_validmap); + if (wpd->lock_flags) + MEM_freeN((void *)wpd->lock_flags); + if (wpd->active.lock) + MEM_freeN((void *)wpd->active.lock); + if (wpd->mirror.lock) + MEM_freeN((void *)wpd->mirror.lock); + if (wpd->precomputed_weight) + MEM_freeN(wpd->precomputed_weight); + + MEM_freeN(wpd); + } + + /* and particles too */ + if (ob->particlesystem.first) { + ParticleSystem *psys; + int i; + + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + for (i = 0; i < PSYS_TOT_VG; i++) { + if (psys->vgroup[i] == ob->actdef) { + psys->recalc |= ID_RECALC_PSYS_RESET; + break; + } + } + } + } + + DEG_id_tag_update(ob->data, 0); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; } - static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int retval; + int retval; - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, wpaint_stroke_test_start, - wpaint_stroke_update_step, NULL, - wpaint_stroke_done, event->type); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + wpaint_stroke_test_start, + wpaint_stroke_update_step, + NULL, + wpaint_stroke_done, + event->type); - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int wpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, wpaint_stroke_test_start, - wpaint_stroke_update_step, NULL, - wpaint_stroke_done, 0); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + wpaint_stroke_test_start, + wpaint_stroke_update_step, + NULL, + wpaint_stroke_done, + 0); - /* frees op->customdata */ - paint_stroke_exec(C, op); + /* frees op->customdata */ + paint_stroke_exec(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void wpaint_cancel(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - if (ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } + Object *ob = CTX_data_active_object(C); + if (ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op); } void PAINT_OT_weight_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Weight Paint"; - ot->idname = "PAINT_OT_weight_paint"; - ot->description = "Paint a stroke in the current vertex group's weights"; + /* identifiers */ + ot->name = "Weight Paint"; + ot->idname = "PAINT_OT_weight_paint"; + ot->description = "Paint a stroke in the current vertex group's weights"; - /* api callbacks */ - ot->invoke = wpaint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = wpaint_exec; - ot->poll = weight_paint_poll; - ot->cancel = wpaint_cancel; + /* api callbacks */ + ot->invoke = wpaint_invoke; + ot->modal = paint_stroke_modal; + ot->exec = wpaint_exec; + ot->poll = weight_paint_poll; + ot->cancel = wpaint_cancel; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot); } /* ************ set / clear vertex paint mode ********** */ @@ -2392,64 +2446,62 @@ void PAINT_OT_weight_paint(wmOperatorType *ot) */ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Object *ob = CTX_data_active_object(C); - const int mode_flag = OB_MODE_VERTEX_PAINT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + Main *bmain = CTX_data_main(C); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Object *ob = CTX_data_active_object(C); + const int mode_flag = OB_MODE_VERTEX_PAINT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } - Mesh *me = BKE_mesh_from_object(ob); + Mesh *me = BKE_mesh_from_object(ob); - /* toggle: end vpaint */ - if (is_mode_set) { - ED_object_vpaintmode_exit_ex(ob); - } - else { - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - wmWindowManager *wm = CTX_wm_manager(C); - ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); - BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint); - } + /* toggle: end vpaint */ + if (is_mode_set) { + ED_object_vpaintmode_exit_ex(ob); + } + else { + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + wmWindowManager *wm = CTX_wm_manager(C); + ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint); + } - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - /* update modifier stack for mapping requirements */ - DEG_id_tag_update(&me->id, 0); + /* update modifier stack for mapping requirements */ + DEG_id_tag_update(&me->id, 0); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - WM_toolsystem_update_from_context_view3d(C); + WM_toolsystem_update_from_context_view3d(C); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint Mode"; - ot->idname = "PAINT_OT_vertex_paint_toggle"; - ot->description = "Toggle the vertex paint mode in 3D view"; + /* identifiers */ + ot->name = "Vertex Paint Mode"; + ot->idname = "PAINT_OT_vertex_paint_toggle"; + ot->description = "Toggle the vertex paint mode in 3D view"; - /* api callbacks */ - ot->exec = vpaint_mode_toggle_exec; - ot->poll = paint_poll_test; + /* api callbacks */ + ot->exec = vpaint_mode_toggle_exec; + ot->poll = paint_poll_test; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } - - /* ********************** vertex paint operator ******************* */ /* Implementation notes: @@ -2471,815 +2523,847 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) */ typedef struct PolyFaceMap { - struct PolyFaceMap *next, *prev; - int facenr; + struct PolyFaceMap *next, *prev; + int facenr; } PolyFaceMap; struct VPaintData { - ViewContext vc; - struct NormalAnglePrecalc normal_angle_precalc; + ViewContext vc; + struct NormalAnglePrecalc normal_angle_precalc; - uint paintcol; + uint paintcol; - struct VertProjHandle *vp_handle; - struct CoNo *vertexcosnos; + struct VertProjHandle *vp_handle; + struct CoNo *vertexcosnos; - /* modify 'me->mcol' directly, since the derived mesh is drawing from this - * array, otherwise we need to refresh the modifier stack */ - bool use_fast_update; + /* modify 'me->mcol' directly, since the derived mesh is drawing from this + * array, otherwise we need to refresh the modifier stack */ + bool use_fast_update; - /* loops tagged as having been painted, to apply shared vertex color - * blending only to modified loops */ - bool *mlooptag; + /* loops tagged as having been painted, to apply shared vertex color + * blending only to modified loops */ + bool *mlooptag; - bool is_texbrush; + bool is_texbrush; - /* Special storage for smear brush, avoid feedback loop - update each step. */ - struct { - uint *color_prev; - uint *color_curr; - } smear; + /* Special storage for smear brush, avoid feedback loop - update each step. */ + struct { + uint *color_prev; + uint *color_curr; + } smear; }; static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - struct PaintStroke *stroke = op->customdata; - VPaint *vp = ts->vpaint; - Brush *brush = BKE_paint_brush(&vp->paint); - struct VPaintData *vpd; - Object *ob = CTX_data_active_object(C); - Mesh *me; - SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - /* context checks could be a poll() */ - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) - return false; - - ED_mesh_color_ensure(me, NULL); - if (me->mloopcol == NULL) - return false; - - /* make mode data storage */ - vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); - paint_stroke_set_mode_data(stroke, vpd); - ED_view3d_viewcontext_init(C, &vpd->vc); - view_angle_limits_init( - &vpd->normal_angle_precalc, vp->paint.brush->falloff_angle, - (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); - - vpd->paintcol = vpaint_get_current_col(scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); - - vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; - - /* are we painting onto a modified mesh?, - * if not we can skip face map trickiness */ - if (vertex_paint_use_fast_update_check(ob)) { - vpd->use_fast_update = true; -/* printf("Fast update!\n");*/ - } - else { - vpd->use_fast_update = false; -/* printf("No fast update!\n");*/ - } - - /* to keep tracked of modified loops for shared vertex color blending */ - if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) { - vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); - } - - if (brush->vertexpaint_tool == VPAINT_TOOL_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; - vpd->vp_handle = ED_vpaint_proj_handle_create(depsgraph, scene, ob, &vpd->vertexcosnos); - ob->sculpt->building_vp_handle = false; - } - - /* If not previously created, create vertex/weight paint mode session data */ - vertex_paint_init_stroke(depsgraph, scene, ob); - vwpaint_update_cache_invariants(C, vp, ss, op, mouse); - vertex_paint_init_session_data(ts, ob); - - if (ob->sculpt->mode.vpaint.previous_color != NULL) { - memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop); - } - - return 1; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + struct PaintStroke *stroke = op->customdata; + VPaint *vp = ts->vpaint; + Brush *brush = BKE_paint_brush(&vp->paint); + struct VPaintData *vpd; + Object *ob = CTX_data_active_object(C); + Mesh *me; + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + /* context checks could be a poll() */ + me = BKE_mesh_from_object(ob); + if (me == NULL || me->totpoly == 0) + return false; + + ED_mesh_color_ensure(me, NULL); + if (me->mloopcol == NULL) + return false; + + /* make mode data storage */ + vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); + paint_stroke_set_mode_data(stroke, vpd); + ED_view3d_viewcontext_init(C, &vpd->vc); + view_angle_limits_init(&vpd->normal_angle_precalc, + vp->paint.brush->falloff_angle, + (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + + vpd->paintcol = vpaint_get_current_col( + scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); + + vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; + + /* are we painting onto a modified mesh?, + * if not we can skip face map trickiness */ + if (vertex_paint_use_fast_update_check(ob)) { + vpd->use_fast_update = true; + /* printf("Fast update!\n");*/ + } + else { + vpd->use_fast_update = false; + /* printf("No fast update!\n");*/ + } + + /* to keep tracked of modified loops for shared vertex color blending */ + if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) { + vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); + } + + if (brush->vertexpaint_tool == VPAINT_TOOL_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; + vpd->vp_handle = ED_vpaint_proj_handle_create(depsgraph, scene, ob, &vpd->vertexcosnos); + ob->sculpt->building_vp_handle = false; + } + + /* If not previously created, create vertex/weight paint mode session data */ + vertex_paint_init_stroke(depsgraph, scene, ob); + vwpaint_update_cache_invariants(C, vp, ss, op, mouse); + vertex_paint_init_session_data(ts, ob); + + if (ob->sculpt->mode.vpaint.previous_color != NULL) { + memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop); + } + + return 1; } static void do_vpaint_brush_calc_average_color_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - - StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - char *col; - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - - struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n; - accum->len = 0; - memset(accum->value, 0, sizeof(accum->value)); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) { - /* If the vertex is selected for painting. */ - const MVert *mv = &data->me->mvert[v_index]; - if (!use_vert_sel || mv->flag & SELECT) { - accum->len += gmap->vert_to_loop[v_index].count; - /* if a vertex is within the brush region, then add it's color to the blend. */ - for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - col = (char *)(&lcol[l_index]); - /* Color is squared to compensate the sqrt color encoding. */ - accum->value[0] += col[0] * col[0]; - accum->value[1] += col[1] * col[1]; - accum->value[2] += col[2] * col[2]; - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static float tex_color_alpha_ubyte( - SculptThreadedTaskData *data, const float v_co[3], - uint *r_color) -{ - float rgba[4]; - float rgba_br[3]; - tex_color_alpha(data->vp, &data->vpd->vc, v_co, rgba); - rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol); - mul_v3_v3(rgba_br, rgba); - rgb_float_to_uchar((uchar *)r_color, rgba_br); - return rgba[3]; -} - -static void do_vpaint_brush_draw_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - const Scene *scene = CTX_data_scene(data->C); - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* Note: Grids are 1:1 with corners (aka loops). - * For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &data->me->mvert[v_index]; - - /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { - /* Calc the dot prod. between ray norm on surf and current vert - * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - uint color_final = data->vpd->paintcol; - - /* If we're painting with a texture, sample the texture color and alpha. */ - float tex_alpha = 1.0; - if (data->vpd->is_texbrush) { - /* Note: we may want to paint alpha as vertex color alpha. */ - tex_alpha = tex_color_alpha_ubyte( - data, data->vpd->vertexcosnos[v_index].co, - &color_final); - } - /* For each poly owning this vert, paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; - } - const float final_alpha = - 255 * brush_fade * brush_strength * - tex_alpha * brush_alpha_pressure * grid_alpha; - - /* Mix the new color with the original based on final_alpha. */ - lcol[l_index] = vpaint_blend( - data->vp, lcol[l_index], color_orig, color_final, - final_alpha, 255 * brush_strength); - } - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_vpaint_brush_blur_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - Scene *scene = CTX_data_scene(data->C); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &data->me->mvert[v_index]; - - /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - - /* Get the average poly color */ - uint color_final = 0; - int total_hit_loops = 0; - uint blend[4] = {0}; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - total_hit_loops += mp->totloop; - for (int k = 0; k < mp->totloop; k++) { - const uint l_index = mp->loopstart + k; - const char *col = (const char *)(&lcol[l_index]); - /* Color is squared to compensate the sqrt color encoding. */ - blend[0] += (uint)col[0] * (uint)col[0]; - blend[1] += (uint)col[1] * (uint)col[1]; - blend[2] += (uint)col[2] * (uint)col[2]; - blend[3] += (uint)col[3] * (uint)col[3]; - } - } - } - if (total_hit_loops != 0) { - /* Use rgb^2 color averaging. */ - char *col = (char *)(&color_final); - col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops))); - col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops))); - col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops))); - col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops))); - - /* For each poly owning this vert, - * paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; - } - const float final_alpha = - 255 * brush_fade * brush_strength * - brush_alpha_pressure * grid_alpha; - /* Mix the new color with the original - * based on the brush strength and the curve. */ - lcol[l_index] = vpaint_blend( - data->vp, lcol[l_index], color_orig, *((uint *)col), - final_alpha, 255 * brush_strength); - } - } - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_vpaint_brush_smear_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - Scene *scene = CTX_data_scene(data->C); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - float brush_dir[3]; - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); - - if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv_curr = &data->me->mvert[v_index]; - - /* if the vertex is selected for painting. */ - if (!use_vert_sel || mv_curr->flag & SELECT) { - /* Calc the dot prod. between ray norm on surf and current vert - * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - - bool do_color = false; - /* Minimum dot product between brush direction and current - * to neighbor direction is 0.0, meaning orthogonal. */ - float stroke_dot_max = 0.0f; - - /* Get the color of the loop in the opposite - * direction of the brush movement */ - uint color_final = 0; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - UNUSED_VARS_NDEBUG(l_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - const MLoop *ml_other = &data->me->mloop[mp->loopstart]; - for (int k = 0; k < mp->totloop; k++, ml_other++) { - const uint v_other_index = ml_other->v; - if (v_other_index != v_index) { - const MVert *mv_other = &data->me->mvert[v_other_index]; - - /* Get the direction from the - * selected vert to the neighbor. */ - float other_dir[3]; - sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); - - normalize_v3(other_dir); - - const float stroke_dot = dot_v3v3(other_dir, brush_dir); - - if (stroke_dot > stroke_dot_max) { - stroke_dot_max = stroke_dot; - color_final = data->vpd->smear.color_prev[mp->loopstart + k]; - do_color = true; - } - } - } - } - } - - if (do_color) { - const float final_alpha = - 255 * brush_fade * brush_strength * - brush_alpha_pressure * grid_alpha; - - /* For each poly owning this vert, - * paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - /* Get the previous loop color */ - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; - } - /* Mix the new color with the original - * based on the brush strength and the curve. */ - 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]; - } - } - } - } - } - } - } - BKE_pbvh_vertex_iter_end; - } -} - -static void calculate_average_color(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode) -{ - struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); - data->custom_data = accum; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range( - 0, totnode, - data, - do_vpaint_brush_calc_average_color_cb_ex, - &settings); - - uint accum_len = 0; - uint accum_value[3] = {0}; - uchar blend[4] = {0}; - for (int i = 0; i < totnode; i++) { - accum_len += accum[i].len; - accum_value[0] += accum[i].value[0]; - accum_value[1] += accum[i].value[1]; - accum_value[2] += accum[i].value[2]; - } - if (accum_len != 0) { - blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len))); - blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len))); - blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len))); - blend[3] = 255; - data->vpd->paintcol = *((uint *)blend); - } - - MEM_SAFE_FREE(data->custom_data); /* 'accum' */ -} - -static void vpaint_paint_leaves( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, - Object *ob, Mesh *me, PBVHNode **nodes, int totnode) -{ - const Brush *brush = ob->sculpt->cache->brush; - - SculptThreadedTaskData data = { - .C = C, .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .vpd = vpd, - .lcol = (uint *)me->mloopcol, .me = me, - }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { - case VPAINT_TOOL_AVERAGE: - calculate_average_color(&data, nodes, totnode); - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_draw_task_cb_ex, - &settings); - break; - case VPAINT_TOOL_BLUR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_blur_task_cb_ex, - &settings); - break; - case VPAINT_TOOL_SMEAR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_smear_task_cb_ex, - &settings); - break; - case VPAINT_TOOL_DRAW: - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_draw_task_cb_ex, - &settings); - break; - } -} - -static void vpaint_do_paint( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, - Object *ob, Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle) -{ - SculptSession *ss = ob->sculpt; - ss->cache->radial_symmetry_pass = i; - sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - - int totnode; - PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode); - - /* Paint those leaves. */ - vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode); - - if (nodes) { - MEM_freeN(nodes); - } -} - -static void vpaint_do_radial_symmetry( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob, Mesh *me, - Brush *brush, const char symm, const int axis) -{ - for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { - const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle); - } + void *__restrict userdata, const int n, const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + + StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + char *col; + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + + struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n; + accum->len = 0; + memset(accum->value, 0, sizeof(accum->value)); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) { + /* If the vertex is selected for painting. */ + const MVert *mv = &data->me->mvert[v_index]; + if (!use_vert_sel || mv->flag & SELECT) { + accum->len += gmap->vert_to_loop[v_index].count; + /* if a vertex is within the brush region, then add it's color to the blend. */ + for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + col = (char *)(&lcol[l_index]); + /* Color is squared to compensate the sqrt color encoding. */ + accum->value[0] += col[0] * col[0]; + accum->value[1] += col[1] * col[1]; + accum->value[2] += col[2] * col[2]; + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static float tex_color_alpha_ubyte(SculptThreadedTaskData *data, + const float v_co[3], + uint *r_color) +{ + float rgba[4]; + float rgba_br[3]; + tex_color_alpha(data->vp, &data->vpd->vc, v_co, rgba); + rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol); + mul_v3_v3(rgba_br, rgba); + rgb_float_to_uchar((uchar *)r_color, rgba_br); + return rgba[3]; +} + +static void do_vpaint_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + const Scene *scene = CTX_data_scene(data->C); + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* Note: Grids are 1:1 with corners (aka loops). + * For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &data->me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + /* Calc the dot prod. between ray norm on surf and current vert + * (ie splash prevention factor), and only paint front facing verts. */ + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + uint color_final = data->vpd->paintcol; + + /* If we're painting with a texture, sample the texture color and alpha. */ + float tex_alpha = 1.0; + if (data->vpd->is_texbrush) { + /* Note: we may want to paint alpha as vertex color alpha. */ + tex_alpha = tex_color_alpha_ubyte( + data, data->vpd->vertexcosnos[v_index].co, &color_final); + } + /* For each poly owning this vert, paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + uint color_orig = 0; /* unused when array is NULL */ + if (ss->mode.vpaint.previous_color != NULL) { + /* Get the previous loop color */ + if (ss->mode.vpaint.previous_color[l_index] == 0) { + ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + } + color_orig = ss->mode.vpaint.previous_color[l_index]; + } + const float final_alpha = 255 * brush_fade * brush_strength * tex_alpha * + brush_alpha_pressure * grid_alpha; + + /* Mix the new color with the original based on final_alpha. */ + lcol[l_index] = vpaint_blend(data->vp, + lcol[l_index], + color_orig, + color_final, + final_alpha, + 255 * brush_strength); + } + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_vpaint_brush_blur_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + Scene *scene = CTX_data_scene(data->C); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &data->me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + + /* Get the average poly color */ + uint color_final = 0; + int total_hit_loops = 0; + uint blend[4] = {0}; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + total_hit_loops += mp->totloop; + for (int k = 0; k < mp->totloop; k++) { + const uint l_index = mp->loopstart + k; + const char *col = (const char *)(&lcol[l_index]); + /* Color is squared to compensate the sqrt color encoding. */ + blend[0] += (uint)col[0] * (uint)col[0]; + blend[1] += (uint)col[1] * (uint)col[1]; + blend[2] += (uint)col[2] * (uint)col[2]; + blend[3] += (uint)col[3] * (uint)col[3]; + } + } + } + if (total_hit_loops != 0) { + /* Use rgb^2 color averaging. */ + char *col = (char *)(&color_final); + col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops))); + col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops))); + col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops))); + col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops))); + + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + uint color_orig = 0; /* unused when array is NULL */ + if (ss->mode.vpaint.previous_color != NULL) { + /* Get the previous loop color */ + if (ss->mode.vpaint.previous_color[l_index] == 0) { + ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + } + color_orig = ss->mode.vpaint.previous_color[l_index]; + } + const float final_alpha = 255 * brush_fade * brush_strength * + brush_alpha_pressure * grid_alpha; + /* Mix the new color with the original + * based on the brush strength and the curve. */ + lcol[l_index] = vpaint_blend(data->vp, + lcol[l_index], + color_orig, + *((uint *)col), + final_alpha, + 255 * brush_strength); + } + } + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_vpaint_brush_smear_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + Scene *scene = CTX_data_scene(data->C); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + float brush_dir[3]; + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + sub_v3_v3v3(brush_dir, cache->location, cache->last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + + if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv_curr = &data->me->mvert[v_index]; + + /* if the vertex is selected for painting. */ + if (!use_vert_sel || mv_curr->flag & SELECT) { + /* Calc the dot prod. between ray norm on surf and current vert + * (ie splash prevention factor), and only paint front facing verts. */ + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + + bool do_color = false; + /* Minimum dot product between brush direction and current + * to neighbor direction is 0.0, meaning orthogonal. */ + float stroke_dot_max = 0.0f; + + /* Get the color of the loop in the opposite + * direction of the brush movement */ + uint color_final = 0; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + UNUSED_VARS_NDEBUG(l_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + const MLoop *ml_other = &data->me->mloop[mp->loopstart]; + for (int k = 0; k < mp->totloop; k++, ml_other++) { + const uint v_other_index = ml_other->v; + if (v_other_index != v_index) { + const MVert *mv_other = &data->me->mvert[v_other_index]; + + /* Get the direction from the + * selected vert to the neighbor. */ + float other_dir[3]; + sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); + project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + + normalize_v3(other_dir); + + const float stroke_dot = dot_v3v3(other_dir, brush_dir); + + if (stroke_dot > stroke_dot_max) { + stroke_dot_max = stroke_dot; + color_final = data->vpd->smear.color_prev[mp->loopstart + k]; + do_color = true; + } + } + } + } + } + + if (do_color) { + const float final_alpha = 255 * brush_fade * brush_strength * brush_alpha_pressure * + grid_alpha; + + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + /* Get the previous loop color */ + uint color_orig = 0; /* unused when array is NULL */ + if (ss->mode.vpaint.previous_color != NULL) { + /* Get the previous loop color */ + if (ss->mode.vpaint.previous_color[l_index] == 0) { + ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + } + color_orig = ss->mode.vpaint.previous_color[l_index]; + } + /* Mix the new color with the original + * based on the brush strength and the curve. */ + 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]; + } + } + } + } + } + } + } + BKE_pbvh_vertex_iter_end; + } +} + +static void calculate_average_color(SculptThreadedTaskData *data, + PBVHNode **UNUSED(nodes), + int totnode) +{ + struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); + data->custom_data = accum; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); + + uint accum_len = 0; + uint accum_value[3] = {0}; + uchar blend[4] = {0}; + for (int i = 0; i < totnode; i++) { + accum_len += accum[i].len; + accum_value[0] += accum[i].value[0]; + accum_value[1] += accum[i].value[1]; + accum_value[2] += accum[i].value[2]; + } + if (accum_len != 0) { + blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len))); + blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len))); + blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len))); + blend[3] = 255; + data->vpd->paintcol = *((uint *)blend); + } + + MEM_SAFE_FREE(data->custom_data); /* 'accum' */ +} + +static void vpaint_paint_leaves(bContext *C, + Sculpt *sd, + VPaint *vp, + struct VPaintData *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode) +{ + const Brush *brush = ob->sculpt->cache->brush; + + SculptThreadedTaskData data = { + .C = C, + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .vp = vp, + .vpd = vpd, + .lcol = (uint *)me->mloopcol, + .me = me, + }; + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { + case VPAINT_TOOL_AVERAGE: + calculate_average_color(&data, nodes, totnode); + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + break; + case VPAINT_TOOL_BLUR: + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); + break; + case VPAINT_TOOL_SMEAR: + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); + break; + case VPAINT_TOOL_DRAW: + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + break; + } +} + +static void vpaint_do_paint(bContext *C, + Sculpt *sd, + VPaint *vp, + struct VPaintData *vpd, + Object *ob, + Mesh *me, + Brush *brush, + const char symm, + const int axis, + const int i, + const float angle) +{ + SculptSession *ss = ob->sculpt; + ss->cache->radial_symmetry_pass = i; + sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); + + int totnode; + PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode); + + /* Paint those leaves. */ + vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode); + + if (nodes) { + MEM_freeN(nodes); + } +} + +static void vpaint_do_radial_symmetry(bContext *C, + Sculpt *sd, + VPaint *vp, + struct VPaintData *vpd, + Object *ob, + Mesh *me, + Brush *brush, + const char symm, + const int axis) +{ + for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { + const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle); + } } /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */ static void vpaint_do_symmetrical_brush_actions( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob) -{ - Brush *brush = BKE_paint_brush(&vp->paint); - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const char symm = vp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - int i = 0; - - /* initial stroke */ - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); - - cache->symmetry = symm; - - /* symm is a bit combination of XYZ - 1 is mirror - * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 1; i <= symm; i++) { - if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { - cache->mirror_symmetry_pass = i; - cache->radial_symmetry_pass = 0; - sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); - - if (i & (1 << 0)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); - } - if (i & (1 << 1)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); - } - if (i & (1 << 2)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); - } - } - } - - copy_v3_v3(cache->true_last_location, cache->true_location); - cache->is_last_valid = true; + bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob) +{ + Brush *brush = BKE_paint_brush(&vp->paint); + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const char symm = vp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + int i = 0; + + /* initial stroke */ + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); + + cache->symmetry = symm; + + /* symm is a bit combination of XYZ - 1 is mirror + * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for (i = 1; i <= symm; i++) { + if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { + cache->mirror_symmetry_pass = i; + cache->radial_symmetry_pass = 0; + sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); + + if (i & (1 << 0)) { + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); + } + if (i & (1 << 1)) { + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + } + if (i & (1 << 2)) { + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); + } + } + } + + copy_v3_v3(cache->true_last_location, cache->true_location); + cache->is_last_valid = true; } static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - struct VPaintData *vpd = paint_stroke_mode_data(stroke); - VPaint *vp = ts->vpaint; - ViewContext *vc = &vpd->vc; - Object *ob = vc->obact; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + struct VPaintData *vpd = paint_stroke_mode_data(stroke); + VPaint *vp = ts->vpaint; + ViewContext *vc = &vpd->vc; + Object *ob = vc->obact; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - vwpaint_update_cache_variants(C, vp, ob, itemptr); + vwpaint_update_cache_variants(C, vp, ob, itemptr); - float mat[4][4]; - float mval[2]; + float mat[4][4]; + float mval[2]; - ED_view3d_init_mats_rv3d(ob, vc->rv3d); + ED_view3d_init_mats_rv3d(ob, vc->rv3d); - /* load projection matrix */ - mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + /* load projection matrix */ + mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); - swap_m4m4(vc->rv3d->persmat, mat); + swap_m4m4(vc->rv3d->persmat, mat); - vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob); + vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob); - swap_m4m4(vc->rv3d->persmat, mat); + swap_m4m4(vc->rv3d->persmat, mat); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { - memcpy(vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop); - } + if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { + memcpy( + vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop); + } - /* calculate pivot for rotation around seletion if needed */ - /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, mval); + /* calculate pivot for rotation around seletion if needed */ + /* also needed for "View Selected" on last stroke */ + paint_last_stroke_update(scene, vc->ar, mval); - ED_region_tag_redraw(vc->ar); + ED_region_tag_redraw(vc->ar); - if (vpd->use_fast_update == false) { - /* recalculate modifier stack to get new colors, slow, - * avoid this if we can! */ - DEG_id_tag_update(ob->data, 0); - } - else { - /* Flush changes through DEG. */ - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE); - } + if (vpd->use_fast_update == false) { + /* recalculate modifier stack to get new colors, slow, + * avoid this if we can! */ + DEG_id_tag_update(ob->data, 0); + } + else { + /* Flush changes through DEG. */ + DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE); + } } static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) { - struct VPaintData *vpd = paint_stroke_mode_data(stroke); - ViewContext *vc = &vpd->vc; - Object *ob = vc->obact; + struct VPaintData *vpd = paint_stroke_mode_data(stroke); + ViewContext *vc = &vpd->vc; + Object *ob = vc->obact; - if (vpd->is_texbrush) { - ED_vpaint_proj_handle_free(vpd->vp_handle); - } + if (vpd->is_texbrush) { + ED_vpaint_proj_handle_free(vpd->vp_handle); + } - 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); + 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); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - MEM_freeN(vpd); + MEM_freeN(vpd); - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; } static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int retval; + int retval; - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, vpaint_stroke_test_start, - vpaint_stroke_update_step, NULL, - vpaint_stroke_done, event->type); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + vpaint_stroke_test_start, + vpaint_stroke_update_step, + NULL, + vpaint_stroke_done, + event->type); - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } - /* add modal handler */ - WM_event_add_modal_handler(C, op); + /* add modal handler */ + WM_event_add_modal_handler(C, op); - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int vpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, vpaint_stroke_test_start, - vpaint_stroke_update_step, NULL, - vpaint_stroke_done, 0); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + vpaint_stroke_test_start, + vpaint_stroke_update_step, + NULL, + vpaint_stroke_done, + 0); - /* frees op->customdata */ - paint_stroke_exec(C, op); + /* frees op->customdata */ + paint_stroke_exec(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void vpaint_cancel(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - if (ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } + Object *ob = CTX_data_active_object(C); + if (ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op); } void PAINT_OT_vertex_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint"; - ot->idname = "PAINT_OT_vertex_paint"; - ot->description = "Paint a stroke in the active vertex color layer"; + /* identifiers */ + ot->name = "Vertex Paint"; + ot->idname = "PAINT_OT_vertex_paint"; + ot->description = "Paint a stroke in the active vertex color layer"; - /* api callbacks */ - ot->invoke = vpaint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = vpaint_exec; - ot->poll = vertex_paint_poll; - ot->cancel = vpaint_cancel; + /* api callbacks */ + ot->invoke = vpaint_invoke; + ot->modal = paint_stroke_modal; + ot->exec = vpaint_exec; + ot->poll = vertex_paint_poll; + ot->cancel = vpaint_cancel; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot); } -- cgit v1.2.3