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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2019-04-17 07:17:24 +0300
committerCampbell Barton <ideasman42@gmail.com>2019-04-17 07:21:24 +0300
commite12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch)
tree8cf3453d12edb177a218ef8009357518ec6cab6a /source/blender/editors/sculpt_paint/paint_vertex.c
parentb3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff)
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
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_vertex.c')
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c5580
1 files changed, 2832 insertions, 2748 deletions
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);
}