diff options
author | Campbell Barton <ideasman42@gmail.com> | 2017-09-29 10:10:15 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2017-09-29 11:06:52 +0300 |
commit | 288b2d0aa6bc8097f92e85f8d16fd2125d6eb2e9 (patch) | |
tree | ab8d3e4efced84746d8c2ffb0ee5608d48d50e54 /source/blender/editors/sculpt_paint | |
parent | 97eefc1fd9069794cbfbba23de01fc1813f738d7 (diff) |
Vertex Paint: Alpha Support
GSOC 2017 by Darshan Kadu, see: D2859.
This is a partial merge of some of the features from
the soc-2017-vertex_paint branch.
- Alpha painting & drawing.
- 10 new color blending modes.
- Support for vertex select in vertex paint mode.
Diffstat (limited to 'source/blender/editors/sculpt_paint')
6 files changed, 561 insertions, 75 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 46753df4e13..fa669d236b9 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC paint_undo.c paint_utils.c paint_vertex.c + paint_vertex_color_ops.c paint_vertex_proj.c sculpt.c sculpt_undo.c diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 0ec7d97a04d..16570b0fdde 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -123,6 +123,8 @@ void PAINT_OT_vertex_paint(struct wmOperatorType *ot); unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp); +/* paint_vertex_color_ops.c */ +void PAINT_OT_vertex_color_from_weight(struct wmOperatorType *ot); /* paint_vertex_proj.c */ struct VertProjHandle; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 4ebf14ed0b9..23231b7ccc7 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1349,6 +1349,7 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_vertex_color_hsv); WM_operatortype_append(PAINT_OT_vertex_color_invert); WM_operatortype_append(PAINT_OT_vertex_color_levels); + WM_operatortype_append(PAINT_OT_vertex_color_from_weight); /* face-select */ WM_operatortype_append(PAINT_OT_face_select_linked); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index a88b834d601..18b71dedc07 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -83,6 +83,8 @@ #include "sculpt_intern.h" #include "paint_intern.h" /* own include */ +#define EPS_SATURATION 0.0005f + /* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */ struct VPaintAverageAccum { uint len; @@ -256,29 +258,6 @@ static void do_shared_vertexcol(Mesh *me, bool *mlooptag) MEM_freeN(scol); } -static bool make_vertexcol(Object *ob) /* single ob */ -{ - Mesh *me; - - if (ID_IS_LINKED_DATABLOCK(ob) || - ((me = BKE_mesh_from_object(ob)) == NULL) || - (me->totpoly == 0) || - (me->edit_btmesh)) - { - return false; - } - - /* copies from shadedisplist to mcol */ - if (!me->mloopcol && me->totloop) { - CustomData_add_layer(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop); - BKE_mesh_update_customdata_pointers(me, true); - } - - DAG_id_tag_update(&me->id, 0); - - return (me->mloopcol != NULL); -} - /* mirror_vgroup is set to -1 when invalid */ static int wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) { @@ -342,12 +321,13 @@ bool ED_vpaint_fill(Object *ob, uint paintcol) int i, j; if (((me = BKE_mesh_from_object(ob)) == NULL) || - (me->mloopcol == NULL && (make_vertexcol(ob) == false))) + (ED_mesh_color_ensure(me, NULL) == false)) { return false; } const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; mp = me->mpoly; for (i = 0; i < me->totpoly; i++, mp++) { @@ -356,9 +336,16 @@ bool ED_vpaint_fill(Object *ob, uint paintcol) if (use_face_sel && !(mp->flag & ME_FACE_SEL)) continue; - for (j = 0; j < mp->totloop; j++, lcol++) { - *(int *)lcol = paintcol; - } + j = 0; + do { + uint vidx = me->mloop[mp->loopstart + j].v; + if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { + *(int *)lcol = paintcol; + } + lcol++; + j++; + } while (j < mp->totloop); + } /* remove stale me->mcol, will be added later */ @@ -465,7 +452,7 @@ bool ED_vpaint_smooth(Object *ob) bool *mlooptag; if (((me = BKE_mesh_from_object(ob)) == NULL) || - (me->mloopcol == NULL && (make_vertexcol(ob) == false))) + (ED_mesh_color_ensure(me, NULL) == false)) { return false; } @@ -512,7 +499,7 @@ bool ED_vpaint_color_transform( const MPoly *mp; if (((me = BKE_mesh_from_object(ob)) == NULL) || - (me->mloopcol == NULL && (make_vertexcol(ob) == false))) + (ED_mesh_color_ensure(me, NULL) == false)) { return false; } @@ -612,15 +599,17 @@ BLI_INLINE uint mcol_blend(uint col1, uint col2, int fac) int r1 = cp1[0] * cp1[0]; int g1 = cp1[1] * cp1[1]; int b1 = cp1[2] * cp1[2]; + int a1 = cp1[3] * cp1[3]; int r2 = cp2[0] * cp2[0]; int g2 = cp2[1] * cp2[1]; int b2 = cp2[2] * cp2[2]; + int a2 = cp2[3] * cp2[3]; cp[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255))); cp[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255))); cp[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255))); - cp[3] = 255; + cp[3] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255))); return col; } @@ -645,7 +634,8 @@ BLI_INLINE uint mcol_add(uint col1, uint col2, int fac) cp[1] = (temp > 254) ? 255 : temp; temp = cp1[2] + divide_round_i((fac * cp2[2]), 255); cp[2] = (temp > 254) ? 255 : temp; - cp[3] = 255; + temp = cp1[3] + divide_round_i((fac * cp2[3]), 255); + cp[3] = (temp > 254) ? 255 : temp; return col; } @@ -670,7 +660,8 @@ BLI_INLINE uint mcol_sub(uint col1, uint col2, int fac) cp[1] = (temp < 0) ? 0 : temp; temp = cp1[2] - divide_round_i((fac * cp2[2]), 255); cp[2] = (temp < 0) ? 0 : temp; - cp[3] = 255; + temp = cp1[3] - divide_round_i((fac * cp2[3]), 255); + cp[3] = (temp < 0) ? 0 : temp; return col; } @@ -695,7 +686,7 @@ BLI_INLINE uint mcol_mul(uint col1, uint col2, int fac) cp[0] = divide_round_i(mfac * cp1[0] * 255 + fac * cp2[0] * cp1[0], 255 * 255); cp[1] = divide_round_i(mfac * cp1[1] * 255 + fac * cp2[1] * cp1[1], 255 * 255); cp[2] = divide_round_i(mfac * cp1[2] * 255 + fac * cp2[2] * cp1[2], 255 * 255); - cp[3] = 255; + cp[3] = divide_round_i(mfac * cp1[3] * 255 + fac * cp2[3] * cp1[3], 255 * 255); return col; } @@ -728,7 +719,7 @@ BLI_INLINE uint mcol_lighten(uint col1, uint col2, int fac) cp[0] = divide_round_i(mfac * cp1[0] + fac * cp2[0], 255); cp[1] = divide_round_i(mfac * cp1[1] + fac * cp2[1], 255); cp[2] = divide_round_i(mfac * cp1[2] + fac * cp2[2], 255); - cp[3] = 255; + cp[3] = divide_round_i(mfac * cp1[3] + fac * cp2[3], 255); return col; } @@ -761,24 +752,373 @@ BLI_INLINE uint mcol_darken(uint col1, uint col2, int fac) cp[0] = divide_round_i((mfac * cp1[0] + fac * cp2[0]), 255); cp[1] = divide_round_i((mfac * cp1[1] + fac * cp2[1]), 255); cp[2] = divide_round_i((mfac * cp1[2] + fac * cp2[2]), 255); - cp[3] = 255; + cp[3] = divide_round_i((mfac * cp1[3] + fac * cp2[3]), 255); + return col; +} + +BLI_INLINE uint mcol_colordodge(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac,temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + temp = (cp2[0] == 255) ? 255 : min_ii((cp1[0] * 225) / (255 - cp2[0]), 255); + cp[0] = (mfac * cp1[0] + temp * fac) / 255; + temp = (cp2[1] == 255) ? 255 : min_ii((cp1[1] * 225) / (255 - cp2[1]), 255); + cp[1] = (mfac * cp1[1] + temp * fac) / 255; + temp = (cp2[2] == 255) ? 255 : min_ii((cp1[2] * 225 )/ (255 - cp2[2]), 255); + cp[2] = (mfac * cp1[2] + temp * fac) / 255; + temp = (cp2[3] == 255) ? 255 : min_ii((cp1[3] * 225) / (255 - cp2[3]), 255); + cp[3] = (mfac * cp1[3] + temp * fac) / 255; + return col; +} + +BLI_INLINE uint mcol_difference(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac, temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + temp = abs(cp1[0] - cp2[0]); + cp[0] = (mfac * cp1[0] + temp * fac) / 255; + temp = abs(cp1[1] - cp2[1]); + cp[1] = (mfac * cp1[1] + temp * fac) / 255; + temp = abs(cp1[2] - cp2[2]); + cp[2] = (mfac * cp1[2] + temp * fac) / 255; + temp = abs(cp1[3] - cp2[3]); + cp[3] = (mfac * cp1[3] + temp * fac) / 255; + return col; +} + +BLI_INLINE uint mcol_screen(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac, temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + temp = max_ii(255 - (((255 - cp1[0]) * (255 - cp2[0])) / 255), 0); + cp[0] = (mfac * cp1[0] + temp * fac) / 255; + temp = max_ii(255 - (((255 - cp1[1]) * (255 - cp2[1])) / 255), 0); + cp[1] = (mfac * cp1[1] + temp * fac) / 255; + temp = max_ii(255 - (((255 - cp1[2]) * (255 - cp2[2])) / 255), 0); + cp[2] = (mfac * cp1[2] + temp * fac) / 255; + temp = max_ii(255 - (((255 - cp1[3]) * (255 - cp2[3])) / 255), 0); + cp[3] = (mfac * cp1[3] + temp * fac) / 255; + return col; +} + +BLI_INLINE uint mcol_hardlight(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac, temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp2[i] > 127) { + temp = 255 - ((255 - 2 * (cp2[i] - 127)) * (255 - cp1[i]) / 255); + } + else { + temp = (2 * cp2[i] * cp1[i]) >> 8; + } + cp[i] = min_ii((mfac * cp1[i] + temp * fac) / 255, 255); + } + return col; +} + +BLI_INLINE uint mcol_overlay(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac, temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp1[i] > 127) { + temp = 255 - ((255 - 2 * (cp1[i] - 127)) * (255 - cp2[i]) / 255); + } + else { + temp = (2 * cp2[i] * cp1[i]) >> 8; + } + cp[i] = min_ii((mfac * cp1[i] + temp * fac) / 255, 255); + } + return col; +} + +BLI_INLINE uint mcol_softlight(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac, temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp1[i] < 127) { + temp = ((2 * ((cp2[i] / 2) + 64)) * cp1[i]) / 255; + } + else { + temp = 255 - (2 * (255 - ((cp2[i] / 2) + 64)) * (255 - cp1[i]) / 255); + } + cp[i] = (temp * fac + cp1[i] * mfac) / 255; + } + return col; +} + +BLI_INLINE uint mcol_exclusion(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac, temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + int i = 0; + + for (i = 0; i < 4; i++) { + temp = 127 - ((2 * (cp1[i] - 127) * (cp2[i] - 127)) / 255); + cp[i] = (temp * fac + cp1[i] * mfac) / 255; + } + return col; +} + +BLI_INLINE uint mcol_luminosity(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2); + + v1 = v2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255; + cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255; + cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255; + cp[3] = ((int)(cp2[3]) * fac + mfac * cp1[3]) / 255; + return col; +} + +BLI_INLINE uint mcol_saturation(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2); + + if (s1 > EPS_SATURATION) { + s1 = s2; + } + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255; + cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255; + cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255; + return col; +} + +BLI_INLINE uint mcol_hue(uint col1, uint col2, int fac) +{ + uchar *cp1, *cp2, *cp; + int mfac; + uint col = 0; + + if (fac == 0) { + return col1; + } + + mfac = 255 - fac; + + cp1 = (uchar *)&col1; + cp2 = (uchar *)&col2; + cp = (uchar *)&col; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2); + + h1 = h2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255; + cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255; + cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255; + cp[3] = ((int)(cp2[3]) * fac + mfac * cp1[3]) / 255; + return col; +} + +BLI_INLINE uint mcol_alpha_add(uint col1, int fac) +{ + uchar *cp1, *cp; + int temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + cp1 = (uchar *)&col1; + cp = (uchar *)&col; + + temp = cp1[3] + fac; + cp[3] = (temp > 254) ? 255 : temp; + return col; } +BLI_INLINE uint mcol_alpha_sub(uint col1, int fac) +{ + uchar *cp1, *cp; + int temp; + uint col = 0; + + if (fac == 0) { + return col1; + } + + cp1 = (uchar *)&col1; + cp = (uchar *)&col; + + temp = cp1[3] - fac; + cp[3] = temp < 0 ? 0 : temp; + + return col; +} + + /* wpaint has 'wpaint_blend_tool' */ -static uint vpaint_blend_tool(const int tool, const uint col, - const uint paintcol, const int alpha_i) +static uint vpaint_blend_tool( + const int tool, const uint col, + const uint paintcol, const int alpha_i) { switch (tool) { case PAINT_BLEND_MIX: - case PAINT_BLEND_BLUR: return mcol_blend(col, paintcol, alpha_i); - case PAINT_BLEND_AVERAGE: return mcol_blend(col, paintcol, alpha_i); - case PAINT_BLEND_SMEAR: return mcol_blend(col, paintcol, alpha_i); - case PAINT_BLEND_ADD: return mcol_add(col, paintcol, alpha_i); - case PAINT_BLEND_SUB: return mcol_sub(col, paintcol, alpha_i); - case PAINT_BLEND_MUL: return mcol_mul(col, paintcol, alpha_i); - case PAINT_BLEND_LIGHTEN: return mcol_lighten(col, paintcol, alpha_i); - case PAINT_BLEND_DARKEN: return mcol_darken(col, paintcol, alpha_i); + case PAINT_BLEND_BLUR: return mcol_blend(col, paintcol, alpha_i); + case PAINT_BLEND_AVERAGE: return mcol_blend(col, paintcol, alpha_i); + case PAINT_BLEND_SMEAR: return mcol_blend(col, paintcol, alpha_i); + case PAINT_BLEND_ADD: return mcol_add(col, paintcol, alpha_i); + case PAINT_BLEND_SUB: return mcol_sub(col, paintcol, alpha_i); + case PAINT_BLEND_MUL: return mcol_mul(col, paintcol, alpha_i); + case PAINT_BLEND_LIGHTEN: return mcol_lighten(col, paintcol, alpha_i); + case PAINT_BLEND_DARKEN: return mcol_darken(col, paintcol, alpha_i); + case PAINT_BLEND_COLORDODGE: return mcol_colordodge(col, paintcol, alpha_i); + case PAINT_BLEND_DIFFERENCE: return mcol_difference(col, paintcol, alpha_i); + case PAINT_BLEND_SCREEN: return mcol_screen(col, paintcol, alpha_i); + case PAINT_BLEND_HARDLIGHT: return mcol_hardlight(col, paintcol, alpha_i); + case PAINT_BLEND_OVERLAY: return mcol_overlay(col, paintcol, alpha_i); + case PAINT_BLEND_SOFTLIGHT: return mcol_softlight(col, paintcol, alpha_i); + case PAINT_BLEND_EXCLUSION: return mcol_exclusion(col, paintcol, alpha_i); + case PAINT_BLEND_LUMINOCITY: return mcol_luminosity(col, paintcol, alpha_i); + case PAINT_BLEND_SATURATION: return mcol_saturation(col, paintcol, alpha_i); + case PAINT_BLEND_HUE: return mcol_hue(col, paintcol, alpha_i); + /* non-color */ + case PAINT_BLEND_ALPHA_SUB: return mcol_alpha_sub(col, alpha_i); + case PAINT_BLEND_ALPHA_ADD: return mcol_alpha_add(col, alpha_i); default: BLI_assert(0); return 0; @@ -787,27 +1127,27 @@ static uint vpaint_blend_tool(const int tool, const uint col, /* wpaint has 'wpaint_blend' */ static uint vpaint_blend( - VPaint *vp, uint col, uint colorig, - const uint paintcol, const int alpha_i, + 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) { Brush *brush = BKE_paint_brush(&vp->paint); const int tool = brush->vertexpaint_tool; - col = vpaint_blend_tool(tool, col, paintcol, alpha_i); + uint color_blend = vpaint_blend_tool(tool, color_curr, color_paint, alpha_i); /* if no spray, clip color adding with colorig & orig alpha */ if ((vp->flag & VP_SPRAY) == 0) { - uint testcol, a; + uint color_test, a; char *cp, *ct, *co; - - testcol = vpaint_blend_tool(tool, colorig, paintcol, brush_alpha_value_i); - - cp = (char *)&col; - ct = (char *)&testcol; - co = (char *)&colorig; - + + color_test = vpaint_blend_tool(tool, 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]; @@ -820,7 +1160,16 @@ static uint vpaint_blend( } } - return col; + if ((brush->flag & BRUSH_LOCK_ALPHA) && + !ELEM(tool, PAINT_BLEND_ALPHA_SUB, PAINT_BLEND_ALPHA_ADD)) + { + char *cp, *cc; + cp = (char *)&color_blend; + cc = (char *)&color_curr; + cp[3] = cc[3]; + } + + return color_blend; } @@ -1738,12 +2087,15 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) { /* Create maps */ struct SculptVertexPaintGeomMap *gmap = NULL; + const Brush *brush = NULL; if (ob->mode == OB_MODE_VERTEX_PAINT) { gmap = &ob->sculpt->mode.vpaint.gmap; + brush = BKE_paint_brush(&ts->vpaint->paint); ob->sculpt->mode_type = OB_MODE_VERTEX_PAINT; } else if (ob->mode == OB_MODE_WEIGHT_PAINT) { gmap = &ob->sculpt->mode.wpaint.gmap; + brush = BKE_paint_brush(&ts->wpaint->paint); ob->sculpt->mode_type = OB_MODE_WEIGHT_PAINT; } else { @@ -1780,6 +2132,16 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) else { MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color); } + + if (brush && brush->flag & BRUSH_ACCUMULATE) { + if (ob->sculpt->mode.vpaint.previous_accum == NULL) { + ob->sculpt->mode.vpaint.previous_accum = + MEM_callocN(me->totloop * sizeof(uint), "previous_color"); + } + } + else { + MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_accum); + } } else if (ob->mode == OB_MODE_WEIGHT_PAINT) { if ((ts->wpaint->flag & VP_SPRAY) == 0) { @@ -3104,6 +3466,9 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) 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); + } /* If the cache is not released by a cancel or a done, free it now. */ if (ob->sculpt->cache) { @@ -3118,9 +3483,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) else { ob->mode |= mode_flag; - if (me->mloopcol == NULL) { - make_vertexcol(ob); - } + ED_mesh_color_ensure(me, NULL); if (vp == NULL) vp = scene->toolsettings->vpaint = new_vpaint(0); @@ -3229,8 +3592,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f if (me == NULL || me->totpoly == 0) return false; - if (me->mloopcol == NULL) - make_vertexcol(ob); + ED_mesh_color_ensure(me, NULL); if (me->mloopcol == NULL) return false; @@ -3295,8 +3657,7 @@ static void do_vpaint_brush_calc_average_color_cb_ex( StrokeCache *cache = ss->cache; uint *lcol = data->lcol; char *col; - - 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_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n; accum->len = 0; @@ -3315,7 +3676,7 @@ static void do_vpaint_brush_calc_average_color_cb_ex( if (BKE_brush_curve_strength(data->brush, test.dist, cache->radius) > 0.0) { /* If the vertex is selected for painting. */ const MVert *mv = &data->me->mvert[v_index]; - if (!use_face_sel || mv->flag & SELECT) { + 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++) { @@ -3367,12 +3728,13 @@ static void do_vpaint_brush_draw_task_cb_ex( 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_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; sculpt_brush_test_init(ss, &test); - /* For each vertex*/ + /* For each vertex */ PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3386,7 +3748,7 @@ static void do_vpaint_brush_draw_task_cb_ex( const MVert *mv = &data->me->mvert[v_index]; /* If the vertex is selected for painting. */ - if (!use_face_sel || mv->flag & SELECT) { + 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. */ const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; @@ -3416,9 +3778,16 @@ static void do_vpaint_brush_draw_task_cb_ex( } color_orig = ss->mode.vpaint.previous_color[l_index]; } - const float final_alpha = + float final_alpha = 255 * brush_fade * brush_strength * view_dot * tex_alpha * brush_alpha_pressure * grid_alpha; + + if (brush->flag & BRUSH_ACCUMULATE) { + float mask_accum = ss->mode.vpaint.previous_accum[l_index]; + final_alpha = min_ff(final_alpha + mask_accum, 255.0f); + ss->mode.vpaint.previous_accum[l_index] = final_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, @@ -3447,6 +3816,7 @@ static void do_vpaint_brush_blur_task_cb_ex( 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_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; @@ -3459,7 +3829,7 @@ static void do_vpaint_brush_blur_task_cb_ex( /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop cooresponds to the current grid. - Otherwise, take the current vert. */ + * Otherwise, take the current vert. */ const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; const MVert *mv = &data->me->mvert[v_index]; @@ -3469,7 +3839,7 @@ static void do_vpaint_brush_blur_task_cb_ex( const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius); /* If the vertex is selected for painting. */ - if (!use_face_sel || mv->flag & SELECT) { + if (!use_vert_sel || mv->flag & SELECT) { /* Get the average poly color */ uint color_final = 0; int total_hit_loops = 0; @@ -3547,6 +3917,7 @@ static void do_vpaint_brush_smear_task_cb_ex( 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_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); @@ -3562,15 +3933,15 @@ static void do_vpaint_brush_smear_task_cb_ex( /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop cooresponds to the current grid. - Otherwise, take the current vert. */ + * Otherwise, take the current vert. */ const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; const MVert *mv_curr = &data->me->mvert[v_index]; /* if the vertex is selected for painting. */ - if (!use_face_sel || mv_curr->flag & SELECT) { + 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. */ + * (ie splash prevention factor), and only paint front facing verts. */ const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; if (view_dot > 0.0f) { const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c new file mode 100644 index 00000000000..5325d660f91 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -0,0 +1,111 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/sculpt_paint/paint_vertex_color_ops.c + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_mesh.h" +#include "BKE_deform.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_mesh.h" + +#include "paint_intern.h" /* own include */ + + +static int vertex_weight_paint_mode_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_mesh_from_object(ob); + return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) && + (me && me->totpoly && me->dvert); +} + +static bool vertex_paint_from_weight(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me; + const MPoly *mp; + int vgroup_active; + + if (((me = BKE_mesh_from_object(ob)) == NULL || + (ED_mesh_color_ensure(me, NULL)) == false)) + { + return false; + } + + /* TODO: respect selection. */ + mp = me->mpoly; + vgroup_active = ob->actdef - 1; + for (int i = 0; i < me->totpoly; i++, mp++) { + MLoopCol *lcol = &me->mloopcol[mp->loopstart]; + uint j = 0; + do{ + uint vidx = me->mloop[mp->loopstart + j].v; + const float weight = defvert_find_weight(&me->dvert[vidx], vgroup_active); + const uchar grayscale = weight * 255; + lcol->r = grayscale; + lcol->b = grayscale; + lcol->g = grayscale; + lcol++; + j++; + } while (j < mp->totloop); + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return true; +} + +static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) +{ + if (vertex_paint_from_weight(C)) { + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Color from Weight"; + ot->idname = "PAINT_OT_vertex_color_from_weight"; + ot->description = "Converts active weight into greyscale vertex colors"; + + /* api callback */ + ot->exec = vertex_paint_from_weight_exec; + ot->poll = vertex_weight_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* TODO: invert, alpha */ +} diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 850d4631311..3d1c7e94d17 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -192,14 +192,14 @@ typedef struct { bool original; } SculptSearchSphereData; -void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test); +void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test); bool sculpt_brush_test(SculptBrushTest *test, const float co[3]); bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3]); bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3]); bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]); bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v); float tex_strength( - SculptSession *ss, struct Brush *br, + struct SculptSession *ss, struct Brush *br, const float point[3], const float len, const short vno[3], |