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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c310
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h8
-rw-r--r--source/blender/makesdna/DNA_brush_types.h7
-rw-r--r--source/blender/makesrna/intern/rna_brush.c7
5 files changed, 323 insertions, 13 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 5bc4883d1ea..a247725468c 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -376,6 +376,10 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
row = col.row()
row.prop(brush, "elastic_deform_compressibility", slider=True)
+ col.separator()
+ row = col.row()
+ row.prop(brush, "use_automasking_topology")
+
# topology_rake_factor
if (
capabilities.has_topology_rake and
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index b05d9db87f7..0a4c77b5656 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -28,6 +28,8 @@
#include "BLI_blenlib.h"
#include "BLI_dial_2d.h"
#include "BLI_hash.h"
+#include "BLI_gsqueue.h"
+#include "BLI_stack.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@@ -116,7 +118,7 @@ static void sculpt_vertex_random_access_init(SculptSession *ss)
}
}
-static int UNUSED_FUNCTION(sculpt_active_vertex_get)(SculptSession *ss)
+static int sculpt_active_vertex_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -341,6 +343,99 @@ static void sculpt_vertex_mask_clamp(SculptSession *ss, int index, float min, fl
}
}
+static void do_nearest_vertex_get_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ PBVHVertexIter vd;
+ int node_nearest_vertex_index = -1;
+ float node_nearest_vertex_distance_squared = FLT_MAX;
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
+ if (distance_squared < node_nearest_vertex_distance_squared &&
+ distance_squared < data->max_distance_squared) {
+ node_nearest_vertex_index = vd.index;
+ node_nearest_vertex_distance_squared = distance_squared;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ BLI_mutex_lock(&data->mutex);
+ if (data->nearest_vertex_index == -1) {
+ data->nearest_vertex_index = node_nearest_vertex_index;
+ }
+ else if (node_nearest_vertex_distance_squared <
+ len_squared_v3v3(data->nearest_vertex_search_co,
+ sculpt_vertex_co_get(ss, data->nearest_vertex_index))) {
+ data->nearest_vertex_index = node_nearest_vertex_index;
+ }
+ BLI_mutex_unlock(&data->mutex);
+}
+
+int sculpt_nearest_vertex_get(
+ Sculpt *sd, Object *ob, float co[3], float max_distance, bool use_original)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHNode **nodes = NULL;
+ int totnode;
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .sd = sd,
+ .radius_squared = max_distance * max_distance,
+ .original = use_original,
+ .center = co,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+ if (totnode == 0) {
+ return -1;
+ }
+
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .max_distance_squared = max_distance * max_distance,
+ .nearest_vertex_index = -1,
+ };
+ copy_v3_v3(task_data.nearest_vertex_search_co, co);
+
+ BLI_mutex_init(&task_data.mutex);
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT);
+ BLI_task_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings);
+ BLI_mutex_end(&task_data.mutex);
+
+ return task_data.nearest_vertex_index;
+}
+
+static bool is_symmetry_iteration_valid(char i, char symm)
+{
+ return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)));
+}
+
+/* Checks if a vertex is inside the brush radius from any of its mirrored axis */
+static bool sculpt_is_vertex_inside_brush_radius_symm(float vertex[3],
+ float br_co[3],
+ float radius,
+ char symm)
+{
+ for (char i = 0; i <= symm; ++i) {
+ if (is_symmetry_iteration_valid(i, symm)) {
+ float location[3];
+ flip_v3_v3(location, br_co, (char)i);
+ if (len_squared_v3v3(location, vertex) < radius * radius) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
/** \name Tool Capabilities
*
* Avoid duplicate checks, internal logic only,
@@ -976,6 +1071,145 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test,
#endif
+/* Automasking */
+
+static bool sculpt_automasking_enabled(SculptSession *ss, const Brush *br)
+{
+ // REMOVE WITH PBVH_GRIDS
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ return false;
+ }
+
+ if (sculpt_stroke_is_dynamic_topology(ss, br)) {
+ return false;
+ }
+ if (br->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+ return true;
+ }
+ return false;
+}
+
+static float sculpt_automasking_factor_get(SculptSession *ss, const Brush *br, int vert)
+{
+ if (ss->cache->automask) {
+ return ss->cache->automask[vert];
+ }
+ else {
+ return 1.0f;
+ }
+}
+
+static void sculpt_automasking_end(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ if (ss->cache && ss->cache->automask) {
+ MEM_freeN(ss->cache->automask);
+ }
+}
+
+static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
+{
+ if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
+ return true;
+ }
+ return false;
+}
+
+typedef struct VertexTopologyIterator {
+ int v;
+ int it;
+} VertexTopologyIterator;
+
+static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+{
+
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ if (!sculpt_automasking_enabled(ss, brush)) {
+ return NULL;
+ }
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
+ BLI_assert(!"Topology masking: pmap missing");
+ return NULL;
+ }
+
+ bool *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(bool),
+ "visited vertices");
+
+ BLI_Stack *not_visited_vertices = BLI_stack_new(sizeof(VertexTopologyIterator),
+ "not vertices stack");
+
+ VertexTopologyIterator mevit;
+
+ /* Add active vertex and symmetric vertices to the stack. */
+ float location[3];
+ const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+ for (char i = 0; i <= symm; ++i) {
+ if (is_symmetry_iteration_valid(i, symm)) {
+ flip_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)), i);
+ if (i == 0) {
+ mevit.v = sculpt_active_vertex_get(ss);
+ }
+ else {
+ mevit.v = sculpt_nearest_vertex_get(
+ sd, ob, location, ss->cache->radius * ss->cache->radius, false);
+ }
+ if (mevit.v != -1) {
+ mevit.it = 1;
+ BLI_stack_push(not_visited_vertices, &mevit);
+ }
+ }
+ }
+
+ copy_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)));
+ bool use_radius = sculpt_automasking_is_constrained_by_radius(brush);
+
+ /* Flood fill automask to connected vertices. Limited to vertices inside the brush radius if the
+ * tool requires it */
+ while (!BLI_stack_is_empty(not_visited_vertices)) {
+ VertexTopologyIterator c_mevit;
+ BLI_stack_pop(not_visited_vertices, &c_mevit);
+ SculptVertexNeighborIter ni;
+ sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni)
+ {
+ if (!visited_vertices[(int)ni.index]) {
+ VertexTopologyIterator new_entry;
+ new_entry.v = ni.index;
+ automask_factor[new_entry.v] = 1.0f;
+ visited_vertices[(int)ni.index] = true;
+ if (!use_radius ||
+ sculpt_is_vertex_inside_brush_radius_symm(
+ sculpt_vertex_co_get(ss, new_entry.v), location, ss->cache->radius, symm)) {
+ BLI_stack_push(not_visited_vertices, &new_entry);
+ }
+ }
+ }
+ sculpt_vertex_neighbors_iter_end(ni)
+ }
+
+ BLI_stack_free(not_visited_vertices);
+
+ MEM_freeN(visited_vertices);
+
+ return automask_factor;
+}
+
+static void sculpt_automasking_init(Sculpt *sd, Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ ss->cache->automask = MEM_callocN(sizeof(float) * sculpt_vertex_count_get(ss),
+ "automask_factor");
+
+ if (brush->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+ sculpt_vertex_random_access_init(ss);
+ sculpt_topology_automasking_init(sd, ob, ss->cache->automask);
+ }
+}
+
/* ===== Sculpting =====
*/
static void flip_v3(float v[3], const char symm)
@@ -1479,6 +1713,7 @@ float tex_strength(SculptSession *ss,
const short vno[3],
const float fno[3],
const float mask,
+ const int vertex_index,
const int thread_id)
{
StrokeCache *cache = ss->cache;
@@ -1549,6 +1784,9 @@ float tex_strength(SculptSession *ss,
/* Paint mask */
avg *= 1.0f - mask;
+ /* Automasking */
+ avg *= sculpt_automasking_factor_get(ss, br, vertex_index);
+
return avg;
}
@@ -1557,7 +1795,12 @@ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
{
SculptSearchSphereData *data = data_v;
float *center, nearest[3];
- center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location;
+ if (data->center) {
+ center = data->center;
+ }
+ else {
+ center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location;
+ }
float t[3], bb_min[3], bb_max[3];
int i;
@@ -1642,6 +1885,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
.sd = sd,
.radius_squared = ss->cache ? SQUARE(ss->cache->radius * radius_scale) : ss->cursor_radius,
.original = use_original,
+ .center = NULL,
};
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode);
}
@@ -2072,6 +2316,7 @@ static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
+ vd.index,
tls->thread_id);
if (smooth_mask) {
float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask;
@@ -2127,6 +2372,7 @@ static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
smooth_mask ? 0.0f : *vd.mask,
+ vd.index,
tls->thread_id);
if (smooth_mask) {
float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask;
@@ -2186,11 +2432,17 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const float fade =
- bstrength *
- tex_strength(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, tls->thread_id) *
- ss->cache->pressure;
+ const float fade = bstrength *
+ tex_strength(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ *vd.mask,
+ vd.index,
+ tls->thread_id) *
+ ss->cache->pressure;
float avg[3], val[3];
@@ -2334,7 +2586,7 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata,
const float fade =
bstrength *
tex_strength(
- ss, brush, co, sqrtf(test.dist), NULL, fno, strength_mask, tls->thread_id);
+ ss, brush, co, sqrtf(test.dist), NULL, fno, strength_mask, 0, tls->thread_id);
float f = 1.0f / 16.0f;
if (x == 0 || x == gridsize - 1) {
@@ -2495,7 +2747,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
{
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, tls->thread_id);
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, tls->thread_id);
(*vd.mask) += fade * bstrength;
CLAMP(*vd.mask, 0, 1);
@@ -2570,6 +2822,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -2646,6 +2899,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -2723,6 +2977,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
float val1[3];
float val2[3];
@@ -2833,6 +3088,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
float val[3];
@@ -2901,6 +3157,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
@@ -3156,6 +3413,8 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(final_disp, 1.0f - *vd.mask);
}
+ mul_v3_fl(final_disp, sculpt_automasking_factor_get(ss, brush, vd.index));
+
copy_v3_v3(proxy[vd.i], final_disp);
if (vd.mvert) {
@@ -3216,6 +3475,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -3289,6 +3549,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
@@ -3409,6 +3670,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -3482,6 +3744,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
sub_v3_v3v3(vec, orig_data.co, ss->cache->location);
@@ -3561,6 +3824,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
float *disp = &layer_disp[vd.i];
float val[3];
@@ -3647,6 +3911,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
float val[3];
@@ -3833,6 +4098,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -3927,6 +4193,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -4024,6 +4291,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -4152,6 +4420,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -4244,6 +4513,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -4324,6 +4594,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
+ vd.index,
tls->thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -4533,6 +4804,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
update_brush_local_mat(sd, ob);
}
+ if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+ if (sculpt_automasking_enabled(ss, brush)) {
+ sculpt_automasking_init(sd, ob);
+ }
+ }
+
/* Apply one type of brush action */
switch (brush->sculpt_tool) {
case SCULPT_TOOL_DRAW:
@@ -5553,9 +5830,12 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
/* Returns true if any of the smoothing modes are active (currently
* one of smooth brush, autosmooth, mask smooth, or shift-key
* smooth) */
-static bool sculpt_any_smooth_mode(const Brush *brush, StrokeCache *cache, int stroke_mode)
+static bool sculpt_needs_conectivity_info(const Brush *brush, SculptSession *ss, int stroke_mode)
{
- return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (cache && cache->alt_smooth) ||
+ if (ss && sculpt_automasking_enabled(ss, brush)) {
+ return true;
+ }
+ return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (ss && ss->cache && ss->cache->alt_smooth) ||
(brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) ||
((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)));
}
@@ -5565,7 +5845,7 @@ static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const B
SculptSession *ss = ob->sculpt;
View3D *v3d = CTX_wm_view3d(C);
- bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0);
+ bool need_pmap = sculpt_needs_conectivity_info(brush, ss, 0);
if (ss->kb || ss->modifiers_active || (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false);
@@ -5920,7 +6200,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
view3d_operator_needs_opengl(C);
sculpt_brush_init_tex(scene, sd, ss);
- is_smooth = sculpt_any_smooth_mode(brush, NULL, mode);
+ is_smooth = sculpt_needs_conectivity_info(brush, ss, mode);
BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask);
}
@@ -6181,6 +6461,10 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
}
}
+ if (sculpt_automasking_enabled(ss, brush)) {
+ sculpt_automasking_end(ob);
+ }
+
sculpt_cache_free(ss->cache);
ss->cache = NULL;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 359929b7005..b02ccca3d4a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -181,6 +181,10 @@ typedef struct SculptThreadedTaskData {
float *prev_mask;
+ float max_distance_squared;
+ float nearest_vertex_search_co[3];
+ int nearest_vertex_index;
+
ThreadMutex mutex;
} SculptThreadedTaskData;
@@ -208,6 +212,7 @@ typedef struct {
struct Sculpt *sd;
struct SculptSession *ss;
float radius_squared;
+ float *center;
bool original;
} SculptSearchSphereData;
@@ -241,6 +246,7 @@ float tex_strength(struct SculptSession *ss,
const short vno[3],
const float fno[3],
const float mask,
+ const int vertex_index,
const int thread_id);
/* just for vertex paint. */
@@ -342,6 +348,8 @@ typedef struct StrokeCache {
float true_gravity_direction[3];
float gravity_direction[3];
+ float *automask;
+
rcti previous_r; /* previous redraw rectangle */
rcti current_r; /* current redraw rectangle */
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index c5d741fd25a..1906259f3e9 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -206,6 +206,10 @@ typedef enum eBrushElasticDeformType {
BRUSH_ELASTIC_DEFORM_TWIST = 4,
} eBrushElasticDeformType;
+typedef enum eAutomasking_flag {
+ BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0),
+} eAutomasking_flag;
+
typedef struct Brush {
ID id;
@@ -312,6 +316,9 @@ typedef struct Brush {
float texture_sample_bias;
int curve_preset;
+ int automasking_flags;
+
+ char _pad1[4];
int elastic_deform_type;
float elastic_deform_compressibility;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 5f574cc0f8e..5c972ed1a35 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1969,6 +1969,13 @@ static void rna_def_brush(BlenderRNA *brna)
"When locked keep using normal of surface where stroke was initiated");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "use_automasking_topology", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_TOPOLOGY);
+ RNA_def_property_ui_text(prop,
+ "Topology Automasking",
+ "Affect only vertices connected to the active vertex under the brush");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "use_scene_spacing", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, brush_spacing_unit_items);