Sculpt Mode: 2D falloff option
authorCampbell Barton <ideasman42@gmail.com>
Thu, 5 Oct 2017 13:18:11 +0000 (00:18 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 5 Oct 2017 14:20:31 +0000 (01:20 +1100)
This makes brush influence into a tube instead of a sphere.
It can be used along the outline of a mesh to adjust it's silhouette.

Note that all this takes advantage of changes from vertex paint,
from testing this seems useful so exposing from the brush options.

release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/BKE_pbvh.h
source/blender/blenkernel/intern/pbvh_bmesh.c
source/blender/blenloader/intern/versioning_270.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_scene_types.h
source/blender/makesrna/intern/rna_brush.c
source/blender/makesrna/intern/rna_sculpt_paint.c

index 5aca4c554d865221a8d501abb06697efbd6f655b..d795e09e435f0ae3382ee2a6152cc7f7963d8a06 100644 (file)
@@ -1083,6 +1083,8 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
             row = col.row()
             row.prop(brush, "use_frontface", text="Front Faces Only")
 
+            col.row().prop(brush, "use_projected", expand=True)
+
             # direction
             col.separator()
             col.row().prop(brush, "direction", expand=True)
@@ -1137,6 +1139,8 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
                 col.prop(brush, "use_accumulate")
                 col.separator()
 
+            col.row().prop(brush, "use_projected")
+
             col = layout.column()
             col.prop(toolsettings, "use_auto_normalize", text="Auto Normalize")
             col.prop(toolsettings, "use_multipaint", text="Multi-Paint")
@@ -1168,9 +1172,12 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
             # row.prop(brush, "use_pressure_jitter", toggle=True, text="")
             col.separator()
             col.prop(brush, "vertex_tool", text="Blend")
+
             col.prop(brush, "use_accumulate")
             col.prop(brush, "use_alpha")
 
+            col.row().prop("use_projected")
+
             col.separator()
             col.template_ID(settings, "palette", new="palette.new")
 
@@ -1763,8 +1770,6 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):
         col = layout.column()
         col.label("Falloff:")
         row = col.row()
-        row.prop(wpaint, "falloff_shape", expand=True)
-        row = col.row()
         row.prop(wpaint, "use_backface_culling")
         row = col.row()
         row.prop(wpaint, "use_normal_falloff")
@@ -1806,8 +1811,6 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel):
         col = layout.column()
         col.label("Falloff:")
         row = col.row()
-        row.prop(vpaint, "falloff_shape", expand=True)
-        row = col.row()
         row.prop(vpaint, "use_backface_culling")
         row = col.row()
         row.prop(vpaint, "use_normal_falloff")
index c6b9b6a4de608f659b9b6c48af1515bdf12489b0..2daa2ef7182c5fa0b79c1330d27097336adbface 100644 (file)
@@ -168,7 +168,7 @@ typedef enum {
 bool BKE_pbvh_bmesh_update_topology(
         PBVH *bvh, PBVHTopologyUpdateMode mode,
         const float center[3], const float view_normal[3],
-        float radius);
+        float radius, const bool use_frontface, const bool use_projected);
 
 /* Node Access */
 
index 4bfda8ebf2831f4a98d422054c40f3eef7d361bc..187891e7210fcb45babbbaeeef45885c15faa56c 100644 (file)
@@ -718,20 +718,24 @@ static void pbvh_bmesh_node_drop_orig(PBVHNode *node)
 
 /****************************** EdgeQueue *****************************/
 
-typedef struct {
+struct EdgeQueue;
+
+typedef struct EdgeQueue {
        Heap *heap;
        const float *center;
+       float  center_proj[3];  /* for when we use projected coords. */
        float radius_squared;
        float limit_len_squared;
 #ifdef USE_EDGEQUEUE_EVEN_SUBDIV
        float limit_len;
 #endif
 
-#ifdef USE_EDGEQUEUE_FRONTFACE
+       bool (*edge_queue_tri_in_range)(const struct EdgeQueue *q, BMFace *f);
+
        const float *view_normal;
+#ifdef USE_EDGEQUEUE_FRONTFACE
        unsigned int use_view_normal : 1;
 #endif
-
 } EdgeQueue;
 
 typedef struct {
@@ -785,7 +789,6 @@ static bool edge_queue_tri_in_sphere(const EdgeQueue *q, BMFace *f)
        float c[3];
 
        /* Get closest point in triangle to sphere center */
-       // BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v_tri, 3);
        BM_face_as_array_vert_tri(f, v_tri);
 
        closest_on_tri_to_point_v3(c, q->center, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
@@ -794,6 +797,25 @@ static bool edge_queue_tri_in_sphere(const EdgeQueue *q, BMFace *f)
        return len_squared_v3v3(q->center, c) <= q->radius_squared;
 }
 
+static bool edge_queue_tri_in_circle(const EdgeQueue *q, BMFace *f)
+{
+       BMVert *v_tri[3];
+       float c[3];
+       float tri_proj[3][3];
+
+       /* Get closest point in triangle to sphere center */
+       BM_face_as_array_vert_tri(f, v_tri);
+
+       project_plane_normalized_v3_v3v3(tri_proj[0], v_tri[0]->co, q->view_normal);
+       project_plane_normalized_v3_v3v3(tri_proj[1], v_tri[1]->co, q->view_normal);
+       project_plane_normalized_v3_v3v3(tri_proj[2], v_tri[2]->co, q->view_normal);
+
+       closest_on_tri_to_point_v3(c, q->center_proj, tri_proj[0], tri_proj[1], tri_proj[2]);
+
+       /* Check if triangle intersects the sphere */
+       return len_squared_v3v3(q->center_proj, c) <= q->radius_squared;
+}
+
 /* Return true if the vertex mask is less than 1.0, false otherwise */
 static bool check_mask(EdgeQueueContext *eq_ctx, BMVert *v)
 {
@@ -929,7 +951,7 @@ static void long_edge_queue_face_add(
        }
 #endif
 
-       if (edge_queue_tri_in_sphere(eq_ctx->q, f)) {
+       if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) {
                /* Check each edge of the face */
                BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
                BMLoop *l_iter = l_first;
@@ -960,7 +982,7 @@ static void short_edge_queue_face_add(
        }
 #endif
 
-       if (edge_queue_tri_in_sphere(eq_ctx->q, f)) {
+       if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) {
                BMLoop *l_iter;
                BMLoop *l_first;
 
@@ -984,7 +1006,7 @@ static void short_edge_queue_face_add(
 static void long_edge_queue_create(
         EdgeQueueContext *eq_ctx,
         PBVH *bvh, const float center[3], const float view_normal[3],
-        float radius)
+        float radius, const bool use_frontface, const bool use_projected)
 {
        eq_ctx->q->heap = BLI_heap_new();
        eq_ctx->q->center = center;
@@ -994,13 +1016,22 @@ static void long_edge_queue_create(
        eq_ctx->q->limit_len = bvh->bm_max_edge_len;
 #endif
 
-#ifdef USE_EDGEQUEUE_FRONTFACE
        eq_ctx->q->view_normal = view_normal;
-       eq_ctx->q->use_view_normal = (view_normal != NULL);
+
+#ifdef USE_EDGEQUEUE_FRONTFACE
+       eq_ctx->q->use_view_normal = use_frontface;
 #else
-       UNUSED_VARS(view_normal);
+       UNUSED_VARS(use_frontface);
 #endif
 
+       if (use_projected) {
+               eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle;
+               project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal);
+       }
+       else {
+               eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere;
+       }
+
 #ifdef USE_EDGEQUEUE_TAG_VERIFY
        pbvh_bmesh_edge_tag_verify(bvh);
 #endif
@@ -1037,7 +1068,7 @@ static void long_edge_queue_create(
 static void short_edge_queue_create(
         EdgeQueueContext *eq_ctx,
         PBVH *bvh, const float center[3], const float view_normal[3],
-        float radius)
+        float radius, const bool use_frontface, const bool use_projected)
 {
        eq_ctx->q->heap = BLI_heap_new();
        eq_ctx->q->center = center;
@@ -1047,13 +1078,22 @@ static void short_edge_queue_create(
        eq_ctx->q->limit_len = bvh->bm_min_edge_len;
 #endif
 
-#ifdef USE_EDGEQUEUE_FRONTFACE
        eq_ctx->q->view_normal = view_normal;
-       eq_ctx->q->use_view_normal = (view_normal != NULL);
+
+#ifdef USE_EDGEQUEUE_FRONTFACE
+       eq_ctx->q->use_view_normal = use_frontface;
 #else
-       UNUSED_VARS(view_normal);
+       UNUSED_VARS(use_frontface);
 #endif
 
+       if (use_projected) {
+               eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle;
+               project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal);
+       }
+       else {
+               eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere;
+       }
+
        for (int n = 0; n < bvh->totnode; n++) {
                PBVHNode *node = &bvh->nodes[n];
 
@@ -1895,7 +1935,7 @@ void BKE_pbvh_build_bmesh(
 bool BKE_pbvh_bmesh_update_topology(
         PBVH *bvh, PBVHTopologyUpdateMode mode,
         const float center[3], const float view_normal[3],
-        float radius)
+        float radius, const bool use_frontface, const bool use_projected)
 {
        /* 2 is enough for edge faces - manifold edge */
        BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2);
@@ -1918,7 +1958,7 @@ bool BKE_pbvh_bmesh_update_topology(
                    cd_vert_mask_offset, cd_vert_node_offset, cd_face_node_offset,
                };
 
-               short_edge_queue_create(&eq_ctx, bvh, center, view_normal, radius);
+               short_edge_queue_create(&eq_ctx, bvh, center, view_normal, radius, use_frontface, use_projected);
                modified |= pbvh_bmesh_collapse_short_edges(
                        &eq_ctx, bvh, &deleted_faces);
                BLI_heap_free(q.heap, NULL);
@@ -1933,7 +1973,7 @@ bool BKE_pbvh_bmesh_update_topology(
                    cd_vert_mask_offset, cd_vert_node_offset, cd_face_node_offset,
                };
 
-               long_edge_queue_create(&eq_ctx, bvh, center, view_normal, radius);
+               long_edge_queue_create(&eq_ctx, bvh, center, view_normal, radius, use_frontface, use_projected);
                modified |= pbvh_bmesh_subdivide_long_edges(
                        &eq_ctx, bvh, &edge_loops);
                BLI_heap_free(q.heap, NULL);
index e1a4137d06959c1a3bfa04c54e59bc7c939314aa..08e8d904b88bf307f026f8b82960538852347ccc 100644 (file)
@@ -1683,7 +1683,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
                        }
                }
 
-               if (!DNA_struct_elem_find(fd->filesdna, "VPaint", "char", "falloff_shape")) {
+               if (!DNA_struct_elem_find(fd->filesdna, "VPaint", "char", "normal_angle")) {
                        for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
                                ToolSettings *ts = scene->toolsettings;
                                for (int i = 0; i < 2; i++) {
index a6b41b51ca65bdded7aaf69484102c3fcca7a2d6..7c54277639aee88bab5aff55e42b40d0897b1493 100644 (file)
@@ -1500,22 +1500,6 @@ static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintIn
        }
 }
 
-static SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(
-        SculptSession *ss, SculptBrushTest *test, char falloff_shape)
-{
-       sculpt_brush_test_init(ss, test);
-       SculptBrushTestFn sculpt_brush_test_sq_fn;
-       if (falloff_shape == VP_FALLOFF_SHAPE_SPHERE) {
-               sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq;
-       }
-       else {
-               /* VP_FALLOFF_SHAPE_TUBE */
-               plane_from_point_normal_v3(test->plane, test->location, ss->cache->view_normal);
-               sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq;
-       }
-       return sculpt_brush_test_sq_fn;
-}
-
 static void do_wpaint_brush_blur_task_cb_ex(
         void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
 {
@@ -1536,7 +1520,7 @@ static void do_wpaint_brush_blur_task_cb_ex(
 
        SculptBrushTest test;
        SculptBrushTestFn sculpt_brush_test_sq_fn =
-               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        /* For each vertex */
        PBVHVertexIter vd;
@@ -1629,7 +1613,7 @@ static void do_wpaint_brush_smear_task_cb_ex(
 
                SculptBrushTest test;
                SculptBrushTestFn sculpt_brush_test_sq_fn =
-                       sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+                       sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
                /* For each vertex */
                PBVHVertexIter vd;
@@ -1729,7 +1713,7 @@ static void do_wpaint_brush_draw_task_cb_ex(
 
        SculptBrushTest test;
        SculptBrushTestFn sculpt_brush_test_sq_fn =
-               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        /* For each vertex */
        PBVHVertexIter vd;
@@ -1794,7 +1778,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
 
        SculptBrushTest test;
        SculptBrushTestFn sculpt_brush_test_sq_fn =
-               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        /* For each vertex */
        PBVHVertexIter vd;
@@ -1896,7 +1880,7 @@ static PBVHNode **vwpaint_pbvh_gather_generic(
        PBVHNode **nodes = NULL;
 
        /* Build a list of all nodes that are potentially within the brush's area of influence */
-       if (wp->falloff_shape == VP_FALLOFF_SHAPE_SPHERE) {
+       if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
                SculptSearchSphereData data = {
                        .ss = ss,
                        .sd = sd,
@@ -2455,7 +2439,7 @@ static void do_vpaint_brush_calc_average_color_cb_ex(
 
        SculptBrushTest test;
        SculptBrushTestFn sculpt_brush_test_sq_fn =
-               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        /* For each vertex */
        PBVHVertexIter vd;
@@ -2524,7 +2508,7 @@ static void do_vpaint_brush_draw_task_cb_ex(
 
        SculptBrushTest test;
        SculptBrushTestFn sculpt_brush_test_sq_fn =
-               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        /* For each vertex */
        PBVHVertexIter vd;
@@ -2613,7 +2597,7 @@ static void do_vpaint_brush_blur_task_cb_ex(
 
        SculptBrushTest test;
        SculptBrushTestFn sculpt_brush_test_sq_fn =
-               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        /* For each vertex */
        PBVHVertexIter vd;
@@ -2726,7 +2710,7 @@ static void do_vpaint_brush_smear_task_cb_ex(
 
                SculptBrushTest test;
                SculptBrushTestFn sculpt_brush_test_sq_fn =
-                       sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
+                       sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
                /* For each vertex */
                PBVHVertexIter vd;
index f74ac68262f72fbf67d9ca3bb8026978124ace62..487ab32c5a6c821ad5def9ee49e13a6487a93590 100644 (file)
@@ -524,7 +524,8 @@ void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
        test->dist = 0.0f;   /* just for initialize */
 
        /* Only for 2D projection. */
-       zero_v4(test->plane);
+       zero_v4(test->plane_view);
+       zero_v4(test->plane_tool);
 
        test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass;
 
@@ -590,7 +591,7 @@ bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3
 bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3])
 {
        float co_proj[3];
-       closest_to_plane_normalized_v3(co_proj, test->plane, co);
+       closest_to_plane_normalized_v3(co_proj, test->plane_view, co);
        float distsq = len_squared_v3v3(co_proj, test->location);
 
        if (distsq <= test->radius_squared) {
@@ -634,6 +635,22 @@ bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float loca
        }
 }
 
+SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(
+        SculptSession *ss, SculptBrushTest *test, char falloff_shape)
+{
+       sculpt_brush_test_init(ss, test);
+       SculptBrushTestFn sculpt_brush_test_sq_fn;
+       if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+               sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq;
+       }
+       else {
+               /* PAINT_FALLOFF_SHAPE_TUBE */
+               plane_from_point_normal_v3(test->plane_view, test->location, ss->cache->view_normal);
+               sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq;
+       }
+       return sculpt_brush_test_sq_fn;
+}
+
 static float frontface(const Brush *br, const float sculpt_normal[3],
                        const short no[3], const float fno[3])
 {
@@ -1249,7 +1266,7 @@ bool sculpt_search_circle_cb(PBVHNode *node, void *data_v)
        const float dist_sq = dist_squared_ray_to_aabb(
                data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth);
 
-       return dist_sq < data->radius_squared;
+       return dist_sq < data->radius_squared || 1;
 }
 
 /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
@@ -1268,6 +1285,37 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float
        }
 }
 
+static PBVHNode **sculpt_pbvh_gather_generic(
+        Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius_scale, int *r_totnode)
+{
+       SculptSession *ss = ob->sculpt;
+       PBVHNode **nodes = NULL;
+
+       /* Build a list of all nodes that are potentially within the brush's area of influence */
+       if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
+               SculptSearchSphereData data = {
+                       .ss = ss,
+                       .sd = sd,
+                       .radius_squared = SQUARE(ss->cache->radius * radius_scale),
+                       .original = use_original,
+               };
+               BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode);
+       }
+       else {
+               struct DistRayAABB_Precalc dist_ray_to_aabb_precalc;
+               dist_squared_ray_to_aabb_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal);
+               SculptSearchCircleData data = {
+                       .ss = ss,
+                       .sd = sd,
+                       .radius_squared = SQUARE(ss->cache->radius * radius_scale),
+                       .original = use_original,
+                       .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
+               };
+               BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode);
+       }
+       return nodes;
+}
+
 /* Calculate primary direction of movement for many brushes */
 static void calc_sculpt_normal(
         Sculpt *sd, Object *ob,
@@ -1569,19 +1617,19 @@ static void do_smooth_brush_mesh_task_cb_ex(
        float bstrength = data->strength;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
 
        CLAMP(bstrength, 0.0f, 1.0f);
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno,
-                                              smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
-                                              thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), thread_id);
                        if (smooth_mask) {
                                float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask;
                                val *= fade * bstrength;
@@ -1617,18 +1665,19 @@ static void do_smooth_brush_bmesh_task_cb_ex(
        float bstrength = data->strength;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
 
        CLAMP(bstrength, 0.0f, 1.0f);
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, smooth_mask ? 0.0f : *vd.mask,
-                                              thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, smooth_mask ? 0.0f : *vd.mask, thread_id);
                        if (smooth_mask) {
                                float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask;
                                val *= fade * bstrength;
@@ -1664,7 +1713,6 @@ static void do_smooth_brush_multires_task_cb_ex(
        const bool smooth_mask = data->smooth_mask;
        float bstrength = data->strength;
 
-       SculptBrushTest test;
        CCGElem **griddata, *gddata;
        CCGKey key;
 
@@ -1677,7 +1725,9 @@ static void do_smooth_brush_multires_task_cb_ex(
        int *grid_indices, totgrid, gridsize;
        int i, x, y;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        CLAMP(bstrength, 0.0f, 1.0f);
 
@@ -1764,10 +1814,11 @@ static void do_smooth_brush_multires_task_cb_ex(
                                fno = CCG_elem_offset_no(&key, gddata, index);
                                mask = CCG_elem_offset_mask(&key, gddata, index);
 
-                               if (sculpt_brush_test_sphere(&test, co)) {
+                               if (sculpt_brush_test_sq_fn(&test, co)) {
                                        const float strength_mask = (smooth_mask ? 0.0f : *mask);
                                        const float fade = bstrength * tex_strength(
-                                                              ss, brush, co, test.dist, NULL, fno, strength_mask, thread_id);
+                                               ss, brush, co, sqrtf(test.dist),
+                                               NULL, fno, strength_mask, thread_id);
                                        float f = 1.0f / 16.0f;
 
                                        if (x == 0 || x == gridsize - 1)
@@ -1878,14 +1929,17 @@ static void do_mask_brush_draw_task_cb_ex(
        const float bstrength = ss->cache->bstrength;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
-                       const float fade = tex_strength(ss, brush, vd.co, test.dist, vd.no, vd.fno, 0.0f, thread_id);
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+                       const float fade = tex_strength(
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, 0.0f, thread_id);
 
                        (*vd.mask) += fade * bstrength;
                        CLAMP(*vd.mask, 0, 1);
@@ -1935,19 +1989,21 @@ static void do_draw_brush_task_cb_ex(
        const float *offset = data->offset;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        /* offset vertex */
                        const float fade = tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        mul_v3_v3fl(proxy[vd.i], offset, fade);
 
@@ -1992,19 +2048,21 @@ static void do_crease_brush_task_cb_ex(
        const float *offset = data->offset;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        /* offset vertex */
                        const float fade = tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
                        float val1[3];
                        float val2[3];
 
@@ -2077,19 +2135,21 @@ static void do_pinch_brush_task_cb_ex(
        const Brush *brush = data->brush;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
                        float val[3];
 
                        sub_v3_v3v3(val, test.location, vd.co);
@@ -2124,7 +2184,6 @@ static void do_grab_brush_task_cb_ex(
        const float *grab_delta = data->grab_delta;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        SculptOrigVertData orig_data;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
@@ -2133,16 +2192,18 @@ static void do_grab_brush_task_cb_ex(
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
                sculpt_orig_vert_data_update(&orig_data, &vd);
 
-               if (sculpt_brush_test_sphere(&test, orig_data.co)) {
+               if (sculpt_brush_test_sq_fn(&test, orig_data.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, orig_data.co, test.dist, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f,
-                                              thread_id);
+                               ss, brush, orig_data.co, sqrtf(test.dist),
+                               orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
 
@@ -2184,19 +2245,21 @@ static void do_nudge_brush_task_cb_ex(
        const float *cono = data->cono;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        mul_v3_v3fl(proxy[vd.i], cono, fade);
 
@@ -2239,7 +2302,6 @@ static void do_snake_hook_brush_task_cb_ex(
        const float *grab_delta = data->grab_delta;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
        const bool do_rake_rotation = ss->cache->is_rake_rotation_valid;
@@ -2249,13 +2311,16 @@ static void do_snake_hook_brush_task_cb_ex(
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
 
@@ -2339,7 +2404,6 @@ static void do_thumb_brush_task_cb_ex(
        const float *cono = data->cono;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        SculptOrigVertData orig_data;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
@@ -2348,16 +2412,18 @@ static void do_thumb_brush_task_cb_ex(
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
                sculpt_orig_vert_data_update(&orig_data, &vd);
 
-               if (sculpt_brush_test_sphere(&test, orig_data.co)) {
+               if (sculpt_brush_test_sq_fn(&test, orig_data.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, orig_data.co, test.dist, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f,
-                                              thread_id);
+                               ss, brush, orig_data.co, sqrtf(test.dist),
+                               orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        mul_v3_v3fl(proxy[vd.i], cono, fade);
 
@@ -2399,7 +2465,6 @@ static void do_rotate_brush_task_cb_ex(
        const float angle = data->angle;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        SculptOrigVertData orig_data;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
@@ -2408,17 +2473,19 @@ static void do_rotate_brush_task_cb_ex(
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
                sculpt_orig_vert_data_update(&orig_data, &vd);
 
-               if (sculpt_brush_test_sphere(&test, orig_data.co)) {
+               if (sculpt_brush_test_sq_fn(&test, orig_data.co)) {
                        float vec[3], rot[3][3];
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, orig_data.co, test.dist, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f,
-                                              thread_id);
+                               ss, brush, orig_data.co, sqrtf(test.dist),
+                               orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        sub_v3_v3v3(vec, orig_data.co, ss->cache->location);
                        axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade);
@@ -2461,7 +2528,6 @@ static void do_layer_brush_task_cb_ex(
        const float *offset = data->offset;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        SculptOrigVertData orig_data;
        float *layer_disp;
        const float bstrength = ss->cache->bstrength;
@@ -2476,15 +2542,18 @@ static void do_layer_brush_task_cb_ex(
        layer_disp = BKE_pbvh_node_layer_disp_get(ss->pbvh, data->nodes[n]);
        BLI_mutex_unlock(&data->mutex);
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
                sculpt_orig_vert_data_update(&orig_data, &vd);
 
-               if (sculpt_brush_test_sphere(&test, orig_data.co)) {
+               if (sculpt_brush_test_sq_fn(&test, orig_data.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
                        float *disp = &layer_disp[vd.i];
                        float val[3];
 
@@ -2544,19 +2613,21 @@ static void do_inflate_brush_task_cb_ex(
        const Brush *brush = data->brush;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = bstrength * tex_strength(
-                                              ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
                        float val[3];
 
                        if (vd.fno)
@@ -2704,29 +2775,31 @@ static void do_flatten_brush_task_cb_ex(
        const float *area_co = data->area_co;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
-       plane_from_point_normal_v3(test.plane, area_co, area_no);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
+
+       plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        float intr[3];
                        float val[3];
 
-                       closest_to_plane_normalized_v3(intr, test.plane, vd.co);
+                       closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
 
                        sub_v3_v3v3(val, intr, vd.co);
 
                        if (plane_trim(ss->cache, brush, val)) {
                                const float fade = bstrength * tex_strength(
-                                                      ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f,
-                                                      thread_id);
+                                       ss, brush, vd.co, sqrtf(test.dist),
+                                       vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                                mul_v3_v3fl(proxy[vd.i], val, fade);
 
@@ -2780,24 +2853,26 @@ static void do_clay_brush_task_cb_ex(
        const float *area_co = data->area_co;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const bool flip = (ss->cache->bstrength < 0);
        const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
-       plane_from_point_normal_v3(test.plane, area_co, area_no);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
+
+       plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
-                       if (plane_point_side_flip(vd.co, test.plane, flip)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+                       if (plane_point_side_flip(vd.co, test.plane_tool, flip)) {
                                float intr[3];
                                float val[3];
 
-                               closest_to_plane_normalized_v3(intr, test.plane, vd.co);
+                               closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
 
                                sub_v3_v3v3(val, intr, vd.co);
 
@@ -2805,8 +2880,8 @@ static void do_clay_brush_task_cb_ex(
                                        /* note, the normal from the vertices is ignored,
                                         * causes glitch with planes, see: T44390 */
                                        const float fade = bstrength * tex_strength(
-                                                              ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f,
-                                                              thread_id);
+                                               ss, brush, vd.co, sqrtf(test.dist),
+                                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                                        mul_v3_v3fl(proxy[vd.i], val, fade);
 
@@ -2873,16 +2948,16 @@ static void do_clay_strips_brush_task_cb_ex(
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
        sculpt_brush_test_init(ss, &test);
-       plane_from_point_normal_v3(test.plane, area_co, area_no_sp);
+       plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
                if (sculpt_brush_test_cube(&test, vd.co, mat)) {
-                       if (plane_point_side_flip(vd.co, test.plane, flip)) {
+                       if (plane_point_side_flip(vd.co, test.plane_tool, flip)) {
                                float intr[3];
                                float val[3];
 
-                               closest_to_plane_normalized_v3(intr, test.plane, vd.co);
+                               closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
 
                                sub_v3_v3v3(val, intr, vd.co);
 
@@ -2890,8 +2965,8 @@ static void do_clay_strips_brush_task_cb_ex(
                                        /* note, the normal from the vertices is ignored,
                                         * causes glitch with planes, see: T44390 */
                                        const float fade = bstrength * tex_strength(
-                                                              ss, brush, vd.co, ss->cache->radius * test.dist,
-                                                              vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                                               ss, brush, vd.co, ss->cache->radius * test.dist,
+                                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                                        mul_v3_v3fl(proxy[vd.i], val, fade);
 
@@ -2974,30 +3049,32 @@ static void do_fill_brush_task_cb_ex(
        const float *area_co = data->area_co;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
-       plane_from_point_normal_v3(test.plane, area_co, area_no);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
+
+       plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
-                       if (plane_point_side(vd.co, test.plane)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+                       if (plane_point_side(vd.co, test.plane_tool)) {
                                float intr[3];
                                float val[3];
 
-                               closest_to_plane_normalized_v3(intr, test.plane, vd.co);
+                               closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
 
                                sub_v3_v3v3(val, intr, vd.co);
 
                                if (plane_trim(ss->cache, brush, val)) {
                                        const float fade = bstrength * tex_strength(
-                                                              ss, brush, vd.co, sqrtf(test.dist),
-                                                              vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
+                                               ss, brush, vd.co, sqrtf(test.dist),
+                                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                                        mul_v3_v3fl(proxy[vd.i], val, fade);
 
@@ -3053,30 +3130,31 @@ static void do_scrape_brush_task_cb_ex(
        const float *area_co = data->area_co;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
        const float bstrength = ss->cache->bstrength;
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
-       plane_from_point_normal_v3(test.plane, area_co, area_no);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
+       plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
        {
-               if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
-                       if (!plane_point_side(vd.co, test.plane)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+                       if (!plane_point_side(vd.co, test.plane_tool)) {
                                float intr[3];
                                float val[3];
 
-                               closest_to_plane_normalized_v3(intr, test.plane, vd.co);
+                               closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
 
                                sub_v3_v3v3(val, intr, vd.co);
 
                                if (plane_trim(ss->cache, brush, val)) {
                                        const float fade = bstrength * tex_strength(
-                                                              ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f,
-                                                              thread_id);
+                                               ss, brush, vd.co, sqrtf(test.dist),
+                                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                                        mul_v3_v3fl(proxy[vd.i], val, fade);
 
@@ -3131,18 +3209,19 @@ static void do_gravity_task_cb_ex(
        float *offset = data->offset;
 
        PBVHVertexIter vd;
-       SculptBrushTest test;
        float (*proxy)[3];
 
        proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
 
-       sculpt_brush_test_init(ss, &test);
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape);
 
        BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
-               if (sculpt_brush_test_sphere_sq(&test, vd.co)) {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
                        const float fade = tex_strength(
-                                              ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f,
-                                              thread_id);
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, thread_id);
 
                        mul_v3_v3fl(proxy[vd.i], offset, fade);
 
@@ -3226,22 +3305,12 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
 static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *UNUSED(ups))
 {
        SculptSession *ss = ob->sculpt;
-       SculptSearchSphereData data;
-       PBVHNode **nodes = NULL;
-       float radius;
-       int n, totnode;
-
-       /* Build a list of all nodes that are potentially within the
-        * brush's area of influence */
-       data.ss = ss;
-       data.sd = sd;
-
-       radius = ss->cache->radius * 1.25f;
-
-       data.radius_squared = radius * radius;
-       data.original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original;
 
-       BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+       int n, totnode;
+       /* Build a list of all nodes that are potentially within the brush's area of influence */
+       const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original;
+       const float radius_scale = 1.25f;
+       PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
 
        /* Only act if some verts are inside the brush area */
        if (totnode) {
@@ -3273,8 +3342,10 @@ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, Unified
                        BKE_pbvh_bmesh_update_topology(
                                ss->pbvh, mode,
                                ss->cache->location,
-                               (brush->flag & BRUSH_FRONTFACE) ? ss->cache->view_normal : NULL,
-                               ss->cache->radius);
+                               ss->cache->view_normal,
+                               ss->cache->radius,
+                               (brush->flag & BRUSH_FRONTFACE) != 0,
+                               (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE));
                }
 
                MEM_freeN(nodes);
@@ -3297,16 +3368,12 @@ static void do_brush_action_task_cb(void *userdata, const int n)
 static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups)
 {
        SculptSession *ss = ob->sculpt;
-       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 = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original;
-       BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+       const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original;
+       const float radius_scale = 1.0f;
+       PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
 
        /* Only act if some verts are inside the brush area */
        if (totnode) {
@@ -4461,10 +4528,9 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
                }
        }
 
-       /* We may want to move this into a brush option, it could be useful in sculpt mode too. */
-       const bool use_nearest = ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) != 0);
-
-       if (hit == false && use_nearest) {
+       if (hit == false) {
+               const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
+               if (ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) {
                SculptFindNearestToRayData srd = {
                        .original = original,
                        .ss = ob->sculpt,
@@ -4483,6 +4549,7 @@ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2])
                        mul_v3_fl(out, srd.depth);
                        add_v3_v3(out, ray_start);
                }
+               }
        }
 
        //used in vwpaint
@@ -5520,7 +5587,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
 
        while (BKE_pbvh_bmesh_update_topology(
                       ss->pbvh, PBVH_Collapse | PBVH_Subdivide,
-                      center, NULL, size))
+                      center, NULL, size, false, false))
        {
                for (i = 0; i < totnodes; i++)
                        BKE_pbvh_node_mark_topology_update(nodes[i]);
index b9b662f4157fb2cb1c8d84b50b397083fd5dc919..73581d402cfdcda215343240697990653136a6c4 100644 (file)
@@ -182,7 +182,10 @@ typedef struct SculptBrushTest {
        int mirror_symmetry_pass;
 
        /* For circle (not sphere) projection. */
-       float plane[4];
+       float plane_view[4];
+
+       /* Some tool code uses a plane for it's calculateions. */
+       float plane_tool[4];
 
        /* View3d clipping - only set rv3d for clipping */
        struct RegionView3D *clip_rv3d;
@@ -213,6 +216,10 @@ bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float loca
 bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]);
 bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v);
 bool sculpt_search_circle_cb(PBVHNode *node, void *data_v);
+
+SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(
+        SculptSession *ss, SculptBrushTest *test, char falloff_shape);
+
 float tex_strength(
         struct SculptSession *ss, const struct Brush *br,
         const float point[3],
index 67a35d4e20798a1d24b757d69f912112a73acef3..6f239ec558b95f84edfd9605c01dea92be139713 100644 (file)
@@ -95,8 +95,11 @@ typedef struct Brush {
        float plane_offset;     /* offset for plane brushes (clay, flatten, fill, scrape) */
 
        int gradient_spacing;
-       int gradient_stroke_mode; /* source for stroke color gradient application */
-       int gradient_fill_mode;   /* source for fill tool color gradient application */
+       char gradient_stroke_mode; /* source for stroke color gradient application */
+       char gradient_fill_mode;   /* source for fill tool color gradient application */
+
+       char falloff_shape;     /* Projection shape (sphere, circle) */
+       char pad[5];
 
        char sculpt_tool;       /* active sculpt tool */
        char vertexpaint_tool;  /* active vertex/weight paint blend mode (poorly named) */
@@ -343,6 +346,12 @@ typedef enum BlurKernelType {
        KERNEL_BOX
 } BlurKernelType;
 
+/* Brush.falloff_shape */
+enum {
+       PAINT_FALLOFF_SHAPE_SPHERE = 0,
+       PAINT_FALLOFF_SHAPE_TUBE = 1,
+};
+
 #define MAX_BRUSH_PIXEL_RADIUS 500
 
 #endif  /* __DNA_BRUSH_TYPES_H__ */
index 89a8a410f3ea61e87f3843bd6cacce756c3d927a..830c584529c174a41bc1efd39bde793886cb5569 100644 (file)
@@ -1118,7 +1118,8 @@ typedef struct UvSculpt {
 typedef struct VPaint {
        Paint paint;
        short flag;
-       char falloff_shape, normal_angle;
+       char normal_angle;
+       char pad;
        int radial_symm[3]; /* For mirrored painting */
 } VPaint;
 
@@ -1132,12 +1133,6 @@ enum {
        VP_FLAG_VGROUP_RESTRICT     = (1 << 7)
 };
 
-/* VPaint.falloff_shape */
-enum {
-       VP_FALLOFF_SHAPE_SPHERE = 0,
-       VP_FALLOFF_SHAPE_TUBE = 1,
-};
-
 /* ------------------------------------------- */
 /* GPencil Stroke Sculpting */
 
index 0923fc92567196fa0b59ee8ff96204e128ea372e..e6e8f6f5f95b36e9037419e96712d66499a0cedc 100644 (file)
@@ -1117,6 +1117,12 @@ static void rna_def_brush(BlenderRNA *brna)
        RNA_def_property_update(prop, 0, "rna_Brush_update");
 
        /* flag */
+       /* This is an enum but its unlikely we add other shapes, so expose as a boolean. */
+       prop = RNA_def_property(srna, "use_projected", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "falloff_shape", BRUSH_AIRBRUSH);
+       RNA_def_property_ui_text(prop, "2D Falloff", "Apply brush influence in 2D circle instead of a sphere");
+       RNA_def_property_update(prop, 0, "rna_Brush_update");
+
        prop = RNA_def_property(srna, "use_airbrush", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_AIRBRUSH);
        RNA_def_property_ui_text(prop, "Airbrush", "Keep applying paint effect while holding mouse (spray)");
index 6e493e2c86564d7723a47e70b37963dfcbe7e24d..6bd4aefed7a6e85c56aa80c59b30a56a30bbed4d 100644 (file)
@@ -671,18 +671,6 @@ static void rna_def_vertex_paint(BlenderRNA *brna)
        RNA_def_struct_path_func(srna, "rna_VertexPaint_path");
        RNA_def_struct_ui_text(srna, "Vertex Paint", "Properties of vertex and weight paint mode");
 
-       static EnumPropertyItem prop_falloff_items[] = {
-               {VP_FALLOFF_SHAPE_SPHERE, "SPHERE", 0, "Sphere", "Spherical falloff from the brush"},
-               {VP_FALLOFF_SHAPE_TUBE, "TUBE", 0, "Circle", "Circular falloff from the brush along the view"},
-               {0, NULL, 0, NULL, NULL}
-       };
-
-       prop = RNA_def_property(srna, "falloff_shape", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_sdna(prop, NULL, "falloff_shape");
-       RNA_def_property_enum_items(prop, prop_falloff_items);
-       RNA_def_property_ui_text(prop, "Falloff", "");
-       RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
        prop = RNA_def_property(srna, "use_backface_culling", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", VP_FLAG_PROJECT_BACKFACE);
        RNA_def_property_ui_text(prop, "Cull", "Ignore vertices pointing away from the view (faster)");