Vertex/Weight Paint: Use PBVH for painting
authorCampbell Barton <ideasman42@gmail.com>
Wed, 27 Sep 2017 15:38:17 +0000 (01:38 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 27 Sep 2017 15:47:37 +0000 (01:47 +1000)
2016 GSOC project by @nathanvollmer, see D2150

- Mirrored painting and radial symmetry, like in sculpt mode.
- Volume based splash prevention,
  which avoids painting vertices far away from the 3D brush location.
- Normal based splash prevention,
  which avoids painting vertices with normals opposite the normal
  at the 3D brush location.
- Blur mode now uses a nearest neighbor average.
- Average mode, which averages the color/weight
  of the vertices within the brush
- Smudge mode, which pulls the colors/weights
  along the direction of the brush
- RGB^2 color blending, which gives a more accurate
  blend between two colors
- multithreading support. (PBVH leaves are painted in parallel.)
- Foreground/background color picker in vertex paint

25 files changed:
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/BKE_paint.h
source/blender/blenkernel/BKE_pbvh.h
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/paint.c
source/blender/blenkernel/intern/pbvh.c
source/blender/blenkernel/intern/pbvh_intern.h
source/blender/blenkernel/intern/scene.c
source/blender/blenkernel/intern/subsurf_ccg.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_270.c
source/blender/blenloader/intern/versioning_defaults.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/editors/sculpt_paint/paint_ops.c
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/sculpt_paint/sculpt_intern.h
source/blender/makesdna/DNA_brush_types.h
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_brush.c
source/blender/makesrna/intern/rna_sculpt_paint.c

index eb1b2e5971dc755e8c047d6e2f0b766f12cd0bba..8562cb419b42ac7fd24f4f886e4e77fa57eb195f 100644 (file)
@@ -51,6 +51,19 @@ def draw_keyframing_tools(context, layout):
     row.operator("anim.keyframe_delete_v3d", text="Remove")
 
 
+# Used by vertex & weight paint
+def draw_vpaint_symmetry(layout, vpaint):
+    col = layout.column(align=True)
+    col.label(text="Mirror:")
+    row = col.row(align=True)
+
+    row.prop(vpaint, "use_symmetry_x", text="X", toggle=True)
+    row.prop(vpaint, "use_symmetry_y", text="Y", toggle=True)
+    row.prop(vpaint, "use_symmetry_z", text="Z", toggle=True)
+
+    col = layout.column()
+    col.prop(vpaint, "radial_symmetry", text="Radial")
+
 # ********** default tools for object-mode ****************
 
 
@@ -1134,7 +1147,11 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
             self.prop_unified_color_picker(col, context, brush, "color", value_slider=True)
             if settings.palette:
                 col.template_palette(settings, "palette", color=True)
-            self.prop_unified_color(col, context, brush, "color", text="")
+            row = col.row(align=True)
+            self.prop_unified_color(row, context, brush, "color", text="")
+            self.prop_unified_color(row, context, brush, "secondary_color", text="")
+            row.separator()
+            row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="")
 
             col.separator()
             row = col.row(align=True)
@@ -1717,6 +1734,19 @@ class VIEW3D_PT_tools_weightpaint(View3DPanel, Panel):
         props.data_type = 'VGROUP_WEIGHTS'
 
 
+class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel):
+    bl_category = "Tools"
+    bl_context = "weightpaint"
+    bl_options = {'DEFAULT_CLOSED'}
+    bl_label = "Symmetry"
+
+    def draw(self, context):
+        layout = self.layout
+        toolsettings = context.tool_settings
+        wpaint = toolsettings.weight_paint
+        draw_vpaint_symmetry(layout, wpaint)
+
+
 class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):
     bl_category = "Options"
     bl_context = "weightpaint"
@@ -1779,6 +1809,20 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel):
 #~         col.label(text="Multiply:")
 #~         col.prop(vpaint, "mul", text="")
 
+
+class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel):
+    bl_category = "Tools"
+    bl_context = "vertexpaint"
+    bl_options = {'DEFAULT_CLOSED'}
+    bl_label = "Symmetry"
+
+    def draw(self, context):
+        layout = self.layout
+        toolsettings = context.tool_settings
+        vpaint = toolsettings.vertex_paint
+        draw_vpaint_symmetry(layout, vpaint)
+
+
 # ********** default tools for texture-paint ****************
 
 
@@ -2058,8 +2102,10 @@ classes = (
     VIEW3D_PT_sculpt_symmetry,
     VIEW3D_PT_tools_brush_appearance,
     VIEW3D_PT_tools_weightpaint,
+    VIEW3D_PT_tools_weightpaint_symmetry,
     VIEW3D_PT_tools_weightpaint_options,
     VIEW3D_PT_tools_vertexpaint,
+    VIEW3D_PT_tools_vertexpaint_symmetry,
     VIEW3D_PT_tools_imagepaint_external,
     VIEW3D_PT_tools_imagepaint_symmetry,
     VIEW3D_PT_tools_projectpaint,
index f7e6ab59d2058712e14d0ed2b936b9699206ce79..886936006535b21c6841578239184e95b35e92f5 100644 (file)
@@ -160,6 +160,14 @@ void paint_update_brush_rake_rotation(struct UnifiedPaintSettings *ups, struct B
 
 void BKE_paint_stroke_get_average(struct Scene *scene, struct Object *ob, float stroke[3]);
 
+/* Used for both vertex color and weight paint */
+struct SculptVertexPaintGeomMap {
+       int *vert_map_mem;
+       struct MeshElemMap *vert_to_loop;
+       int *poly_map_mem;
+       struct MeshElemMap *vert_to_poly;
+};
+
 /* Session data (mode-specific) */
 
 typedef struct SculptSession {
@@ -205,10 +213,38 @@ typedef struct SculptSession {
 
        struct SculptStroke *stroke;
        struct StrokeCache *cache;
+
+       union {
+               struct {
+                       struct SculptVertexPaintGeomMap gmap;
+
+                       /* For non-airbrush painting to re-apply from the original (MLoop aligned). */
+                       unsigned int *previous_color;
+               } vpaint;
+
+               struct {
+                       struct SculptVertexPaintGeomMap gmap;
+
+                       /* Vertex aligned arrays of weights. */
+                       /* For non-airbrush painting to re-apply from the original. */
+                       float *previous_weight;
+                       /* Keep track of how much each vertex has been painted (non-airbrush only). */
+                       float *alpha_weight;
+               } wpaint;
+
+               //struct {
+               //ToDo: identify sculpt-only fields
+               //} sculpt;
+       } mode;
+       int mode_type;
+
+       /* This flag prevents PBVH from being freed when creating the vp_handle for texture paint. */
+       bool building_vp_handle;
 } SculptSession;
 
 void BKE_sculptsession_free(struct Object *ob);
 void BKE_sculptsession_free_deformMats(struct SculptSession *ss);
+void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss);
 void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder);
 void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
 void BKE_sculpt_update_mesh_elements(struct Scene *scene, struct Sculpt *sd, struct Object *ob,
index 927303f8b3c6d89a39fd5aa6e273d19e699e6b6a..cc84be6e2c141fef5a9e0665669ccb11eb841bbe 100644 (file)
@@ -32,6 +32,7 @@
 
 struct CCGElem;
 struct CCGKey;
+struct CCGDerivedMesh;
 struct CustomData;
 struct DMFlagMat;
 struct MPoly;
@@ -71,7 +72,7 @@ void BKE_pbvh_build_grids(PBVH *bvh, struct CCGElem **grid_elems,
                           struct CCGKey *key, void **gridfaces, struct DMFlagMat *flagmats,
                           unsigned int **grid_hidden);
 void BKE_pbvh_build_bmesh(PBVH *bvh, struct BMesh *bm, bool smooth_shading, struct BMLog *log, const int cd_vert_node_offset, const int cd_face_node_offset);
-
+void BKE_pbvh_set_ccgdm(PBVH *bvh, struct CCGDerivedMesh *ccgdm);
 void BKE_pbvh_free(PBVH *bvh);
 void BKE_pbvh_free_layer_disp(PBVH *bvh);
 
@@ -118,6 +119,7 @@ void BKE_pbvh_raycast_project_ray_root(
 void BKE_pbvh_node_draw(PBVHNode *node, void *data);
 void BKE_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3],
                    int (*setMaterial)(int matnr, void *attribs), bool wireframe, bool fast);
+void BKE_pbvh_draw_BB(PBVH *bvh);
 
 /* PBVH Access */
 typedef enum {
@@ -141,6 +143,7 @@ int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden,
 
 /* multires level, only valid for type == PBVH_GRIDS */
 void BKE_pbvh_get_grid_key(const PBVH *pbvh, struct CCGKey *key);
+struct CCGDerivedMesh *BKE_pbvh_get_ccgdm(const PBVH *bvh);
 
 /* Only valid for type == PBVH_BMESH */
 struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh);
index ace79f4125b1eb76c3696838d184e03ee17cda89..9dae4c5eae70d91c9a42f164a2637f74e28d3661 100644 (file)
@@ -2663,7 +2663,7 @@ static void mesh_build_data(
        ob->lastDataMask = dataMask;
        ob->lastNeedMapping = need_mapping;
 
-       if ((ob->mode & OB_MODE_SCULPT) && ob->sculpt) {
+       if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
                /* create PBVH immediately (would be created on the fly too,
                 * but this avoids waiting on first stroke) */
 
index 2c61cf28691f6093e99e4401d17a8fa68bd0e961..e97de07752e5d469c22e4ac91fa55714b3fd1a5e 100644 (file)
@@ -662,6 +662,11 @@ static void cdDM_drawMappedFaces(
 
        const int *index_mp_to_orig  = dm->getPolyDataArray(dm, CD_ORIGINDEX);
 
+       if (cddm->pbvh) {
+               if (G.debug_value == 14)
+                       BKE_pbvh_draw_BB(cddm->pbvh);
+       }
+
        /* fist, setup common buffers */
        GPU_vertex_setup(dm);
        GPU_triangle_setup(dm);
index d76ef61302321080f6a0cccfdc4fbe2aea4dd544..44058c989ff0b0ce5ee23d317a53484c1fd82abb 100644 (file)
@@ -2682,7 +2682,7 @@ void BKE_object_sculpt_modifiers_changed(Object *ob)
 {
        SculptSession *ss = ob->sculpt;
 
-       if (ss) {
+       if (ss && ss->building_vp_handle == false) {
                if (!ss->cache) {
                        /* we free pbvh on changes, except during sculpt since it can't deal with
                         * changing PVBH node organization, we hope topology does not change in
@@ -2693,6 +2693,9 @@ void BKE_object_sculpt_modifiers_changed(Object *ob)
                        }
 
                        BKE_sculptsession_free_deformMats(ob->sculpt);
+
+                       /* In vertex/weight paint, force maps to be rebuilt. */
+                       BKE_sculptsession_free_vwpaint_data(ob->sculpt);
                }
                else {
                        PBVHNode **nodes;
index 9fc2136df86d53836675f6884d4db7fc5288ddfa..25ea6ad079f7e25d0c7966e68039097ee2005b9a 100644 (file)
@@ -673,6 +673,29 @@ void BKE_sculptsession_free_deformMats(SculptSession *ss)
        MEM_SAFE_FREE(ss->deform_imats);
 }
 
+void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss)
+{
+       struct SculptVertexPaintGeomMap *gmap = NULL;
+       if (ss->mode_type == OB_MODE_VERTEX_PAINT) {
+               gmap = &ss->mode.vpaint.gmap;
+
+               MEM_SAFE_FREE(ss->mode.vpaint.previous_color);
+       }
+       else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) {
+               gmap = &ss->mode.wpaint.gmap;
+
+               MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight);
+               MEM_SAFE_FREE(ss->mode.wpaint.previous_weight);
+       }
+       else {
+               return;
+       }
+       MEM_SAFE_FREE(gmap->vert_to_loop);
+       MEM_SAFE_FREE(gmap->vert_map_mem);
+       MEM_SAFE_FREE(gmap->vert_to_poly);
+       MEM_SAFE_FREE(gmap->poly_map_mem);
+}
+
 /* Write out the sculpt dynamic-topology BMesh to the Mesh */
 static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder)
 {
@@ -714,10 +737,7 @@ void BKE_sculptsession_bm_to_me_for_render(Object *object)
                         */
                        BKE_object_free_derived_caches(object);
 
-                       if (object->sculpt->pbvh) {
-                               BKE_pbvh_free(object->sculpt->pbvh);
-                               object->sculpt->pbvh = NULL;
-                       }
+                       MEM_SAFE_FREE(object->sculpt->pbvh);
 
                        sculptsession_bm_to_me_update_data_only(object, false);
 
@@ -764,6 +784,8 @@ void BKE_sculptsession_free(Object *ob)
                if (ss->deform_imats)
                        MEM_freeN(ss->deform_imats);
 
+               BKE_sculptsession_free_vwpaint_data(ob->sculpt);
+
                MEM_freeN(ss);
 
                ob->sculpt = NULL;
@@ -848,6 +870,8 @@ void BKE_sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob,
        ss->modifiers_active = sculpt_modifiers_active(scene, sd, ob);
        ss->show_diffuse_color = (sd->flags & SCULPT_SHOW_DIFFUSE) != 0;
 
+       ss->building_vp_handle = false;
+
        if (need_mask) {
                if (mmd == NULL) {
                        if (!CustomData_has_layer(&me->vdata, CD_PAINT_MASK)) {
@@ -876,7 +900,8 @@ void BKE_sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob,
 
        dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
 
-       if (mmd) {
+       /* VWPaint require mesh info for loop lookup, so require sculpt mode here */
+       if (mmd && ob->mode & OB_MODE_SCULPT) {
                ss->multires = mmd;
                ss->totvert = dm->getNumVerts(dm);
                ss->totpoly = dm->getNumPolys(dm);
index 53dfffe2b97f3bb566ecde0f9aad749860a8c4a5..4b154d3301c7c23a965eb3511af18d013dea6a48 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "BKE_pbvh.h"
 #include "BKE_ccg.h"
+#include "BKE_subsurf.h"
 #include "BKE_DerivedMesh.h"
 #include "BKE_global.h"
 #include "BKE_mesh.h" /* for BKE_mesh_calc_normals */
@@ -606,6 +607,10 @@ void BKE_pbvh_build_grids(PBVH *bvh, CCGElem **grids,
        MEM_freeN(prim_bbc);
 }
 
+void BKE_pbvh_set_ccgdm(PBVH *bvh, CCGDerivedMesh *ccgdm) {
+       bvh->ccgdm = ccgdm;
+}
+
 PBVH *BKE_pbvh_new(void)
 {
        PBVH *bvh = MEM_callocN(sizeof(PBVH), "pbvh");
@@ -1156,7 +1161,7 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode)
        }
 }
 
-static void pbvh_draw_BB(PBVH *bvh)
+void BKE_pbvh_draw_BB(PBVH *bvh)
 {
        GPU_pbvh_BB_draw_init();
 
@@ -1329,6 +1334,11 @@ void BKE_pbvh_get_grid_key(const PBVH *bvh, CCGKey *key)
        *key = bvh->gridkey;
 }
 
+CCGDerivedMesh *BKE_pbvh_get_ccgdm(const PBVH *bvh) {
+       return bvh->ccgdm;
+}
+
+
 BMesh *BKE_pbvh_get_bmesh(PBVH *bvh)
 {
        BLI_assert(bvh->type == PBVH_BMESH);
@@ -1860,7 +1870,7 @@ void BKE_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*fnors)[3],
        }
 
        if (G.debug_value == 14)
-               pbvh_draw_BB(bvh);
+               BKE_pbvh_draw_BB(bvh);
 }
 
 void BKE_pbvh_grids_update(PBVH *bvh, CCGElem **grids, void **gridfaces,
index 19d3b31bd3172c40baec47d7f30b67eebb0fb59c..010573185683f225a0dbe40abbbf87dd142a347b 100644 (file)
@@ -149,6 +149,8 @@ struct PBVH {
         * objects in sculpt mode with different sizes at the same time, so now storing that common gpu buffer
         * in an opaque pointer per pbvh. See T47637. */
        struct GridCommonGPUBuffer *grid_common_gpu_buffer;
+       /* The ccgdm is required for CD_ORIGINDEX lookup in vertex paint + multires */
+       struct CCGDerivedMesh *ccgdm;
 
        /* Only used during BVH build and update,
         * don't need to remain valid after */
index 5b809386267dfaeb0a1b550ce0f7c11b0c25697c..c0dcac3365682feff47d9352ed08e7906c075475 100644 (file)
@@ -217,16 +217,10 @@ void BKE_scene_copy_data(Main *bmain, Scene *sce_dst, const Scene *sce_src, cons
                ToolSettings *ts = sce_dst->toolsettings = MEM_dupallocN(sce_dst->toolsettings);
                if (ts->vpaint) {
                        ts->vpaint = MEM_dupallocN(ts->vpaint);
-                       ts->vpaint->paintcursor = NULL;
-                       ts->vpaint->vpaint_prev = NULL;
-                       ts->vpaint->wpaint_prev = NULL;
                        BKE_paint_copy(&ts->vpaint->paint, &ts->vpaint->paint, flag_subdata);
                }
                if (ts->wpaint) {
                        ts->wpaint = MEM_dupallocN(ts->wpaint);
-                       ts->wpaint->paintcursor = NULL;
-                       ts->wpaint->vpaint_prev = NULL;
-                       ts->wpaint->wpaint_prev = NULL;
                        BKE_paint_copy(&ts->wpaint->paint, &ts->wpaint->paint, flag_subdata);
                }
                if (ts->sculpt) {
@@ -335,16 +329,10 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type)
                if (ts) {
                        if (ts->vpaint) {
                                ts->vpaint = MEM_dupallocN(ts->vpaint);
-                               ts->vpaint->paintcursor = NULL;
-                               ts->vpaint->vpaint_prev = NULL;
-                               ts->vpaint->wpaint_prev = NULL;
                                BKE_paint_copy(&ts->vpaint->paint, &ts->vpaint->paint, 0);
                        }
                        if (ts->wpaint) {
                                ts->wpaint = MEM_dupallocN(ts->wpaint);
-                               ts->wpaint->paintcursor = NULL;
-                               ts->wpaint->vpaint_prev = NULL;
-                               ts->wpaint->wpaint_prev = NULL;
                                BKE_paint_copy(&ts->wpaint->paint, &ts->wpaint->paint, 0);
                        }
                        if (ts->sculpt) {
index f4ff4dfa019d9e3ed4c3588ebb7a9ac4986ff11d..7c5ee42b7bc9a09bbe53d628139e4d7069d42933 100644 (file)
@@ -3683,6 +3683,11 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
        int gridFaces = gridSize - 1, totface;
        int prev_mat_nr = -1;
 
+       if (ccgdm->pbvh) {
+               if (G.debug_value == 14)
+                       BKE_pbvh_draw_BB(ccgdm->pbvh);
+       }
+
 #ifdef WITH_OPENSUBDIV
        if (ccgdm->useGpuBackend) {
                int new_matnr;
@@ -4416,7 +4421,8 @@ static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
        if (!ob->sculpt)
                return NULL;
 
-       grid_pbvh = ccgDM_use_grid_pbvh(ccgdm);
+       /* In vwpaint, we always use a grid_pbvh for multires/subsurf */
+       grid_pbvh = (!(ob->mode & OB_MODE_SCULPT) || ccgDM_use_grid_pbvh(ccgdm));
 
        if (ob->sculpt->pbvh) {
                if (grid_pbvh) {
@@ -4432,12 +4438,18 @@ static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
                ccgdm->pbvh = ob->sculpt->pbvh;
        }
 
-       if (ccgdm->pbvh)
+       if (ccgdm->pbvh) {
+               /* For vertex paint, keep track of ccgdm */
+               if (!(ob->mode & OB_MODE_SCULPT)) {
+                       BKE_pbvh_set_ccgdm(ccgdm->pbvh, ccgdm);
+               }
                return ccgdm->pbvh;
+       }
 
        /* no pbvh exists yet, we need to create one. only in case of multires
         * we build a pbvh over the modified mesh, in other cases the base mesh
         * is being sculpted, so we build a pbvh from that. */
+       /* Note: vwpaint always builds a pbvh over the modified mesh. */
        if (grid_pbvh) {
                ccgdm_create_grids(dm);
 
@@ -4468,6 +4480,10 @@ static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
        if (ccgdm->pbvh)
                pbvh_show_diffuse_color_set(ccgdm->pbvh, ob->sculpt->show_diffuse_color);
 
+       /* For vertex paint, keep track of ccgdm */
+       if (!(ob->mode & OB_MODE_SCULPT) && ccgdm->pbvh) {
+               BKE_pbvh_set_ccgdm(ccgdm->pbvh, ccgdm);
+       }
        return ccgdm->pbvh;
 }
 
index f2d958adb955d34ce044eef1c3a40ebf17475733..3b7662be2b2d09338ad8fac6c38252622b7b76bf 100644 (file)
@@ -6003,16 +6003,6 @@ static void direct_link_scene(FileData *fd, Scene *sce)
                sce->toolsettings->particle.scene = NULL;
                sce->toolsettings->particle.object = NULL;
                sce->toolsettings->gp_sculpt.paintcursor = NULL;
-
-               /* in rare cases this is needed, see [#33806] */
-               if (sce->toolsettings->vpaint) {
-                       sce->toolsettings->vpaint->vpaint_prev = NULL;
-                       sce->toolsettings->vpaint->tot = 0;
-               }
-               if (sce->toolsettings->wpaint) {
-                       sce->toolsettings->wpaint->wpaint_prev = NULL;
-                       sce->toolsettings->wpaint->tot = 0;
-               }
                
                /* relink grease pencil drawing brushes */
                link_list(fd, &sce->toolsettings->gp_brushes);
index f87d04fa0a3f0f9b624bb5344c21c58905b901d0..808914459fe4a2d3e131a465d4c1ec1f5a6dd6e2 100644 (file)
@@ -60,6 +60,7 @@
 #include "DNA_genfile.h"
 
 #include "BKE_animsys.h"
+#include "BKE_brush.h"
 #include "BKE_colortools.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
@@ -1681,6 +1682,24 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
                                }
                        }
                }
+
+               {
+                       Brush *br;
+                       br = (Brush *)BKE_libblock_find_name_ex(main, ID_BR, "Average");
+                       if (!br) {
+                               br = BKE_brush_add(main, "Average", OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT);
+                               br->vertexpaint_tool = PAINT_BLEND_AVERAGE;
+                               br->ob_mode = OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT;
+                       }
+
+                       br = (Brush *)BKE_libblock_find_name_ex(main, ID_BR, "Smear");
+                       if (!br) {
+                               br = BKE_brush_add(main, "Smear", OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT);
+                               br->vertexpaint_tool = PAINT_BLEND_SMEAR;
+                               br->ob_mode = OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT;
+                       }
+               }
+
        }
 }
 
index 0ae8570846ed4f4c63fe20a4eb640c484cf51d01..eb8a72e12a775a42839d8e10dc830bbbb91a7ff6 100644 (file)
@@ -109,6 +109,16 @@ void BLO_update_defaults_startup_blend(Main *bmain)
                                sculpt->detail_size = 12;
                        }
                        
+                       if (ts->vpaint) {
+                               VPaint *vp = ts->vpaint;
+                               vp->radial_symm[0] = vp->radial_symm[1] = vp->radial_symm[2] = 1;
+                       }
+
+                       if (ts->wpaint) {
+                               VPaint *wp = ts->wpaint;
+                               wp->radial_symm[0] = wp->radial_symm[1] = wp->radial_symm[2] = 1;
+                       }
+
                        if (ts->gp_sculpt.brush[0].size == 0) {
                                GP_BrushEdit_Settings *gset = &ts->gp_sculpt;
                                GP_EditBrush_Data *brush;
index bf344e1f7210a58af133f8e8bb2670d4b9ed08da..79ce440251d47d143ff41995427185cc89d81cc0 100644 (file)
@@ -1448,7 +1448,20 @@ void PAINT_OT_texture_paint_toggle(wmOperatorType *ot)
 static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op))
 {
        UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
-       Brush *br = image_paint_brush(C);
+
+       Brush *br;
+       Object *ob = CTX_data_active_object(C);
+       if (!(ob && (ob->mode & OB_MODE_VERTEX_PAINT))) {
+               br = image_paint_brush(C);
+       }
+       else {
+               /* At the moment, wpaint does not support the color flipper. 
+                * So for now we're only handling vpaint */
+               ToolSettings *ts = CTX_data_tool_settings(C);
+               VPaint *vp = ts->vpaint;
+               br = BKE_paint_brush(&vp->paint);
+       }
+
        if (ups->flag & UNIFIED_PAINT_COLOR) {
                swap_v3_v3(ups->rgb, ups->secondary_rgb);
        }
@@ -1467,7 +1480,12 @@ static int brush_colors_flip_poll(bContext *C)
                if (br->imagepaint_tool == PAINT_TOOL_DRAW)
                        return 1;
        }
-
+       else {
+               Object *ob = CTX_data_active_object(C);
+               if (ob && (ob->mode & OB_MODE_VERTEX_PAINT)) {
+                       return 1;
+               }
+       }
        return 0;
 }
 
index 7e05ab929aeb2b9de0534feaff8a98a65e1682f7..0ec7d97a04db48008b4ef758b46e47d9f284164d 100644 (file)
@@ -97,7 +97,7 @@ int vertex_paint_poll(struct bContext *C);
 int vertex_paint_mode_poll(struct bContext *C);
 
 bool ED_vpaint_fill(struct Object *ob, unsigned int paintcol);
-bool ED_wpaint_fill(struct VPaint *wp, struct Object *ob, float paintweight);
+bool ED_wpaint_fill(struct Object *ob, float paintweight);
 
 bool ED_vpaint_smooth(struct Object *ob);
 
index 123a70d5044c3013e5dda7dafce3a642c2fe9738..4ebf14ed0b91bcb9bf3adccb22ace06baf1756c7 100644 (file)
@@ -1613,6 +1613,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
        keymap->poll = vertex_paint_mode_poll;
 
        WM_keymap_verify_item(keymap, "PAINT_OT_vertex_paint", LEFTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "PAINT_OT_brush_colors_flip", XKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "PAINT_OT_sample_color", SKEY, KM_PRESS, 0, 0);
 
        WM_keymap_add_item(keymap,
index 729dd9dc57b57274ff138d63668831e390115931..a88b834d60124988b5fa4cfa52e0c247b06c13f1 100644 (file)
@@ -35,7 +35,7 @@
 #include "BLI_math.h"
 #include "BLI_array_utils.h"
 #include "BLI_bitmap.h"
-#include "BLI_stack.h"
+#include "BLI_task.h"
 #include "BLI_string_utils.h"
 
 #include "IMB_imbuf.h"
@@ -66,6 +66,7 @@
 #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_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;
 };
 
+static void defweight_prev_init(const MDeformWeight *dw, float *weight_prev)
+{
+       if (UNLIKELY(*weight_prev == -1.0f)) {
+               *weight_prev = dw ? dw->weight : 0.0f;
+       }
+}
+
 /* 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)
@@ -174,24 +191,19 @@ static VPaint *new_vpaint(int wpaint)
        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;
+       return *(uint *)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;
+       const MPoly *mp;
        int (*scol)[4];
        int i, j;
        bool has_shared = false;
@@ -205,7 +217,7 @@ static void do_shared_vertexcol(Mesh *me, bool *mlooptag)
 
        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;
+                       const 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;
@@ -228,7 +240,7 @@ static void do_shared_vertexcol(Mesh *me, bool *mlooptag)
 
                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;
+                               const 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]) {
@@ -292,55 +304,42 @@ static int wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active)
        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;
-       }
-}
+struct WPaintPrev {
+       struct MDeformVert *wpaint_prev;        /* previous vertex weights */
+       int tot;                                                        /* allocation size of prev buffers */
+};
 
-static void free_wpaint_prev(VPaint *vp)
+static void wpaint_prev_init(struct WPaintPrev *wpp)
 {
-       if (vp->wpaint_prev) {
-               BKE_defvert_array_free(vp->wpaint_prev, vp->tot);
-               vp->wpaint_prev = NULL;
-               vp->tot = 0;
-       }
+       wpp->wpaint_prev = NULL;
+       wpp->tot = 0;
 }
 
-static void copy_vpaint_prev(VPaint *vp, unsigned int *lcol, int tot)
+static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount)
 {
-       free_vpaint_prev(vp);
+       wpaint_prev_init(wpp);
 
-       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);
-       
+       if (dverts && dcount) {
+               wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
+               wpp->tot = dcount;
+               BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount);
+       }
 }
 
-static void copy_wpaint_prev(VPaint *wp, MDeformVert *dverts, int dcount)
+static void wpaint_prev_destroy(struct WPaintPrev *wpp)
 {
-       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);
+       if (wpp->wpaint_prev) {
+               BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot);
        }
+       wpp->wpaint_prev = NULL;
+       wpp->tot = 0;
 }
 
-bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
+bool ED_vpaint_fill(Object *ob, uint paintcol)
 {
        Mesh *me;
-       MPoly *mp;
+       const MPoly *mp;
        int i, j;
-       bool selected;
 
        if (((me = BKE_mesh_from_object(ob)) == NULL) ||
            (me->mloopcol == NULL && (make_vertexcol(ob) == false)))
@@ -348,13 +347,13 @@ bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
                return false;
        }
 
-       selected = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       const bool use_face_sel = (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))
+               if (use_face_sel && !(mp->flag & ME_FACE_SEL))
                        continue;
 
                for (j = 0; j < mp->totloop; j++, lcol++) {
@@ -372,13 +371,13 @@ bool ED_vpaint_fill(Object *ob, unsigned int paintcol)
 
 
 /* fills in the selected faces with the current weight and vertex group */
-bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
+bool ED_wpaint_fill(Object *ob, float paintweight)
 {
        Mesh *me = ob->data;
-       MPoly *mp;
+       const MPoly *mp;
        MDeformWeight *dw, *dw_prev;
        int vgroup_active, vgroup_mirror = -1;
-       unsigned int index;
+       uint index;
        const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
 
        /* mutually exclusive, could be made into a */
@@ -395,17 +394,18 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
                vgroup_mirror = wpaint_mirror_vgroup_ensure(ob, vgroup_active);
        }
        
-       copy_wpaint_prev(wp, me->dvert, me->totvert);
+       struct WPaintPrev wpp;
+       wpaint_prev_create(&wpp, me->dvert, me->totvert);
        
        for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
-               unsigned int fidx = mp->totloop - 1;
+               uint 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;
+                       uint vidx = me->mloop[mp->loopstart + fidx].v;
 
                        if (!me->dvert[vidx].flag) {
                                if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
@@ -414,7 +414,7 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
 
                                dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
                                if (dw) {
-                                       dw_prev = defvert_verify_index(wp->wpaint_prev + vidx, vgroup_active);
+                                       dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active);
                                        dw_prev->weight = dw->weight; /* set the undo weight */
                                        dw->weight = paintweight;
 
@@ -424,11 +424,11 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
                                                        /* 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);
+                                                               dw_prev = defvert_verify_index(wpp.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 = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active);
                                                        }
                                                        dw_prev->weight = dw->weight; /* set the undo weight */
                                                        dw->weight = paintweight;
@@ -448,7 +448,7 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
                }
        }
 
-       copy_wpaint_prev(wp, NULL, 0);
+       wpaint_prev_destroy(&wpp);
 
        DAG_id_tag_update(&me->id, 0);
 
@@ -458,12 +458,11 @@ bool ED_wpaint_fill(VPaint *wp, Object *ob, float paintweight)
 bool ED_vpaint_smooth(Object *ob)
 {
        Mesh *me;
-       MPoly *mp;
+       const MPoly *mp;
 
        int i, j;
 
        bool *mlooptag;
-       bool selected;
 
        if (((me = BKE_mesh_from_object(ob)) == NULL) ||
            (me->mloopcol == NULL && (make_vertexcol(ob) == false)))
@@ -471,17 +470,17 @@ bool ED_vpaint_smooth(Object *ob)
                return false;
        }
 
-       selected = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       const bool use_face_sel = (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;
+               const MLoop *ml = me->mloop + mp->loopstart;
                int ml_index = mp->loopstart;
 
-               if (selected && !(mp->flag & ME_FACE_SEL))
+               if (use_face_sel && !(mp->flag & ME_FACE_SEL))
                        continue;
 
                for (j = 0; j < mp->totloop; j++, ml_index++, ml++) {
@@ -518,13 +517,13 @@ bool ED_vpaint_color_transform(
                return false;
        }
 
-       const bool do_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       const bool use_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)) {
+               if (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
                        continue;
                }
 
@@ -555,7 +554,7 @@ void vpaint_dogamma(Scene *scene)
        Object *ob;
        float igam, fac;
        int a, temp;
-       unsigned char *cp, gamtab[256];
+       uchar *cp, gamtab[256];
 
        ob = OBACT;
        me = BKE_mesh_from_object(ob);
@@ -577,7 +576,7 @@ void vpaint_dogamma(Scene *scene)
        }
 
        a = 4 * me->totface;
-       cp = (unsigned char *)me->mcol;
+       cp = (uchar *)me->mcol;
        while (a--) {
 
                cp[1] = gamtab[cp[1]];
@@ -589,11 +588,11 @@ void vpaint_dogamma(Scene *scene)
 }
 #endif
 
-BLI_INLINE unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac)
+BLI_INLINE uint mcol_blend(uint col1, uint col2, int fac)
 {
-       unsigned char *cp1, *cp2, *cp;
+       uchar *cp1, *cp2, *cp;
        int mfac;
-       unsigned int col = 0;
+       uint col = 0;
 
        if (fac == 0) {
                return col1;
@@ -605,31 +604,40 @@ BLI_INLINE unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac
 
        mfac = 255 - fac;
 
-       cp1 = (unsigned char *)&col1;
-       cp2 = (unsigned char *)&col2;
-       cp  = (unsigned char *)&col;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&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);
+       /* Updated to use the rgb squared color model which blends nicer. */
+       int r1 = cp1[0] * cp1[0];
+       int g1 = cp1[1] * cp1[1];
+       int b1 = cp1[2] * cp1[2];
+
+       int r2 = cp2[0] * cp2[0];
+       int g2 = cp2[1] * cp2[1];
+       int b2 = cp2[2] * cp2[2];
+
+       cp[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255)));
+       cp[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255)));
+       cp[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255)));
        cp[3] = 255;
 
        return col;
 }
 
-BLI_INLINE unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac)
+BLI_INLINE uint mcol_add(uint col1, uint col2, int fac)
 {
-       unsigned char *cp1, *cp2, *cp;
+       uchar *cp1, *cp2, *cp;
        int temp;
-       unsigned int col = 0;
+       uint col = 0;
 
        if (fac == 0) {
                return col1;
        }
 
-       cp1 = (unsigned char *)&col1;
-       cp2 = (unsigned char *)&col2;
-       cp  = (unsigned char *)&col;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
 
        temp = cp1[0] + divide_round_i((fac * cp2[0]), 255);
        cp[0] = (temp > 254) ? 255 : temp;
@@ -642,19 +650,19 @@ BLI_INLINE unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac)
        return col;
 }
 
-BLI_INLINE unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac)
+BLI_INLINE uint mcol_sub(uint col1, uint col2, int fac)
 {
-       unsigned char *cp1, *cp2, *cp;
+       uchar *cp1, *cp2, *cp;
        int temp;
-       unsigned int col = 0;
+       uint col = 0;
 
        if (fac == 0) {
                return col1;
        }
 
-       cp1 = (unsigned char *)&col1;
-       cp2 = (unsigned char *)&col2;
-       cp  = (unsigned char *)&col;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
 
        temp = cp1[0] - divide_round_i((fac * cp2[0]), 255);
        cp[0] = (temp < 0) ? 0 : temp;
@@ -667,11 +675,11 @@ BLI_INLINE unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac)
        return col;
 }
 
-BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
+BLI_INLINE uint mcol_mul(uint col1, uint col2, int fac)
 {
-       unsigned char *cp1, *cp2, *cp;
+       uchar *cp1, *cp2, *cp;
        int mfac;
-       unsigned int col = 0;
+       uint col = 0;
 
        if (fac == 0) {
                return col1;
@@ -679,9 +687,9 @@ BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
 
        mfac = 255 - fac;
 
-       cp1 = (unsigned char *)&col1;
-       cp2 = (unsigned char *)&col2;
-       cp  = (unsigned char *)&col;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
 
        /* first mul, then blend the fac */
        cp[0] = divide_round_i(mfac * cp1[0] * 255 + fac * cp2[0] * cp1[0], 255 * 255);
@@ -692,11 +700,11 @@ BLI_INLINE unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
        return col;
 }
 
-BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int fac)
+BLI_INLINE uint mcol_lighten(uint col1, uint col2, int fac)
 {
-       unsigned char *cp1, *cp2, *cp;
+       uchar *cp1, *cp2, *cp;
        int mfac;
-       unsigned int col = 0;
+       uint col = 0;
 
        if (fac == 0) {
                return col1;
@@ -707,9 +715,9 @@ BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int f
 
        mfac = 255 - fac;
 
-       cp1 = (unsigned char *)&col1;
-       cp2 = (unsigned char *)&col2;
-       cp  = (unsigned char *)&col;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
 
        /* See if are lighter, if so mix, else don't do anything.
         * if the paint col is darker then the original, then ignore */
@@ -725,11 +733,11 @@ BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int f
        return col;
 }
 
-BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fac)
+BLI_INLINE uint mcol_darken(uint col1, uint col2, int fac)
 {
-       unsigned char *cp1, *cp2, *cp;
+       uchar *cp1, *cp2, *cp;
        int mfac;
-       unsigned int col = 0;
+       uint col = 0;
 
        if (fac == 0) {
                return col1;
@@ -740,9 +748,9 @@ BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fa
 
        mfac = 255 - fac;
 
-       cp1 = (unsigned char *)&col1;
-       cp2 = (unsigned char *)&col2;
-       cp  = (unsigned char *)&col;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
 
        /* See if were darker, if so mix, else don't do anything.
         * if the paint col is brighter then the original, then ignore */
@@ -758,12 +766,14 @@ BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fa
 }
 
 /* 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)
+static uint vpaint_blend_tool(const int tool, const uint col,
+                                      const uint paintcol, const int alpha_i)
 {
        switch (tool) {
                case PAINT_BLEND_MIX:
                case PAINT_BLEND_BLUR:     return mcol_blend(col, paintcol, alpha_i);
+               case PAINT_BLEND_AVERAGE:  return mcol_blend(col, paintcol, alpha_i);
+               case PAINT_BLEND_SMEAR:    return mcol_blend(col, paintcol, alpha_i);
                case PAINT_BLEND_ADD:      return mcol_add(col, paintcol, alpha_i);
                case PAINT_BLEND_SUB:      return mcol_sub(col, paintcol, alpha_i);
                case PAINT_BLEND_MUL:      return mcol_mul(col, paintcol, alpha_i);
@@ -776,10 +786,11 @@ static unsigned int vpaint_blend_tool(const int tool, const unsigned int 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(
+        VPaint *vp, uint col, uint colorig,
+        const uint paintcol, const int alpha_i,
+        /* pre scaled from [0-1] --> [0-255] */
+        const int brush_alpha_value_i)
 {
        Brush *brush = BKE_paint_brush(&vp->paint);
        const int tool = brush->vertexpaint_tool;
@@ -788,7 +799,7 @@ static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colo
 
        /* if no spray, clip color adding with colorig & orig alpha */
        if ((vp->flag & VP_SPRAY) == 0) {
-               unsigned int testcol, a;
+               uint testcol, a;
                char *cp, *ct, *co;
                
                testcol = vpaint_blend_tool(tool, colorig, paintcol, brush_alpha_value_i);
@@ -813,49 +824,10 @@ static unsigned int vpaint_blend(VPaint *vp, unsigned int col, unsigned int colo
 }
 
 
-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])
+static float calc_vp_strength_col_dl(
+        VPaint *vp, const ViewContext *vc, const float co[3],
+        const float mval[2], const float brush_size_pressure, float rgba[4])
 {
        float co_ss[2];  /* screenspace */
 
@@ -891,10 +863,11 @@ static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co
        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])
+static float calc_vp_alpha_col_dl(
+        VPaint *vp, const 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);
 
@@ -960,6 +933,8 @@ static float wpaint_blend_tool(const int tool,
 {
        switch (tool) {
                case PAINT_BLEND_MIX:
+               case PAINT_BLEND_AVERAGE:
+               case PAINT_BLEND_SMEAR:
                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);
@@ -973,9 +948,9 @@ static float wpaint_blend_tool(const int tool,
 }
 
 /* vpaint has 'vpaint_blend' */
-static float wpaint_blend(VPaint *wp, float weight, float weight_prev,
+static float wpaint_blend(VPaint *wp, float weight,
                           const float alpha, float paintval,
-                          const float brush_alpha_value,
+                          const float UNUSED(brush_alpha_value),
                           const short do_flip)
 {
        Brush *brush = BKE_paint_brush(&wp->paint);
@@ -1000,21 +975,6 @@ static float wpaint_blend(VPaint *wp, float weight, float weight_prev,
 
        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);
-
-               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;
-               }
-       }
-
        return weight;
 }
 
@@ -1035,7 +995,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
        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;
+               uint index;
 
                view3d_operator_needs_opengl(C);
                ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
@@ -1146,7 +1106,7 @@ static EnumPropertyItem *weight_paint_sample_enum_itemf(
                                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;
+                               uint index;
 
                                const int mval[2] = {
                                    win->eventstate->x - vc.ar->winrct.xmin,
@@ -1164,8 +1124,8 @@ static EnumPropertyItem *weight_paint_sample_enum_itemf(
                                }
                                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;
+                                               const MPoly *mp = &me->mpoly[index];
+                                               uint fidx = mp->totloop - 1;
 
                                                do {
                                                        MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
@@ -1245,7 +1205,7 @@ void PAINT_OT_weight_sample_group(wmOperatorType *ot)
 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++) {
@@ -1291,7 +1251,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) {
@@ -1476,7 +1436,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,
@@ -1524,14 +1484,14 @@ static void do_weight_paint_vertex_single(
         /* vars which remain the same for every vert */
         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;
 
        /* mirror vars */
        int index_mirr;
@@ -1542,14 +1502,12 @@ static void do_weight_paint_vertex_single(
 
        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) {
+       if (dw == NULL) {
                return;
        }
 
@@ -1607,7 +1565,7 @@ static void do_weight_paint_vertex_single(
         * then there is no need to run the more complicated checks */
 
        {
-               dw->weight = wpaint_blend(wp, dw->weight, dw_prev->weight, alpha, paintweight,
+               dw->weight = wpaint_blend(wp, dw->weight, alpha, paintweight,
                                          wpi->brush_alpha_value, wpi->do_flip);
 
                /* WATCH IT: take care of the ordering of applying mirror -> normalize,
@@ -1669,11 +1627,10 @@ static void do_weight_paint_vertex_multi(
         /* vars which remain the same for every vert */
         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 */
@@ -1681,7 +1638,7 @@ static void do_weight_paint_vertex_multi(
        MDeformVert *dv_mirr = NULL;
 
        /* weights */
-       float oldw, curw, neww, change, curw_mirr, change_mirr;
+       float curw, 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) {
@@ -1693,8 +1650,6 @@ static void do_weight_paint_vertex_multi(
        }
 
        /* 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);
 
@@ -1703,7 +1658,7 @@ static void do_weight_paint_vertex_multi(
                return;
        }
 
-       neww = wpaint_blend(wp, curw, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
+       neww = wpaint_blend(wp, curw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip);
 
        change = neww / curw;
 
@@ -1759,7 +1714,7 @@ static void do_weight_paint_vertex(
         /* vars which remain the same for every vert */
         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);
@@ -1769,6 +1724,82 @@ 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;
+       if (ob->mode == OB_MODE_VERTEX_PAINT) {
+               gmap = &ob->sculpt->mode.vpaint.gmap;
+               ob->sculpt->mode_type = OB_MODE_VERTEX_PAINT;
+       }
+       else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
+               gmap = &ob->sculpt->mode.wpaint.gmap;
+               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 ((ts->vpaint->flag & VP_SPRAY) == 0) {
+                       if (ob->sculpt->mode.vpaint.previous_color == NULL) {
+                               ob->sculpt->mode.vpaint.previous_color =
+                                       MEM_callocN(me->totloop * sizeof(uint), "previous_color");
+                       }
+               }
+               else {
+                       MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color);
+               }
+       }
+       else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
+               if ((ts->wpaint->flag & VP_SPRAY) == 0) {
+                       if (ob->sculpt->mode.wpaint.alpha_weight == NULL) {
+                               ob->sculpt->mode.wpaint.alpha_weight =
+                                       MEM_callocN(me->totvert * sizeof(float), "alpha_weight");
+                       }
+                       if (ob->sculpt->mode.wpaint.previous_weight == NULL) {
+                               ob->sculpt->mode.wpaint.previous_weight =
+                                       MEM_mallocN(me->totvert * sizeof(float), "previous_weight");
+                       }
+               }
+               else {
+                       MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight);
+                       MEM_SAFE_FREE(ob->sculpt->mode.wpaint.previous_weight);
+               }
+       }
+
+}
+
 /* *************** set wpaint operator ****************** */
 
 /**
@@ -1805,6 +1836,14 @@ 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 {
@@ -1820,6 +1859,12 @@ 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,
@@ -1877,7 +1922,6 @@ struct WPaintVGroupIndex {
 
 struct WPaintData {
        ViewContext vc;
-       int *indexar;
 
        struct WeightPaintGroupData active, mirror;
 
@@ -1895,14 +1939,6 @@ 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;
 };
 
@@ -1982,19 +2018,129 @@ static bool wpaint_ensure_data(
        return true;
 }
 
-static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UNUSED(mouse[2]))
+/* Initialize the stroke cache invariants from operator properties */
+static void vwpaint_update_cache_invariants(
+        bContext *C, VPaint *vd, 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;
+       Brush *brush = BKE_paint_brush(&vd->paint);
+       ViewContext *vc = paint_stroke_view_context(op->customdata);
+       Object *ob = CTX_data_active_object(C);
+       float mat[3][3];
+       float view_dir[3] = {0.0f, 0.0f, 1.0f};
+       int mode;
+
+       /* VW paint needs to allocate stroke cache before update is called. */
+       if (!ss->cache) {
+               cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
+               ss->cache = cache;
+       }
+       else {
+               cache = ss->cache;
+       }
+
+       /* Initial mouse location */
+       if (mouse)
+               copy_v2_v2(cache->initial_mouse, mouse);
+       else
+               zero_v2(cache->initial_mouse);
+
+       mode = RNA_enum_get(op->ptr, "mode");
+       cache->invert = mode == BRUSH_STROKE_INVERT;
+       cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH;
+       /* not very nice, but with current events system implementation
+       * we can't handle brush appearance inversion hotkey separately (sergey) */
+       if (cache->invert) ups->draw_inverted = true;
+       else ups->draw_inverted = false;
+
+       copy_v2_v2(cache->mouse, cache->initial_mouse);
+       /* Truly temporary data that isn't stored in properties */
+       cache->vc = vc;
+       cache->brush = brush;
+       cache->first_time = 1;
+
+       /* cache projection matrix */
+       ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat);
+
+       invert_m4_m4(ob->imat, ob->obmat);
+       copy_m3_m4(mat, cache->vc->rv3d->viewinv);
+       mul_m3_v3(mat, view_dir);
+       copy_m3_m4(mat, ob->imat);
+       mul_m3_v3(mat, view_dir);
+       normalize_v3_v3(cache->true_view_normal, view_dir);
+
+       copy_v3_v3(cache->view_normal, cache->true_view_normal);
+       cache->bstrength = BKE_brush_alpha_get(scene, brush);
+       cache->is_last_valid = false;
+}
+
+/* Initialize the stroke cache variants from operator properties */
+static void vwpaint_update_cache_variants(bContext *C, VPaint *vd, Object *ob, PointerRNA *ptr)
+{
+       Scene *scene = CTX_data_scene(C);
+       SculptSession *ss = ob->sculpt;
+       StrokeCache *cache = ss->cache;
+       Brush *brush = BKE_paint_brush(&vd->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);
+       }
+
+       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");
+       }
+
+       /* Truly temporary data that isn't stored in properties */
+       if (cache->first_time) {
+               if (!BKE_brush_use_locked_size(scene, brush)) {
+                       cache->initial_radius = paint_calc_object_space_radius(
+                               cache->vc, cache->true_location, BKE_brush_size_get(scene, brush));
+                       BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius);
+               }
+               else {
+                       cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush);
+               }
+       }
+
+       if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, 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 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);
+       SculptSession *ss = ob->sculpt;
+       VPaint *vd = CTX_data_tool_settings(C)->wpaint;
 
        float mat[4][4], imat[4][4];
 
@@ -2094,62 +2240,598 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UN
        }
 
        /* painting on subsurfs should give correct points too, this returns me->totvert amount */
+       ob->sculpt->building_vp_handle = true;
        wpd->vp_handle = ED_vpaint_proj_handle_create(scene, ob, &wpd->vertexcosnos);
+       ob->sculpt->building_vp_handle = false;
+
+       /* imat for normals */
+       mul_m4_m4m4(mat, wpd->vc.rv3d->viewmat, ob->obmat);
+       invert_m4_m4(imat, mat);
+       copy_m3_m4(wpd->wpimat, imat);
 
-       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, vd, 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 (ss->mode.wpaint.previous_weight != NULL) {
+               copy_vn_fl(ss->mode.wpaint.previous_weight, me->totvert, -1.0f);
        }
 
-       if ((brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
-           (brush->flag & BRUSH_ACCUMULATE))
+       return true;
+}
+
+static void calc_area_normal_and_center_task_cb(void *userdata, const int n)
+{
+       SculptThreadedTaskData *data = userdata;
+       SculptSession *ss = data->ob->sculpt;
+       float(*area_nos)[3] = data->area_nos;
+       float(*area_cos)[3] = data->area_cos;
+
+       float private_co[2][3] = {{0.0f}};
+       float private_no[2][3] = {{0.0f}};
+       int   private_count[2] = {0};
+
+       SculptBrushTest test;
+       sculpt_brush_test_init(ss, &test);
+
+       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__);
+               const float *co;
+
+               co = vd.co;
+
+               if (sculpt_brush_test_fast(&test, co)) {
+                       float no_buf[3];
+                       const float *no;
+                       int flip_index;
+
+                       if (vd.no) {
+                               normal_short_to_float_v3(no_buf, vd.no);
+                               no = no_buf;
+                       }
+                       else {
+                               no = vd.fno;
+                       }
+
+                       flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f);
+                       if (area_cos)
+                               add_v3_v3(private_co[flip_index], co);
+                       if (area_nos)
+                               add_v3_v3(private_no[flip_index], no);
+                       private_count[flip_index] += 1;
+               }
        }
+       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);
 
-       return true;
+       BLI_mutex_lock(&data->mutex);
+
+       /* for flatten center */
+       if (area_cos) {
+               add_v3_v3(area_cos[0], private_co[0]);
+               add_v3_v3(area_cos[1], private_co[1]);
+       }
+
+       /* for area normal */
+       if (area_nos) {
+               add_v3_v3(area_nos[0], private_no[0]);
+               add_v3_v3(area_nos[1], private_no[1]);
+       }
+
+       /* weights */
+       data->count[0] += private_count[0];
+       data->count[1] += private_count[1];
+
+       BLI_mutex_unlock(&data->mutex);
 }
 
-static float wpaint_blur_weight_single(const MDeformVert *dv, const WeightPaintInfo *wpi)
+static void calc_area_normal(
+        VPaint *vp, Object *ob,
+        PBVHNode **nodes, int totnode,
+        float r_area_no[3])
 {
-       return defvert_find_weight(dv, wpi->active.index);
+       /* 0=towards view, 1=flipped */
+       float area_nos[2][3] = {{0.0f}};
+
+       int count[2] = {0};
+
+       SculptThreadedTaskData data = {
+               .vp = vp, .ob = ob, .nodes = nodes, .totnode = totnode,
+               .area_cos = NULL, .area_nos = area_nos, .count = count,
+       };
+       BLI_mutex_init(&data.mutex);
+
+       BLI_task_parallel_range(
+               0, totnode, &data, calc_area_normal_and_center_task_cb, true);
+
+       BLI_mutex_end(&data.mutex);
+
+       /* for area normal */
+       for (int i = 0; i < ARRAY_SIZE(area_nos); i++) {
+               if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) {
+                       break;
+               }
+       }
 }
 
-static float wpaint_blur_weight_multi(const MDeformVert *dv, const WeightPaintInfo *wpi)
+static float dot_vf3vs3(const float brushNormal[3], const short vertexNormal[3])
 {
-       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;
+       float normal[3];
+       normal_short_to_float_v3(normal, vertexNormal);
+       return dot_v3v3(brushNormal, normal);
 }
 
-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 *))
+/* Flip all the editdata across the axis/axes specified by symm. Used to
+ * calculate multiple modifications to the mesh when symmetry is enabled. */
+static void calc_brushdata_symm(
+        VPaint *vd, StrokeCache *cache, const char symm,
+        const char axis, const float angle)
 {
-       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);
+       (void)vd; /* unused */
+
+       flip_v3_v3(cache->location, cache->true_location, symm);
+       flip_v3_v3(cache->last_location, cache->true_last_location, symm);
+       flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm);
+       flip_v3_v3(cache->view_normal, cache->true_view_normal, symm);
+
+       unit_m4(cache->symm_rot_mat);
+       unit_m4(cache->symm_rot_mat_inv);
+       zero_v3(cache->plane_offset);
+
+       if (axis) { /* expects XYZ */
+               rotate_m4(cache->symm_rot_mat, axis, angle);
+               rotate_m4(cache->symm_rot_mat_inv, axis, -angle);
+       }
+
+       mul_m4_v3(cache->symm_rot_mat, cache->location);
+       mul_m4_v3(cache->symm_rot_mat, cache->last_location);
+       mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry);
+
+       if (cache->supports_gravity) {
+               flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm);
+               mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction);
+       }
+
+       if (cache->is_rake_rotation_valid) {
+               flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm);
+       }
+}
+
+static void get_brush_alpha_data(
+        Scene *scene, SculptSession *ss, 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 =
+               *r_brush_alpha_value *
+               (BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : 1.0f);
+}
+
+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;
+
+       Brush *brush = data->brush;
+       StrokeCache *cache = ss->cache;
+       Scene *scene = CTX_data_scene(data->C);
+
+       const float brush_strength = cache->bstrength;
+       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_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;
+       sculpt_brush_test_init(ss, &test);
+
+       /* 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(&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];
+                                               const MDeformVert *dv = &data->me->dvert[ml->v];
+                                               weight_final += defvert_find_weight(dv, data->wpi->active.index);
+                                       }
+                               }
+
+                               /* Apply the weight to the vertex. */
+                               if (total_hit_loops != 0) {
+                                       const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                                       if (view_dot > 0.0f) {
+                                               const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
+                                               const float final_alpha =
+                                                       view_dot * brush_fade * brush_strength *
+                                                       grid_alpha * brush_alpha_pressure;
+                                               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);
+                                       }
+                               }
+                       }
                }
-               paintweight /= map->count;
        }
-       else {
-               paintweight = blur_weight_func(&dvert[vidx], wpi);
+       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;
+
+       Brush *brush = data->brush;
+       Scene *scene = CTX_data_scene(data->C);
+       StrokeCache *cache = ss->cache;
+       const float brush_strength = cache->bstrength;
+       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_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);
+       if (normalize_v3(brush_dir) != 0.0f) {
+
+               SculptBrushTest test;
+               sculpt_brush_test_init(ss, &test);
+
+               /* 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_fast(&test, vd.co)) {
+                               const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                               if (view_dot > 0.0f) {
+                                       bool do_color = false;
+
+                                       /* 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];
+                                       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) {
+                                               /* 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];
+                                                       for (int k = 0; k < mp->totloop; k++) {
+                                                               const uint l_index = mp->loopstart + k;
+                                                               const MLoop *ml = &data->me->mloop[l_index];
+                                                               const uint v_other_index = ml->v;
+                                                               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);
+                                                               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;
+                                                                       MDeformVert *dv = &data->me->dvert[v_other_index];
+                                                                       weight_final = defvert_find_weight(dv, data->wpi->active.index);
+                                                                       do_color = true;
+                                                               }
+                                                       }
+                                               }
+                                               /* Apply weight to vertex */
+                                               if (do_color) {
+                                                       const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
+                                                       const float final_alpha =
+                                                               view_dot * brush_fade * brush_strength *
+                                                               grid_alpha * brush_alpha_pressure;
+                                                       do_weight_paint_vertex(
+                                                               data->vp, data->ob, data->wpi,
+                                                               v_index, final_alpha, (float)weight_final);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               BKE_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_wpaint_brush_draw_task_cb_ex(
+        void *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);
+
+       Brush *brush = data->brush;
+       StrokeCache *cache = ss->cache;
+       const float brush_strength = cache->bstrength;
+       const float paintweight = BKE_brush_weight_get(scene, brush);
+       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_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;
+       sculpt_brush_test_init(ss, &test);
+
+       /* 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(&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) {
+                               const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                               if (view_dot > 0.0f) {
+                                       const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
+                                       float final_alpha = view_dot * brush_fade * brush_strength * grid_alpha * brush_alpha_pressure;
+
+                                       /* Non-spray logic. */
+                                       if ((data->vp->flag & VP_SPRAY) == 0) {
+                                               /* Only paint if we have greater alpha. */
+                                               if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) {
+                                                       ss->mode.wpaint.alpha_weight[v_index] = final_alpha;
+                                               }
+                                               else {
+                                                       continue;
+                                               }
+
+                                               MDeformVert *dv = &data->me->dvert[v_index];
+                                               MDeformWeight *dw = defvert_find_index(dv, data->wpi->active.index);
+                                               float *weight_prev = &ss->mode.wpaint.previous_weight[v_index];
+                                               defweight_prev_init(dw, weight_prev);
+                                               if (dw) {
+                                                       dw->weight = *weight_prev;
+                                               }
+                                       }
+
+                                       do_weight_paint_vertex(
+                                               data->vp, data->ob, data->wpi,
+                                               v_index, final_alpha, paintweight);
+                               }
+                       }
+               }
+       }
+       BKE_pbvh_vertex_iter_end;
+}
+
+static void do_wpaint_brush_calc_average_weight_cb_ex(
+        void *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);
+
+       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;
+       sculpt_brush_test_init(ss, &test);
+
+       /* 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(&test, vd.co)) {
+                       const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                       if (view_dot > 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 += defvert_find_weight(dv, data->wpi->active.index);
+                               }
+                       }
+               }
+       }
+       BKE_pbvh_vertex_iter_end;
+}
+
+static void calculate_average_weight(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode)
+{
+       Scene *scene = CTX_data_scene(data->C);
+       UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+
+       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;
+               if (ups->flag & UNIFIED_PAINT_WEIGHT)
+                       ups->weight = (float)accum_weight;
+               else
+                       data->brush->weight = (float)accum_weight;
+       }
+
+       MEM_SAFE_FREE(data->custom_data);  /* 'accum' */
+}
+
+
+static void wpaint_paint_leaves(
+        bContext *C, Object *ob, Sculpt *sd, VPaint *vp, struct WPaintData *wpd, WeightPaintInfo *wpi,
+        Mesh *me, PBVHNode **nodes, int totnode)
+{
+       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,
+       };
+
+       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, true, false);
+                       break;
+               case PAINT_BLEND_SMEAR:
+                       BLI_task_parallel_range_ex(
+                               0, totnode, &data, NULL, 0,
+                               do_wpaint_brush_smear_task_cb_ex, true, false);
+                       break;
+               case PAINT_BLEND_BLUR:
+                       BLI_task_parallel_range_ex(
+                               0, totnode, &data, NULL, 0,
+                               do_wpaint_brush_blur_task_cb_ex, true, false);
+                       break;
+               default:
+                       BLI_task_parallel_range_ex(
+                               0, totnode, &data, NULL, 0,
+                               do_wpaint_brush_draw_task_cb_ex, true, false);
+                       break;
        }
+}
+
+static void wpaint_do_paint(
+        bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi,
+        Mesh *me, Brush *UNUSED(brush), const char symm, const int axis, const int i, const float angle)
+{
+       SculptSession *ss = ob->sculpt;
+       ss->cache->radial_symmetry_pass = i;
+       calc_brushdata_symm(wp, ss->cache, symm, axis, angle);
+
+       SculptSearchSphereData data;
+       PBVHNode **nodes = NULL;
+       int totnode;
 
-       return paintweight;
+
+       /* Build a list of all nodes that are potentially within the brush's area of influence */
+       data.ss = ss;
+       data.sd = sd;
+       data.radius_squared = ss->cache->radius_squared;
+       data.original = true;
+       BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+
+       calc_area_normal(wp, ob, nodes, totnode, ss->cache->sculpt_normal_symm);
+       wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode);
+
+       if (nodes)
+               MEM_freeN(nodes);
+}
+
+static void wpaint_do_radial_symmetry(
+        bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi,
+        Mesh *me, Brush *brush, const char symm, const int axis)
+{
+       for (int i = 1; i < wp->radial_symm[axis - 'X']; i++) {
+               const float angle = (2.0 * M_PI) * i / wp->radial_symm[axis - 'X'];
+               wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, symm, axis, i, angle);
+       }
+}
+
+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;
+                       calc_brushdata_symm(wp, 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)
@@ -2160,24 +2842,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;
@@ -2190,13 +2865,8 @@ 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);
@@ -2204,7 +2874,6 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
        /* 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;
@@ -2222,180 +2891,49 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
        wpi.brush_alpha_value =  brush_alpha_value;
        /* *** done setting up WeightPaintInfo *** */
 
+       wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);
 
+       swap_m4m4(vc->rv3d->persmat, mat);
 
-       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;
-       }
-
-       /* incase we have modifiers */
-       ED_vpaint_proj_handle_update(wpd->vp_handle, vc->ar, mval);
-
-       /* make sure each vertex gets treated only once */
-       /* and calculate filter weight */
-       paintweight = BKE_brush_weight_get(scene, brush);
-
-       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;
-                       }
+       rcti r;
+       if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) {
+               if (ss->cache) {
+                       ss->cache->current_r = r;
                }
-               else {
-                       for (i = 0; i < totvert; i++) {
-                               me->dvert[i].flag = SELECT;
-                       }
-               }
-       }
 
-       /* 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)
@@ -2407,23 +2945,9 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
                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);
-               }
-
                MEM_freeN(wpd);
        }
        
-       /* frees prev buffer */
-       copy_wpaint_prev(ts->wpaint, NULL, 0);
-       
        /* and particles too */
        if (ob->particlesystem.first) {
                ParticleSystem *psys;
@@ -2442,6 +2966,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;
 }
 
 
@@ -2449,9 +2976,10 @@ 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);
@@ -2468,9 +2996,10 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
 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);
@@ -2480,6 +3009,12 @@ 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);
 }
 
@@ -2516,7 +3051,7 @@ static int weight_paint_set_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
        }
 
-       if (ED_wpaint_fill(scene->toolsettings->wpaint, obact, vgroup_weight)) {
+       if (ED_wpaint_fill(obact, vgroup_weight)) {
                ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
                return OPERATOR_FINISHED;
        }
@@ -2570,6 +3105,14 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
                        BKE_mesh_flush_select_from_polys(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 {
@@ -2585,6 +3128,16 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
                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 */
@@ -2638,13 +3191,12 @@ typedef struct PolyFaceMap {
        int facenr;
 } PolyFaceMap;
 
-typedef struct VPaintData {
+struct VPaintData {
        ViewContext vc;
-       unsigned int paintcol;
-       int *indexar;
+       uint paintcol;
 
        struct VertProjHandle *vp_handle;
-       DMCoNo *vertexcosnos;
+       struct DMCoNo *vertexcosnos;
 
        float vpimat[3][3];
 
@@ -2657,9 +3209,9 @@ 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]))
+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;
@@ -2670,6 +3222,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
        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);
@@ -2682,13 +3235,10 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
                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);
-
-       vpd->indexar = get_indexarray(me);
        vpd->paintcol = vpaint_get_current_col(scene, vp);
 
        vpd->is_texbrush = !(brush->vertexpaint_tool == PAINT_BLEND_BLUR) &&
@@ -2710,84 +3260,535 @@ 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);
-       
+       /* 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;
+       }
+
        /* 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 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;
 
-       if (brush->vertexpaint_tool == PAINT_BLEND_BLUR) {
-               unsigned int blend[4] = {0};
-               unsigned int tcol;
-               char *col;
-
-               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];
-               }
-
-               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];
-
-               vpd->paintcol = *((unsigned int *)col);
-       }
-
-       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);
-
-               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);
+       const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+
+       struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n;
+       accum->len = 0;
+       memset(accum->value, 0, sizeof(accum->value));
+
+       SculptBrushTest test;
+       sculpt_brush_test_init(ss, &test);
+
+       /* 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_fast(&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, test.dist, cache->radius) > 0.0) {
+                               /* If the vertex is selected for painting. */
+                               const MVert *mv = &data->me->mvert[v_index];
+                               if (!use_face_sel || mv->flag & SELECT) {
+                                       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];
+                                       }
+                               }
+                       }
                }
-               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 handle_texture_brush(
+        SculptThreadedTaskData *data, PBVHVertexIter vd, float size_pressure, float alpha_pressure,
+        float *r_alpha, uint *r_color)
+{
+       SculptSession *ss = data->ob->sculpt;
+       CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
+       const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
+
+       float rgba[4];
+       float rgba_br[3];
+
+       *r_alpha = calc_vp_alpha_col_dl(
+               data->vp, &data->vpd->vc, data->vpd->vpimat,
+               &data->vpd->vertexcosnos[v_index], ss->cache->mouse, size_pressure, alpha_pressure, 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);
+}
 
-                       if (mlooptag) mlooptag[i] = 1;
+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;
+
+       Brush *brush = data->brush;
+       StrokeCache *cache = ss->cache;
+       const float brush_strength = cache->bstrength;
+       uint *lcol = data->lcol;
+       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_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+
+       SculptBrushTest test;
+       sculpt_brush_test_init(ss, &test);
+
+       /* 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(&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_face_sel || mv->flag & SELECT) {
+                               /* Calc the dot prod. between ray norm on surf and current vert
+                                * (ie splash prevention factor), and only paint front facing verts. */
+                               const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                               if (view_dot > 0.0f) {
+                                       const float brush_fade = BKE_brush_curve_strength(brush, 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) {
+                                               handle_texture_brush(
+                                                       data, vd, brush_size_pressure, brush_alpha_pressure,
+                                                       &tex_alpha, &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 * view_dot *
+                                                               tex_alpha * brush_alpha_pressure * grid_alpha;
+                                                       /* Mix the new color with the original based on final_alpha. */
+                                                       lcol[l_index] = vpaint_blend(
+                                                               data->vp, lcol[l_index], color_orig, color_final,
+                                                               final_alpha, 255 * brush_strength);
+                                               }
+                                       }
+                               }
+                       }
                }
        }
+       BKE_pbvh_vertex_iter_end;
+}
+
+static void do_vpaint_brush_blur_task_cb_ex(
+        void *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;
+       Brush *brush = data->brush;
+       StrokeCache *cache = ss->cache;
+       const float brush_strength = cache->bstrength;
+       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_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+
+       SculptBrushTest test;
+       sculpt_brush_test_init(ss, &test);
+
+       /* 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(&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];
+
+                       const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                       if (view_dot > 0.0f) {
+                               const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
+
+                               /* If the vertex is selected for painting. */
+                               if (!use_face_sel || mv->flag & SELECT) {
+                                       /* 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 * view_dot *
+                                                                       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;
+       Brush *brush = data->brush;
+       StrokeCache *cache = ss->cache;
+       const float brush_strength = cache->bstrength;
+       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_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+
+       sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
+       if (normalize_v3(brush_dir) != 0.0f) {
+
+               SculptBrushTest test;
+               sculpt_brush_test_init(ss, &test);
+
+               /* 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(&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_face_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. */
+                                       const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0;
+                                       if (view_dot > 0.0f) {
+                                               const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius);
+
+                                               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);
+                                                       const MPoly *mp = &data->me->mpoly[p_index];
+                                                       if (!use_face_sel || mp->flag & ME_FACE_SEL) {
+                                                               for (int k = 0; k < mp->totloop; k++) {
+                                                                       const MLoop *ml = &data->me->mloop[l_index];
+                                                                       const uint v_other_index = ml->v;
+                                                                       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);
+                                                                       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 = lcol[l_index];
+                                                                               do_color = true;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+
+                                               if (do_color) {
+                                                       /* 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];
+                                                                       }
+                                                                       const float final_alpha =
+                                                                               255 * brush_fade * brush_strength *
+                                                                               view_dot * 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, color_final,
+                                                                               final_alpha, 255 * brush_strength);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+               BKE_pbvh_vertex_iter_end;
+       }
+}
+
+static void calculate_average_color(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode)
+{
+       struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__);
+       data->custom_data = accum;
+
+       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)
+{
+       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 *vd, struct VPaintData *vpd,
+        Object *ob, Mesh *me, Brush *UNUSED(brush), const char symm, const int axis, const int i, const float angle)
+{
+       SculptSession *ss = ob->sculpt;
+       ss->cache->radial_symmetry_pass = i;
+       calc_brushdata_symm(vd, ss->cache, symm, axis, angle);
+       SculptSearchSphereData data;
+       PBVHNode **nodes = NULL;
+       int totnode;
+
+       /* Build a list of all nodes that are potentially within the brush's area of influence */
+       data.ss = ss;
+       data.sd = sd;
+       data.radius_squared = ss->cache->radius_squared;
+       data.original = true;
+       BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+
+       calc_area_normal(vd, ob, nodes, totnode, ss->cache->sculpt_normal_symm);
+
+       /* Paint those leaves. */
+       vpaint_paint_leaves(C, sd, vd, vpd, ob, me, nodes, totnode);
+
+       if (nodes) {
+               MEM_freeN(nodes);
+       }
+}
+
+static void vpaint_do_radial_symmetry(
+        bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob, Mesh *me,
+        Brush *brush, const char symm, const int axis)
+{
+       for (int i = 1; i < vd->radial_symm[axis - 'X']; i++) {
+               const float angle = (2.0 * M_PI) * i / vd->radial_symm[axis - 'X'];
+               vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, symm, axis, i, angle);
+       }
+}
+
+static void vpaint_do_symmetrical_brush_actions(
+        bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob)
+{
+       Brush *brush = BKE_paint_brush(&vd->paint);
+       Mesh *me = ob->data;
+       SculptSession *ss = ob->sculpt;
+       StrokeCache *cache = ss->cache;
+       const char symm = vd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+       int i = 0;
+
+       /* initial stroke */
+       vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0);
+       vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X');
+       vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y');
+       vpaint_do_radial_symmetry(C, sd, vd, 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;
+                       calc_brushdata_symm(vd, cache, i, 0, 0);
+
+                       if (i & (1 << 0)) {
+                               vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0);
+                               vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X');
+                       }
+                       if (i & (1 << 1)) {
+                               vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Y', 0, 0);
+                               vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y');
+                       }
+                       if (i & (1 << 2)) {
+                               vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Z', 0, 0);
+                               vpaint_do_radial_symmetry(C, sd, vd, 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)
@@ -2796,65 +3797,26 @@ 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);
+       vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob);
 
-       /* clear modified tag for blur tool */
-       if (vpd->mlooptag)
-               memset(vpd->mlooptag, 0, sizeof(bool) * me->totloop);
-
-       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);
-       }
-
        /* calculate pivot for rotation around seletion if needed */
        /* also needed for "View Selected" on last stroke */
        paint_last_stroke_update(scene, vc->ar, mval);
@@ -2874,32 +3836,26 @@ 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->mlooptag)
                MEM_freeN(vpd->mlooptag);
 
        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,
+       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);
        
@@ -2919,7 +3875,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
 static int vpaint_exec(bContext *C, wmOperator *op)
 {
-       op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start,
+       op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, vpaint_stroke_test_start,
                                          vpaint_stroke_update_step, NULL,
                                          vpaint_stroke_done, 0);
 
@@ -2931,6 +3887,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);
 }
 
@@ -3014,6 +3976,11 @@ typedef struct DMGradient_vertStore {
        } flag;
 } DMGradient_vertStore;
 
+typedef struct DMGradient_vertStoreBase {
+       struct WPaintPrev wpp;
+       DMGradient_vertStore elem[0];
+} DMGradient_vertStoreBase;
+
 typedef struct DMGradient_userData {
        struct ARegion *ar;
        Scene *scene;
@@ -3024,7 +3991,7 @@ typedef struct DMGradient_userData {
        float        sco_line_div;  /* store (1.0f / len_v2v2(sco_start, sco_end)) */
        int def_nr;
        bool is_init;
-       DMGradient_vertStore *vert_cache;
+       DMGradient_vertStoreBase *vert_cache;
        /* only for init */
        BLI_bitmap *vert_visit;
 
@@ -3037,7 +4004,7 @@ typedef struct 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];
+       DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
        float alpha;
 
        if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
@@ -3088,7 +4055,7 @@ static void gradientVertUpdate__mapFunc(
        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];
+               DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
                if (vs->sco[0] != FLT_MAX) {
                        gradientVert_update(grad_data, index);
                }
@@ -3108,14 +4075,14 @@ static void gradientVertInit__mapFunc(
                 * 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];
+                       DMGradient_vertStore *vs = &grad_data->vert_cache->elem[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;
+                               const MDeformWeight *dw;
                                dw = defvert_find_index(dv, grad_data->def_nr);
                                if (dw) {
                                        vs->weight_orig = dw->weight;
@@ -3141,34 +4108,37 @@ static void gradientVertInit__mapFunc(
 static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
        int ret = WM_gesture_straightline_modal(C, op, event);
+       wmGesture *gesture = op->customdata;
+       DMGradient_vertStoreBase *vert_cache = gesture->userdata;
+       bool do_gesture_free = false;
 
        if (ret & OPERATOR_RUNNING_MODAL) {
                if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {  /* XXX, hardcoded */
                        /* generally crap! redo! */
-                       WM_gesture_straightline_cancel(C, op);
+                       do_gesture_free = true;
                        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) {
+               if (vert_cache->wpp.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);
+                       BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
+                       wpaint_prev_destroy(&vert_cache->wpp);
                }
 
                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);
+               wpaint_prev_destroy(&vert_cache->wpp);
+       }
+
+       if (do_gesture_free) {
+               WM_gesture_straightline_cancel(C, op);
        }
 
        return ret;
@@ -3177,7 +4147,7 @@ static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEven
 static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
 {
        wmGesture *gesture = op->customdata;
-       DMGradient_vertStore *vert_cache;
+       DMGradient_vertStoreBase *vert_cache;
        struct ARegion *ar = CTX_wm_region(C);
        Scene *scene = CTX_data_scene(C);
        Object *ob = CTX_data_active_object(C);
@@ -3195,12 +4165,13 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
 
        if (is_interactive) {
                if (gesture->userdata == NULL) {
-                       VPaint *wp = scene->toolsettings->wpaint;
-
-                       gesture->userdata = MEM_mallocN(sizeof(DMGradient_vertStore) * me->totvert, __func__);
+                       gesture->userdata = MEM_mallocN(
+                               sizeof(DMGradient_vertStoreBase) +
+                               (sizeof(DMGradient_vertStore) * me->totvert),
+                               __func__);
                        data.is_init = true;
 
-                       copy_wpaint_prev(wp, me->dvert, me->totvert);
+                       wpaint_prev_create(&((DMGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
 
                        /* on init only, convert face -> vert sel  */
                        if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
@@ -3216,7 +4187,10 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
                }
 
                data.is_init = true;
-               vert_cache = MEM_mallocN(sizeof(DMGradient_vertStore) * me->totvert, __func__);
+               vert_cache = MEM_mallocN(
+                       sizeof(DMGradient_vertStoreBase) +
+                       (sizeof(DMGradient_vertStore) * me->totvert),
+                       __func__);
        }
 
        data.ar = ar;
index c415e6b007e3a9ba7723fcd9b3146e3c28998b7c..3c21fa5e9a2d0b157e6b643d86ad3eb81d059e33 100644 (file)
@@ -39,7 +39,6 @@
 #include "BLI_blenlib.h"
 #include "BLI_dial.h"
 #include "BLI_task.h"
-#include "BLI_threads.h"
 #include "BLI_utildefines.h"
 #include "BLI_ghash.h"
 
@@ -165,111 +164,12 @@ static bool sculpt_brush_needs_rake_rotation(const Brush *brush)
        return SCULPT_TOOL_HAS_RAKE(brush->sculpt_tool) && (brush->rake_factor != 0.0f);
 }
 
-/* Factor of brush to have rake point following behind
- * (could be configurable but this is reasonable default). */
-#define SCULPT_RAKE_BRUSH_FACTOR 0.25f
-
-struct SculptRakeData {
-       float follow_dist;
-       float follow_co[3];
-};
-
 typedef enum StrokeFlags {
        CLIP_X = 1,
        CLIP_Y = 2,
        CLIP_Z = 4
 } StrokeFlags;
 
-/* Cache stroke properties. Used because
- * RNA property lookup isn't particularly fast.
- *
- * For descriptions of these settings, check the operator properties.
- */
-typedef struct StrokeCache {
-       /* Invariants */
-       float initial_radius;
-       float scale[3];
-       int flag;
-       float clip_tolerance[3];
-       float initial_mouse[2];
-
-       /* Variants */
-       float radius;
-       float radius_squared;
-       float true_location[3];
-       float location[3];
-
-       bool pen_flip;
-       bool invert;
-       float pressure;
-       float mouse[2];
-       float bstrength;
-       float normal_weight;  /* from brush (with optional override) */
-
-       /* The rest is temporary storage that isn't saved as a property */
-
-       bool first_time; /* Beginning of stroke may do some things special */
-
-       /* from ED_view3d_ob_project_mat_get() */
-       float projection_mat[4][4];
-
-       /* Clean this up! */
-       ViewContext *vc;
-       Brush *brush;
-
-       float special_rotation;
-       float grab_delta[3], grab_delta_symmetry[3];
-       float old_grab_location[3], orig_grab_location[3];
-
-       /* screen-space rotation defined by mouse motion */
-       float   rake_rotation[4], rake_rotation_symmetry[4];
-       bool is_rake_rotation_valid;
-       struct SculptRakeData rake_data;
-
-       int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only;
-                      * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
-       int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/
-       float true_view_normal[3];
-       float view_normal[3];
-
-       /* sculpt_normal gets calculated by calc_sculpt_normal(), then the
-        * sculpt_normal_symm gets updated quickly with the usual symmetry
-        * transforms */
-       float sculpt_normal[3];
-       float sculpt_normal_symm[3];
-
-       /* Used for area texture mode, local_mat gets calculated by
-        * calc_brush_local_mat() and used in tex_strength(). */
-       float brush_local_mat[4][4];
-       
-       float plane_offset[3]; /* used to shift the plane around when doing tiled strokes */
-       int tile_pass;
-
-       float last_center[3];
-       int radial_symmetry_pass;
-       float symm_rot_mat[4][4];
-       float symm_rot_mat_inv[4][4];
-       bool original;
-       float anchored_location[3];
-
-       float vertex_rotation; /* amount to rotate the vertices when using rotate brush */
-       Dial *dial;
-       
-       char saved_active_brush_name[MAX_ID_NAME];
-       char saved_mask_brush_tool;
-       int saved_smooth_size; /* smooth tool copies the size of the current tool */
-       bool alt_smooth;
-
-       float plane_trim_squared;
-
-       bool supports_gravity;
-       float true_gravity_direction[3];
-       float gravity_direction[3];
-
-       rcti previous_r; /* previous redraw rectangle */
-       rcti current_r; /* current redraw rectangle */
-} StrokeCache;
-
 /************** Access to original unmodified vertex data *************/
 
 typedef struct {
@@ -476,41 +376,6 @@ static bool sculpt_stroke_is_dynamic_topology(
 
 /*** paint mesh ***/
 
-/* Single struct used by all BLI_task threaded callbacks, let's avoid adding 10's of those... */
-typedef struct SculptThreadedTaskData {
-       Sculpt *sd;
-       Object *ob;
-       Brush *brush;
-       PBVHNode **nodes;
-       int totnode;
-
-       /* Data specific to some callbacks. */
-       /* Note: even if only one or two of those are used at a time, keeping them separated, names help figuring out
-        *       what it is, and memory overhead is ridiculous anyway... */
-       float flippedbstrength;
-       float angle;
-       float strength;
-       bool smooth_mask;
-       bool has_bm_orco;
-
-       SculptProjectVector *spvc;
-       float *offset;
-       float *grab_delta;
-       float *cono;
-       float *area_no;
-       float *area_no_sp;
-       float *area_co;
-       float (*mat)[4];
-       float (*vertCos)[3];
-
-       /* 0=towards view, 1=flipped */
-       float (*area_cos)[3];
-       float (*area_nos)[3];
-       int *count;
-
-       ThreadMutex mutex;
-} SculptThreadedTaskData;
-
 static void paint_mesh_restore_co_task_cb(void *userdata, const int n)
 {
        SculptThreadedTaskData *data = userdata;
@@ -600,7 +465,7 @@ static void sculpt_extend_redraw_rect_previous(Object *ob, rcti *rect)
 }
 
 /* Get a screen-space rectangle of the modified area */
-static bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d,
+bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d,
                                    Object *ob, rcti *rect)
 {
        PBVH *pbvh = ob->sculpt->pbvh;
@@ -650,17 +515,7 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar,
 
 /************************ Brush Testing *******************/
 
-typedef struct SculptBrushTest {
-       float radius_squared;
-       float location[3];
-       float dist;
-       int mirror_symmetry_pass;
-
-       /* View3d clipping - only set rv3d for clipping */
-       RegionView3D *clip_rv3d;
-} SculptBrushTest;
-
-static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
+void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
 {
        RegionView3D *rv3d = ss->cache->vc->rv3d;
 
@@ -689,7 +544,7 @@ BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const fl
        return ED_view3d_clipping_test(rv3d, symm_co, true);
 }
 
-static bool sculpt_brush_test(SculptBrushTest *test, const float co[3])
+bool sculpt_brush_test(SculptBrushTest *test, const float co[3])
 {
        float distsq = len_squared_v3v3(co, test->location);
 
@@ -705,7 +560,7 @@ static bool sculpt_brush_test(SculptBrushTest *test, const float co[3])
        }
 }
 
-static bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3])
+bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3])
 {
        float distsq = len_squared_v3v3(co, test->location);
 
@@ -721,7 +576,7 @@ static bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3])
        }
 }
 
-static bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3])
+bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3])
 {
        if (sculpt_brush_test_clipping(test, co)) {
                return 0;
@@ -729,7 +584,7 @@ static bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3
        return len_squared_v3v3(co, test->location) <= test->radius_squared;
 }
 
-static bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4])
+bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4])
 {
        float side = M_SQRT1_2;
        float local_co[3];
@@ -1237,13 +1092,13 @@ static float brush_strength(
 }
 
 /* Return a multiplier for brush strength on a particular vertex. */
-static float tex_strength(SculptSession *ss, Brush *br,
-                          const float brush_point[3],
-                          const float len,
-                          const short vno[3],
-                          const float fno[3],
-                          const float mask,
-                          const int thread_id)
+float tex_strength(SculptSession *ss, Brush *br,
+                   const float brush_point[3],
+                   const float len,
+                   const short vno[3],
+                   const float fno[3],
+                   const float mask,
+                   const int thread_id)
 {
        StrokeCache *cache = ss->cache;
        const Scene *scene = cache->vc->scene;
@@ -1316,15 +1171,8 @@ static float tex_strength(SculptSession *ss, Brush *br,
        return avg;
 }
 
-typedef struct {
-       Sculpt *sd;
-       SculptSession *ss;
-       float radius_squared;
-       bool original;
-} SculptSearchSphereData;
-
 /* Test AABB against sphere */
-static bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
+bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
 {
        SculptSearchSphereData *data = data_v;
        float *center = data->ss->cache->location, nearest[3];
@@ -1632,6 +1480,22 @@ typedef struct SculptDoBrushSmoothGridDataChunk {
        size_t tmpgrid_size;
 } SculptDoBrushSmoothGridDataChunk;
 
+typedef struct {
+       SculptSession *ss;
+       const float *ray_start, *ray_normal;
+       bool hit;
+       float dist;
+       bool original;
+       PBVHNode* node;
+} SculptRaycastData;
+
+typedef struct {
+       const float *ray_start, *ray_normal;
+       bool hit;
+       float dist;
+       float detail;
+} SculptDetailRaycastData;
+
 static void do_smooth_brush_mesh_task_cb_ex(
         void *userdata, void *UNUSED(userdata_chunk), const int n, const int thread_id)
 {
@@ -3948,7 +3812,7 @@ static const char *sculpt_tool_name(Sculpt *sd)
  * Operator for applying a stroke (various attributes including mouse path)
  * using the current brush. */
 
-static void sculpt_cache_free(StrokeCache *cache)
+void sculpt_cache_free(StrokeCache *cache)
 {
        if (cache->dial)
                MEM_freeN(cache->dial);
@@ -4398,21 +4262,6 @@ static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob)
        }
 }
 
-typedef struct {
-       SculptSession *ss;
-       const float *ray_start, *ray_normal;
-       bool hit;
-       float dist;
-       bool original;
-} SculptRaycastData;
-
-typedef struct {
-       const float *ray_start, *ray_normal;
-       bool hit;
-       float dist;
-       float detail;
-} SculptDetailRaycastData;
-
 static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
 {
        if (BKE_pbvh_node_get_tmin(node) < *tmin) {
@@ -4437,6 +4286,9 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
                {
                        srd->hit = 1;
                        *tmin = srd->dist;
+
+                       //for vwpaint testing
+                       srd->node = node;
                }
        }
 }
@@ -4521,12 +4373,17 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
        srd.dist = dist;
 
        BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd,
-                        ray_start, ray_normal, srd.original);
+                        ray_start, ray_normal, srd.original);
 
        copy_v3_v3(out, ray_normal);
        mul_v3_fl(out, srd.dist);
        add_v3_v3(out, ray_start);
 
+       //used in vwpaint
+       if (cache && srd.hit){
+               copy_v3_v3(cache->true_location, out);
+       }
+
        return srd.hit;
 }
 
index 108fe3532e31340a744475030116775dbd16237e..850d46313117089cc47e5eea0e094f0dc65c361f 100644 (file)
 #include "DNA_key_types.h"
 
 #include "BLI_bitmap.h"
+#include "BLI_threads.h"
+
 #include "BKE_pbvh.h"
 
 struct bContext;
 struct KeyBlock;
 struct Object;
 struct SculptUndoNode;
+struct SculptOrigVertData;
 
 int sculpt_mode_poll(struct bContext *C);
 int sculpt_mode_poll_view3d(struct bContext *C);
@@ -115,6 +118,194 @@ typedef struct SculptUndoNode {
        char shapeName[sizeof(((KeyBlock *)0))->name];
 } SculptUndoNode;
 
+/* Factor of brush to have rake point following behind
+* (could be configurable but this is reasonable default). */
+#define SCULPT_RAKE_BRUSH_FACTOR 0.25f
+
+struct SculptRakeData {
+       float follow_dist;
+       float follow_co[3];
+};
+
+/* Single struct used by all BLI_task threaded callbacks, let's avoid adding 10's of those... */
+typedef struct SculptThreadedTaskData {
+       bContext *C;
+       struct Sculpt *sd;
+       struct Object *ob;
+       struct Brush *brush;
+       struct PBVHNode **nodes;
+       int totnode;
+
+       struct VPaint *vp;
+       struct VPaintData *vpd;
+       struct WPaintData *wpd;
+       struct WeightPaintInfo *wpi;
+       unsigned int *lcol;
+       struct Mesh *me;
+       /* For passing generic params. */
+       void *custom_data;
+
+
+       /* Data specific to some callbacks. */
+       /* Note: even if only one or two of those are used at a time, keeping them separated, names help figuring out
+       *       what it is, and memory overhead is ridiculous anyway... */
+       float flippedbstrength;
+       float angle;
+       float strength;
+       bool smooth_mask;
+       bool has_bm_orco;
+
+       struct SculptProjectVector *spvc;
+       float *offset;
+       float *grab_delta;
+       float *cono;
+       float *area_no;
+       float *area_no_sp;
+       float *area_co;
+       float(*mat)[4];
+       float(*vertCos)[3];
+
+       /* 0=towards view, 1=flipped */
+       float(*area_cos)[3];
+       float(*area_nos)[3];
+       int *count;
+
+       ThreadMutex mutex;
+
+} SculptThreadedTaskData;
+
+/*************** Brush testing declarations ****************/
+typedef struct SculptBrushTest {
+       float radius_squared;
+       float location[3];
+       float dist;
+       int mirror_symmetry_pass;
+
+       /* View3d clipping - only set rv3d for clipping */
+       struct RegionView3D *clip_rv3d;
+} SculptBrushTest;
+
+typedef struct {
+       struct Sculpt *sd;
+       struct SculptSession *ss;
+       float radius_squared;
+       bool original;
+} SculptSearchSphereData;
+
+void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test);
+bool sculpt_brush_test(SculptBrushTest *test, const float co[3]);
+bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3]);
+bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3]);
+bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]);
+bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v);
+float tex_strength(
+        SculptSession *ss, struct Brush *br,
+        const float point[3],
+        const float len,
+        const short vno[3],
+        const float fno[3],
+        const float mask,
+        const int thread_id);
+
+
+/* Cache stroke properties. Used because
+* RNA property lookup isn't particularly fast.
+*
+* For descriptions of these settings, check the operator properties.
+*/
+
+typedef struct StrokeCache {
+       /* Invariants */
+       float initial_radius;
+       float scale[3];
+       int flag;
+       float clip_tolerance[3];
+       float initial_mouse[2];
+
+       /* Variants */
+       float radius;
+       float radius_squared;
+       float true_location[3];
+       float true_last_location[3];
+       float location[3];
+       float last_location[3];
+       bool is_last_valid;
+
+       bool pen_flip;
+       bool invert;
+       float pressure;
+       float mouse[2];
+       float bstrength;
+       float normal_weight;  /* from brush (with optional override) */
+
+       /* The rest is temporary storage that isn't saved as a property */
+
+       bool first_time; /* Beginning of stroke may do some things special */
+
+       /* from ED_view3d_ob_project_mat_get() */
+       float projection_mat[4][4];
+
+       /* Clean this up! */
+       struct ViewContext *vc;
+       struct Brush *brush;
+
+       float special_rotation;
+       float grab_delta[3], grab_delta_symmetry[3];
+       float old_grab_location[3], orig_grab_location[3];
+
+       /* screen-space rotation defined by mouse motion */
+       float   rake_rotation[4], rake_rotation_symmetry[4];
+       bool is_rake_rotation_valid;
+       struct SculptRakeData rake_data;
+
+       /* Symmetry index between 0 and 7 bit combo 0 is Brush only;
+        * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
+       int symmetry;
+       int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/
+       float true_view_normal[3];
+       float view_normal[3];
+
+       /* sculpt_normal gets calculated by calc_sculpt_normal(), then the
+       * sculpt_normal_symm gets updated quickly with the usual symmetry
+       * transforms */
+       float sculpt_normal[3];
+       float sculpt_normal_symm[3];
+
+       /* Used for area texture mode, local_mat gets calculated by
+       * calc_brush_local_mat() and used in tex_strength(). */
+       float brush_local_mat[4][4];
+
+       float plane_offset[3]; /* used to shift the plane around when doing tiled strokes */
+       int tile_pass;
+
+       float last_center[3];
+       int radial_symmetry_pass;
+       float symm_rot_mat[4][4];
+       float symm_rot_mat_inv[4][4];
+       bool original;
+       float anchored_location[3];
+
+       float vertex_rotation; /* amount to rotate the vertices when using rotate brush */
+       struct Dial *dial;
+
+       char saved_active_brush_name[MAX_ID_NAME];
+       char saved_mask_brush_tool;
+       int saved_smooth_size; /* smooth tool copies the size of the current tool */
+       bool alt_smooth;
+
+       float plane_trim_squared;
+
+       bool supports_gravity;
+       float true_gravity_direction[3];
+       float gravity_direction[3];
+
+       rcti previous_r; /* previous redraw rectangle */
+       rcti current_r; /* current redraw rectangle */
+
+} StrokeCache;
+
+void sculpt_cache_free(StrokeCache *cache);
+
 SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type);
 SculptUndoNode *sculpt_undo_get_node(PBVHNode *node);
 void sculpt_undo_push_begin(const char *name);
@@ -124,6 +315,8 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]);
 
 void sculpt_update_object_bounding_box(struct Object *ob);
 
+bool sculpt_get_redraw_rect(struct ARegion *ar, struct RegionView3D *rv3d, Object *ob, rcti *rect);
+
 #define SCULPT_THREADED_LIMIT 4
 
 #endif
index 4e4c769f396808e80c6b5c7c99b68da9e87acdbe..eecfd88606f532c72f8d2073bacefd2567d74040 100644 (file)
@@ -315,7 +315,9 @@ enum {
        PAINT_BLEND_MUL = 3,
        PAINT_BLEND_BLUR = 4,
        PAINT_BLEND_LIGHTEN = 5,
-       PAINT_BLEND_DARKEN = 6
+       PAINT_BLEND_DARKEN = 6,
+       PAINT_BLEND_AVERAGE = 7,
+       PAINT_BLEND_SMEAR = 8,
 };
 
 typedef enum {
index 6d79e6d49f8bb534fb08ae832371cf0ef19cd94b..0f341aa40013aab987f7a3e678cf182cc3aa15ea 100644 (file)
@@ -683,6 +683,9 @@ typedef enum ObjectMode {
 /* any mode where the brush system is used */
 #define OB_MODE_ALL_PAINT (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)
 
+/* any mode that uses ob->sculpt */
+#define OB_MODE_ALL_SCULPT (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)
+
 #define MAX_DUPLI_RECUR 8
 
 #ifdef __cplusplus
index 740d640a6da3fd7ccd03de6b006b27220de8b55f..3201b75ee1e0985a33ce50c8a523443b720aace5 100644 (file)
@@ -1117,13 +1117,8 @@ typedef struct UvSculpt {
 /* Vertex Paint */
 typedef struct VPaint {
        Paint paint;
-
        short flag, pad;
-       int tot;                                                        /* allocation size of prev buffers */
-       unsigned int *vpaint_prev;                      /* previous mesh colors */
-       struct MDeformVert *wpaint_prev;        /* previous vertex weights */
-       
-       void *paintcursor;                                      /* wm handle */
+       int radial_symm[3]; /* For mirrored painting */
 } VPaint;
 
 /* VPaint.flag */
index ac348c1750cc5ddb1abf9e5361033d7807f149e1..7b3636f1615d08f9d5f170a17bc528c0f40568e6 100644 (file)
@@ -94,6 +94,8 @@ EnumPropertyItem rna_enum_brush_vertex_tool_items[] = {
        {PAINT_BLEND_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", "Blur the color with surrounding values"},
        {PAINT_BLEND_LIGHTEN, "LIGHTEN", ICON_BRUSH_LIGHTEN, "Lighten", "Use lighten blending mode while painting"},
        {PAINT_BLEND_DARKEN, "DARKEN", ICON_BRUSH_DARKEN, "Darken", "Use darken blending mode while painting"},
+       {PAINT_BLEND_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", "Use average blending mode while painting" },
+       {PAINT_BLEND_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", "Use smear blending mode while painting" },
        {0, NULL, 0, NULL, NULL}
 };
        
index 9e018de1e0cb10f0a45debc67c711af1f21ee607..09da34d59d86650117a55115910e6f618813292d 100644 (file)
@@ -687,6 +687,15 @@ static void rna_def_vertex_paint(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_ONLYVGROUP);
        RNA_def_property_ui_text(prop, "Restrict", "Restrict painting to vertices in the group");
        RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+       /* Mirroring */
+       prop = RNA_def_property(srna, "radial_symmetry", PROP_INT, PROP_XYZ);
+       RNA_def_property_int_sdna(prop, NULL, "radial_symm");
+       RNA_def_property_int_default(prop, 1);
+       RNA_def_property_range(prop, 1, 64);
+       RNA_def_property_ui_range(prop, 1, 32, 1, 1);
+       RNA_def_property_ui_text(prop, "Radial Symmetry Count X Axis",
+                                "Number of times to copy strokes across the surface");
 }
 
 static void rna_def_image_paint(BlenderRNA *brna)