Cleanup: Use C++ threading API in sculpt/paint code

Remove the global `SculptThreadedTaskData` struct which contained
the arguments to ALL multi-threaded sculpt functions. Use the C++
threading API instead of the old task API, moving the arguments
previously stored in the shared struct to actual function arguments.

Pull Request: https://projects.blender.org/blender/blender/pulls/111525
This commit is contained in:
Hans Goudey 2023-08-25 18:18:35 +02:00 committed by Hans Goudey
parent 4151691552
commit cf9fcbf24e
21 changed files with 1519 additions and 2160 deletions

View File

@ -103,23 +103,19 @@ struct MaskTaskData {
float view_normal[3];
};
static void mask_flood_fill_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void mask_flood_fill_task(MaskTaskData &data, const int i)
{
MaskTaskData *data = static_cast<MaskTaskData *>(userdata);
PBVHNode *node = data.nodes[i];
PBVHNode *node = data->nodes[i];
const PaintMaskFloodMode mode = data->mode;
const float value = data->value;
const PaintMaskFloodMode mode = data.mode;
const float value = data.value;
bool redraw = false;
PBVHVertexIter vi;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
SCULPT_undo_push_node(data.ob, node, SCULPT_UNDO_MASK);
BKE_pbvh_vertex_iter_begin (data->pbvh, node, vi, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (data.pbvh, node, vi, PBVH_ITER_UNIQUE) {
float prevmask = *vi.mask;
mask_flood_fill_set_elem(vi.mask, mode, value);
if (prevmask != *vi.mask) {
@ -130,7 +126,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata,
if (redraw) {
BKE_pbvh_node_mark_update_mask(node);
if (data->multires) {
if (data.multires) {
BKE_pbvh_node_mark_normals_update(node);
}
}
@ -138,6 +134,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata,
static int mask_flood_fill_exec(bContext *C, wmOperator *op)
{
using namespace blender;
const Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -166,9 +163,11 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
data.mode = mode;
data.value = value;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, mask_flood_fill_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
mask_flood_fill_task(data, i);
}
});
if (multires) {
multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);

View File

@ -293,7 +293,7 @@ Vector<PBVHNode *> pbvh_gather_generic(Object *ob, VPaint *wp, Sculpt *sd, Brush
ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); });
if (use_normal) {
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, ss->cache->sculpt_normal_symm);
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, ss->cache->sculpt_normal_symm);
}
else {
zero_v3(ss->cache->sculpt_normal_symm);

View File

@ -1043,34 +1043,20 @@ static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintIn
return weight;
}
static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
const MDeformVert *dv = &data->wpi->dvert[n];
data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi);
}
static void precompute_weight_values(
bContext *C, Object *ob, Brush *brush, WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me)
Object *ob, Brush *brush, WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me)
{
using namespace blender;
if (wpd->precomputed_weight_ready && !vwpaint::brush_use_accumulate_ex(brush, ob->mode)) {
return;
}
/* threaded loop over vertices */
SculptThreadedTaskData data;
data.C = C;
data.ob = ob;
data.wpd = wpd;
data.wpi = wpi;
data.me = me;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
BLI_task_parallel_range(0, me->totvert, &data, do_wpaint_precompute_weight_cb_ex, &settings);
threading::parallel_for(IndexRange(me->totvert), 512, [&](const IndexRange range) {
for (const int i : range) {
const MDeformVert *dv = &wpi->dvert[i];
wpd->precomputed_weight[i] = wpaint_get_active_weight(dv, wpi);
}
});
wpd->precomputed_weight_ready = true;
}
@ -1079,40 +1065,42 @@ static void precompute_weight_values(
/** \name Weight paint brushes.
* \{ */
static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_wpaint_brush_blur_task(const Scene *scene,
Object *ob,
const Brush *brush,
VPaint *vp,
WPaintData *wpd,
const WeightPaintInfo *wpi,
Mesh *me,
PBVHNode *node)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
const Brush *brush = data->brush;
const StrokeCache *cache = ss->cache;
Scene *scene = CTX_data_scene(data->C);
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
vwpaint::get_brush_alpha_data(
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint::use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape);
ss, brush->falloff_shape);
const blender::bke::AttributeAccessor attributes = data->me->attributes();
const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -1134,7 +1122,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
total_hit_loops += face.size();
for (const int vert : ss->corner_verts.slice(face)) {
weight_final += data->wpd->precomputed_weight[vert];
weight_final += wpd->precomputed_weight[vert];
}
}
@ -1147,7 +1135,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f;
if (!vwpaint::test_brush_angle_falloff(
*brush, data->wpd->normal_angle_precalc, angle_cos, &brush_strength))
*brush, wpd->normal_angle_precalc, angle_cos, &brush_strength))
{
continue;
}
@ -1166,23 +1154,25 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
weight_final /= total_hit_loops;
/* Only paint visible verts */
do_weight_paint_vertex(data->vp, data->ob, data->wpi, v_index, final_alpha, weight_final);
do_weight_paint_vertex(vp, ob, wpi, v_index, final_alpha, weight_final);
}
BKE_pbvh_vertex_iter_end;
}
static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_wpaint_brush_smear_task(const Scene *scene,
Object *ob,
const Brush *brush,
VPaint *vp,
WPaintData *wpd,
const WeightPaintInfo *wpi,
Mesh *me,
PBVHNode *node)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
const Brush *brush = data->brush;
const Scene *scene = CTX_data_scene(data->C);
const StrokeCache *cache = ss->cache;
if (!cache->is_last_valid) {
return;
@ -1191,9 +1181,9 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
vwpaint::get_brush_alpha_data(
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint::use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
float brush_dir[3];
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
@ -1202,19 +1192,19 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
return;
}
const blender::bke::AttributeAccessor attributes = data->me->attributes();
const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape);
ss, brush->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -1236,7 +1226,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f;
if (!vwpaint::test_brush_angle_falloff(
*brush, data->wpd->normal_angle_precalc, angle_cos, &brush_strength))
*brush, wpd->normal_angle_precalc, angle_cos, &brush_strength))
{
continue;
}
@ -1266,7 +1256,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot;
weight_final = data->wpd->precomputed_weight[v_other_index];
weight_final = wpd->precomputed_weight[v_other_index];
do_color = true;
}
}
@ -1281,48 +1271,50 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
continue;
}
do_weight_paint_vertex(
data->vp, data->ob, data->wpi, v_index, final_alpha, float(weight_final));
do_weight_paint_vertex(vp, ob, wpi, v_index, final_alpha, float(weight_final));
}
}
BKE_pbvh_vertex_iter_end;
}
static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_wpaint_brush_draw_task(const Scene *scene,
Object *ob,
const Brush *brush,
VPaint *vp,
WPaintData *wpd,
const WeightPaintInfo *wpi,
Mesh *me,
const float strength,
PBVHNode *node)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const Scene *scene = CTX_data_scene(data->C);
const Brush *brush = data->brush;
const StrokeCache *cache = ss->cache;
/* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used,
* however in this case we calculate a new weight each time. */
const float paintweight = data->strength;
const float paintweight = strength;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
vwpaint::get_brush_alpha_data(
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint::use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape);
ss, brush->falloff_shape);
const blender::bke::AttributeAccessor attributes = data->me->attributes();
const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -1342,7 +1334,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f;
if (!vwpaint::test_brush_angle_falloff(
*brush, data->wpd->normal_angle_precalc, angle_cos, &brush_strength))
*brush, wpd->normal_angle_precalc, angle_cos, &brush_strength))
{
continue;
}
@ -1358,42 +1350,44 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
}
}
do_weight_paint_vertex(data->vp, data->ob, data->wpi, v_index, final_alpha, paintweight);
do_weight_paint_vertex(vp, ob, wpi, v_index, final_alpha, paintweight);
}
BKE_pbvh_vertex_iter_end;
}
static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static WPaintAverageAccum do_wpaint_brush_calc_average_weight(Object *ob,
const Mesh *me,
const Brush *brush,
const VPaint *vp,
WeightPaintInfo *wpi,
PBVHNode *node)
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const bool use_normal = vwpaint::use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
const bool use_normal = vwpaint::use_normal(vp);
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
WPaintAverageAccum *accum = (WPaintAverageAccum *)data->custom_data + n;
accum->len = 0;
accum->value = 0.0;
WPaintAverageAccum accum{};
accum.len = 0;
accum.value = 0.0;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, data->brush->falloff_shape);
ss, brush->falloff_shape);
const blender::bke::AttributeAccessor attributes = data->me->attributes();
const blender::bke::AttributeAccessor attributes = me->attributes();
const blender::VArray<bool> select_vert = *attributes.lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -1402,8 +1396,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata,
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
1.0f;
if (angle_cos <= 0.0f ||
BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) <= 0.0f)
{
BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius) <= 0.0f) {
continue;
}
@ -1414,23 +1407,32 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata,
continue;
}
const MDeformVert *dv = &data->wpi->dvert[v_index];
accum->len += 1;
accum->value += wpaint_get_active_weight(dv, data->wpi);
const MDeformVert *dv = &wpi->dvert[v_index];
accum.len += 1;
accum.value += wpaint_get_active_weight(dv, wpi);
}
BKE_pbvh_vertex_iter_end;
return accum;
}
static void calculate_average_weight(SculptThreadedTaskData *data, Span<PBVHNode *> nodes)
static float calculate_average_weight(Object *ob,
const Mesh *me,
const Brush *brush,
VPaint *vp,
WeightPaintInfo *wpi,
Span<PBVHNode *> nodes)
{
WPaintAverageAccum *accum = (WPaintAverageAccum *)MEM_mallocN(sizeof(*accum) * nodes.size(),
__func__);
data->custom_data = accum;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(
0, nodes.size(), data, do_wpaint_brush_calc_average_weight_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
accum[i] = do_wpaint_brush_calc_average_weight(ob, me, brush, vp, wpi, nodes[i]);
}
});
float strength = 0.0f;
uint accum_len = 0;
double accum_weight = 0.0;
@ -1440,15 +1442,16 @@ static void calculate_average_weight(SculptThreadedTaskData *data, Span<PBVHNode
}
if (accum_len != 0) {
accum_weight /= accum_len;
data->strength = float(accum_weight);
strength = float(accum_weight);
}
MEM_SAFE_FREE(data->custom_data); /* 'accum' */
MEM_SAFE_FREE(accum);
return strength;
}
static void wpaint_paint_leaves(bContext *C,
Object *ob,
Sculpt *sd,
VPaint *vp,
WPaintData *wpd,
WeightPaintInfo *wpi,
@ -1458,40 +1461,70 @@ static void wpaint_paint_leaves(bContext *C,
Scene *scene = CTX_data_scene(C);
const Brush *brush = ob->sculpt->cache->brush;
/* threaded loop over nodes */
SculptThreadedTaskData data = {nullptr};
data.C = C;
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.vp = vp;
data.wpd = wpd;
data.wpi = wpi;
data.me = me;
/* Use this so average can modify its weight without touching the brush. */
data.strength = BKE_brush_weight_get(scene, brush);
float strength = BKE_brush_weight_get(scene, brush);
if (brush->weightpaint_tool == WPAINT_TOOL_AVERAGE) {
strength = calculate_average_weight(ob, me, brush, vp, wpi, nodes);
}
/* NOTE: current mirroring code cannot be run in parallel */
TaskParallelSettings settings;
const bool use_threading = !ME_USING_MIRROR_X_VERTEX_GROUPS(me);
BKE_pbvh_parallel_range_settings(&settings, use_threading, nodes.size());
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
case WPAINT_TOOL_AVERAGE:
calculate_average_weight(&data, nodes);
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_draw_task_cb_ex, &settings);
break;
case WPAINT_TOOL_SMEAR:
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_smear_task_cb_ex, &settings);
break;
case WPAINT_TOOL_BLUR:
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_blur_task_cb_ex, &settings);
break;
case WPAINT_TOOL_DRAW:
BLI_task_parallel_range(0, nodes.size(), &data, do_wpaint_brush_draw_task_cb_ex, &settings);
break;
if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
/* NOTE: current mirroring code cannot be run in parallel */
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
case WPAINT_TOOL_AVERAGE: {
for (const int i : nodes.index_range()) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
break;
}
case WPAINT_TOOL_SMEAR:
for (const int i : nodes.index_range()) {
do_wpaint_brush_smear_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
}
break;
case WPAINT_TOOL_BLUR:
for (const int i : nodes.index_range()) {
do_wpaint_brush_blur_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
}
break;
case WPAINT_TOOL_DRAW:
for (const int i : nodes.index_range()) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
break;
}
}
else {
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
case WPAINT_TOOL_AVERAGE: {
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
});
break;
}
case WPAINT_TOOL_SMEAR:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_smear_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
}
});
break;
case WPAINT_TOOL_BLUR:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_blur_task(scene, ob, brush, vp, wpd, wpi, me, nodes[i]);
}
});
break;
case WPAINT_TOOL_DRAW:
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_wpaint_brush_draw_task(scene, ob, brush, vp, wpd, wpi, me, strength, nodes[i]);
}
});
break;
}
}
}
/** \} */
@ -1665,7 +1698,7 @@ static void wpaint_do_paint(bContext *C,
Vector<PBVHNode *> nodes = vwpaint::pbvh_gather_generic(ob, wp, sd, brush);
wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes);
wpaint_paint_leaves(C, ob, wp, wpd, wpi, me, nodes);
}
static void wpaint_do_radial_symmetry(bContext *C,
@ -1808,7 +1841,7 @@ static void wpaint_stroke_update_step(bContext *C,
/* *** done setting up WeightPaintInfo *** */
if (wpd->precomputed_weight) {
precompute_weight_values(C, ob, brush, wpd, &wpi, mesh);
precompute_weight_values(ob, brush, wpd, &wpi, mesh);
}
wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);

View File

@ -1028,53 +1028,34 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3],
return is_in_symmetry_area;
}
struct NearestVertexTLSData {
struct NearestVertexData {
PBVHVertRef nearest_vertex;
float nearest_vertex_distance_squared;
float nearest_vertex_distance_sq;
};
static void do_nearest_vertex_get_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void nearest_vertex_get_node(PBVH *pbvh,
const float nearest_vertex_search_co[3],
const float max_distance_sq,
PBVHNode *node,
NearestVertexData *nvtd)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
NearestVertexTLSData *nvtd = static_cast<NearestVertexTLSData *>(tls->userdata_chunk);
PBVHVertexIter vd;
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 < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared)
BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) {
float distance_squared = len_squared_v3v3(vd.co, nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_sq && distance_squared < max_distance_sq)
{
nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
nvtd->nearest_vertex_distance_sq = distance_squared;
}
}
BKE_pbvh_vertex_iter_end;
}
static void nearest_vertex_get_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
NearestVertexTLSData *join = static_cast<NearestVertexTLSData *>(chunk_join);
NearestVertexTLSData *nvtd = static_cast<NearestVertexTLSData *>(chunk);
if (join->nearest_vertex.i == PBVH_REF_NONE) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
PBVHVertRef SCULPT_nearest_vertex_get(
Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Vector<PBVHNode *> nodes;
SculptSearchSphereData data{};
data.sd = sd;
@ -1082,31 +1063,28 @@ PBVHVertRef SCULPT_nearest_vertex_get(
data.original = use_original;
data.center = co;
nodes = blender::bke::pbvh::search_gather(
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(
ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); });
if (nodes.is_empty()) {
return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
SculptThreadedTaskData task_data{};
task_data.sd = sd;
task_data.ob = ob;
task_data.nodes = nodes;
task_data.max_distance_squared = max_distance * max_distance;
const float max_distance_sq = max_distance * max_distance;
copy_v3_v3(task_data.nearest_vertex_search_co, co);
NearestVertexTLSData nvtd;
nvtd.nearest_vertex.i = PBVH_REF_NONE;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
settings.func_reduce = nearest_vertex_get_reduce;
settings.userdata_chunk = &nvtd;
settings.userdata_chunk_size = sizeof(NearestVertexTLSData);
BLI_task_parallel_range(0, nodes.size(), &task_data, do_nearest_vertex_get_task_cb, &settings);
return nvtd.nearest_vertex;
return threading::parallel_reduce(
nodes.index_range(),
1,
NearestVertexData{PBVH_REF_NONE, FLT_MAX},
[&](const IndexRange range, NearestVertexData nearest) {
for (const int i : range) {
nearest_vertex_get_node(ss->pbvh, co, max_distance_sq, nodes[i], &nearest);
}
return nearest;
},
[](const NearestVertexData a, const NearestVertexData b) {
return a.nearest_vertex_distance_sq < b.nearest_vertex_distance_sq ? a : b;
})
.nearest_vertex;
}
bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
@ -1482,15 +1460,91 @@ bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *bru
/** \name Sculpt Paint Mesh
* \{ */
static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void paint_mesh_restore_node(Object *ob, const SculptUndoType type, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
SculptUndoNode *unode;
if (ss->bm) {
unode = SCULPT_undo_push_node(ob, node, type);
}
else {
unode = SCULPT_undo_get_node(node, type);
}
if (!unode) {
return;
}
switch (type) {
case SCULPT_UNDO_MASK: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
*vd.mask = orig_vert_data.mask;
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(node);
break;
}
case SCULPT_UNDO_COLOR: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
SCULPT_vertex_color_set(ss, vd.vertex, orig_vert_data.col);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(node);
break;
}
case SCULPT_UNDO_FACE_SETS: {
SculptOrigFaceData orig_face_data;
SCULPT_orig_face_data_unode_init(&orig_face_data, ob, unode);
PBVHFaceIter fd;
BKE_pbvh_face_iter_begin (ss->pbvh, node, fd) {
SCULPT_orig_face_data_update(&orig_face_data, &fd);
if (fd.face_set) {
*fd.face_set = orig_face_data.face_set;
}
}
BKE_pbvh_face_iter_end(fd);
BKE_pbvh_node_mark_update_face_sets(node);
break;
}
case SCULPT_UNDO_COORDS: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
copy_v3_v3(vd.co, orig_vert_data.co);
if (vd.is_mesh) {
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update(node);
break;
}
default:
break;
}
}
static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
SculptUndoType type;
switch (data->brush->sculpt_tool) {
switch (brush->sculpt_tool) {
case SCULPT_TOOL_MASK:
type = SCULPT_UNDO_MASK;
break;
@ -1506,99 +1560,22 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
break;
}
SculptUndoNode *unode;
if (ss->bm) {
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type);
/* Disable multi-threading when dynamic-topology is enabled. Otherwise,
* new entries might be inserted by #SCULPT_undo_push_node() into the #GHash
* used internally by #BM_log_original_vert_co() by a different thread. See #33787. */
for (const int i : nodes.index_range()) {
paint_mesh_restore_node(ob, type, nodes[i]);
}
}
else {
unode = SCULPT_undo_get_node(data->nodes[n], type);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
paint_mesh_restore_node(ob, type, nodes[i]);
}
});
}
if (!unode) {
return;
}
switch (type) {
case SCULPT_UNDO_MASK: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
*vd.mask = orig_vert_data.mask;
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
break;
}
case SCULPT_UNDO_COLOR: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
SCULPT_vertex_color_set(ss, vd.vertex, orig_vert_data.col);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(data->nodes[n]);
break;
}
case SCULPT_UNDO_FACE_SETS: {
SculptOrigFaceData orig_face_data;
SCULPT_orig_face_data_unode_init(&orig_face_data, data->ob, unode);
PBVHFaceIter fd;
BKE_pbvh_face_iter_begin (ss->pbvh, data->nodes[n], fd) {
SCULPT_orig_face_data_update(&orig_face_data, &fd);
if (fd.face_set) {
*fd.face_set = orig_face_data.face_set;
}
}
BKE_pbvh_face_iter_end(fd);
BKE_pbvh_node_mark_update_face_sets(data->nodes[n]);
break;
}
case SCULPT_UNDO_COORDS: {
SculptOrigVertData orig_vert_data;
SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_vert_data, &vd);
copy_v3_v3(vd.co, orig_vert_data.co);
if (vd.is_mesh) {
BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update(data->nodes[n]);
break;
}
default:
break;
}
}
static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
/**
* Disable multi-threading when dynamic-topology is enabled. Otherwise,
* new entries might be inserted by #SCULPT_undo_push_node() into the #GHash
* used internally by #BM_log_original_vert_co() by a different thread. See #33787.
*/
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, paint_mesh_restore_co_task_cb, &settings);
BKE_pbvh_node_color_buffer_free(ss->pbvh);
}
@ -1990,7 +1967,7 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache)
* \note These are all _very_ similar, when changing one, check others.
* \{ */
struct AreaNormalCenterTLSData {
struct AreaNormalCenterData {
/* 0 = towards view, 1 = flipped */
float area_cos[2][3];
float area_nos[2][3];
@ -1998,15 +1975,16 @@ struct AreaNormalCenterTLSData {
int count_co[2];
};
static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void calc_area_normal_and_center_task(Object *ob,
const Brush *brush,
const bool use_area_nos,
const bool use_area_cos,
const bool has_bm_orco,
PBVHNode *node,
AreaNormalCenterData *anctd,
bool &r_any_vertex_sampled)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
AreaNormalCenterTLSData *anctd = static_cast<AreaNormalCenterTLSData *>(tls->userdata_chunk);
const bool use_area_nos = data->use_area_nos;
const bool use_area_cos = data->use_area_cos;
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
SculptUndoNode *unode = nullptr;
@ -2015,40 +1993,40 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
bool normal_test_r, area_test_r;
if (ss->cache && !ss->cache->accum) {
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
use_original = (unode->co || unode->bm_entry);
}
SculptBrushTest normal_test;
SculptBrushTestFn sculpt_brush_normal_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &normal_test, data->brush->falloff_shape);
ss, &normal_test, brush->falloff_shape);
/* Update the test radius to sample the normal using the normal radius of the brush. */
if (data->brush->ob_mode == OB_MODE_SCULPT) {
if (brush->ob_mode == OB_MODE_SCULPT) {
float test_radius = sqrtf(normal_test.radius_squared);
test_radius *= data->brush->normal_radius_factor;
test_radius *= brush->normal_radius_factor;
normal_test.radius = test_radius;
normal_test.radius_squared = test_radius * test_radius;
}
SculptBrushTest area_test;
SculptBrushTestFn sculpt_brush_area_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &area_test, data->brush->falloff_shape);
ss, &area_test, brush->falloff_shape);
if (data->brush->ob_mode == OB_MODE_SCULPT) {
if (brush->ob_mode == OB_MODE_SCULPT) {
float test_radius = sqrtf(area_test.radius_squared);
/* Layer brush produces artifacts with normal and area radius */
/* Enable area radius control only on Scrape for now */
if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) &&
data->brush->area_radius_factor > 0.0f)
if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) &&
brush->area_radius_factor > 0.0f)
{
test_radius *= data->brush->area_radius_factor;
if (ss->cache && data->brush->flag2 & BRUSH_AREA_RADIUS_PRESSURE) {
test_radius *= brush->area_radius_factor;
if (ss->cache && brush->flag2 & BRUSH_AREA_RADIUS_PRESSURE) {
test_radius *= ss->cache->pressure;
}
}
else {
test_radius *= data->brush->normal_radius_factor;
test_radius *= brush->normal_radius_factor;
}
area_test.radius = test_radius;
area_test.radius_squared = test_radius * test_radius;
@ -2056,13 +2034,12 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
/* When the mesh is edited we can't rely on original coords
* (original mesh may not even have verts in brush radius). */
if (use_original && data->has_bm_orco) {
if (use_original && has_bm_orco) {
float(*orco_coords)[3];
int(*orco_tris)[3];
int orco_tris_num;
BKE_pbvh_node_get_bm_orco_data(
data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, nullptr);
BKE_pbvh_node_get_bm_orco_data(node, &orco_tris, &orco_tris_num, &orco_coords, nullptr);
for (int i = 0; i < orco_tris_num; i++) {
const float *co_tri[3] = {
@ -2112,7 +2089,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
}
}
else {
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float co[3];
/* For bm_vert only. */
@ -2145,7 +2122,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
float no[3];
int flip_index;
data->any_vertex_sampled = true;
r_any_vertex_sampled = true;
if (use_original) {
copy_v3_v3(no, no_s);
@ -2189,50 +2166,48 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
}
}
static void calc_area_normal_and_center_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
static AreaNormalCenterData calc_area_normal_and_center_reduce(const AreaNormalCenterData &a,
const AreaNormalCenterData &b)
{
AreaNormalCenterTLSData *join = static_cast<AreaNormalCenterTLSData *>(chunk_join);
AreaNormalCenterTLSData *anctd = static_cast<AreaNormalCenterTLSData *>(chunk);
AreaNormalCenterData joined{};
/* For flatten center. */
add_v3_v3(join->area_cos[0], anctd->area_cos[0]);
add_v3_v3(join->area_cos[1], anctd->area_cos[1]);
add_v3_v3v3(joined.area_cos[0], a.area_cos[0], b.area_cos[0]);
add_v3_v3v3(joined.area_cos[1], a.area_cos[1], b.area_cos[1]);
/* For area normal. */
add_v3_v3(join->area_nos[0], anctd->area_nos[0]);
add_v3_v3(join->area_nos[1], anctd->area_nos[1]);
add_v3_v3v3(joined.area_nos[0], a.area_nos[0], b.area_nos[0]);
add_v3_v3v3(joined.area_nos[1], a.area_nos[1], b.area_nos[1]);
/* Weights. */
add_v2_v2_int(join->count_no, anctd->count_no);
add_v2_v2_int(join->count_co, anctd->count_co);
add_v2_v2v2_int(joined.count_no, a.count_no, b.count_no);
add_v2_v2v2_int(joined.count_co, a.count_co, b.count_co);
return joined;
}
void SCULPT_calc_area_center(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_co[3])
{
using namespace blender;
const Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush);
int n;
/* Intentionally set 'sd' to nullptr since we share logic with vertex paint. */
SculptThreadedTaskData data{};
data.sd = nullptr;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.has_bm_orco = has_bm_orco;
data.use_area_cos = true;
bool any_vertex_sampled = false;
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
BLI_task_parallel_range(0, nodes.size(), &data, calc_area_normal_and_center_task_cb, &settings);
const AreaNormalCenterData anctd = threading::parallel_reduce(
nodes.index_range(),
1,
AreaNormalCenterData{},
[&](const IndexRange range, AreaNormalCenterData anctd) {
for (const int i : range) {
calc_area_normal_and_center_task(
ob, brush, false, true, has_bm_orco, nodes[i], &anctd, any_vertex_sampled);
}
return anctd;
},
calc_area_normal_and_center_reduce);
/* For flatten center. */
for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
@ -2258,33 +2233,32 @@ void SCULPT_calc_area_center(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, flo
void SCULPT_calc_area_normal(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_no[3])
{
const Brush *brush = BKE_paint_brush(&sd->paint);
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, r_area_no);
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, r_area_no);
}
bool SCULPT_pbvh_calc_area_normal(
const Brush *brush, Object *ob, Span<PBVHNode *> nodes, bool use_threading, float r_area_no[3])
bool SCULPT_pbvh_calc_area_normal(const Brush *brush,
Object *ob,
Span<PBVHNode *> nodes,
float r_area_no[3])
{
using namespace blender;
SculptSession *ss = ob->sculpt;
const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush);
/* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */
SculptThreadedTaskData data{};
data.sd = nullptr;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.has_bm_orco = has_bm_orco;
data.use_area_nos = true;
data.any_vertex_sampled = false;
bool any_vertex_sampled = false;
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, use_threading, nodes.size());
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
BLI_task_parallel_range(0, nodes.size(), &data, calc_area_normal_and_center_task_cb, &settings);
const AreaNormalCenterData anctd = threading::parallel_reduce(
nodes.index_range(),
1,
AreaNormalCenterData{},
[&](const IndexRange range, AreaNormalCenterData anctd) {
for (const int i : range) {
calc_area_normal_and_center_task(
ob, brush, true, false, has_bm_orco, nodes[i], &anctd, any_vertex_sampled);
}
return anctd;
},
calc_area_normal_and_center_reduce);
/* For area normal. */
for (int i = 0; i < ARRAY_SIZE(anctd.area_nos); i++) {
@ -2293,35 +2267,32 @@ bool SCULPT_pbvh_calc_area_normal(
}
}
return data.any_vertex_sampled;
return any_vertex_sampled;
}
void SCULPT_calc_area_normal_and_center(
Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float r_area_no[3], float r_area_co[3])
{
using namespace blender;
const Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush);
int n;
/* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */
SculptThreadedTaskData data{};
data.sd = nullptr;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.has_bm_orco = has_bm_orco;
data.use_area_cos = true;
data.use_area_nos = true;
bool any_vertex_sampled = false;
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
BLI_task_parallel_range(0, nodes.size(), &data, calc_area_normal_and_center_task_cb, &settings);
const AreaNormalCenterData anctd = threading::parallel_reduce(
nodes.index_range(),
1,
AreaNormalCenterData{},
[&](const IndexRange range, AreaNormalCenterData anctd) {
for (const int i : range) {
calc_area_normal_and_center_task(
ob, brush, true, true, has_bm_orco, nodes[i], &anctd, any_vertex_sampled);
}
return anctd;
},
calc_area_normal_and_center_reduce);
/* For flatten center. */
for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
@ -3278,26 +3249,20 @@ float SCULPT_brush_plane_offset_get(Sculpt *sd, SculptSession *ss)
/** \name Sculpt Gravity Brush
* \{ */
static void do_gravity_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_gravity_task(SculptSession *ss,
const Brush *brush,
const float *offset,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float *offset = data->offset;
PBVHVertexIter vd;
float(*proxy)[3];
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -3323,6 +3288,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
static void do_gravity(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -3335,17 +3301,11 @@ static void do_gravity(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bst
mul_v3_v3v3(offset, gravity_vector, ss->cache->scale);
mul_v3_fl(offset, bstrength);
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.offset = offset;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_gravity_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_gravity_task(ss, brush, offset, nodes[i]);
}
});
}
/** \} */
@ -3455,41 +3415,38 @@ static void sculpt_topology_update(Sculpt *sd,
mul_m4_v3(ob->object_to_world, location);
}
static void do_brush_action_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_brush_action_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
bool need_coords = ss->cache->supports_gravity;
if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
BKE_pbvh_node_mark_update_face_sets(data->nodes[n]);
if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
BKE_pbvh_node_mark_update_face_sets(node);
/* Draw face sets in smooth mode moves the vertices. */
if (ss->cache->alt_smooth) {
need_coords = true;
}
else {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
}
}
else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
else if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(node);
}
else if (SCULPT_tool_is_paint(data->brush->sculpt_tool)) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
BKE_pbvh_node_mark_update_color(data->nodes[n]);
else if (SCULPT_tool_is_paint(brush->sculpt_tool)) {
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COLOR);
BKE_pbvh_node_mark_update_color(node);
}
else {
need_coords = true;
}
if (need_coords) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(node);
}
}
@ -3499,6 +3456,7 @@ static void do_brush_action(Sculpt *sd,
UnifiedPaintSettings *ups,
PaintModeSettings *paint_mode_settings)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Vector<PBVHNode *> nodes, texnodes;
@ -3599,15 +3557,11 @@ static void do_brush_action(Sculpt *sd,
float location[3];
if (!use_pixels) {
SculptThreadedTaskData task_data{};
task_data.sd = sd;
task_data.ob = ob;
task_data.brush = brush;
task_data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &task_data, do_brush_action_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_brush_action_task(ob, brush, nodes[i]);
}
});
}
if (sculpt_brush_needs_normal(ss, sd, brush)) {
@ -5194,7 +5148,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
}
/* Calculate the sampled normal. */
if (SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, sampled_normal)) {
if (SCULPT_pbvh_calc_area_normal(brush, ob, nodes, sampled_normal)) {
copy_v3_v3(out->normal, sampled_normal);
copy_v3_v3(ss->cursor_sampled_normal, sampled_normal);
}
@ -6018,63 +5972,42 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
}
struct NearestVertexFakeNeighborTLSData {
struct NearestVertexFakeNeighborData {
PBVHVertRef nearest_vertex;
float nearest_vertex_distance_squared;
float nearest_vertex_distance_sq;
int current_topology_id;
};
static void do_fake_neighbor_search_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_fake_neighbor_search_task(SculptSession *ss,
const float nearest_vertex_search_co[3],
const float max_distance_sq,
PBVHNode *node,
NearestVertexFakeNeighborData *nvtd)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
NearestVertexFakeNeighborTLSData *nvtd = static_cast<NearestVertexFakeNeighborTLSData *>(
tls->userdata_chunk);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
int vd_topology_id = SCULPT_vertex_island_get(ss, vd.vertex);
if (vd_topology_id != nvtd->current_topology_id &&
ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE)
{
float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared)
{
float distance_squared = len_squared_v3v3(vd.co, nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_sq &&
distance_squared < max_distance_sq) {
nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
nvtd->nearest_vertex_distance_sq = distance_squared;
}
}
}
BKE_pbvh_vertex_iter_end;
}
static void fake_neighbor_search_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
NearestVertexFakeNeighborTLSData *join = static_cast<NearestVertexFakeNeighborTLSData *>(
chunk_join);
NearestVertexFakeNeighborTLSData *nvtd = static_cast<NearestVertexFakeNeighborTLSData *>(chunk);
if (join->nearest_vertex.i == PBVH_REF_NONE) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
Object *ob,
const PBVHVertRef vertex,
float max_distance)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Vector<PBVHNode *> nodes;
SculptSearchSphereData data{};
data.ss = ss;
@ -6083,32 +6016,43 @@ static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
data.original = false;
data.center = SCULPT_vertex_co_get(ss, vertex);
nodes = blender::bke::pbvh::search_gather(
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(
ss->pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &data); });
if (nodes.is_empty()) {
return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
SculptThreadedTaskData task_data{};
task_data.sd = sd;
task_data.ob = ob;
task_data.nodes = nodes;
task_data.max_distance_squared = max_distance * max_distance;
const float3 nearest_vertex_search_co = SCULPT_vertex_co_get(ss, vertex);
const float max_distance_sq = max_distance * max_distance;
copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex));
NearestVertexFakeNeighborTLSData nvtd;
NearestVertexFakeNeighborData nvtd;
nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
nvtd.nearest_vertex_distance_sq = FLT_MAX;
nvtd.current_topology_id = SCULPT_vertex_island_get(ss, vertex);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
settings.func_reduce = fake_neighbor_search_reduce;
settings.userdata_chunk = &nvtd;
settings.userdata_chunk_size = sizeof(NearestVertexFakeNeighborTLSData);
BLI_task_parallel_range(0, nodes.size(), &task_data, do_fake_neighbor_search_task_cb, &settings);
nvtd = threading::parallel_reduce(
nodes.index_range(),
1,
nvtd,
[&](const IndexRange range, NearestVertexFakeNeighborData nvtd) {
for (const int i : range) {
do_fake_neighbor_search_task(
ss, nearest_vertex_search_co, max_distance_sq, nodes[i], &nvtd);
}
return nvtd;
},
[](const NearestVertexFakeNeighborData &a, const NearestVertexFakeNeighborData &b) {
NearestVertexFakeNeighborData joined = a;
if (joined.nearest_vertex.i == PBVH_REF_NONE) {
joined.nearest_vertex = b.nearest_vertex;
joined.nearest_vertex_distance_sq = b.nearest_vertex_distance_sq;
}
else if (b.nearest_vertex_distance_sq < joined.nearest_vertex_distance_sq) {
joined.nearest_vertex = b.nearest_vertex;
joined.nearest_vertex_distance_sq = b.nearest_vertex_distance_sq;
}
return joined;
});
return nvtd.nearest_vertex;
}

View File

@ -647,23 +647,18 @@ static float sculpt_boundary_displacement_from_grab_delta_get(SculptSession *ss,
return dist_signed_to_plane_v3(pos, plane);
}
/* Deformation tasks callbacks. */
static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_boundary_brush_bend_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
const Brush *brush = data->brush;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float strength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@ -673,10 +668,9 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
}
const float angle = angle_factor * M_PI;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@ -707,29 +701,24 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_boundary_brush_slide_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
const Brush *brush = data->brush;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float strength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@ -758,29 +747,24 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_boundary_brush_inflate_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
const Brush *brush = data->brush;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float strength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@ -809,27 +793,22 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_boundary_brush_grab_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
const Brush *brush = data->brush;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float strength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@ -857,25 +836,20 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_boundary_brush_twist_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const int symm_area = ss->cache->mirror_symmetry_pass;
SculptBoundary *boundary = ss->cache->boundaries[symm_area];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
const Brush *brush = data->brush;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float strength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@ -885,7 +859,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
}
const float angle = angle_factor * M_PI;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@ -916,24 +890,20 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_boundary_brush_smooth_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
const int symmetry_pass = ss->cache->mirror_symmetry_pass;
const SculptBoundary *boundary = ss->cache->boundaries[symmetry_pass];
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
const Brush *brush = data->brush;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float strength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@ -977,6 +947,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -1026,39 +997,48 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
return;
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
switch (brush->boundary_deform_type) {
case BRUSH_BOUNDARY_DEFORM_BEND:
BLI_task_parallel_range(
0, nodes.size(), &data, do_boundary_brush_bend_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_boundary_brush_bend_task(ob, brush, nodes[i]);
}
});
break;
case BRUSH_BOUNDARY_DEFORM_EXPAND:
BLI_task_parallel_range(
0, nodes.size(), &data, do_boundary_brush_slide_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_boundary_brush_slide_task(ob, brush, nodes[i]);
}
});
break;
case BRUSH_BOUNDARY_DEFORM_INFLATE:
BLI_task_parallel_range(
0, nodes.size(), &data, do_boundary_brush_inflate_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_boundary_brush_inflate_task(ob, brush, nodes[i]);
}
});
break;
case BRUSH_BOUNDARY_DEFORM_GRAB:
BLI_task_parallel_range(
0, nodes.size(), &data, do_boundary_brush_grab_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_boundary_brush_grab_task(ob, brush, nodes[i]);
}
});
break;
case BRUSH_BOUNDARY_DEFORM_TWIST:
BLI_task_parallel_range(
0, nodes.size(), &data, do_boundary_brush_twist_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_boundary_brush_twist_task(ob, brush, nodes[i]);
}
});
break;
case BRUSH_BOUNDARY_DEFORM_SMOOTH:
BLI_task_parallel_range(
0, nodes.size(), &data, do_boundary_brush_smooth_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_boundary_brush_smooth_task(ob, brush, nodes[i]);
}
});
break;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -291,17 +291,17 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_
cloth_brush_reallocate_constraints(cloth_sim);
}
static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_cloth_brush_build_constraints_task(Object *ob,
const Brush *brush,
SculptClothSimulation *cloth_sim,
float *cloth_sim_initial_location,
float cloth_sim_radius,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
PBVHNode *node = data->nodes[n];
SculptSession *ss = ob->sculpt;
const int node_index = POINTER_AS_INT(BLI_ghash_lookup(data->cloth_sim->node_state_index, node));
if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_UNINITIALIZED) {
const int node_index = POINTER_AS_INT(BLI_ghash_lookup(cloth_sim->node_state_index, node));
if (cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_UNINITIALIZED) {
/* The simulation already contains constraints for this node. */
return;
}
@ -330,11 +330,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
/* Only limit the constraint creation to a radius when the simulation is local. */
const float cloth_sim_radius_squared = brush->cloth_simulation_area_type ==
BRUSH_CLOTH_SIMULATION_AREA_LOCAL ?
data->cloth_sim_radius * data->cloth_sim_radius :
cloth_sim_radius * cloth_sim_radius :
FLT_MAX;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location);
const float len_squared = len_squared_v3v3(vd.co, cloth_sim_initial_location);
if (len_squared < cloth_sim_radius_squared) {
SculptVertexNeighborIter ni;
@ -348,8 +348,8 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (data->cloth_sim->softbody_strength > 0.0f) {
cloth_brush_add_softbody_constraint(data->cloth_sim, node_index, vd.index, 1.0f);
if (cloth_sim->softbody_strength > 0.0f) {
cloth_brush_add_softbody_constraint(cloth_sim, node_index, vd.index, 1.0f);
}
/* As we don't know the order of the neighbor vertices, we create all possible combinations
@ -360,14 +360,10 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
for (int c_i = 0; c_i < tot_indices; c_i++) {
for (int c_j = 0; c_j < tot_indices; c_j++) {
if (c_i != c_j && !cloth_brush_sim_has_length_constraint(
data->cloth_sim, build_indices[c_i], build_indices[c_j]))
cloth_sim, build_indices[c_i], build_indices[c_j]))
{
cloth_brush_add_length_constraint(ss,
data->cloth_sim,
node_index,
build_indices[c_i],
build_indices[c_j],
use_persistent);
cloth_brush_add_length_constraint(
ss, cloth_sim, node_index, build_indices[c_i], build_indices[c_j], use_persistent);
}
}
}
@ -381,7 +377,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
/* With plane falloff the strength of the constraints is set when applying the
* deformation forces. */
cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_GRAB_STRENGTH);
cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_GRAB_STRENGTH);
}
else if (len_squared < radius_squared) {
/* With radial falloff deformation constraints are created with different strengths and
@ -389,22 +385,22 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
const float fade = BKE_brush_curve_strength(
brush, sqrtf(len_squared), ss->cache->radius);
cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, fade * CLOTH_DEFORMATION_GRAB_STRENGTH);
cloth_sim, node_index, vd.index, fade * CLOTH_DEFORMATION_GRAB_STRENGTH);
}
}
else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) {
/* Cloth Snake Hook creates deformation constraint with fixed strength because the strength
* is controlled per iteration using cloth_sim->deformation_strength. */
cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH);
cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_SNAKEHOOK_STRENGTH);
}
}
else if (data->cloth_sim->deformation_pos) {
else if (cloth_sim->deformation_pos) {
/* Any other tool that target the cloth simulation handle the falloff in
* their own code when modifying the deformation coordinates of the simulation, so
* deformation constraints are created with a fixed strength for all vertices. */
cloth_brush_add_deformation_constraint(
data->cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
cloth_sim, node_index, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH);
}
if (pin_simulation_boundary) {
@ -414,7 +410,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(void *__restrict userdat
if (sim_falloff < 1.0f) {
/* Create constraints with more strength the closer the vertex is to the simulation
* boundary. */
cloth_brush_add_pin_constraint(data->cloth_sim, node_index, vd.index, 1.0f - sim_falloff);
cloth_brush_add_pin_constraint(cloth_sim, node_index, vd.index, 1.0f - sim_falloff);
}
}
}
@ -429,17 +425,17 @@ static void cloth_brush_apply_force_to_vertex(SculptSession * /*ss*/,
madd_v3_v3fl(cloth_sim->acceleration[vertex_index], force, 1.0f / cloth_sim->mass);
}
static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_cloth_brush_apply_forces_task(Object *ob,
const Sculpt *sd,
const Brush *brush,
const float *offset,
const float *grab_delta,
float (*imat)[4],
float *area_co,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
SculptClothSimulation *cloth_sim = ss->cache->cloth_sim;
const float *offset = data->offset;
const float *grab_delta = data->grab_delta;
float(*imat)[4] = data->mat;
const bool use_falloff_plane = brush->cloth_force_falloff_type ==
BRUSH_CLOTH_FORCE_FALLOFF_PLANE;
@ -449,8 +445,8 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
/* For Pinch Perpendicular Deform Type. */
float x_object_space[3];
@ -465,20 +461,20 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
float plane_normal[3];
if (use_falloff_plane) {
normalize_v3_v3(plane_normal, grab_delta);
plane_from_point_normal_v3(deform_plane, data->area_co, plane_normal);
plane_from_point_normal_v3(deform_plane, area_co, plane_normal);
}
/* Gravity */
float gravity[3] = {0.0f};
if (ss->cache->supports_gravity) {
madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor);
madd_v3_v3fl(gravity, ss->cache->gravity_direction, -sd->gravity_factor);
}
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]);
ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float force[3];
@ -722,30 +718,27 @@ static void cloth_brush_solve_collision(Object *object,
}
}
static void do_cloth_brush_solve_simulation_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_cloth_brush_solve_simulation_task(Object *ob,
const Brush *brush,
SculptClothSimulation *cloth_sim,
const float time_step,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
PBVHNode *node = data->nodes[n];
PBVHVertexIter vd;
SculptClothSimulation *cloth_sim = data->cloth_sim;
const float time_step = data->cloth_time_step;
const int node_index = POINTER_AS_INT(BLI_ghash_lookup(data->cloth_sim->node_state_index, node));
if (data->cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_ACTIVE) {
const int node_index = POINTER_AS_INT(BLI_ghash_lookup(cloth_sim->node_state_index, node));
if (cloth_sim->node_state[node_index] != SCULPT_CLOTH_NODE_ACTIVE) {
return;
}
AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]);
ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float sim_location[3];
@ -775,7 +768,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(void *__restrict userdata
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
if (cloth_sim->collider_list != nullptr) {
cloth_brush_solve_collision(data->ob, cloth_sim, i);
cloth_brush_solve_collision(ob, cloth_sim, i);
}
copy_v3_v3(cloth_sim->last_iteration_pos[i], cloth_sim->pos[i]);
@ -908,32 +901,24 @@ void SCULPT_cloth_brush_do_simulation_step(Sculpt *sd,
SculptClothSimulation *cloth_sim,
Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
/* Update the constraints. */
cloth_brush_satisfy_constraints(ss, brush, cloth_sim);
/* Solve the simulation and write the final step to the mesh. */
SculptThreadedTaskData solve_simulation_data{};
solve_simulation_data.sd = sd;
solve_simulation_data.ob = ob;
solve_simulation_data.brush = brush;
solve_simulation_data.nodes = nodes;
solve_simulation_data.cloth_time_step = CLOTH_SIMULATION_TIME_STEP;
solve_simulation_data.cloth_sim = cloth_sim;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0,
nodes.size(),
&solve_simulation_data,
do_cloth_brush_solve_simulation_task_cb_ex,
&settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_cloth_brush_solve_simulation_task(
ob, brush, cloth_sim, CLOTH_SIMULATION_TIME_STEP, nodes[i]);
}
});
}
static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -941,26 +926,14 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
float mat[4][4];
float area_no[3];
float area_co[3];
float imat[4][4];
float offset[3];
SculptThreadedTaskData apply_forces_data{};
apply_forces_data.sd = sd;
apply_forces_data.ob = ob;
apply_forces_data.brush = brush;
apply_forces_data.nodes = nodes;
apply_forces_data.area_no = area_no;
apply_forces_data.area_co = area_co;
apply_forces_data.mat = imat;
BKE_curvemapping_init(brush->curve);
/* Initialize the grab delta. */
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
normalize_v3(grab_delta);
apply_forces_data.grab_delta = grab_delta;
if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
return;
}
@ -971,8 +944,6 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius);
mul_v3_v3(offset, ss->cache->scale);
mul_v3_fl(offset, 2.0f);
apply_forces_data.offset = offset;
}
if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR ||
@ -991,10 +962,6 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
mat[3][3] = 1.0f;
normalize_m4(mat);
apply_forces_data.area_co = area_co;
apply_forces_data.area_no = area_no;
apply_forces_data.mat = mat;
/* Update matrix for the cursor preview. */
if (ss->cache->mirror_symmetry_pass == 0) {
copy_m4_m4(ss->cache->stroke_local_mat, mat);
@ -1010,10 +977,11 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, Span<PBVHNode
}
}
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(
0, nodes.size(), &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_cloth_brush_apply_forces_task(ob, sd, brush, offset, grab_delta, mat, area_co, nodes[i]);
}
});
}
/* Allocates nodes state and initializes them to Uninitialized, so constraints can be created for
@ -1089,31 +1057,20 @@ void SCULPT_cloth_brush_ensure_nodes_constraints(
float initial_location[3],
const float radius)
{
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint);
/* TODO: Multi-threaded needs to be disabled for this task until implementing the optimization of
* storing the constraints per node. */
/* Currently all constrains are added to the same global array which can't be accessed from
* different threads. */
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, false, nodes.size());
cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints");
SculptThreadedTaskData build_constraints_data{};
build_constraints_data.sd = sd;
build_constraints_data.ob = ob;
build_constraints_data.brush = brush;
build_constraints_data.nodes = nodes;
build_constraints_data.cloth_sim = cloth_sim;
build_constraints_data.cloth_sim_initial_location = initial_location;
build_constraints_data.cloth_sim_radius = radius;
BLI_task_parallel_range(0,
nodes.size(),
&build_constraints_data,
do_cloth_brush_build_constraints_task_cb_ex,
&settings);
for (const int i : nodes.index_range()) {
do_cloth_brush_build_constraints_task(
ob, brush, cloth_sim, initial_location, radius, nodes[i]);
}
BLI_edgeset_free(cloth_sim->created_length_constraints);
}
@ -1399,18 +1356,16 @@ static void cloth_filter_apply_forces_to_vertices(const int v_index,
cloth_brush_apply_force_to_vertex(nullptr, filter_cache->cloth_sim, final_force, v_index);
}
static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void cloth_filter_apply_forces_task(Object *ob,
Sculpt *sd,
const eSculptClothFilterType filter_type,
const float filter_strength,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
Sculpt *sd = data->sd;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptSession *ss = ob->sculpt;
SculptClothSimulation *cloth_sim = ss->filter_cache->cloth_sim;
const eSculptClothFilterType filter_type = eSculptClothFilterType(data->filter_type);
const bool is_deformation_filter = cloth_filter_is_deformation_filter(filter_type);
float sculpt_gravity[3] = {0.0f};
@ -1420,10 +1375,10 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
else {
sculpt_gravity[2] = -1.0f;
}
mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength);
mul_v3_fl(sculpt_gravity, sd->gravity_factor * filter_strength);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
@ -1447,30 +1402,30 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) {
/* When using the view orientation apply gravity in the -Y axis, this way objects will
* fall down instead of backwards. */
force[1] = -data->filter_strength * fade;
force[1] = -filter_strength * fade;
}
else {
force[2] = -data->filter_strength * fade;
force[2] = -filter_strength * fade;
}
SCULPT_filter_to_object_space(force, ss->filter_cache);
break;
case CLOTH_FILTER_INFLATE: {
float normal[3];
SCULPT_vertex_normal_get(ss, vd.vertex, normal);
mul_v3_v3fl(force, normal, fade * data->filter_strength);
mul_v3_v3fl(force, normal, fade * filter_strength);
} break;
case CLOTH_FILTER_EXPAND:
cloth_sim->length_constraint_tweak[vd.index] += fade * data->filter_strength * 0.01f;
cloth_sim->length_constraint_tweak[vd.index] += fade * filter_strength * 0.01f;
zero_v3(force);
break;
case CLOTH_FILTER_PINCH:
sub_v3_v3v3(force, ss->filter_cache->cloth_sim_pinch_point, vd.co);
normalize_v3(force);
mul_v3_fl(force, fade * data->filter_strength);
mul_v3_fl(force, fade * filter_strength);
break;
case CLOTH_FILTER_SCALE:
unit_m3(transform);
scale_m3_fl(transform, 1.0f + (fade * data->filter_strength));
scale_m3_fl(transform, 1.0f + (fade * filter_strength));
copy_v3_v3(temp, cloth_sim->init_pos[vd.index]);
mul_m3_v3(transform, temp);
sub_v3_v3v3(disp, temp, cloth_sim->init_pos[vd.index]);
@ -1493,6 +1448,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
using namespace blender;
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
SculptSession *ss = ob->sculpt;
@ -1526,17 +1482,15 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.filter_type = filter_type;
data.filter_strength = filter_strength;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, cloth_filter_apply_forces_task_cb, &settings);
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
cloth_filter_apply_forces_task(ob,
sd,
eSculptClothFilterType(filter_type),
filter_strength,
ss->filter_cache->nodes[i]);
}
});
/* Activate all nodes. */
SCULPT_cloth_sim_activate_nodes(ss->filter_cache->cloth_sim, ss->filter_cache->nodes);

View File

@ -1262,13 +1262,8 @@ static void sculpt_expand_cancel(bContext *C, wmOperator * /*op*/)
/**
* Callback to update mask data per PBVH node.
*/
static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void sculpt_expand_mask_update_task(SculptSession *ss, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
ExpandCache *expand_cache = ss->expand_cache;
bool any_changed = false;
@ -1342,13 +1337,8 @@ static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expan
/**
* Callback to update vertex colors per PBVH node.
*/
static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void sculpt_expand_colors_update_task(SculptSession *ss, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
ExpandCache *expand_cache = ss->expand_cache;
bool any_changed = false;
@ -1476,8 +1466,8 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa
static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ExpandCache *expand_cache = ss->expand_cache;
int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex);
@ -1501,26 +1491,23 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHV
sculpt_expand_face_sets_restore(ss, expand_cache);
}
/* Update the mesh sculpt data. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = expand_cache->nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, expand_cache->nodes.size());
switch (expand_cache->target) {
case SCULPT_EXPAND_TARGET_MASK:
BLI_task_parallel_range(
0, expand_cache->nodes.size(), &data, sculpt_expand_mask_update_task_cb, &settings);
threading::parallel_for(expand_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_expand_mask_update_task(ss, expand_cache->nodes[i]);
}
});
break;
case SCULPT_EXPAND_TARGET_FACE_SETS:
sculpt_expand_face_sets_update(ss, expand_cache);
break;
case SCULPT_EXPAND_TARGET_COLORS:
BLI_task_parallel_range(
0, expand_cache->nodes.size(), &data, sculpt_expand_colors_update_task_cb, &settings);
threading::parallel_for(expand_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_expand_colors_update_task(ss, expand_cache->nodes[i]);
}
});
break;
}

View File

@ -113,32 +113,27 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo
/* Draw Face Sets Brush. */
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_draw_face_sets_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
{
using namespace blender;
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
const Span<float3> positions(
reinterpret_cast<const float3 *>(SCULPT_mesh_deformed_positions_get(ss)),
SCULPT_vertex_count_get(ss));
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
bool changed = false;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
@ -196,37 +191,35 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
if (changed) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
}
}
static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_relax_face_sets_brush_task(Object *ob,
const Brush *brush,
const int iteration,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
float bstrength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
/* This operations needs a strength tweak as the relax deformation is too weak by default. */
if (relax_face_sets && data->iteration < 2) {
if (relax_face_sets && iteration < 2) {
bstrength *= 1.5f;
}
const int thread_id = BLI_task_parallel_thread_id(tls);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@ -257,6 +250,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -268,25 +262,24 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nod
BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
if (ss->cache->alt_smooth) {
SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < 4; i++) {
data.iteration = i;
BLI_task_parallel_range(
0, nodes.size(), &data, do_relax_face_sets_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_relax_face_sets_brush_task(ob, brush, i, nodes[i]);
}
});
}
}
else {
BLI_task_parallel_range(0, nodes.size(), &data, do_draw_face_sets_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_draw_face_sets_brush_task(ob, brush, nodes[i]);
}
});
}
}

View File

@ -72,24 +72,22 @@ static EnumPropertyItem prop_color_filter_types[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static void color_filter_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void color_filter_task(Object *ob,
const int mode,
const float filter_strength,
const float *filter_fill_color,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const int mode = data->filter_type;
SculptSession *ss = ob->sculpt;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COLOR);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->filter_cache->automasking, &automask_data, node);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
@ -98,7 +96,7 @@ static void color_filter_task_cb(void *__restrict userdata,
float brightness, contrast, gain, delta, offset;
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
fade *= filter_strength;
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f) {
@ -111,7 +109,7 @@ static void color_filter_task_cb(void *__restrict userdata,
switch (mode) {
case COLOR_FILTER_FILL: {
float fill_color_rgba[4];
copy_v3_v3(fill_color_rgba, data->filter_fill_color);
copy_v3_v3(fill_color_rgba, filter_fill_color);
fill_color_rgba[3] = 1.0f;
fade = clamp_f(fade, 0.0f, 1.0f);
mul_v4_fl(fill_color_rgba, fade);
@ -223,7 +221,7 @@ static void color_filter_task_cb(void *__restrict userdata,
SCULPT_vertex_color_set(ss, vd.vertex, final_color);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(data->nodes[n]);
BKE_pbvh_node_mark_update_color(node);
}
static void sculpt_color_presmooth_init(SculptSession *ss)
@ -269,7 +267,7 @@ static void sculpt_color_presmooth_init(SculptSession *ss)
static void sculpt_color_filter_apply(bContext *C, wmOperator *op, Object *ob)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
using namespace blender;
SculptSession *ss = ob->sculpt;
const int mode = RNA_enum_get(op->ptr, "type");
@ -283,20 +281,11 @@ static void sculpt_color_filter_apply(bContext *C, wmOperator *op, Object *ob)
sculpt_color_presmooth_init(ss);
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.filter_type = mode;
data.filter_strength = filter_strength;
data.filter_fill_color = fill_color;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, color_filter_task_cb, &settings);
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
color_filter_task(ob, mode, filter_strength, fill_color, ss->filter_cache->nodes[i]);
}
});
SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
}

View File

@ -60,16 +60,10 @@ static EnumPropertyItem prop_mask_filter_types[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static void mask_filter_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void mask_filter_task(SculptSession *ss, const int mode, float *prev_mask, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
bool update = false;
const int mode = data->filter_type;
float contrast = 0.0f;
PBVHVertexIter vd;
@ -110,7 +104,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
case MASK_FILTER_GROW:
max = 0.0f;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
float vmask_f = prev_mask[ni.index];
if (vmask_f > max) {
max = vmask_f;
}
@ -121,7 +115,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
case MASK_FILTER_SHRINK:
min = 1.0f;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
float vmask_f = prev_mask[ni.index];
if (vmask_f < min) {
min = vmask_f;
}
@ -158,10 +152,10 @@ static void mask_filter_task_cb(void *__restrict userdata,
static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
{
using namespace blender;
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const Scene *scene = CTX_data_scene(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int filter_type = RNA_enum_get(op->ptr, "filter_type");
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
@ -203,16 +197,11 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
}
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = nodes;
data.filter_type = filter_type;
data.prev_mask = prev_mask;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, mask_filter_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
mask_filter_task(ss, filter_type, prev_mask, nodes[i]);
}
});
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
MEM_freeN(prev_mask);
@ -226,21 +215,18 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
void SCULPT_mask_filter_smooth_apply(Sculpt *sd,
void SCULPT_mask_filter_smooth_apply(Sculpt * /*sd*/,
Object *ob,
Span<PBVHNode *> nodes,
const int smooth_iterations)
{
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = nodes;
data.filter_type = MASK_FILTER_SMOOTH;
using namespace blender;
for (int i = 0; i < smooth_iterations; i++) {
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, mask_filter_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
mask_filter_task(ob->sculpt, MASK_FILTER_SMOOTH, nullptr, nodes[i]);
}
});
}
}

View File

@ -98,16 +98,6 @@ void SCULPT_filter_zero_disabled_axis_components(float r_v[3], FilterCache *filt
SCULPT_filter_to_object_space(r_v, filter_cache);
}
static void filter_cache_init_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
PBVHNode *node = data->nodes[i];
SCULPT_undo_push_node(data->ob, node, SculptUndoType(data->filter_undo_type));
}
void SCULPT_filter_cache_init(bContext *C,
Object *ob,
Sculpt *sd,
@ -116,6 +106,7 @@ void SCULPT_filter_cache_init(bContext *C,
float area_normal_radius,
float start_strength)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
@ -147,16 +138,9 @@ void SCULPT_filter_cache_init(bContext *C,
BKE_pbvh_update_normals(ss->pbvh, nullptr);
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.filter_undo_type = undo_type;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, filter_cache_init_task_cb, &settings);
for (const int i : ss->filter_cache->nodes.index_range()) {
SCULPT_undo_push_node(ob, ss->filter_cache->nodes[i], SculptUndoType(undo_type));
}
/* Setup orientation matrices. */
copy_m4_m4(ss->filter_cache->obmat, ob->object_to_world);
@ -207,7 +191,7 @@ void SCULPT_filter_cache_init(bContext *C,
pbvh, [&](PBVHNode &node) { return SCULPT_search_sphere(&node, &search_data2); });
if (BKE_paint_brush(&sd->paint) &&
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, true, ss->filter_cache->initial_normal))
SCULPT_pbvh_calc_area_normal(brush, ob, nodes, ss->filter_cache->initial_normal))
{
copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal);
}
@ -363,18 +347,15 @@ static bool sculpt_mesh_filter_is_continuous(eSculptMeshFilterType type)
MESH_FILTER_RELAX_FACE_SETS);
}
static void mesh_filter_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void mesh_filter_task(Object *ob,
const eSculptMeshFilterType filter_type,
const float filter_strength,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
const eSculptMeshFilterType filter_type = eSculptMeshFilterType(data->filter_type);
SculptSession *ss = ob->sculpt;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
/* When using the relax face sets meshes filter,
* each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */
@ -382,7 +363,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
* boundaries. */
const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node);
SCULPT_automasking_node_begin(ob, ss, ss->filter_cache->automasking, &automask_data, node);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
@ -392,7 +373,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3];
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
fade *= filter_strength;
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
@ -674,25 +655,22 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
}
}
static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void mesh_filter_surface_smooth_displace_task(Object *ob,
const float filter_strength,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[i]);
SCULPT_automasking_node_begin(ob, ss, ss->filter_cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
fade *= filter_strength;
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f) {
@ -759,6 +737,7 @@ static void sculpt_mesh_update_status_bar(bContext *C, wmOperator *op)
static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
{
using namespace blender;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@ -767,24 +746,19 @@ static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op)
SCULPT_vertex_random_access_ensure(ss);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.filter_type = filter_type;
data.filter_strength = filter_strength;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, mesh_filter_task_cb, &settings);
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
mesh_filter_task(
ob, eSculptMeshFilterType(filter_type), filter_strength, ss->filter_cache->nodes[i]);
}
});
if (filter_type == MESH_FILTER_SURFACE_SMOOTH) {
BLI_task_parallel_range(0,
ss->filter_cache->nodes.size(),
&data,
mesh_filter_surface_smooth_displace_task_cb,
&settings);
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
mesh_filter_surface_smooth_displace_task(ob, filter_strength, ss->filter_cache->nodes[i]);
}
});
}
ss->filter_cache->iteration_count++;

View File

@ -242,127 +242,6 @@ struct SculptRakeData {
float angle;
};
/**
* Generic thread data. The size of this struct has gotten a little out of hand;
* normally we would split it up, but it might be better to see if we can't eliminate it
* altogether after moving to C++ (where we'll be able to use lambdas).
*/
struct SculptThreadedTaskData {
bContext *C;
Sculpt *sd;
Object *ob;
const Brush *brush;
Span<PBVHNode *> nodes;
VPaint *vp;
WPaintData *wpd;
WeightPaintInfo *wpi;
unsigned int *lcol;
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;
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];
/* When true, the displacement stored in the proxies will be applied to the original coordinates
* instead of to the current coordinates. */
bool use_proxies_orco;
/* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to
* the stroke direction are needed. */
float (*stroke_xz)[3];
int filter_type;
float filter_strength;
float *filter_fill_color;
bool use_area_cos;
bool use_area_nos;
/* 0=towards view, 1=flipped */
float (*area_cos)[3];
float (*area_nos)[3];
int *count_no;
int *count_co;
bool any_vertex_sampled;
float *wet_mix_sampled_color;
float *prev_mask;
float *pose_factor;
float *pose_initial_co;
int pose_chain_segment;
float multiplane_scrape_angle;
float multiplane_scrape_planes[2][4];
float max_distance_squared;
float nearest_vertex_search_co[3];
/* Stabilized strength for the Clay Thumb brush. */
float clay_strength;
int mask_expand_update_it;
bool mask_expand_invert_mask;
bool mask_expand_use_normals;
bool mask_expand_keep_prev_mask;
bool mask_expand_create_face_set;
float transform_mats[8][4][4];
float elastic_transform_mat[4][4];
float elastic_transform_pivot[3];
float elastic_transform_pivot_init[3];
float elastic_transform_radius;
/* Boundary brush */
float boundary_deform_strength;
float cloth_time_step;
SculptClothSimulation *cloth_sim;
float *cloth_sim_initial_location;
float cloth_sim_radius;
/* Mask By Color Tool */
float mask_by_color_threshold;
bool mask_by_color_invert;
bool mask_by_color_preserve_mask;
/* Index of the vertex that is going to be used as a reference for the colors. */
PBVHVertRef mask_by_color_vertex;
float *mask_by_color_floodfill;
int face_set;
int filter_undo_type;
int mask_init_mode;
int mask_init_seed;
ThreadMutex mutex;
int iteration;
};
/*************** Brush testing declarations ****************/
struct SculptBrushTest {
float radius_squared;
@ -1595,7 +1474,6 @@ void SCULPT_relax_vertex(SculptSession *ss,
bool SCULPT_pbvh_calc_area_normal(const Brush *brush,
Object *ob,
Span<PBVHNode *> nodes,
bool use_threading,
float r_area_no[3]);
/**

View File

@ -74,15 +74,16 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op)
ED_workspace_status_text(C, nullptr);
}
static void sculpt_expand_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void sculpt_expand_task(Object *ob,
const int update_it,
const bool mask_expand_use_normals,
const bool mask_expand_create_face_set,
const bool mask_expand_keep_prev_mask,
const bool mask_expand_invert_mask,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
int update_it = data->mask_expand_update_it;
PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex);
@ -92,7 +93,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
int vi = vd.index;
float final_mask = *vd.mask;
if (data->mask_expand_use_normals) {
if (mask_expand_use_normals) {
if (ss->filter_cache->normal_factor[active_vertex_i] <
ss->filter_cache->normal_factor[vd.index]) {
final_mask = 1.0f;
@ -111,7 +112,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
}
}
if (data->mask_expand_create_face_set) {
if (mask_expand_create_face_set) {
if (final_mask == 1.0f) {
SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set);
face_sets_changed = true;
@ -120,11 +121,11 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
}
else {
if (data->mask_expand_keep_prev_mask) {
if (mask_expand_keep_prev_mask) {
final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask);
}
if (data->mask_expand_invert_mask) {
if (mask_expand_invert_mask) {
final_mask = 1.0f - final_mask;
}
@ -137,12 +138,13 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
if (face_sets_changed) {
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_FACE_SETS);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS);
}
}
static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
using namespace blender;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -261,20 +263,24 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
ss->face_sets[i] = ss->filter_cache->prev_face_set[i];
}
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.mask_expand_update_it = mask_expand_update_it;
data.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals");
data.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert");
data.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
data.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, sculpt_expand_task_cb, &settings);
const bool use_normals = RNA_boolean_get(op->ptr, "use_normals");
const bool invert_mask = RNA_boolean_get(op->ptr, "invert");
const bool keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_expand_task(ob,
mask_expand_update_it,
use_normals,
create_face_set,
keep_prev_mask,
invert_mask,
ss->filter_cache->nodes[i]);
}
});
ss->filter_cache->mask_update_current_it = mask_expand_update_it;
}
@ -330,6 +336,7 @@ static bool mask_expand_floodfill_cb(
static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
using namespace blender;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -438,20 +445,20 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
MEM_SAFE_FREE(ss->filter_cache->edge_factor);
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.mask_expand_update_it = 0;
data.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals");
data.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert");
data.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
data.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
const bool invert_mask = RNA_boolean_get(op->ptr, "invert");
const bool keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask");
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, sculpt_expand_task_cb, &settings);
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_expand_task(ob,
0,
use_normals,
create_face_set,
keep_prev_mask,
invert_mask,
ss->filter_cache->nodes[i]);
}
});
const char *status_str = TIP_(
"Move the mouse to expand the mask from the active vertex. LMB: confirm mask, ESC/RMB: "

View File

@ -73,17 +73,11 @@ static EnumPropertyItem prop_sculpt_mask_init_mode_types[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static void mask_init_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void mask_init_task(Object *ob, const int mode, const int seed, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
const int mode = data->mask_init_mode;
const int seed = data->mask_init_seed;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
switch (mode) {
case SCULPT_MASK_INIT_RANDOM_PER_VERTEX:
@ -100,11 +94,12 @@ static void mask_init_task_cb(void *__restrict userdata,
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(data->nodes[i]);
BKE_pbvh_node_mark_update_mask(node);
}
static int sculpt_mask_init_exec(bContext *C, wmOperator *op)
{
using namespace blender;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -129,15 +124,13 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op)
SCULPT_topology_islands_ensure(ob);
}
SculptThreadedTaskData data{};
data.ob = ob;
data.nodes = nodes;
data.mask_init_mode = mode;
data.mask_init_seed = PIL_check_seconds_timer();
const int mask_init_seed = PIL_check_seconds_timer();
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, mask_init_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
mask_init_task(ob, mode, mask_init_seed, nodes[i]);
}
});
multires_stitch_grids(ob);

View File

@ -35,23 +35,20 @@ struct MultiplaneScrapeSampleData {
int area_count[2];
};
static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void calc_multiplane_scrape_surface_task(Object *ob,
const Brush *brush,
const float (*mat)[4],
PBVHNode *node,
MultiplaneScrapeSampleData *mssd)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
MultiplaneScrapeSampleData *mssd = static_cast<MultiplaneScrapeSampleData *>(
tls->userdata_chunk);
float(*mat)[4] = data->mat;
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
/* Apply the brush normal radius to the test before sampling. */
float test_radius = sqrtf(test.radius_squared);
@ -59,10 +56,9 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
test.radius_squared = test_radius * test_radius;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -101,51 +97,30 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
}
}
static void calc_multiplane_scrape_surface_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
static void do_multiplane_scrape_brush_task(Object *ob,
const Brush *brush,
const float (*mat)[4],
const float (*scrape_planes)[4],
const float angle,
PBVHNode *node)
{
MultiplaneScrapeSampleData *join = static_cast<MultiplaneScrapeSampleData *>(chunk_join);
MultiplaneScrapeSampleData *mssd = static_cast<MultiplaneScrapeSampleData *>(chunk);
add_v3_v3(join->area_cos[0], mssd->area_cos[0]);
add_v3_v3(join->area_cos[1], mssd->area_cos[1]);
add_v3_v3(join->area_nos[0], mssd->area_nos[0]);
add_v3_v3(join->area_nos[1], mssd->area_nos[1]);
join->area_count[0] += mssd->area_count[0];
join->area_count[1] += mssd->area_count[1];
}
static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*mat)[4] = data->mat;
float(*scrape_planes)[4] = data->multiplane_scrape_planes;
float angle = data->multiplane_scrape_angle;
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
float(*proxy)[3];
const float bstrength = fabsf(ss->cache->bstrength);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -215,6 +190,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -278,23 +254,29 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
if (brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_DYNAMIC) {
/* Sample the individual normal and area center of the two areas at both sides of the cursor.
*/
SculptThreadedTaskData sample_data{};
sample_data.sd = nullptr;
sample_data.ob = ob;
sample_data.brush = brush;
sample_data.nodes = nodes;
sample_data.mat = mat;
const MultiplaneScrapeSampleData mssd = threading::parallel_reduce(
nodes.index_range(),
1,
MultiplaneScrapeSampleData{},
[&](const IndexRange range, MultiplaneScrapeSampleData mssd) {
for (const int i : range) {
calc_multiplane_scrape_surface_task(ob, brush, mat, nodes[i], &mssd);
}
return mssd;
},
[](const MultiplaneScrapeSampleData &a, const MultiplaneScrapeSampleData &b) {
MultiplaneScrapeSampleData joined = a;
MultiplaneScrapeSampleData mssd = {{{0}}};
add_v3_v3v3(joined.area_cos[0], a.area_cos[0], b.area_cos[0]);
add_v3_v3v3(joined.area_cos[1], a.area_cos[1], b.area_cos[1]);
TaskParallelSettings sample_settings;
BKE_pbvh_parallel_range_settings(&sample_settings, true, nodes.size());
sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce;
sample_settings.userdata_chunk = &mssd;
sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData);
add_v3_v3v3(joined.area_nos[0], a.area_nos[0], b.area_nos[0]);
add_v3_v3v3(joined.area_nos[1], a.area_nos[1], b.area_nos[1]);
BLI_task_parallel_range(
0, nodes.size(), &sample_data, calc_multiplane_scrape_surface_task_cb, &sample_settings);
joined.area_count[0] = a.area_count[0] + b.area_count[0];
joined.area_count[1] = a.area_count[1] + b.area_count[1];
return joined;
});
float sampled_plane_normals[2][3];
float sampled_plane_co[2][3];
@ -348,14 +330,6 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
}
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.mat = mat;
data.multiplane_scrape_angle = ss->cache->multiplane_scrape_angle;
/* Calculate the final left and right scrape planes. */
float plane_no[3];
float plane_no_rot[3];
@ -363,24 +337,28 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, Span<PBVHNode *>
float mat_inv[4][4];
invert_m4_m4(mat_inv, mat);
float multiplane_scrape_planes[2][4];
mul_v3_mat3_m4v3(plane_no, mat, area_no);
rotate_v3_v3v3fl(
plane_no_rot, plane_no, y_axis, DEG2RADF(-ss->cache->multiplane_scrape_angle * 0.5f));
mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot);
normalize_v3(plane_no);
plane_from_point_normal_v3(data.multiplane_scrape_planes[1], area_co, plane_no);
plane_from_point_normal_v3(multiplane_scrape_planes[1], area_co, plane_no);
mul_v3_mat3_m4v3(plane_no, mat, area_no);
rotate_v3_v3v3fl(
plane_no_rot, plane_no, y_axis, DEG2RADF(ss->cache->multiplane_scrape_angle * 0.5f));
mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot);
normalize_v3(plane_no);
plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no);
plane_from_point_normal_v3(multiplane_scrape_planes[0], area_co, plane_no);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(
0, nodes.size(), &data, do_multiplane_scrape_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_multiplane_scrape_brush_task(
ob, brush, mat, multiplane_scrape_planes, ss->cache->multiplane_scrape_angle, nodes[i]);
}
});
}
void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,

View File

@ -730,23 +730,21 @@ struct MaskByColorContiguousFloodFillData {
float initial_color[3];
};
static void do_mask_by_color_contiguous_update_nodes_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_mask_by_color_contiguous_update_node(Object *ob,
const float *mask_by_color_floodfill,
const bool invert,
const bool preserve_mask,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
bool update_node = false;
const bool invert = data->mask_by_color_invert;
const bool preserve_mask = data->mask_by_color_preserve_mask;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
const float current_mask = *vd.mask;
const float new_mask = data->mask_by_color_floodfill[vd.index];
const float new_mask = mask_by_color_floodfill[vd.index];
*vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
if (current_mask == *vd.mask) {
continue;
@ -755,11 +753,11 @@ static void do_mask_by_color_contiguous_update_nodes_cb(void *__restrict userdat
}
BKE_pbvh_vertex_iter_end;
if (update_node) {
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
BKE_pbvh_node_mark_update_mask(node);
}
}
static bool sculpt_mask_by_color_contiguous_floodfill_cb(
static bool sculpt_mask_by_color_contiguous_floodfill(
SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
@ -790,6 +788,7 @@ static void sculpt_mask_by_color_contiguous(Object *object,
const bool invert,
const bool preserve_mask)
{
using namespace blender;
SculptSession *ss = object->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@ -815,47 +814,38 @@ static void sculpt_mask_by_color_contiguous(Object *object,
copy_v3_v3(ffd.initial_color, color);
SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd);
SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill, &ffd);
SCULPT_floodfill_free(&flood);
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
SculptThreadedTaskData data{};
data.ob = object;
data.nodes = nodes;
data.mask_by_color_floodfill = new_mask;
data.mask_by_color_vertex = vertex;
data.mask_by_color_threshold = threshold;
data.mask_by_color_invert = invert;
data.mask_by_color_preserve_mask = preserve_mask;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(
0, nodes.size(), &data, do_mask_by_color_contiguous_update_nodes_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_mask_by_color_contiguous_update_node(object, new_mask, invert, preserve_mask, nodes[i]);
}
});
MEM_freeN(new_mask);
}
static void do_mask_by_color_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_mask_by_color_task(Object *ob,
const float threshold,
const bool invert,
const bool preserve_mask,
const PBVHVertRef mask_by_color_vertex,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
bool update_node = false;
const float threshold = data->mask_by_color_threshold;
const bool invert = data->mask_by_color_invert;
const bool preserve_mask = data->mask_by_color_preserve_mask;
float active_color[4];
SCULPT_vertex_color_get(ss, data->mask_by_color_vertex, active_color);
SCULPT_vertex_color_get(ss, mask_by_color_vertex, active_color);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float col[4];
SCULPT_vertex_color_get(ss, vd.vertex, col);
@ -870,7 +860,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
}
BKE_pbvh_vertex_iter_end;
if (update_node) {
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
BKE_pbvh_node_mark_update_mask(node);
}
}
@ -880,21 +870,16 @@ static void sculpt_mask_by_color_full_mesh(Object *object,
const bool invert,
const bool preserve_mask)
{
using namespace blender;
SculptSession *ss = object->sculpt;
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
SculptThreadedTaskData data{};
data.ob = object;
data.nodes = nodes;
data.mask_by_color_vertex = vertex;
data.mask_by_color_threshold = threshold;
data.mask_by_color_invert = invert;
data.mask_by_color_preserve_mask = preserve_mask;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_mask_by_color_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_mask_by_color_task(object, threshold, invert, preserve_mask, vertex, nodes[i]);
}
});
}
static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@ -1004,36 +989,24 @@ enum CavityBakeSettingsSource {
AUTOMASK_SETTINGS_BRUSH
};
struct AutomaskBakeTaskData {
SculptSession *ss;
AutomaskingCache *automasking;
Span<PBVHNode *> nodes;
CavityBakeMixMode mode;
float factor;
Object *ob;
};
static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void sculpt_bake_cavity_exec_task(Object *ob,
AutomaskingCache *automasking,
const CavityBakeMixMode mode,
const float factor,
PBVHNode *node)
{
AutomaskBakeTaskData *tdata = static_cast<AutomaskBakeTaskData *>(userdata);
SculptSession *ss = tdata->ss;
PBVHNode *node = tdata->nodes[n];
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
const CavityBakeMixMode mode = tdata->mode;
const float factor = tdata->factor;
SCULPT_undo_push_node(tdata->ob, node, SCULPT_UNDO_MASK);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(tdata->ob, ss, tdata->automasking, &automask_data, node);
SCULPT_automasking_node_begin(ob, ss, automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float automask = SCULPT_automasking_factor_get(
tdata->automasking, ss, vd.vertex, &automask_data);
float automask = SCULPT_automasking_factor_get(automasking, ss, vd.vertex, &automask_data);
float mask;
switch (mode) {
@ -1068,6 +1041,7 @@ static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata,
static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
{
using namespace blender;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -1087,10 +1061,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
AutomaskBakeTaskData tdata;
/* Set up automasking settings.
*/
/* Set up automasking settings. */
Sculpt sd2 = *sd;
CavityBakeSettingsSource src = (CavityBakeSettingsSource)RNA_enum_get(op->ptr,
@ -1150,18 +1121,15 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
SCULPT_stroke_id_next(ob);
tdata.ob = ob;
tdata.mode = mode;
tdata.factor = factor;
tdata.ss = ss;
tdata.nodes = nodes;
tdata.automasking = SCULPT_automasking_cache_init(&sd2, &brush2, ob);
AutomaskingCache *automasking = SCULPT_automasking_cache_init(&sd2, &brush2, ob);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &tdata, sculpt_bake_cavity_exec_task_cb, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_bake_cavity_exec_task(ob, automasking, mode, factor, nodes[i]);
}
});
SCULPT_automasking_cache_free(tdata.automasking);
SCULPT_automasking_cache_free(automasking);
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
SCULPT_undo_push_end(ob);

View File

@ -35,27 +35,22 @@
using blender::Vector;
static void do_color_smooth_task_cb_exec(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_color_smooth_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -84,27 +79,27 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_paint_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_paint_brush_task(Object *ob,
const Brush *brush,
const float (*mat)[4],
float *wet_mix_sampled_color,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
const float bstrength = fabsf(ss->cache->bstrength);
PBVHVertexIter vd;
PBVHColorBufferNode *color_buffer;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COLOR);
color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
color_buffer = BKE_pbvh_node_color_buffer_get(node);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
@ -115,8 +110,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
if (brush->flag & BRUSH_USE_GRADIENT) {
switch (brush->gradient_stroke_mode) {
@ -136,14 +130,14 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
}
}
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
bool affect_vertex = false;
float distance_to_stroke_location = 0.0f;
if (brush->tip_roundness < 1.0f) {
affect_vertex = SCULPT_brush_test_cube(
&test, vd.co, data->mat, brush->tip_roundness, brush->tip_scale_x);
&test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x);
distance_to_stroke_location = ss->cache->radius * test.dist;
}
else {
@ -185,7 +179,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
float buffer_color[4];
mul_v4_v4fl(paint_color, brush_color, fade * ss->cache->paint_brush.flow);
mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * ss->cache->paint_brush.flow);
mul_v4_v4fl(wet_mix_color, wet_mix_sampled_color, fade * ss->cache->paint_brush.flow);
/* Interpolate with the wet_mix color for wet paint mixing. */
blend_color_interpolate_float(
@ -207,28 +201,26 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
struct SampleWetPaintTLSData {
struct SampleWetPaintData {
int tot_samples;
float color[4];
};
static void do_sample_wet_paint_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_sample_wet_paint_task(SculptSession *ss,
const Brush *brush,
PBVHNode *node,
SampleWetPaintData *swptd)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SampleWetPaintTLSData *swptd = static_cast<SampleWetPaintTLSData *>(tls->userdata_chunk);
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
test.radius *= data->brush->wet_paint_radius_factor;
test.radius *= brush->wet_paint_radius_factor;
test.radius_squared = test.radius * test.radius;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -242,23 +234,13 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void sample_wet_paint_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
SampleWetPaintTLSData *join = static_cast<SampleWetPaintTLSData *>(chunk_join);
SampleWetPaintTLSData *swptd = static_cast<SampleWetPaintTLSData *>(chunk);
join->tot_samples += swptd->tot_samples;
add_v4_v4(join->color, swptd->color);
}
void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
Sculpt *sd,
Object *ob,
Span<PBVHNode *> nodes,
Span<PBVHNode *> texnodes)
{
using namespace blender;
if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) {
SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, texnodes);
return;
@ -294,16 +276,11 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
/* Smooth colors mode. */
if (ss->cache->alt_smooth) {
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.mat = mat;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_color_smooth_task_cb_exec, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_color_smooth_task(ob, brush, nodes[i]);
}
});
return;
}
@ -312,23 +289,22 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
/* Wet paint color sampling. */
float wet_color[4] = {0.0f};
if (ss->cache->paint_brush.wet_mix > 0.0f) {
SculptThreadedTaskData task_data{};
task_data.sd = sd;
task_data.ob = ob;
task_data.nodes = nodes;
task_data.brush = brush;
SampleWetPaintTLSData swptd;
swptd.tot_samples = 0;
zero_v4(swptd.color);
TaskParallelSettings settings_sample;
BKE_pbvh_parallel_range_settings(&settings_sample, true, nodes.size());
settings_sample.func_reduce = sample_wet_paint_reduce;
settings_sample.userdata_chunk = &swptd;
settings_sample.userdata_chunk_size = sizeof(SampleWetPaintTLSData);
BLI_task_parallel_range(
0, nodes.size(), &task_data, do_sample_wet_paint_task_cb, &settings_sample);
const SampleWetPaintData swptd = threading::parallel_reduce(
nodes.index_range(),
1,
SampleWetPaintData{},
[&](const IndexRange range, SampleWetPaintData swptd) {
for (const int i : range) {
do_sample_wet_paint_task(ss, brush, nodes[i], &swptd);
}
return swptd;
},
[](const SampleWetPaintData &a, const SampleWetPaintData &b) {
SampleWetPaintData joined{};
joined.tot_samples = a.tot_samples + b.tot_samples;
add_v4_v4v4(joined.color, a.color, b.color);
return joined;
});
if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) {
copy_v4_v4(wet_color, swptd.color);
@ -347,35 +323,24 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings,
}
}
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.wet_mix_sampled_color = wet_color;
data.mat = mat;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_paint_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_paint_brush_task(ob, brush, mat, wet_color, nodes[i]);
}
});
}
static void do_smear_brush_task_cb_exec(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_smear_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
float brush_delta[3];
@ -387,10 +352,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
}
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -514,22 +478,20 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_smear_store_prev_colors_task(SculptSession *ss,
PBVHNode *node,
float (*prev_colors)[4])
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_vertex_color_get(ss, vd.vertex, ss->cache->prev_colors[vd.index]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_vertex_color_get(ss, vd.vertex, prev_colors[vd.index]);
}
BKE_pbvh_vertex_iter_end;
}
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
@ -550,23 +512,25 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
BKE_curvemapping_init(brush->curve);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
/* Smooth colors mode. */
if (ss->cache->alt_smooth) {
BLI_task_parallel_range(0, nodes.size(), &data, do_color_smooth_task_cb_exec, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_color_smooth_task(ob, brush, nodes[i]);
}
});
}
else {
/* Smear mode. */
BLI_task_parallel_range(
0, nodes.size(), &data, do_smear_store_prev_colors_task_cb_exec, &settings);
BLI_task_parallel_range(0, nodes.size(), &data, do_smear_brush_task_cb_exec, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_smear_store_prev_colors_task(ss, nodes[i], ss->cache->prev_colors);
}
});
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_smear_brush_task(ob, brush, nodes[i]);
}
});
}
}

View File

@ -137,27 +137,22 @@ static void pose_solve_scale_chain(SculptPoseIKChain *ik_chain, const float scal
}
}
static void do_pose_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void do_pose_brush_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
SculptSession *ss = ob->sculpt;
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
SculptPoseIKChainSegment *segments = ik_chain->segments;
const Brush *brush = data->brush;
PBVHVertexIter vd;
float disp[3], new_co[3];
float final_pos[3];
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
@ -203,35 +198,36 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
struct PoseGrowFactorTLSData {
struct PoseGrowFactorData {
float pos_avg[3];
int pos_count;
};
static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void pose_brush_grow_factor_task(Object *ob,
const float pose_initial_co[3],
const float *prev_mask,
float *pose_factor,
PBVHNode *node,
PoseGrowFactorData *gftd)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
PoseGrowFactorTLSData *gftd = static_cast<PoseGrowFactorTLSData *>(tls->userdata_chunk);
SculptSession *ss = data->ob->sculpt;
const char symm = SCULPT_mesh_symmetry_xyz_get(data->ob);
SculptSession *ss = ob->sculpt;
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SculptVertexNeighborIter ni;
float max = 0.0f;
/* Grow the factor. */
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
float vmask_f = prev_mask[ni.index];
max = MAX2(vmask_f, max);
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Keep the count of the vertices that where added to the factors in this grow iteration. */
if (max > data->prev_mask[vd.index]) {
data->pose_factor[vd.index] = max;
if (SCULPT_check_vertex_pivot_symmetry(vd.co, data->pose_initial_co, symm)) {
if (max > prev_mask[vd.index]) {
pose_factor[vd.index] = max;
if (SCULPT_check_vertex_pivot_symmetry(vd.co, pose_initial_co, symm)) {
add_v3_v3(gftd->pos_avg, vd.co);
gftd->pos_count++;
}
@ -245,16 +241,15 @@ static void pose_brush_grow_factor_reduce(const void *__restrict /*userdata*/,
void *__restrict chunk_join,
void *__restrict chunk)
{
PoseGrowFactorTLSData *join = static_cast<PoseGrowFactorTLSData *>(chunk_join);
PoseGrowFactorTLSData *gftd = static_cast<PoseGrowFactorTLSData *>(chunk);
PoseGrowFactorData *join = static_cast<PoseGrowFactorData *>(chunk_join);
PoseGrowFactorData *gftd = static_cast<PoseGrowFactorData *>(chunk);
add_v3_v3(join->pos_avg, gftd->pos_avg);
join->pos_count += gftd->pos_count;
}
/* Grow the factor until its boundary is near to the offset pose origin or outside the target
* distance. */
static void sculpt_pose_grow_pose_factor(Sculpt *sd,
Object *ob,
static void sculpt_pose_grow_pose_factor(Object *ob,
SculptSession *ss,
float pose_origin[3],
float pose_target[3],
@ -262,35 +257,40 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
float *r_pose_origin,
float *pose_factor)
{
using namespace blender;
PBVH *pbvh = ob->sculpt->pbvh;
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {});
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = nodes;
data.pose_factor = pose_factor;
data.pose_initial_co = pose_target;
TaskParallelSettings settings;
PoseGrowFactorTLSData gftd;
PoseGrowFactorData gftd;
gftd.pos_count = 0;
zero_v3(gftd.pos_avg);
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
settings.func_reduce = pose_brush_grow_factor_reduce;
settings.userdata_chunk = &gftd;
settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData);
bool grow_next_iteration = true;
float prev_len = FLT_MAX;
data.prev_mask = static_cast<float *>(
float *prev_mask = static_cast<float *>(
MEM_malloc_arrayN(SCULPT_vertex_count_get(ss), sizeof(float), __func__));
while (grow_next_iteration) {
zero_v3(gftd.pos_avg);
gftd.pos_count = 0;
memcpy(data.prev_mask, pose_factor, SCULPT_vertex_count_get(ss) * sizeof(float));
BLI_task_parallel_range(0, nodes.size(), &data, pose_brush_grow_factor_task_cb_ex, &settings);
memcpy(prev_mask, pose_factor, SCULPT_vertex_count_get(ss) * sizeof(float));
gftd = threading::parallel_reduce(
nodes.index_range(),
1,
gftd,
[&](const IndexRange range, PoseGrowFactorData gftd) {
for (const int i : range) {
pose_brush_grow_factor_task(ob, pose_target, prev_mask, pose_factor, nodes[i], &gftd);
}
return gftd;
},
[](const PoseGrowFactorData &a, const PoseGrowFactorData &b) {
PoseGrowFactorData joined;
add_v3_v3v3(joined.pos_avg, a.pos_avg, b.pos_avg);
joined.pos_count = a.pos_count + b.pos_count;
return joined;
});
if (gftd.pos_count != 0) {
mul_v3_fl(gftd.pos_avg, 1.0f / float(gftd.pos_count));
@ -305,7 +305,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
}
else {
grow_next_iteration = false;
memcpy(pose_factor, data.prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float));
memcpy(pose_factor, prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float));
}
}
else {
@ -321,7 +321,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
if (r_pose_origin) {
copy_v3_v3(r_pose_origin, gftd.pos_avg);
}
memcpy(pose_factor, data.prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float));
memcpy(pose_factor, prev_mask, SCULPT_vertex_count_get(ss) * sizeof(float));
}
}
}
@ -332,7 +332,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
grow_next_iteration = false;
}
}
MEM_freeN(data.prev_mask);
MEM_freeN(prev_mask);
}
static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3],
@ -574,29 +574,25 @@ void SCULPT_pose_calc_pose_data(Sculpt *sd,
*/
if (pose_offset != 0.0f && r_pose_factor) {
sculpt_pose_grow_pose_factor(
sd, ob, ss, fdata.pose_origin, fdata.pose_origin, 0, nullptr, r_pose_factor);
ob, ss, fdata.pose_origin, fdata.pose_origin, 0, nullptr, r_pose_factor);
}
}
static void pose_brush_init_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict /*tls*/)
static void pose_brush_init_task(SculptSession *ss, float *pose_factor, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SculptVertexNeighborIter ni;
float avg = 0.0f;
int total = 0;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
avg += data->pose_factor[ni.index];
avg += pose_factor[ni.index];
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
data->pose_factor[vd.index] = avg / total;
pose_factor[vd.index] = avg / total;
}
}
BKE_pbvh_vertex_iter_end;
@ -705,8 +701,7 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd,
for (int i = 1; i < ik_chain->tot_segments; i++) {
/* Grow the factors to get the new segment origin. */
sculpt_pose_grow_pose_factor(sd,
ob,
sculpt_pose_grow_pose_factor(ob,
ss,
nullptr,
next_chain_segment_target,
@ -979,27 +974,24 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br)
{
using namespace blender;
PBVH *pbvh = ob->sculpt->pbvh;
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(pbvh, {});
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = br;
data.nodes = nodes;
/* Init the IK chain that is going to be used to deform the vertices. */
ss->cache->pose_ik_chain = SCULPT_pose_ik_chain_init(
sd, ob, ss, br, ss->cache->true_location, ss->cache->radius);
/* Smooth the weights of each segment for cleaner deformation. */
for (int ik = 0; ik < ss->cache->pose_ik_chain->tot_segments; ik++) {
data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights;
float *pose_factor = ss->cache->pose_ik_chain->segments[ik].weights;
for (int i = 0; i < br->pose_smooth_iterations; i++) {
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, pose_brush_init_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
pose_brush_init_task(ss, pose_factor, nodes[i]);
}
});
}
}
}
@ -1129,6 +1121,7 @@ static void sculpt_pose_align_pivot_local_space(float r_mat[4][4],
void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
@ -1212,15 +1205,11 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
}
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_pose_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_pose_brush_task(ob, brush, nodes[i]);
}
});
}
void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain)

View File

@ -177,14 +177,12 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertR
}
}
static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_enhance_details_brush_task(Object *ob,
Sculpt *sd,
const Brush *brush,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
@ -193,14 +191,13 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -231,6 +228,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -251,27 +249,21 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span<PBVHNode *
}
}
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_enhance_details_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_enhance_details_brush_task(ob, sd, brush, nodes[i]);
}
});
}
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
static void do_smooth_brush_task(Object *ob,
Sculpt *sd,
const Brush *brush,
const bool smooth_mask,
float bstrength,
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
const bool smooth_mask = data->smooth_mask;
float bstrength = data->strength;
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
@ -279,14 +271,13 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -327,6 +318,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
void SCULPT_smooth(
Sculpt *sd, Object *ob, Span<PBVHNode *> nodes, float bstrength, const bool smooth_mask)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -345,18 +337,11 @@ void SCULPT_smooth(
for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last;
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
data.smooth_mask = smooth_mask;
data.strength = strength;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_smooth_brush_task(ob, sd, brush, smooth_mask, strength, nodes[i]);
}
});
}
}
@ -430,12 +415,9 @@ void SCULPT_surface_smooth_displace_step(SculptSession *ss,
}
}
static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
static void do_surface_smooth_brush_laplacian_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
const float bstrength = ss->cache->bstrength;
float alpha = brush->surface_smooth_shape_preservation;
@ -444,15 +426,14 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -482,12 +463,9 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
BKE_pbvh_vertex_iter_end;
}
static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
static void do_surface_smooth_brush_displace_task(Object *ob, const Brush *brush, PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
SculptSession *ss = ob->sculpt;
const float bstrength = ss->cache->bstrength;
const float beta = brush->surface_smooth_current_vertex;
@ -495,13 +473,12 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
ss, &test, brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(nullptr);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@ -526,21 +503,21 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span<PBVHNode *> nodes)
{
using namespace blender;
Brush *brush = BKE_paint_brush(&sd->paint);
/* Threaded loop over nodes. */
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.brush = brush;
data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, nodes.size());
for (int i = 0; i < brush->surface_smooth_iterations; i++) {
BLI_task_parallel_range(
0, nodes.size(), &data, SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex, &settings);
BLI_task_parallel_range(
0, nodes.size(), &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings);
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_surface_smooth_brush_laplacian_task(ob, brush, nodes[i]);
}
});
threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
do_surface_smooth_brush_displace_task(ob, brush, nodes[i]);
}
});
}
}

View File

@ -137,21 +137,16 @@ static void sculpt_transform_matrices_init(SculptSession *ss,
}
}
static void sculpt_transform_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void sculpt_transform_task(Object *ob, const float transform_mats[8][4][4], PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptSession *ss = ob->sculpt;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
PBVHVertexIter vd;
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
float *start_co;
@ -170,7 +165,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
}
copy_v3_v3(transformed_co, start_co);
mul_m4_v3(data->transform_mats[int(symm_area)], transformed_co);
mul_m4_v3(transform_mats[int(symm_area)], transformed_co);
sub_v3_v3v3(disp, transformed_co, start_co);
mul_v3_fl(disp, 1.0f - fade);
add_v3_v3v3(vd.co, start_co, disp);
@ -184,40 +179,38 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update(node);
}
static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
static void sculpt_transform_all_vertices(Object *ob)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
float transform_mats[8][4][4];
sculpt_transform_matrices_init(
ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
ss, symm, ss->filter_cache->transform_displacement_mode, transform_mats);
/* Regular transform applies all symmetry passes at once as it is split by symmetry areas
* (each vertex can only be transformed once by the transform matrix of its area). */
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, sculpt_transform_task_cb, &settings);
threading::parallel_for(ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_transform_task(ob, transform_mats, ss->filter_cache->nodes[i]);
}
});
}
static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict /*tls*/)
static void sculpt_elastic_transform_task(Object *ob,
const float transform_radius,
const float elastic_transform_mat[4][4],
const float elastic_transform_pivot[3],
PBVHNode *node)
{
SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptSession *ss = ob->sculpt;
float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co;
float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS);
KelvinletParams params;
/* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
@ -226,10 +219,9 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
const float force = 1.0f;
const float shear_modulus = 1.0f;
const float poisson_ratio = 0.4f;
BKE_kelvinlet_init_params(
&params, data->elastic_transform_radius, force, shear_modulus, poisson_ratio);
BKE_kelvinlet_init_params(&params, transform_radius, force, shear_modulus, poisson_ratio);
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
@ -239,11 +231,11 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
copy_v3_v3(orig_co, orig_data.co);
copy_v3_v3(transformed_co, vd.co);
mul_m4_v3(data->elastic_transform_mat, transformed_co);
mul_m4_v3(elastic_transform_mat, transformed_co);
sub_v3_v3v3(disp, transformed_co, vd.co);
float final_disp[3];
BKE_kelvinlet_grab_triscale(final_disp, &params, vd.co, data->elastic_transform_pivot, disp);
BKE_kelvinlet_grab_triscale(final_disp, &params, vd.co, elastic_transform_pivot, disp);
mul_v3_fl(final_disp, 20.0f * (1.0f - fade));
copy_v3_v3(proxy[vd.i], final_disp);
@ -259,20 +251,16 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius)
{
using namespace blender;
SculptSession *ss = ob->sculpt;
BLI_assert(ss->filter_cache->transform_displacement_mode ==
SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL);
const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(ob);
SculptThreadedTaskData data{};
data.sd = sd;
data.ob = ob;
data.nodes = ss->filter_cache->nodes;
data.elastic_transform_radius = transform_radius;
float transform_mats[8][4][4];
sculpt_transform_matrices_init(
ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
ss, symm, ss->filter_cache->transform_displacement_mode, transform_mats);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size());
@ -281,13 +269,24 @@ static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float
* displacement proxies as all vertices are modified by all symmetry passes. */
for (ePaintSymmetryFlags symmpass = PAINT_SYMM_NONE; symmpass <= symm; symmpass++) {
if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) {
flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass);
flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass);
float elastic_transform_pivot[3];
flip_v3_v3(elastic_transform_pivot, ss->pivot_pos, symmpass);
float elastic_transform_pivot_init[3];
flip_v3_v3(elastic_transform_pivot_init, ss->init_pivot_pos, symmpass);
const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot);
copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]);
BLI_task_parallel_range(
0, ss->filter_cache->nodes.size(), &data, sculpt_elastic_transform_task_cb, &settings);
const int symm_area = SCULPT_get_vertex_symm_area(elastic_transform_pivot);
float elastic_transform_mat[4][4];
copy_m4_m4(elastic_transform_mat, transform_mats[symm_area]);
threading::parallel_for(
ss->filter_cache->nodes.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
sculpt_elastic_transform_task(ob,
transform_radius,
elastic_transform_mat,
elastic_transform_pivot,
ss->filter_cache->nodes[i]);
}
});
}
}
SCULPT_combine_transform_proxies(sd, ob);
@ -304,7 +303,7 @@ void ED_sculpt_update_modal_transform(bContext *C, Object *ob)
switch (sd->transform_mode) {
case SCULPT_TRANSFORM_MODE_ALL_VERTICES: {
sculpt_transform_all_vertices(sd, ob);
sculpt_transform_all_vertices(ob);
break;
}
case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: {