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:
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_vertex.c')
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c3639
1 files changed, 1685 insertions, 1954 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 991025a4d5d..f8509a3824c 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -27,19 +27,20 @@
/** \file blender/editors/sculpt_paint/paint_vertex.c
* \ingroup edsculpt
+ *
+ * Used for vertex color & weight paint and mode switching.
+ *
+ * \note This file is already big,
+ * use `paint_vertex_color_ops.c` & `paint_vertex_weight_ops.c` for general purpose operators.
*/
#include "MEM_guardedalloc.h"
-#include "BLI_blenlib.h"
+#include "BLI_listbase.h"
+#include "BLI_rect.h"
#include "BLI_math.h"
#include "BLI_array_utils.h"
-#include "BLI_bitmap.h"
-#include "BLI_stack.h"
-
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-#include "IMB_colormanagement.h"
+#include "BLI_task.h"
#include "DNA_armature_types.h"
#include "DNA_mesh_types.h"
@@ -50,39 +51,119 @@
#include "RNA_access.h"
#include "RNA_define.h"
-#include "RNA_enum_types.h"
-#include "BKE_DerivedMesh.h"
-#include "BKE_action.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
-#include "BKE_modifier.h"
#include "BKE_object_deform.h"
#include "BKE_paint.h"
#include "BKE_report.h"
-#include "BKE_colortools.h"
+#include "BKE_subsurf.h"
#include "WM_api.h"
#include "WM_types.h"
-#include "ED_armature.h"
#include "ED_object.h"
#include "ED_mesh.h"
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "bmesh.h"
+#include "BKE_ccg.h"
+
+#include "sculpt_intern.h"
#include "paint_intern.h" /* own include */
-/* small structure to defer applying weight-paint results */
-struct WPaintDefer {
- int index;
- float alpha, weight;
+/* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */
+struct VPaintAverageAccum {
+ uint len;
+ uint value[3];
+};
+
+struct WPaintAverageAccum {
+ 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;
};
+
+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;
+ }
+
+ 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 */
+ }
+
+ 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)
+{
+ 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);
+}
+
+static bool brush_use_accumulate(const Brush *brush)
+{
+ return (brush->flag & BRUSH_ACCUMULATE) != 0 || brush->vertexpaint_tool == PAINT_BLEND_SMEAR;
+}
+
+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;
+}
+
/* check if we can do partial updates and have them draw realtime
* (without rebuilding the 'derivedFinal') */
static bool vertex_paint_use_fast_update_check(Object *ob)
@@ -124,7 +205,7 @@ int vertex_paint_mode_poll(bContext *C)
int vertex_paint_poll(bContext *C)
{
- if (vertex_paint_mode_poll(C) &&
+ if (vertex_paint_mode_poll(C) &&
BKE_paint_brush(&CTX_data_tool_settings(C)->vpaint->paint))
{
ScrArea *sa = CTX_wm_area(C);
@@ -163,639 +244,47 @@ int weight_paint_poll(bContext *C)
return 0;
}
-static VPaint *new_vpaint(int wpaint)
+static VPaint *new_vpaint(void)
{
VPaint *vp = MEM_callocN(sizeof(VPaint), "VPaint");
-
- vp->flag = (wpaint) ? 0 : VP_SPRAY;
+
vp->paint.flags |= PAINT_SHOW_BRUSH;
return vp;
}
-static int *get_indexarray(Mesh *me)
-{
- return MEM_mallocN(sizeof(int) * (me->totpoly + 1), "vertexpaint");
-}
-
-unsigned int vpaint_get_current_col(Scene *scene, VPaint *vp)
+uint vpaint_get_current_col(Scene *scene, VPaint *vp)
{
Brush *brush = BKE_paint_brush(&vp->paint);
- unsigned char col[4];
+ uchar col[4];
rgb_float_to_uchar(col, BKE_brush_color_get(scene, brush));
col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */
- return *(unsigned int *)col;
-}
-
-static void do_shared_vertexcol(Mesh *me, bool *mlooptag)
-{
- const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
- MPoly *mp;
- int (*scol)[4];
- int i, j;
- bool has_shared = false;
-
- /* if no mloopcol: do not do */
- /* if mtexpoly: only the involved faces, otherwise all */
-
- if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) return;
-
- scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol");
-
- for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
- if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
- MLoop *ml = me->mloop + mp->loopstart;
- MLoopCol *lcol = me->mloopcol + mp->loopstart;
- for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
- scol[ml->v][0] += lcol->r;
- scol[ml->v][1] += lcol->g;
- scol[ml->v][2] += lcol->b;
- scol[ml->v][3] += 1;
- has_shared = 1;
- }
- }
- }
-
- if (has_shared) {
- for (i = 0; i < me->totvert; i++) {
- if (scol[i][3] != 0) {
- scol[i][0] = divide_round_i(scol[i][0], scol[i][3]);
- scol[i][1] = divide_round_i(scol[i][1], scol[i][3]);
- scol[i][2] = divide_round_i(scol[i][2], scol[i][3]);
- }
- }
-
- for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
- if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
- MLoop *ml = me->mloop + mp->loopstart;
- MLoopCol *lcol = me->mloopcol + mp->loopstart;
- for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
- if (mlooptag[mp->loopstart + j]) {
- lcol->r = scol[ml->v][0];
- lcol->g = scol[ml->v][1];
- lcol->b = scol[ml->v][2];
- }
- }
- }
- }
- }
-
- 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)
-{
- bDeformGroup *defgroup = BLI_findlink(&ob->defbase, vgroup_active);
-
- if (defgroup) {
- int mirrdef;
- char name_flip[MAXBONENAME];
-
- BKE_deform_flip_side_name(name_flip, defgroup->name, false);
- mirrdef = defgroup_name_index(ob, name_flip);
- if (mirrdef == -1) {
- if (BKE_defgroup_new(ob, name_flip)) {
- mirrdef = BLI_listbase_count(&ob->defbase) - 1;
- }
- }
-
- /* curdef should never be NULL unless this is
- * a lamp and BKE_object_defgroup_add_name fails */
- return mirrdef;
- }
-
- return -1;
-}
-
-static void free_vpaint_prev(VPaint *vp)
-{
- if (vp->vpaint_prev) {
- MEM_freeN(vp->vpaint_prev);
- vp->vpaint_prev = NULL;
- vp->tot = 0;
- }
-}
-
-static void free_wpaint_prev(VPaint *vp)
-{
- if (vp->wpaint_prev) {
- BKE_defvert_array_free(vp->wpaint_prev, vp->tot);
- vp->wpaint_prev = NULL;
- vp->tot = 0;
- }
-}
-
-static void copy_vpaint_prev(VPaint *vp, unsigned int *lcol, int tot)
-{
- free_vpaint_prev(vp);
-
- vp->tot = tot;
-
- if (lcol == NULL || tot == 0) return;
-
- vp->vpaint_prev = MEM_mallocN(sizeof(int) * tot, "vpaint_prev");
- memcpy(vp->vpaint_prev, lcol, sizeof(int) * tot);
-
-}
-
-static void copy_wpaint_prev(VPaint *wp, MDeformVert *dverts, int dcount)
-{
- free_wpaint_prev(wp);
-
- if (dverts && dcount) {
-
- wp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
- wp->tot = dcount;
- BKE_defvert_array_copy(wp->wpaint_prev, dverts, dcount);
- }
-}
-
-bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
-{
- Mesh *me;
- MPoly *mp;
- int i, j;
- bool selected;
-
- if (((me = BKE_mesh_from_object(ob)) == NULL) ||
- (me->mloopcol == NULL && (make_vertexcol(ob) == false)))
- {
- return false;
- }
-
- selected = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
-
- mp = me->mpoly;
- for (i = 0; i < me->totpoly; i++, mp++) {
- MLoopCol *lcol = me->mloopcol + mp->loopstart;
-
- if (selected && !(mp->flag & ME_FACE_SEL))
- continue;
-
- for (j = 0; j < mp->totloop; j++, lcol++) {
- *(int *)lcol = paintcol;
- }
- }
-
- /* remove stale me->mcol, will be added later */
- BKE_mesh_tessface_clear(me);
-
- DAG_id_tag_update(&me->id, 0);
-
- return true;
-}
-
-
-/* fills in the selected faces with the current weight and vertex group */
-bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
-{
- Mesh *me = ob->data;
- MPoly *mp;
- MDeformWeight *dw, *dw_prev;
- int vgroup_active, vgroup_mirror = -1;
- unsigned int index;
- const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
-
- /* mutually exclusive, could be made into a */
- const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me);
-
- if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) {
- return false;
- }
-
- vgroup_active = ob->actdef - 1;
-
- /* if mirror painting, find the other group */
- if (me->editflag & ME_EDIT_MIRROR_X) {
- vgroup_mirror = wpaint_mirror_vgroup_ensure(ob, vgroup_active);
- }
-
- copy_wpaint_prev(wp, me->dvert, me->totvert);
-
- for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
- unsigned int fidx = mp->totloop - 1;
-
- if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
- continue;
- }
-
- do {
- unsigned int vidx = me->mloop[mp->loopstart + fidx].v;
-
- if (!me->dvert[vidx].flag) {
- if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
- continue;
- }
-
- dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
- if (dw) {
- dw_prev = defvert_verify_index(wp->wpaint_prev + vidx, vgroup_active);
- dw_prev->weight = dw->weight; /* set the undo weight */
- dw->weight = paintweight;
-
- if (me->editflag & ME_EDIT_MIRROR_X) { /* x mirror painting */
- int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
- if (j >= 0) {
- /* copy, not paint again */
- if (vgroup_mirror != -1) {
- dw = defvert_verify_index(me->dvert + j, vgroup_mirror);
- dw_prev = defvert_verify_index(wp->wpaint_prev + j, vgroup_mirror);
- }
- else {
- dw = defvert_verify_index(me->dvert + j, vgroup_active);
- dw_prev = defvert_verify_index(wp->wpaint_prev + j, vgroup_active);
- }
- dw_prev->weight = dw->weight; /* set the undo weight */
- dw->weight = paintweight;
- }
- }
- }
- me->dvert[vidx].flag = 1;
- }
-
- } while (fidx--);
- }
-
- {
- MDeformVert *dv = me->dvert;
- for (index = me->totvert; index != 0; index--, dv++) {
- dv->flag = 0;
- }
- }
-
- copy_wpaint_prev(wp, NULL, 0);
-
- DAG_id_tag_update(&me->id, 0);
-
- return true;
-}
-
-bool ED_vpaint_smooth(Object *ob)
-{
- Mesh *me;
- MPoly *mp;
-
- int i, j;
-
- bool *mlooptag;
- bool selected;
-
- if (((me = BKE_mesh_from_object(ob)) == NULL) ||
- (me->mloopcol == NULL && (make_vertexcol(ob) == false)))
- {
- return false;
- }
-
- selected = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
-
- mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
-
- /* simply tag loops of selected faces */
- mp = me->mpoly;
- for (i = 0; i < me->totpoly; i++, mp++) {
- MLoop *ml = me->mloop + mp->loopstart;
- int ml_index = mp->loopstart;
-
- if (selected && !(mp->flag & ME_FACE_SEL))
- continue;
-
- for (j = 0; j < mp->totloop; j++, ml_index++, ml++) {
- mlooptag[ml_index] = true;
- }
- }
-
- /* remove stale me->mcol, will be added later */
- BKE_mesh_tessface_clear(me);
-
- do_shared_vertexcol(me, mlooptag);
-
- MEM_freeN(mlooptag);
-
- DAG_id_tag_update(&me->id, 0);
-
- return true;
-}
-
-/**
- * Apply callback to each vertex of the active vertex color layer.
- */
-bool ED_vpaint_color_transform(
- struct Object *ob,
- VPaintTransform_Callback vpaint_tx_fn,
- const void *user_data)
-{
- Mesh *me;
- const MPoly *mp;
-
- if (((me = BKE_mesh_from_object(ob)) == NULL) ||
- (me->mloopcol == NULL && (make_vertexcol(ob) == false)))
- {
- return false;
- }
-
- const bool do_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
- mp = me->mpoly;
-
- for (int i = 0; i < me->totpoly; i++, mp++) {
- MLoopCol *lcol = &me->mloopcol[mp->loopstart];
-
- if (do_face_sel && !(mp->flag & ME_FACE_SEL)) {
- continue;
- }
-
- for (int j = 0; j < mp->totloop; j++, lcol++) {
- float col[3];
- rgb_uchar_to_float(col, &lcol->r);
-
- vpaint_tx_fn(col, user_data, col);
-
- rgb_float_to_uchar(&lcol->r, col);
- }
- }
-
- /* remove stale me->mcol, will be added later */
- BKE_mesh_tessface_clear(me);
-
- DAG_id_tag_update(&me->id, 0);
-
- return true;
-}
-
-/* XXX: should be re-implemented as a vertex/weight paint 'color correct' operator */
-#if 0
-void vpaint_dogamma(Scene *scene)
-{
- VPaint *vp = scene->toolsettings->vpaint;
- Mesh *me;
- Object *ob;
- float igam, fac;
- int a, temp;
- unsigned char *cp, gamtab[256];
-
- ob = OBACT;
- me = BKE_mesh_from_object(ob);
-
- if (!(ob->mode & OB_MODE_VERTEX_PAINT)) return;
- if (me == 0 || me->mcol == 0 || me->totface == 0) return;
-
- igam = 1.0 / vp->gamma;
- for (a = 0; a < 256; a++) {
-
- fac = ((float)a) / 255.0;
- fac = vp->mul * pow(fac, igam);
-
- temp = 255.9 * fac;
-
- if (temp <= 0) gamtab[a] = 0;
- else if (temp >= 255) gamtab[a] = 255;
- else gamtab[a] = temp;
- }
-
- a = 4 * me->totface;
- cp = (unsigned char *)me->mcol;
- while (a--) {
-
- cp[1] = gamtab[cp[1]];
- cp[2] = gamtab[cp[2]];
- cp[3] = gamtab[cp[3]];
-
- cp += 4;
- }
-}
-#endif
-
-BLI_INLINE unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac)
-{
- unsigned char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col = 0;
-
- if (fac == 0) {
- return col1;
- }
-
- if (fac >= 255) {
- return col2;
- }
-
- mfac = 255 - fac;
-
- cp1 = (unsigned char *)&col1;
- cp2 = (unsigned char *)&col2;
- cp = (unsigned char *)&col;
-
- 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;
-
- return col;
-}
-
-BLI_INLINE unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac)
-{
- unsigned char *cp1, *cp2, *cp;
- int temp;
- unsigned int col = 0;
-
- if (fac == 0) {
- return col1;
- }
-
- cp1 = (unsigned char *)&col1;
- cp2 = (unsigned char *)&col2;
- cp = (unsigned char *)&col;
-
- temp = cp1[0] + divide_round_i((fac * cp2[0]), 255);
- cp[0] = (temp > 254) ? 255 : temp;
- temp = cp1[1] + divide_round_i((fac * cp2[1]), 255);
- 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;
-
- return col;
-}
-
-BLI_INLINE unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac)
-{
- unsigned char *cp1, *cp2, *cp;
- int temp;
- unsigned int col = 0;
-
- if (fac == 0) {
- return col1;
- }
-
- cp1 = (unsigned char *)&col1;
- cp2 = (unsigned char *)&col2;
- cp = (unsigned char *)&col;
-
- temp = cp1[0] - divide_round_i((fac * cp2[0]), 255);
- cp[0] = (temp < 0) ? 0 : temp;
- temp = cp1[1] - divide_round_i((fac * cp2[1]), 255);
- 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;
-
- return col;
-}
-
-BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
-{
- unsigned char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col = 0;
-
- if (fac == 0) {
- return col1;
- }
-
- mfac = 255 - fac;
-
- cp1 = (unsigned char *)&col1;
- cp2 = (unsigned char *)&col2;
- cp = (unsigned char *)&col;
-
- /* first mul, then blend the 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;
-
- return col;
-}
-
-BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int fac)
-{
- unsigned char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col = 0;
-
- if (fac == 0) {
- return col1;
- }
- else if (fac >= 255) {
- return col2;
- }
-
- mfac = 255 - fac;
-
- cp1 = (unsigned char *)&col1;
- cp2 = (unsigned char *)&col2;
- cp = (unsigned char *)&col;
-
- /* See if are lighter, if so mix, else don't do anything.
- * if the paint col is darker then the original, then ignore */
- if (IMB_colormanagement_get_luminance_byte(cp1) > IMB_colormanagement_get_luminance_byte(cp2)) {
- return col1;
- }
-
- 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;
-
- return col;
-}
-
-BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fac)
-{
- unsigned char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col = 0;
-
- if (fac == 0) {
- return col1;
- }
- else if (fac >= 255) {
- return col2;
- }
-
- mfac = 255 - fac;
-
- cp1 = (unsigned char *)&col1;
- cp2 = (unsigned char *)&col2;
- cp = (unsigned char *)&col;
-
- /* See if were darker, if so mix, else don't do anything.
- * if the paint col is brighter then the original, then ignore */
- if (IMB_colormanagement_get_luminance_byte(cp1) < IMB_colormanagement_get_luminance_byte(cp2)) {
- return col1;
- }
-
- 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;
- return col;
-}
-
-/* wpaint has 'wpaint_blend_tool' */
-static unsigned int vpaint_blend_tool(const int tool, const unsigned int col,
- const unsigned int 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_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);
- default:
- BLI_assert(0);
- return 0;
- }
+ return *(uint *)col;
}
/* wpaint has 'wpaint_blend' */
-static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colorig, const
- unsigned int paintcol, const int alpha_i,
- /* pre scaled from [0-1] --> [0-255] */
- const int brush_alpha_value_i)
+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)
{
- Brush *brush = BKE_paint_brush(&vp->paint);
+ const Brush *brush = vp->paint.brush;
const int tool = brush->vertexpaint_tool;
- col = vpaint_blend_tool(tool, col, paintcol, alpha_i);
+ uint color_blend = ED_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) {
- unsigned int testcol, a;
+ /* if no accumulate, clip color adding with colorig & orig alpha */
+ if (!brush_use_accumulate(brush)) {
+ 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 = ED_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];
@@ -808,176 +297,51 @@ static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colo
}
}
- return col;
-}
-
-
-static int sample_backbuf_area(ViewContext *vc, int *indexar, int totpoly, int x, int y, float size)
-{
- struct ImBuf *ibuf;
- int a, tot = 0, index;
-
- /* brecht: disabled this because it obviously fails for
- * brushes with size > 64, why is this here? */
- /*if (size > 64.0) size = 64.0;*/
-
- ibuf = ED_view3d_backbuf_read(vc, x - size, y - size, x + size, y + size);
- if (ibuf) {
- unsigned int *rt = ibuf->rect;
-
- memset(indexar, 0, sizeof(int) * (totpoly + 1));
-
- size = ibuf->x * ibuf->y;
- while (size--) {
-
- if (*rt) {
- index = *rt;
- if (index > 0 && index <= totpoly) {
- indexar[index] = 1;
- }
- }
-
- rt++;
- }
-
- for (a = 1; a <= totpoly; a++) {
- if (indexar[a]) {
- indexar[tot++] = a;
- }
- }
-
- IMB_freeImBuf(ibuf);
- }
-
- return tot;
-}
-
-/* whats _dl mean? */
-static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co[3],
- const float mval[2], const float brush_size_pressure, float rgba[4])
-{
- 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)
+ if ((brush->flag & BRUSH_LOCK_ALPHA) &&
+ !ELEM(tool, PAINT_BLEND_ALPHA_SUB, PAINT_BLEND_ALPHA_ADD))
{
- const float dist_sq = len_squared_v2v2(mval, co_ss);
-
- if (dist_sq <= SQUARE(brush_size_pressure)) {
- Brush *brush = BKE_paint_brush(&vp->paint);
- const float dist = sqrtf(dist_sq);
- float factor;
-
- if (brush->mtex.tex && rgba) {
- if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) {
- BKE_brush_sample_tex_3D(vc->scene, brush, co, rgba, 0, NULL);
- }
- else {
- 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, rgba, 0, NULL);
- }
- factor = rgba[3];
- }
- else {
- factor = 1.0f;
- }
- return factor * BKE_brush_curve_strength_clamped(brush, dist, brush_size_pressure);
- }
+ char *cp, *cc;
+ cp = (char *)&color_blend;
+ cc = (char *)&color_curr;
+ cp[3] = cc[3];
}
- if (rgba)
- zero_v4(rgba);
- return 0.0f;
-}
-
-static float calc_vp_alpha_col_dl(VPaint *vp, ViewContext *vc,
- float vpimat[3][3], const DMCoNo *v_co_no,
- const float mval[2],
- const float brush_size_pressure, const float brush_alpha_pressure, float rgba[4])
-{
- float strength = calc_vp_strength_col_dl(vp, vc, v_co_no->co, mval, brush_size_pressure, rgba);
-
- if (strength > 0.0f) {
- float alpha = brush_alpha_pressure * strength;
- if (vp->flag & VP_NORMALS) {
- float dvec[3];
-
- /* transpose ! */
- dvec[2] = dot_v3v3(vpimat[2], v_co_no->no);
- if (dvec[2] > 0.0f) {
- dvec[0] = dot_v3v3(vpimat[0], v_co_no->no);
- dvec[1] = dot_v3v3(vpimat[1], v_co_no->no);
-
- alpha *= dvec[2] / len_v3(dvec);
- }
- else {
- return 0.0f;
- }
- }
-
- return alpha;
- }
-
- return 0.0f;
-}
-
-
-BLI_INLINE float wval_blend(const float weight, const float paintval, const float alpha)
-{
- const float talpha = min_ff(alpha, 1.0f); /* blending with values over 1 doesn't make sense */
- return (paintval * talpha) + (weight * (1.0f - talpha));
-}
-BLI_INLINE float wval_add(const float weight, const float paintval, const float alpha)
-{
- return weight + (paintval * alpha);
-}
-BLI_INLINE float wval_sub(const float weight, const float paintval, const float alpha)
-{
- return weight - (paintval * alpha);
-}
-BLI_INLINE float wval_mul(const float weight, const float paintval, const float alpha)
-{ /* first mul, then blend the fac */
- return ((1.0f - alpha) + (alpha * paintval)) * weight;
+ return color_blend;
}
-BLI_INLINE float wval_lighten(const float weight, const float paintval, const float alpha)
-{
- return (weight < paintval) ? wval_blend(weight, paintval, alpha) : weight;
-}
-BLI_INLINE float wval_darken(const float weight, const float paintval, const float alpha)
-{
- return (weight > paintval) ? wval_blend(weight, paintval, alpha) : weight;
-}
-
-/* vpaint has 'vpaint_blend_tool' */
-/* result is not clamped from [0-1] */
-static float wpaint_blend_tool(const int tool,
- /* dw->weight */
- const float weight,
- const float paintval, const float alpha)
+static void tex_color_alpha(
+ VPaint *vp, const ViewContext *vc, const float co[3],
+ float r_rgba[4])
{
- switch (tool) {
- case PAINT_BLEND_MIX:
- case PAINT_BLEND_BLUR: return wval_blend(weight, paintval, alpha);
- case PAINT_BLEND_ADD: return wval_add(weight, paintval, alpha);
- case PAINT_BLEND_SUB: return wval_sub(weight, paintval, alpha);
- case PAINT_BLEND_MUL: return wval_mul(weight, paintval, alpha);
- case PAINT_BLEND_LIGHTEN: return wval_lighten(weight, paintval, alpha);
- case PAINT_BLEND_DARKEN: return wval_darken(weight, paintval, alpha);
- default:
- BLI_assert(0);
- return 0.0f;
+ 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(VPaint *wp, float weight, float weight_prev,
- const float alpha, float paintval,
- const float brush_alpha_value,
- const short do_flip)
+static float wpaint_blend(
+ const VPaint *wp, float weight,
+ const float alpha, float paintval,
+ const float UNUSED(brush_alpha_value),
+ const short do_flip)
{
- Brush *brush = BKE_paint_brush(&wp->paint);
+ const Brush *brush = wp->paint.brush;
int tool = brush->vertexpaint_tool;
if (do_flip) {
@@ -994,257 +358,30 @@ static float wpaint_blend(VPaint *wp, float weight, float weight_prev,
tool = PAINT_BLEND_LIGHTEN; break;
}
}
-
- weight = wpaint_blend_tool(tool, weight, paintval, alpha);
- CLAMP(weight, 0.0f, 1.0f);
-
- /* if no spray, clip result with orig weight & orig alpha */
- if ((wp->flag & VP_SPRAY) == 0) {
- float testw = wpaint_blend_tool(tool, weight_prev, paintval, brush_alpha_value);
+ weight = ED_wpaint_blend_tool(tool, weight, paintval, alpha);
- CLAMP(testw, 0.0f, 1.0f);
- if (testw < weight_prev) {
- if (weight < testw) weight = testw;
- else if (weight > weight_prev) weight = weight_prev;
- }
- else {
- if (weight > testw) weight = testw;
- else if (weight < weight_prev) weight = weight_prev;
- }
- }
+ CLAMP(weight, 0.0f, 1.0f);
return weight;
}
-/* ----------------------------------------------------- */
-
-
-/* sets wp->weight to the closest weight value to vertex */
-/* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */
-static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- ViewContext vc;
- Mesh *me;
- bool changed = false;
-
- view3d_set_viewcontext(C, &vc);
- me = BKE_mesh_from_object(vc.obact);
-
- if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) {
- const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
- int v_idx_best = -1;
- unsigned int index;
-
- view3d_operator_needs_opengl(C);
- ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
-
- if (use_vert_sel) {
- if (ED_mesh_pick_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
- v_idx_best = index;
- }
- }
- else {
- if (ED_mesh_pick_face_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
- v_idx_best = index;
- }
- else if (ED_mesh_pick_face(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
- /* this relies on knowning the internal worksings of ED_mesh_pick_face_vert() */
- BKE_report(op->reports, RPT_WARNING, "The modifier used does not support deformed locations");
- }
- }
-
- if (v_idx_best != -1) { /* should always be valid */
- ToolSettings *ts = vc.scene->toolsettings;
- Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
- const int vgroup_active = vc.obact->actdef - 1;
- float vgroup_weight = defvert_find_weight(&me->dvert[v_idx_best], vgroup_active);
-
- /* use combined weight in multipaint mode, since that's what is displayed to the user in the colors */
- if (ts->multipaint) {
- int defbase_tot_sel;
- const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
- bool *defbase_sel = BKE_object_defgroup_selected_get(vc.obact, defbase_tot, &defbase_tot_sel);
-
- if (defbase_tot_sel > 1) {
- if (me->editflag & ME_EDIT_MIRROR_X) {
- BKE_object_defgroup_mirror_selection(
- vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
- }
-
- vgroup_weight = BKE_defvert_multipaint_collective_weight(
- &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize);
-
- /* if autonormalize is enabled, but weights are not normalized, the value can exceed 1 */
- CLAMP(vgroup_weight, 0.0f, 1.0f);
- }
-
- MEM_freeN(defbase_sel);
- }
-
- BKE_brush_weight_set(vc.scene, brush, vgroup_weight);
- changed = true;
- }
- }
-
- if (changed) {
- /* not really correct since the brush didnt change, but redraws the toolbar */
- WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); /* ts->wpaint->paint.brush */
-
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-void PAINT_OT_weight_sample(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Weight Paint Sample Weight";
- ot->idname = "PAINT_OT_weight_sample";
- ot->description = "Use the mouse to sample a weight in the 3D view";
-
- /* api callbacks */
- ot->invoke = weight_sample_invoke;
- ot->poll = weight_paint_mode_poll;
-
- /* flags */
- ot->flag = OPTYPE_UNDO;
-}
-
-/* samples cursor location, and gives menu with vertex groups to activate */
-static bool weight_paint_sample_enum_itemf__helper(const MDeformVert *dvert, const int defbase_tot, int *groups)
-{
- /* this func fills in used vgroup's */
- bool found = false;
- int i = dvert->totweight;
- MDeformWeight *dw;
- for (dw = dvert->dw; i > 0; dw++, i--) {
- if (dw->def_nr < defbase_tot) {
- groups[dw->def_nr] = true;
- found = true;
- }
- }
- return found;
-}
-static EnumPropertyItem *weight_paint_sample_enum_itemf(
- bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
-{
- if (C) {
- wmWindow *win = CTX_wm_window(C);
- if (win && win->eventstate) {
- ViewContext vc;
- Mesh *me;
-
- view3d_set_viewcontext(C, &vc);
- me = BKE_mesh_from_object(vc.obact);
-
- if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) {
- const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
- const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
- int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups");
- bool found = false;
- unsigned int index;
-
- const int mval[2] = {
- win->eventstate->x - vc.ar->winrct.xmin,
- win->eventstate->y - vc.ar->winrct.ymin,
- };
-
- view3d_operator_needs_opengl(C);
- ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
-
- if (use_vert_sel) {
- if (ED_mesh_pick_vert(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
- MDeformVert *dvert = &me->dvert[index];
- found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
- }
- }
- else {
- if (ED_mesh_pick_face(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
- MPoly *mp = &me->mpoly[index];
- unsigned int fidx = mp->totloop - 1;
-
- do {
- MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
- found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
- } while (fidx--);
- }
- }
-
- if (found == false) {
- MEM_freeN(groups);
- }
- else {
- EnumPropertyItem *item = NULL, item_tmp = {0};
- int totitem = 0;
- int i = 0;
- bDeformGroup *dg;
- for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) {
- if (groups[i]) {
- item_tmp.identifier = item_tmp.name = dg->name;
- item_tmp.value = i;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- }
- }
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- MEM_freeN(groups);
- return item;
- }
- }
- }
- }
-
- return DummyRNA_NULL_items;
-}
-
-static int weight_sample_group_exec(bContext *C, wmOperator *op)
+static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
{
- int type = RNA_enum_get(op->ptr, "group");
- ViewContext vc;
- view3d_set_viewcontext(C, &vc);
-
- BLI_assert(type + 1 >= 0);
- vc.obact->actdef = type + 1;
-
- DAG_id_tag_update(&vc.obact->id, OB_RECALC_DATA);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact);
- return OPERATOR_FINISHED;
+ if (newval < oldval)
+ return MIN2(newval, curval);
+ else if (newval > oldval)
+ return MAX2(newval, curval);
+ else
+ return newval;
}
-/* TODO, we could make this a menu into OBJECT_OT_vertex_group_set_active rather than its own operator */
-void PAINT_OT_weight_sample_group(wmOperatorType *ot)
-{
- PropertyRNA *prop = NULL;
-
- /* identifiers */
- ot->name = "Weight Paint Sample Group";
- ot->idname = "PAINT_OT_weight_sample_group";
- ot->description = "Select one of the vertex groups available under current mouse position";
-
- /* api callbacks */
- ot->exec = weight_sample_group_exec;
- ot->invoke = WM_menu_invoke;
- ot->poll = weight_paint_mode_poll;
-
- /* flags */
- ot->flag = OPTYPE_UNDO;
-
- /* keyingset to use (dynamic enum) */
- prop = RNA_def_enum(ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
- RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf);
- RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
- ot->prop = prop;
-}
+/* ----------------------------------------------------- */
static void do_weight_paint_normalize_all(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap)
{
float sum = 0.0f, fac;
- unsigned int i, tot = 0;
+ uint i, tot = 0;
MDeformWeight *dw;
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
@@ -1290,7 +427,7 @@ static bool do_weight_paint_normalize_all_locked(
float sum = 0.0f, fac;
float sum_unlock = 0.0f;
float lock_weight = 0.0f;
- unsigned int i, tot = 0;
+ uint i, tot = 0;
MDeformWeight *dw;
if (lock_flags == NULL) {
@@ -1390,8 +527,9 @@ static void do_weight_paint_normalize_all_locked_try_active(
}
#if 0 /* UNUSED */
-static bool has_unselected_unlocked_bone_group(int defbase_tot, bool *defbase_sel, int selected,
- const bool *lock_flags, const bool *vgroup_validmap)
+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) {
@@ -1475,7 +613,7 @@ static void multipaint_apply_change(MDeformVert *dvert, const int defbase_tot, f
* Variables stored both for 'active' and 'mirror' sides.
*/
struct WeightPaintGroupData {
- /** index of active group or its mirror
+ /** index of active group or its mirror
*
* - 'active' is always `ob->actdef`.
* - 'mirror' is -1 when 'ME_EDIT_MIRROR_X' flag id disabled,
@@ -1504,13 +642,15 @@ typedef struct WeightPaintInfo {
struct WeightPaintGroupData active, mirror;
- const bool *lock_flags; /* boolean array for locked bones,
- * length of defbase_tot */
- const bool *defbase_sel; /* boolean array for selected bones,
- * length of defbase_tot, cant be const because of how its passed */
-
- const bool *vgroup_validmap; /* same as WeightPaintData.vgroup_validmap,
- * only added here for convenience */
+ /* 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;
@@ -1521,16 +661,16 @@ typedef struct WeightPaintInfo {
static void do_weight_paint_vertex_single(
/* vars which remain the same for every vert */
- VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
+ const VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
/* vars which change on each stroke */
- const unsigned int index, float alpha, float paintweight
- )
+ 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, *dw_prev;
+
+ MDeformWeight *dw;
+ float weight_prev;
/* mirror vars */
int index_mirr;
@@ -1539,20 +679,6 @@ static void do_weight_paint_vertex_single(
MDeformVert *dv_mirr;
MDeformWeight *dw_mirr;
- if (wp->flag & VP_ONLYVGROUP) {
- dw = defvert_find_index(dv, wpi->active.index);
- dw_prev = defvert_find_index(wp->wpaint_prev + index, wpi->active.index);
- }
- else {
- dw = defvert_verify_index(dv, wpi->active.index);
- dw_prev = defvert_verify_index(wp->wpaint_prev + index, wpi->active.index);
- }
-
- if (dw == NULL || dw_prev == NULL) {
- return;
- }
-
-
/* 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);
@@ -1568,11 +694,21 @@ static void do_weight_paint_vertex_single(
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_ONLYVGROUP) {
+ if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr);
if (dw_mirr == NULL) {
@@ -1602,12 +738,28 @@ static void do_weight_paint_vertex_single(
dw_mirr = NULL;
}
+ if (!brush_use_accumulate(wp->paint.brush)) {
+ 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 */
{
- dw->weight = wpaint_blend(wp, dw->weight, dw_prev->weight, alpha, paintweight,
- wpi->brush_alpha_value, wpi->do_flip);
+ 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 */
@@ -1666,13 +818,12 @@ static void do_weight_paint_vertex_single(
static void do_weight_paint_vertex_multi(
/* vars which remain the same for every vert */
- VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
+ const VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
/* vars which change on each stroke */
- const unsigned int index, float alpha, float paintweight)
+ const uint index, float alpha, float paintweight)
{
Mesh *me = ob->data;
MDeformVert *dv = &me->dvert[index];
- MDeformVert *dv_prev = &wp->wpaint_prev[index];
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
/* mirror vars */
@@ -1680,7 +831,7 @@ static void do_weight_paint_vertex_multi(
MDeformVert *dv_mirr = NULL;
/* weights */
- float oldw, curw, neww, change, curw_mirr, change_mirr;
+ 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) {
@@ -1689,11 +840,12 @@ static void do_weight_paint_vertex_multi(
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 */
- oldw = BKE_defvert_multipaint_collective_weight(
- dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
curw = BKE_defvert_multipaint_collective_weight(
dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize);
@@ -1702,7 +854,22 @@ static void do_weight_paint_vertex_multi(
return;
}
- neww = wpaint_blend(wp, curw, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
+ if (!brush_use_accumulate(wp->paint.brush)) {
+ 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;
@@ -1756,9 +923,9 @@ static void do_weight_paint_vertex_multi(
static void do_weight_paint_vertex(
/* vars which remain the same for every vert */
- VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
+ const VPaint *wp, Object *ob, const WeightPaintInfo *wpi,
/* vars which change on each stroke */
- const unsigned int index, float alpha, float paintweight)
+ const uint index, float alpha, float paintweight)
{
if (wpi->do_multipaint) {
do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight);
@@ -1768,13 +935,101 @@ static void do_weight_paint_vertex(
}
}
+
+/* Toggle operator for turning vertex paint mode on or off (copied from sculpt.c) */
+static void vertex_paint_init_session(Scene *scene, Object *ob)
+{
+ if (ob->sculpt == NULL) {
+ ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
+ BKE_sculpt_update_mesh_elements(scene, scene->toolsettings->sculpt, ob, 0, false);
+ }
+}
+
+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 {
+ 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(brush)) {
+ 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(brush)) {
+ 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;
+ }
+ }
+ }
+
+}
+
/* *************** set wpaint operator ****************** */
/**
* \note Keep in sync with #vpaint_mode_toggle_exec
*/
static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
-{
+{
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;
@@ -1804,13 +1059,21 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e');
ED_mesh_mirror_topo_table(NULL, NULL, 'e');
+ /* If the cache is not released by a cancel or a done, free it now. */
+ if (ob->sculpt->cache) {
+ sculpt_cache_free(ob->sculpt->cache);
+ ob->sculpt->cache = NULL;
+ }
+
+ BKE_sculptsession_free(ob);
+
paint_cursor_delete_textures();
}
else {
ob->mode |= mode_flag;
if (wp == NULL)
- wp = scene->toolsettings->wpaint = new_vpaint(1);
+ wp = scene->toolsettings->wpaint = new_vpaint();
paint_cursor_start(C, weight_paint_poll);
@@ -1819,8 +1082,14 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
/* weight paint specific */
ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 's');
ED_vgroup_sync_from_pose(ob);
+
+ /* Create vertex/weight paint mode session data */
+ if (ob->sculpt) {
+ BKE_sculptsession_free(ob);
+ }
+ vertex_paint_init_session(scene, ob);
}
-
+
/* Weightpaint works by overriding colors in mesh,
* so need to make sure we recalc on enter and
* exit (exit needs doing regardless because we
@@ -1848,43 +1117,28 @@ static int paint_poll_test(bContext *C)
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";
-
+
/* api callbacks */
ot->exec = wpaint_mode_toggle_exec;
ot->poll = paint_poll_test;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
}
/* ************ weight paint operator ********** */
-enum eWPaintFlag {
- WPAINT_ENSURE_MIRROR = (1 << 0),
-};
-
-struct WPaintVGroupIndex {
- int active;
- int mirror;
-};
-
struct WPaintData {
ViewContext vc;
- int *indexar;
+ struct NormalAnglePrecalc normal_angle_precalc;
struct WeightPaintGroupData active, mirror;
- void *vp_handle;
- DMCoNo *vertexcosnos;
-
- float wpimat[3][3];
-
/* variables for auto normalize */
const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */
const bool *lock_flags;
@@ -1894,110 +1148,138 @@ struct WPaintData {
int defbase_tot_sel; /* number of selected groups */
bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
- /* variables for blur */
- struct {
- MeshElemMap *vmap;
- int *vmap_mem;
- } blur_data;
-
- BLI_Stack *accumulate_stack; /* for reuse (WPaintDefer) */
-
int defbase_tot;
+
+ /* original weight values for use in blur/smear */
+ float *precomputed_weight;
+ bool precomputed_weight_ready;
};
-/* ensure we have data on wpaint start, add if needed */
-static bool wpaint_ensure_data(
- bContext *C, wmOperator *op,
- enum eWPaintFlag flag, struct WPaintVGroupIndex *vgroup_index)
+/* 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);
- Mesh *me = BKE_mesh_from_object(ob);
+ float mat[3][3];
+ float view_dir[3] = {0.0f, 0.0f, 1.0f};
+ int mode;
- if (vgroup_index) {
- vgroup_index->active = -1;
- vgroup_index->mirror = -1;
+ /* VW paint needs to allocate stroke cache before update is called. */
+ if (!ss->cache) {
+ cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
+ ss->cache = cache;
}
-
- if (scene->obedit) {
- return false;
+ else {
+ cache = ss->cache;
}
- if (me == NULL || me->totpoly == 0) {
- return false;
- }
+ /* Initial mouse location */
+ if (mouse)
+ copy_v2_v2(cache->initial_mouse, mouse);
+ else
+ zero_v2(cache->initial_mouse);
- /* if nothing was added yet, we make dverts and a vertex deform group */
- if (!me->dvert) {
- BKE_object_defgroup_data_create(&me->id);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
- }
+ 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;
- /* this happens on a Bone select, when no vgroup existed yet */
- if (ob->actdef <= 0) {
- Object *modob;
- if ((modob = modifiers_isDeformedByArmature(ob))) {
- Bone *actbone = ((bArmature *)modob->data)->act_bone;
- if (actbone) {
- bPoseChannel *pchan = BKE_pose_channel_find_name(modob->pose, actbone->name);
+ 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;
- if (pchan) {
- bDeformGroup *dg = defgroup_find_name(ob, pchan->name);
- if (dg == NULL) {
- dg = BKE_object_defgroup_add_name(ob, pchan->name); /* sets actdef */
- }
- else {
- int actdef = 1 + BLI_findindex(&ob->defbase, dg);
- BLI_assert(actdef >= 0);
- ob->actdef = actdef;
- }
- }
- }
- }
- }
- if (BLI_listbase_is_empty(&ob->defbase)) {
- BKE_object_defgroup_add(ob);
- }
+ /* cache projection matrix */
+ ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat);
- /* ensure we don't try paint onto an invalid group */
- if (ob->actdef <= 0) {
- BKE_report(op->reports, RPT_WARNING, "No active vertex group for painting, aborting");
- return false;
+ 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 vise versa. */
+ if (cache->first_time) {
+ RNA_float_get_array(ptr, "location", cache->true_location);
}
- if (vgroup_index) {
- vgroup_index->active = ob->actdef - 1;
+ 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, ePaintSculpt) || cache->first_time) {
+ cache->pressure = RNA_float_get(ptr, "pressure");
}
- if (flag & WPAINT_ENSURE_MIRROR) {
- if (me->editflag & ME_EDIT_MIRROR_X) {
- int mirror = wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1);
- if (vgroup_index) {
- vgroup_index->mirror = mirror;
- }
+ /* 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);
}
}
- return true;
+ if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, ePaintSculpt)) {
+ 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 UNUSED(mouse[2]))
+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;
- VPaint *wp = ts->wpaint;
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;
- const Brush *brush = BKE_paint_brush(&wp->paint);
-
- float mat[4][4], imat[4][4];
+ SculptSession *ss = ob->sculpt;
+ VPaint *vp = CTX_data_tool_settings(C)->wpaint;
- if (wpaint_ensure_data(C, op, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) {
+ if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) {
return false;
}
@@ -2048,6 +1330,8 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UN
wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData");
paint_stroke_set_mode_data(stroke, wpd);
view3d_set_viewcontext(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;
@@ -2092,63 +1376,580 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UN
wpd->mirror.lock = tmpflags;
}
- /* painting on subsurfs should give correct points too, this returns me->totvert amount */
- wpd->vp_handle = ED_vpaint_proj_handle_create(scene, ob, &wpd->vertexcosnos);
+ if (ELEM(vp->paint.brush->vertexpaint_tool, PAINT_BLEND_SMEAR, PAINT_BLEND_BLUR)) {
+ wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__);
+ }
- wpd->indexar = get_indexarray(me);
- copy_wpaint_prev(wp, me->dvert, me->totvert);
+ /* If not previously created, create vertex/weight paint mode session data */
+ vertex_paint_init_session(scene, ob);
+ vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
+ vertex_paint_init_session_data(ts, ob);
- if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
- BKE_mesh_vert_edge_vert_map_create(
- &wpd->blur_data.vmap, &wpd->blur_data.vmap_mem,
- me->medge, me->totvert, me->totedge);
+ 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);
+}
+
+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);
+}
+
+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);
+
+ 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 *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ const MDeformVert *dv = &data->me->dvert[n];
+
+ 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(brush))
+ return;
+
+ /* threaded loop over vertices */
+ SculptThreadedTaskData data = {
+ .C = C, .ob = ob, .wpd = wpd, .wpi = wpi, .me = me,
+ };
+
+ BLI_task_parallel_range_ex(
+ 0, me->totvert, &data, NULL, 0, do_wpaint_precompute_weight_cb_ex,
+ true, false);
+
+ wpd->precomputed_weight_ready = true;
+}
+
+static void do_wpaint_brush_blur_task_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+ 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 coopresponds to the current grid.
+ * 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 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 visable 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 *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+ 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 cooresponds to the current grid.
+ * 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 */
+ 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;
+ }
+}
- if ((brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
- (brush->flag & BRUSH_ACCUMULATE))
+static void do_wpaint_brush_draw_task_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+ 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)
{
- wpd->accumulate_stack = BLI_stack_new(sizeof(struct WPaintDefer), __func__);
+ /* 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 cooresponds to the current grid.
+ * 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 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;
+}
- /* imat for normals */
- mul_m4_m4m4(mat, wpd->vc.rv3d->viewmat, ob->obmat);
- invert_m4_m4(imat, mat);
- copy_m3_m4(wpd->wpimat, imat);
+static void do_wpaint_brush_calc_average_weight_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ StrokeCache *cache = ss->cache;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
- return true;
+ 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 = 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 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 float wpaint_blur_weight_single(const MDeformVert *dv, const WeightPaintInfo *wpi)
+static void calculate_average_weight(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode)
{
- return defvert_find_weight(dv, wpi->active.index);
+ struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__);
+ data->custom_data = accum;
+
+ BLI_task_parallel_range_ex(
+ 0, totnode, data, NULL, 0, do_wpaint_brush_calc_average_weight_cb_ex,
+ ((data->sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT), false);
+
+ 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 float wpaint_blur_weight_multi(const MDeformVert *dv, const WeightPaintInfo *wpi)
+
+static void wpaint_paint_leaves(
+ bContext *C, Object *ob, Sculpt *sd, VPaint *vp, struct WPaintData *wpd, WeightPaintInfo *wpi,
+ Mesh *me, PBVHNode **nodes, int totnode)
{
- 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;
+ Scene *scene = CTX_data_scene(C);
+ const Brush *brush = ob->sculpt->cache->brush;
+
+ /* threaded loop over nodes */
+ SculptThreadedTaskData data = {
+ .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .wpd = wpd, .wpi = wpi, .me = me, .C = C,
+ };
+
+ /* Use this so average can modify its weight without touching the brush. */
+ data.strength = BKE_brush_weight_get(scene, brush);
+
+ /* current mirroring code cannot be run in parallel */
+ bool use_threading = !(me->editflag & ME_EDIT_MIRROR_X);
+
+ switch (brush->vertexpaint_tool) {
+ case PAINT_BLEND_AVERAGE:
+ calculate_average_weight(&data, nodes, totnode);
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_wpaint_brush_draw_task_cb_ex, use_threading, false);
+ break;
+ case PAINT_BLEND_SMEAR:
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_wpaint_brush_smear_task_cb_ex, use_threading, false);
+ break;
+ case PAINT_BLEND_BLUR:
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_wpaint_brush_blur_task_cb_ex, use_threading, false);
+ break;
+ default:
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_wpaint_brush_draw_task_cb_ex, use_threading, false);
+ break;
+ }
}
-static float wpaint_blur_weight_calc_from_connected(
- const MDeformVert *dvert, WeightPaintInfo *wpi, struct WPaintData *wpd, const unsigned int vidx,
- float (*blur_weight_func)(const MDeformVert *, const WeightPaintInfo *))
+static PBVHNode **vwpaint_pbvh_gather_generic(
+ Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode)
{
- const MeshElemMap *map = &wpd->blur_data.vmap[vidx];
- float paintweight;
- if (map->count != 0) {
- paintweight = 0.0f;
- for (int j = 0; j < map->count; j++) {
- paintweight += blur_weight_func(&dvert[map->indices[j]], wpi);
+ 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);
}
- paintweight /= map->count;
}
else {
- paintweight = blur_weight_func(&dvert[vidx], wpi);
+ 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);
+}
- return paintweight;
+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;
}
static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
@@ -2159,24 +1960,17 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
Brush *brush = BKE_paint_brush(&wp->paint);
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
ViewContext *vc;
- Object *ob;
- Mesh *me;
+ Object *ob = CTX_data_active_object(C);
+
+ SculptSession *ss = ob->sculpt;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+
+ vwpaint_update_cache_variants(C, wp, ob, itemptr);
+
float mat[4][4];
- float paintweight;
- int *indexar;
- unsigned int index, totindex;
float mval[2];
- const bool use_blur = (brush->vertexpaint_tool == PAINT_BLEND_BLUR);
- bool use_vert_sel;
- bool use_face_sel;
- bool use_depth;
-
- const float pressure = RNA_float_get(itemptr, "pressure");
- const float brush_size_pressure =
- BKE_brush_size_get(scene, brush) * (BKE_brush_use_size_pressure(scene, brush) ? pressure : 1.0f);
+
const float brush_alpha_value = BKE_brush_alpha_get(scene, brush);
- const float brush_alpha_pressure =
- brush_alpha_value * (BKE_brush_use_alpha_pressure(scene, brush) ? pressure : 1.0f);
/* intentionally don't initialize as NULL, make sure we initialize all members below */
WeightPaintInfo wpi;
@@ -2189,21 +1983,15 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
return;
}
- float (*blur_weight_func)(const MDeformVert *, const WeightPaintInfo *) =
- wpd->do_multipaint ? wpaint_blur_weight_multi : wpaint_blur_weight_single;
-
vc = &wpd->vc;
ob = vc->obact;
- me = ob->data;
- indexar = wpd->indexar;
-
+
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);
- RNA_float_get_array(itemptr, "mouse", mval);
/* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */
wpi.defbase_tot = wpd->defbase_tot;
@@ -2221,180 +2009,51 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
wpi.brush_alpha_value = brush_alpha_value;
/* *** done setting up WeightPaintInfo *** */
-
-
- swap_m4m4(wpd->vc.rv3d->persmat, mat);
-
- use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
- use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
- use_depth = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0;
-
- /* which faces are involved */
- if (use_depth) {
- char editflag_prev = me->editflag;
-
- /* Ugly hack, to avoid drawing vertex index when getting the face index buffer - campbell */
- me->editflag &= ~ME_EDIT_PAINT_VERT_SEL;
- if (use_vert_sel) {
- /* Ugly x2, we need this so hidden faces don't draw */
- me->editflag |= ME_EDIT_PAINT_FACE_SEL;
- }
- totindex = sample_backbuf_area(vc, indexar, me->totpoly, mval[0], mval[1], brush_size_pressure);
- me->editflag = editflag_prev;
-
- if (use_face_sel && me->totpoly) {
- MPoly *mpoly = me->mpoly;
- for (index = 0; index < totindex; index++) {
- if (indexar[index] && indexar[index] <= me->totpoly) {
- MPoly *mp = &mpoly[indexar[index] - 1];
-
- if ((mp->flag & ME_FACE_SEL) == 0) {
- indexar[index] = 0;
- }
- }
- }
- }
- }
- else {
- indexar = NULL;
+ if (wpd->precomputed_weight) {
+ precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data);
}
- /* incase we have modifiers */
- ED_vpaint_proj_handle_update(wpd->vp_handle, vc->ar, mval);
+ wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);
- /* make sure each vertex gets treated only once */
- /* and calculate filter weight */
- paintweight = BKE_brush_weight_get(scene, brush);
+ swap_m4m4(vc->rv3d->persmat, mat);
- if (use_depth) {
- for (index = 0; index < totindex; index++) {
- if (indexar[index] && indexar[index] <= me->totpoly) {
- MPoly *mpoly = me->mpoly + (indexar[index] - 1);
- MLoop *ml = me->mloop + mpoly->loopstart;
- int i;
+ /* calculate pivot for rotation around seletion if needed */
+ /* also needed for "View Selected" on last stroke */
+ paint_last_stroke_update(scene, vc->ar, mval);
- if (use_vert_sel) {
- for (i = 0; i < mpoly->totloop; i++, ml++) {
- me->dvert[ml->v].flag = (me->mvert[ml->v].flag & SELECT);
- }
- }
- else {
- for (i = 0; i < mpoly->totloop; i++, ml++) {
- me->dvert[ml->v].flag = 1;
- }
- }
- }
- }
- }
- else {
- const unsigned int totvert = me->totvert;
- unsigned int i;
+ DAG_id_tag_update(ob->data, 0);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ swap_m4m4(wpd->vc.rv3d->persmat, mat);
- /* in the case of face selection we need to flush */
- if (use_vert_sel || use_face_sel) {
- for (i = 0; i < totvert; i++) {
- me->dvert[i].flag = me->mvert[i].flag & SELECT;
- }
- }
- else {
- for (i = 0; i < totvert; i++) {
- me->dvert[i].flag = SELECT;
- }
+ rcti r;
+ if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) {
+ if (ss->cache) {
+ ss->cache->current_r = r;
}
- }
- /* accumulate means we refer to the previous,
- * which is either the last update, or when we started painting */
- BLI_Stack *accumulate_stack = wpd->accumulate_stack;
- const bool use_accumulate = (accumulate_stack != NULL);
- BLI_assert(accumulate_stack == NULL || BLI_stack_is_empty(accumulate_stack));
-
- const MDeformVert *dvert_prev = use_accumulate ? me->dvert : wp->wpaint_prev;
-
-#define WP_PAINT(v_idx_var) \
- { \
- unsigned int vidx = v_idx_var; \
- if (me->dvert[vidx].flag) { \
- const float alpha = calc_vp_alpha_col_dl( \
- wp, vc, wpd->wpimat, &wpd->vertexcosnos[vidx], \
- mval, brush_size_pressure, brush_alpha_pressure, NULL); \
- if (alpha) { \
- if (use_blur) { \
- paintweight = wpaint_blur_weight_calc_from_connected( \
- dvert_prev, &wpi, wpd, vidx, blur_weight_func); \
- } \
- if (use_accumulate) { \
- struct WPaintDefer *dweight = BLI_stack_push_r(accumulate_stack); \
- dweight->index = vidx; \
- dweight->alpha = alpha; \
- dweight->weight = paintweight; \
- } \
- else { \
- do_weight_paint_vertex(wp, ob, &wpi, vidx, alpha, paintweight); \
- } \
- } \
- me->dvert[vidx].flag = 0; \
- } \
- } (void)0
-
- if (use_depth) {
- for (index = 0; index < totindex; index++) {
-
- if (indexar[index] && indexar[index] <= me->totpoly) {
- MPoly *mpoly = me->mpoly + (indexar[index] - 1);
- MLoop *ml = me->mloop + mpoly->loopstart;
- int i;
-
- for (i = 0; i < mpoly->totloop; i++, ml++) {
- WP_PAINT(ml->v);
- }
- }
+ /* 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);
}
- }
- else {
- const unsigned int totvert = me->totvert;
- unsigned int i;
- for (i = 0; i < totvert; i++) {
- WP_PAINT(i);
- }
- }
-#undef WP_PAINT
+ 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;
- if (use_accumulate) {
- unsigned int defer_count = BLI_stack_count(accumulate_stack);
- while (defer_count--) {
- struct WPaintDefer *dweight = BLI_stack_peek(accumulate_stack);
- do_weight_paint_vertex(wp, ob, &wpi, dweight->index, dweight->alpha, dweight->weight);
- BLI_stack_discard(accumulate_stack);
- }
+ ss->partial_redraw = 1;
}
-
-
- /* *** free wpi members */
- /* *** done freeing wpi members */
-
-
- 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);
-
- DAG_id_tag_update(ob->data, 0);
- ED_region_tag_redraw(vc->ar);
+ ED_region_tag_redraw_partial(vc->ar, &r);
}
static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
{
- ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
-
+
if (wpd) {
- ED_vpaint_proj_handle_free(wpd->vp_handle);
- MEM_freeN(wpd->indexar);
-
if (wpd->defbase_sel)
MEM_freeN((void *)wpd->defbase_sel);
if (wpd->vgroup_validmap)
@@ -2405,29 +2064,17 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
MEM_freeN((void *)wpd->active.lock);
if (wpd->mirror.lock)
MEM_freeN((void *)wpd->mirror.lock);
-
- if (wpd->blur_data.vmap) {
- MEM_freeN(wpd->blur_data.vmap);
- }
- if (wpd->blur_data.vmap_mem) {
- MEM_freeN(wpd->blur_data.vmap_mem);
- }
-
- if (wpd->accumulate_stack) {
- BLI_stack_free(wpd->accumulate_stack);
- }
+ if (wpd->precomputed_weight)
+ MEM_freeN(wpd->precomputed_weight);
MEM_freeN(wpd);
}
-
- /* frees prev buffer */
- copy_wpaint_prev(ts->wpaint, NULL, 0);
-
+
/* 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) {
@@ -2441,6 +2088,9 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
DAG_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;
}
@@ -2448,10 +2098,11 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
- op->customdata = paint_stroke_new(C, op, NULL, 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;
@@ -2461,15 +2112,16 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
OPERATOR_RETVAL_CHECK(retval);
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
-
+
return OPERATOR_RUNNING_MODAL;
}
static int wpaint_exec(bContext *C, wmOperator *op)
{
- op->customdata = paint_stroke_new(C, op, NULL, 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);
@@ -2479,64 +2131,33 @@ static int wpaint_exec(bContext *C, wmOperator *op)
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;
+ }
+
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";
-
+
/* 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;
-
- paint_stroke_operator_properties(ot);
-}
-
-static int weight_paint_set_exec(bContext *C, wmOperator *op)
-{
- struct Scene *scene = CTX_data_scene(C);
- Object *obact = CTX_data_active_object(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
- float vgroup_weight = BKE_brush_weight_get(scene, brush);
- if (wpaint_ensure_data(C, op, WPAINT_ENSURE_MIRROR, NULL) == false) {
- return OPERATOR_CANCELLED;
- }
-
- if (ED_wpaint_fill(scene->toolsettings->wpaint, obact, vgroup_weight)) {
- ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
-}
-
-void PAINT_OT_weight_set(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Set Weight";
- ot->idname = "PAINT_OT_weight_set";
- ot->description = "Fill the active vertex group with the current paint weight";
-
- /* api callbacks */
- ot->exec = weight_paint_set_exec;
- ot->poll = mask_paint_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ paint_stroke_operator_properties(ot);
}
/* ************ set / clear vertex paint mode ********** */
@@ -2545,7 +2166,7 @@ void PAINT_OT_weight_set(wmOperatorType *ot)
* \note Keep in sync with #wpaint_mode_toggle_exec
*/
static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
-{
+{
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;
@@ -2560,7 +2181,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
}
me = BKE_mesh_from_object(ob);
-
+
/* toggle: end vpaint */
if (is_mode_set) {
ob->mode &= ~mode_flag;
@@ -2568,44 +2189,62 @@ 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) {
+ sculpt_cache_free(ob->sculpt->cache);
+ ob->sculpt->cache = NULL;
+ }
+
+ BKE_sculptsession_free(ob);
paint_cursor_delete_textures();
}
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);
-
+ vp = scene->toolsettings->vpaint = new_vpaint();
+
paint_cursor_start(C, vertex_paint_poll);
BKE_paint_init(scene, ePaintVertex, PAINT_CURSOR_VERTEX_PAINT);
+
+ /* 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(scene, ob);
}
-
+
/* update modifier stack for mapping requirements */
DAG_id_tag_update(&me->id, 0);
-
+
WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
-
+
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";
-
+
/* api callbacks */
ot->exec = vpaint_mode_toggle_exec;
ot->poll = paint_poll_test;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
@@ -2620,7 +2259,7 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
* - validate context (add mcol)
* - create customdata storage
* - call paint once (mouse click)
- * - add modal handler
+ * - add modal handler
*
* Operator->modal()
* - for every mousemove, apply vertex paint
@@ -2637,15 +2276,14 @@ typedef struct PolyFaceMap {
int facenr;
} PolyFaceMap;
-typedef struct VPaintData {
+struct VPaintData {
ViewContext vc;
- unsigned int paintcol;
- int *indexar;
+ struct NormalAnglePrecalc normal_angle_precalc;
- struct VertProjHandle *vp_handle;
- DMCoNo *vertexcosnos;
+ uint paintcol;
- float vpimat[3][3];
+ struct VertProjHandle *vp_handle;
+ struct DMCoNo *vertexcosnos;
/* modify 'me->mcol' directly, since the derived mesh is drawing from this
* array, otherwise we need to refresh the modifier stack */
@@ -2656,9 +2294,15 @@ typedef struct VPaintData {
bool *mlooptag;
bool is_texbrush;
-} VPaintData;
-static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float UNUSED(mouse[2]))
+ /* 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;
@@ -2668,26 +2312,24 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
struct VPaintData *vpd;
Object *ob = CTX_data_active_object(C);
Mesh *me;
- float mat[4][4], imat[4][4];
+ SculptSession *ss = ob->sculpt;
/* context checks could be a poll() */
me = BKE_mesh_from_object(ob);
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;
/* make mode data storage */
- vpd = MEM_callocN(sizeof(struct VPaintData), "VPaintData");
+ vpd = MEM_callocN(sizeof(*vpd), "VPaintData");
paint_stroke_set_mode_data(stroke, vpd);
view3d_set_viewcontext(C, &vpd->vc);
-
- vpd->vp_handle = ED_vpaint_proj_handle_create(vpd->vc.scene, ob, &vpd->vertexcosnos);
+ view_angle_limits_init(&vpd->normal_angle_precalc, vp->paint.brush->falloff_angle,
+ (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
- vpd->indexar = get_indexarray(me);
vpd->paintcol = vpaint_get_current_col(scene, vp);
vpd->is_texbrush = !(brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
@@ -2709,84 +2351,564 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
}
- /* for filtering */
- copy_vpaint_prev(vp, (unsigned int *)me->mloopcol, me->totloop);
-
- /* some old cruft to sort out later */
- mul_m4_m4m4(mat, vpd->vc.rv3d->viewmat, ob->obmat);
- invert_m4_m4(imat, mat);
- copy_m3_m4(vpd->vpimat, imat);
+ if (brush->vertexpaint_tool == PAINT_BLEND_SMEAR) {
+ vpd->smear.color_prev = MEM_mallocN(sizeof(uint) * me->totloop, __func__);
+ memcpy(vpd->smear.color_prev, me->mloopcol, sizeof(uint) * me->totloop);
+ vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev);
+ }
+
+ /* Create projection handle */
+ if (vpd->is_texbrush) {
+ ob->sculpt->building_vp_handle = true;
+ vpd->vp_handle = ED_vpaint_proj_handle_create(scene, ob, &vpd->vertexcosnos);
+ ob->sculpt->building_vp_handle = false;
+ }
+
+ /* If not previously created, create vertex/weight paint mode session data */
+ vertex_paint_init_session(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 vpaint_paint_poly(VPaint *vp, VPaintData *vpd, Mesh *me,
- const unsigned int index, const float mval[2],
- const float brush_size_pressure, const float brush_alpha_pressure)
+static void do_vpaint_brush_calc_average_color_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
{
- ViewContext *vc = &vpd->vc;
- Brush *brush = BKE_paint_brush(&vp->paint);
- MPoly *mpoly = &me->mpoly[index];
- MLoop *ml;
- unsigned int *lcol = ((unsigned int *)me->mloopcol) + mpoly->loopstart;
- unsigned int *lcolorig = ((unsigned int *)vp->vpaint_prev) + mpoly->loopstart;
- bool *mlooptag = (vpd->mlooptag) ? vpd->mlooptag + mpoly->loopstart : NULL;
- float alpha;
- int i, j;
- int totloop = mpoly->totloop;
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+ const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
- int brush_alpha_pressure_i = (int)(brush_alpha_pressure * 255.0f);
+ 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;
- if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
- unsigned int blend[4] = {0};
- unsigned int tcol;
- char *col;
+ 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 (j = 0; j < totloop; j++) {
- col = (char *)(lcol + j);
- blend[0] += col[0];
- blend[1] += col[1];
- blend[2] += col[2];
- blend[3] += col[3];
+ /* 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 = ccgdm ? 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 *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+ 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;
- blend[0] = divide_round_i(blend[0], totloop);
- blend[1] = divide_round_i(blend[1], totloop);
- blend[2] = divide_round_i(blend[2], totloop);
- blend[3] = divide_round_i(blend[3], totloop);
- col = (char *)&tcol;
- col[0] = blend[0];
- col[1] = blend[1];
- col[2] = blend[2];
- col[3] = blend[3];
+ 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);
- vpd->paintcol = *((unsigned int *)col);
+ /* 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 cooresponds to the current grid.
+ * 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];
+
+ /* 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;
+}
- ml = me->mloop + mpoly->loopstart;
- for (i = 0; i < totloop; i++, ml++) {
- float rgba[4];
- unsigned int paintcol;
- alpha = calc_vp_alpha_col_dl(vp, vc, vpd->vpimat,
- &vpd->vertexcosnos[ml->v], mval,
- brush_size_pressure, brush_alpha_pressure, rgba);
+static void do_vpaint_brush_blur_task_cb_ex(
+ void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+
+ 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;
- if (vpd->is_texbrush) {
- float rgba_br[3];
- rgb_uchar_to_float(rgba_br, (const unsigned char *)&vpd->paintcol);
- mul_v3_v3(rgba_br, rgba);
- rgb_float_to_uchar((unsigned char *)&paintcol, rgba_br);
+ 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 cooresponds to the current grid.
+ * 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];
+
+ /* 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 *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+
+ 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 cooresponds to the current grid.
+ * 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_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];
+ }
+ }
+ }
+ }
+ }
+ }
}
- else
- paintcol = vpd->paintcol;
+ BKE_pbvh_vertex_iter_end;
+ }
+}
- if (alpha > 0.0f) {
- const int alpha_i = (int)(alpha * 255.0f);
- lcol[i] = vpaint_blend(vp, lcol[i], lcolorig[i], paintcol, alpha_i, brush_alpha_pressure_i);
+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;
+
+ BLI_task_parallel_range_ex(
+ 0, totnode, data, NULL, 0, do_vpaint_brush_calc_average_color_cb_ex,
+ true, false);
+
+ 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 = {
+ .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .vpd = vpd,
+ .lcol = (uint *)me->mloopcol, .me = me, .C = C,
+ };
+ switch (brush->vertexpaint_tool) {
+ case PAINT_BLEND_AVERAGE:
+ calculate_average_color(&data, nodes, totnode);
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_vpaint_brush_draw_task_cb_ex, true, false);
+ break;
+ case PAINT_BLEND_BLUR:
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_vpaint_brush_blur_task_cb_ex, true, false);
+ break;
+ case PAINT_BLEND_SMEAR:
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_vpaint_brush_smear_task_cb_ex, true, false);
+ break;
+ default:
+ BLI_task_parallel_range_ex(
+ 0, totnode, &data, NULL, 0,
+ do_vpaint_brush_draw_task_cb_ex, true, false);
+ 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);
- if (mlooptag) mlooptag[i] = 1;
+ /* 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;
}
static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
@@ -2795,63 +2917,28 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
ToolSettings *ts = CTX_data_tool_settings(C);
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
VPaint *vp = ts->vpaint;
- Brush *brush = BKE_paint_brush(&vp->paint);
ViewContext *vc = &vpd->vc;
Object *ob = vc->obact;
- Mesh *me = ob->data;
- float mat[4][4];
- int *indexar = vpd->indexar;
- int totindex, index;
- float mval[2];
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- const float pressure = RNA_float_get(itemptr, "pressure");
- const float brush_size_pressure =
- BKE_brush_size_get(scene, brush) * (BKE_brush_use_size_pressure(scene, brush) ? pressure : 1.0f);
- const float brush_alpha_pressure =
- BKE_brush_alpha_get(scene, brush) * (BKE_brush_use_alpha_pressure(scene, brush) ? pressure : 1.0f);
+ vwpaint_update_cache_variants(C, vp, ob, itemptr);
- RNA_float_get_array(itemptr, "mouse", mval);
+ float mat[4][4];
+ float mval[2];
- 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);
- /* which faces are involved */
- totindex = sample_backbuf_area(vc, indexar, me->totpoly, mval[0], mval[1], brush_size_pressure);
-
- if ((me->editflag & ME_EDIT_PAINT_FACE_SEL) && me->mpoly) {
- for (index = 0; index < totindex; index++) {
- if (indexar[index] && indexar[index] <= me->totpoly) {
- const MPoly *mpoly = &me->mpoly[indexar[index] - 1];
-
- if ((mpoly->flag & ME_FACE_SEL) == 0)
- indexar[index] = 0;
- }
- }
- }
-
swap_m4m4(vc->rv3d->persmat, mat);
- /* incase we have modifiers */
- ED_vpaint_proj_handle_update(vpd->vp_handle, vc->ar, mval);
-
- /* clear modified tag for blur tool */
- if (vpd->mlooptag)
- memset(vpd->mlooptag, 0, sizeof(bool) * me->totloop);
+ vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob);
- for (index = 0; index < totindex; index++) {
- if (indexar[index] && indexar[index] <= me->totpoly) {
- vpaint_paint_poly(vp, vpd, me, indexar[index] - 1, mval, brush_size_pressure, brush_alpha_pressure);
- }
- }
-
swap_m4m4(vc->rv3d->persmat, mat);
- /* was disabled because it is slow, but necessary for blur */
- if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
- do_shared_vertexcol(me, vpd->mlooptag);
+ if (vp->paint.brush->vertexpaint_tool == PAINT_BLEND_SMEAR) {
+ memcpy(vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop);
}
/* calculate pivot for rotation around seletion if needed */
@@ -2873,35 +2960,38 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
{
- ToolSettings *ts = CTX_data_tool_settings(C);
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
ViewContext *vc = &vpd->vc;
Object *ob = vc->obact;
- Mesh *me = ob->data;
- ED_vpaint_proj_handle_free(vpd->vp_handle);
- MEM_freeN(vpd->indexar);
-
- /* frees prev buffer */
- copy_vpaint_prev(ts->vpaint, NULL, 0);
+ 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);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
- DAG_id_tag_update(&me->id, 0);
MEM_freeN(vpd);
+
+ sculpt_cache_free(ob->sculpt->cache);
+ ob->sculpt->cache = NULL;
}
static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
- op->customdata = paint_stroke_new(C, op, NULL, 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;
@@ -2912,15 +3002,16 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
OPERATOR_RETVAL_CHECK(retval);
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
-
+
return OPERATOR_RUNNING_MODAL;
}
static int vpaint_exec(bContext *C, wmOperator *op)
{
- op->customdata = paint_stroke_new(C, op, NULL, 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);
@@ -2930,6 +3021,12 @@ static int vpaint_exec(bContext *C, wmOperator *op)
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;
+ }
+
paint_stroke_cancel(C, op);
}
@@ -2939,382 +3036,16 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
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;
-
+
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
paint_stroke_operator_properties(ot);
}
-
-/* ********************** weight from bones operator ******************* */
-
-static int weight_from_bones_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
-
- return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
-}
-
-static int weight_from_bones_exec(bContext *C, wmOperator *op)
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- Object *armob = modifiers_isDeformedByArmature(ob);
- Mesh *me = ob->data;
- int type = RNA_enum_get(op->ptr, "type");
-
- create_vgroups_from_armature(op->reports, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
-
- DAG_id_tag_update(&me->id, 0);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
-
- return OPERATOR_FINISHED;
-}
-
-void PAINT_OT_weight_from_bones(wmOperatorType *ot)
-{
- static EnumPropertyItem type_items[] = {
- {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"},
- {ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"},
- {0, NULL, 0, NULL, NULL}};
-
- /* identifiers */
- ot->name = "Weight from Bones";
- ot->idname = "PAINT_OT_weight_from_bones";
- ot->description = "Set the weights of the groups matching the attached armature's selected bones, "
- "using the distance between the vertices and the bones";
-
- /* api callbacks */
- ot->exec = weight_from_bones_exec;
- ot->invoke = WM_menu_invoke;
- ot->poll = weight_from_bones_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights");
-}
-
-/* *** VGroups Gradient *** */
-typedef struct DMGradient_vertStore {
- float sco[2];
- float weight_orig;
- enum {
- VGRAD_STORE_NOP = 0,
- VGRAD_STORE_DW_EXIST = (1 << 0)
- } flag;
-} DMGradient_vertStore;
-
-typedef struct DMGradient_userData {
- struct ARegion *ar;
- Scene *scene;
- Mesh *me;
- Brush *brush;
- const float *sco_start; /* [2] */
- const float *sco_end; /* [2] */
- float sco_line_div; /* store (1.0f / len_v2v2(sco_start, sco_end)) */
- int def_nr;
- bool is_init;
- DMGradient_vertStore *vert_cache;
- /* only for init */
- BLI_bitmap *vert_visit;
-
- /* options */
- short use_select;
- short type;
- float weightpaint;
-} DMGradient_userData;
-
-static void gradientVert_update(DMGradient_userData *grad_data, int index)
-{
- Mesh *me = grad_data->me;
- DMGradient_vertStore *vs = &grad_data->vert_cache[index];
- float alpha;
-
- if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
- alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end);
- }
- else {
- BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL);
- alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div;
- }
- /* no need to clamp 'alpha' yet */
-
- /* adjust weight */
- alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
-
- if (alpha != 0.0f) {
- MDeformVert *dv = &me->dvert[index];
- MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr);
- // dw->weight = alpha; // testing
- int tool = grad_data->brush->vertexpaint_tool;
- float testw;
-
- /* init if we just added */
- testw = wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha);
- CLAMP(testw, 0.0f, 1.0f);
- dw->weight = testw;
- }
- else {
- MDeformVert *dv = &me->dvert[index];
- if (vs->flag & VGRAD_STORE_DW_EXIST) {
- /* normally we NULL check, but in this case we know it exists */
- MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
- dw->weight = vs->weight_orig;
- }
- else {
- /* wasn't originally existing, remove */
- MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
- if (dw) {
- defvert_remove_group(dv, dw);
- }
- }
- }
-}
-
-static void gradientVertUpdate__mapFunc(
- void *userData, int index, const float UNUSED(co[3]),
- const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
-{
- DMGradient_userData *grad_data = userData;
- Mesh *me = grad_data->me;
- if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
- DMGradient_vertStore *vs = &grad_data->vert_cache[index];
- if (vs->sco[0] != FLT_MAX) {
- gradientVert_update(grad_data, index);
- }
- }
-}
-
-static void gradientVertInit__mapFunc(
- void *userData, int index, const float co[3],
- const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
-{
- DMGradient_userData *grad_data = userData;
- Mesh *me = grad_data->me;
-
- if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
- /* run first pass only,
- * the screen coords of the verts need to be cached because
- * updating the mesh may move them about (entering feedback loop) */
-
- if (BLI_BITMAP_TEST(grad_data->vert_visit, index) == 0) {
- DMGradient_vertStore *vs = &grad_data->vert_cache[index];
- if (ED_view3d_project_float_object(grad_data->ar,
- co, vs->sco,
- V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
- {
- /* ok */
- MDeformVert *dv = &me->dvert[index];
- MDeformWeight *dw;
- dw = defvert_find_index(dv, grad_data->def_nr);
- if (dw) {
- vs->weight_orig = dw->weight;
- vs->flag = VGRAD_STORE_DW_EXIST;
- }
- else {
- vs->weight_orig = 0.0f;
- vs->flag = VGRAD_STORE_NOP;
- }
-
- BLI_BITMAP_ENABLE(grad_data->vert_visit, index);
-
- gradientVert_update(grad_data, index);
- }
- else {
- /* no go */
- copy_v2_fl(vs->sco, FLT_MAX);
- }
- }
- }
-}
-
-static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
-{
- int ret = WM_gesture_straightline_modal(C, op, event);
-
- if (ret & OPERATOR_RUNNING_MODAL) {
- if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { /* XXX, hardcoded */
- /* generally crap! redo! */
- WM_gesture_straightline_cancel(C, op);
- ret &= ~OPERATOR_RUNNING_MODAL;
- ret |= OPERATOR_FINISHED;
- }
- }
-
- if (ret & OPERATOR_CANCELLED) {
- ToolSettings *ts = CTX_data_tool_settings(C);
- VPaint *wp = ts->wpaint;
- Object *ob = CTX_data_active_object(C);
- Mesh *me = ob->data;
- if (wp->wpaint_prev) {
- BKE_defvert_array_free_elems(me->dvert, me->totvert);
- BKE_defvert_array_copy(me->dvert, wp->wpaint_prev, me->totvert);
- free_wpaint_prev(wp);
- }
-
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
- }
- else if (ret & OPERATOR_FINISHED) {
- ToolSettings *ts = CTX_data_tool_settings(C);
- VPaint *wp = ts->wpaint;
- free_wpaint_prev(wp);
- }
-
- return ret;
-}
-
-static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
-{
- wmGesture *gesture = op->customdata;
- DMGradient_vertStore *vert_cache;
- struct ARegion *ar = CTX_wm_region(C);
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- Mesh *me = ob->data;
- int x_start = RNA_int_get(op->ptr, "xstart");
- int y_start = RNA_int_get(op->ptr, "ystart");
- int x_end = RNA_int_get(op->ptr, "xend");
- int y_end = RNA_int_get(op->ptr, "yend");
- float sco_start[2] = {x_start, y_start};
- float sco_end[2] = {x_end, y_end};
- const bool is_interactive = (gesture != NULL);
- DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask);
-
- DMGradient_userData data = {NULL};
-
- if (is_interactive) {
- if (gesture->userdata == NULL) {
- VPaint *wp = scene->toolsettings->wpaint;
-
- gesture->userdata = MEM_mallocN(sizeof(DMGradient_vertStore) * me->totvert, __func__);
- data.is_init = true;
-
- copy_wpaint_prev(wp, me->dvert, me->totvert);
-
- /* on init only, convert face -> vert sel */
- if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
- BKE_mesh_flush_select_from_polys(me);
- }
- }
-
- vert_cache = gesture->userdata;
- }
- else {
- if (wpaint_ensure_data(C, op, 0, NULL) == false) {
- return OPERATOR_CANCELLED;
- }
-
- data.is_init = true;
- vert_cache = MEM_mallocN(sizeof(DMGradient_vertStore) * me->totvert, __func__);
- }
-
- data.ar = ar;
- data.scene = scene;
- data.me = ob->data;
- data.sco_start = sco_start;
- data.sco_end = sco_end;
- data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
- data.def_nr = ob->actdef - 1;
- data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL));
- data.vert_cache = vert_cache;
- data.vert_visit = NULL;
- data.type = RNA_enum_get(op->ptr, "type");
-
- {
- ToolSettings *ts = CTX_data_tool_settings(C);
- VPaint *wp = ts->wpaint;
- struct Brush *brush = BKE_paint_brush(&wp->paint);
-
- curvemapping_initialize(brush->curve);
-
- data.brush = brush;
- data.weightpaint = BKE_brush_weight_get(scene, brush);
- }
-
- ED_view3d_init_mats_rv3d(ob, ar->regiondata);
-
- if (data.is_init) {
- data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__);
-
- dm->foreachMappedVert(dm, gradientVertInit__mapFunc, &data, DM_FOREACH_NOP);
-
- MEM_freeN(data.vert_visit);
- data.vert_visit = NULL;
- }
- else {
- dm->foreachMappedVert(dm, gradientVertUpdate__mapFunc, &data, DM_FOREACH_NOP);
- }
-
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
-
- if (is_interactive == false) {
- MEM_freeN(vert_cache);
- }
-
- return OPERATOR_FINISHED;
-}
-
-static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- int ret;
-
- if (wpaint_ensure_data(C, op, 0, NULL) == false) {
- return OPERATOR_CANCELLED;
- }
-
- ret = WM_gesture_straightline_invoke(C, op, event);
- if (ret & OPERATOR_RUNNING_MODAL) {
- struct ARegion *ar = CTX_wm_region(C);
- if (ar->regiontype == RGN_TYPE_WINDOW) {
- /* TODO, hardcoded, extend WM_gesture_straightline_ */
- if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
- wmGesture *gesture = op->customdata;
- gesture->mode = 1;
- }
- }
- }
- return ret;
-}
-
-void PAINT_OT_weight_gradient(wmOperatorType *ot)
-{
- /* defined in DNA_space_types.h */
- static EnumPropertyItem gradient_types[] = {
- {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""},
- {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""},
- {0, NULL, 0, NULL, NULL}
- };
-
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Weight Gradient";
- ot->idname = "PAINT_OT_weight_gradient";
- ot->description = "Draw a line to apply a weight gradient to selected vertices";
-
- /* api callbacks */
- ot->invoke = paint_weight_gradient_invoke;
- ot->modal = paint_weight_gradient_modal;
- ot->exec = paint_weight_gradient_exec;
- ot->poll = weight_paint_poll;
- ot->cancel = WM_gesture_straightline_cancel;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", "");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-
- WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
-}