diff options
27 files changed, 1049 insertions, 129 deletions
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index a4a328fce1a..72a87703bd5 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -945,10 +945,27 @@ def brush_settings_advanced(layout, context, brush, popover=False): col.prop(brush, "use_automasking_boundary_face_sets", text="Face Sets Boundary") col.prop(brush, "use_automasking_cavity", text="Cavity") col.prop(brush, "use_automasking_cavity_inverted", text="Cavity (Inverted)") + col.prop(brush, "use_automasking_start_normal", text="Area Normal") + col.prop(brush, "use_automasking_view_normal", text="View Normal") col.separator() col.prop(brush, "automasking_boundary_edges_propagation_steps") + sculpt = context.tool_settings.sculpt + + if brush.use_automasking_start_normal: + col.separator() + + col.prop(sculpt, "automasking_start_normal_limit") + col.prop(sculpt, "automasking_start_normal_falloff") + + if brush.use_automasking_view_normal: + col.separator() + + col.prop(brush, "use_automasking_view_occlusion", text="Occlusion") + col.prop(sculpt, "automasking_view_normal_limit") + col.prop(sculpt, "automasking_view_normal_falloff") + if brush.use_automasking_cavity or brush.use_automasking_cavity_inverted: col.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index fcf00ee80f6..ada2993a16f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -5497,6 +5497,8 @@ class VIEW3D_MT_sculpt_automasking_pie(Menu): pie.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary") pie.prop(sculpt, "use_automasking_cavity", text="Cavity") pie.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (Inverted)") + pie.prop(sculpt, "use_automasking_start_normal", text="Area Normal") + pie.prop(sculpt, "use_automasking_view_normal", text="View Normal") class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu): diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index d18b75e78af..6946ab54ca7 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -974,6 +974,21 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary") col.prop(sculpt, "use_automasking_cavity", text="Cavity") col.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (Inverted)") + col.prop(sculpt, "use_automasking_start_normal", text="Area Normal") + col.prop(sculpt, "use_automasking_view_normal", text="View Normal") + + if sculpt.use_automasking_start_normal: + col.separator() + + col.prop(sculpt, "automasking_start_normal_limit") + col.prop(sculpt, "automasking_start_normal_falloff") + + if sculpt.use_automasking_view_normal: + col.separator() + + col.prop(sculpt, "use_automasking_view_occlusion", text="Occlusion") + col.prop(sculpt, "automasking_view_normal_limit") + col.prop(sculpt, "automasking_view_normal_falloff") col.separator() col.prop(sculpt.brush, "automasking_boundary_edges_propagation_steps") diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 386fecfd278..ed7ef5d5efd 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -474,11 +474,6 @@ typedef struct SculptBoundary { } twist; } SculptBoundary; -typedef struct CavityMaskData { - float factor; - int stroke_id; -} CavityMaskData; - typedef struct SculptFakeNeighbors { bool use_fake_neighbors; @@ -554,13 +549,13 @@ typedef struct SculptAttributePointers { /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and * initialized in #SCULPT_automasking_cache_init when needed. */ SculptAttribute *automasking_factor; + SculptAttribute *automasking_occlusion; /* CD_PROP_INT8. */ + SculptAttribute *automasking_stroke_id; + SculptAttribute *automasking_cavity; /* BMesh */ SculptAttribute *dyntopo_node_id_vertex; SculptAttribute *dyntopo_node_id_face; - - SculptAttribute *stroke_id; - SculptAttribute *cavity; } SculptAttributePointers; typedef struct SculptSession { @@ -747,14 +742,16 @@ typedef struct SculptSession { */ bool sticky_shading_color; + uchar stroke_id; + /** * Last used painting canvas key. */ char *last_paint_canvas_key; + float last_normal[3]; - uchar stroke_id; int last_automasking_settings_hash; - uchar last_cavity_stroke_id; + uchar last_automask_stroke_id; } SculptSession; void BKE_sculptsession_free(struct Object *ob); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 08e49550426..e0a27a3d03e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2079,6 +2079,14 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->constant_detail = 3.0f; } + if (!sd->automasking_start_normal_limit) { + sd->automasking_start_normal_limit = 20.0f / 180.0f * M_PI; + sd->automasking_start_normal_falloff = 0.25f; + + sd->automasking_view_normal_limit = 90.0f / 180.0f * M_PI; + sd->automasking_view_normal_falloff = 0.25f; + } + /* Set sane default tiling offsets. */ if (!sd->paint.tile_offset[0]) { sd->paint.tile_offset[0] = 1.0f; diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 1e220d33ff4..59b2d8d31b9 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -34,7 +34,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, /* sculpt_transform.c */ void ED_sculpt_update_modal_transform(struct bContext *C, struct Object *ob); -void ED_sculpt_init_transform(struct bContext *C, struct Object *ob, const char *undo_name); +void ED_sculpt_init_transform(struct bContext *C, struct Object *ob, const int mval[2], const char *undo_name); void ED_sculpt_end_transform(struct bContext *C, struct Object *ob); /* sculpt_undo.c */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 54810436d20..7fc5df8237a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1268,11 +1268,11 @@ static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush /** * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action */ -static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush) +static int sculpt_brush_needs_normal(const SculptSession *ss, Sculpt *sd, const Brush *brush) { return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && (ss->cache->normal_weight > 0.0f)) || - + SCULPT_automasking_needs_normal(ss, sd, brush) || ELEM(brush->sculpt_tool, SCULPT_TOOL_BLOB, SCULPT_TOOL_CREASE, @@ -2413,7 +2413,8 @@ float SCULPT_brush_strength_factor(SculptSession *ss, const float fno[3], float mask, const PBVHVertRef vertex, - int thread_id) + const int thread_id, + AutomaskingNodeData *automask_data) { StrokeCache *cache = ss->cache; const Scene *scene = cache->vc->scene; @@ -2497,7 +2498,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, avg *= 1.0f - mask; /* Auto-masking. */ - avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex); + avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex, automask_data); return avg; } @@ -3087,7 +3088,8 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + NULL); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -3396,7 +3398,7 @@ static void do_brush_action(Sculpt *sd, BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); } - if (sculpt_brush_needs_normal(ss, brush)) { + if (sculpt_brush_needs_normal(ss, sd, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); } @@ -3548,7 +3550,7 @@ static void do_brush_action(Sculpt *sd, SCULPT_bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); } - if (!SCULPT_tool_can_reuse_cavity_mask(brush->sculpt_tool) || + if (!SCULPT_tool_can_reuse_automask(brush->sculpt_tool) || (ss->cache->supports_gravity && sd->gravity_factor > 0.0f)) { /* Clear cavity mask cache. */ ss->last_automasking_settings_hash = 0; @@ -4242,8 +4244,6 @@ static void sculpt_update_cache_invariants( ss->cache = cache; - cache->stroke_id = ss->stroke_id; - /* Set scaling adjustment. */ max_scale = 0.0f; for (int i = 0; i < 3; i++) { @@ -5413,6 +5413,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f } SCULPT_stroke_id_next(ob); + ss->cache->stroke_id = ss->stroke_id; return true; } @@ -6012,6 +6013,70 @@ void SCULPT_fake_neighbors_free(Object *ob) sculpt_pose_fake_neighbors_free(ss); } +void SCULPT_automasking_node_begin(Object *ob, + const SculptSession *UNUSED(ss), + AutomaskingCache *automasking, + AutomaskingNodeData *node_data, + PBVHNode *node) +{ + if (!automasking) { + memset(node_data, 0, sizeof(*node_data)); + return; + } + + node_data->node = node; + node_data->have_orig_data = automasking->settings.flags & + (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); + + if (node_data->have_orig_data) { + SCULPT_orig_vert_data_init(&node_data->orig_data, ob, node, SCULPT_UNDO_COORDS); + } + else { + memset(&node_data->orig_data, 0, sizeof(node_data->orig_data)); + } +} + +void SCULPT_automasking_node_update(SculptSession *UNUSED(ss), + AutomaskingNodeData *automask_data, + PBVHVertexIter *vd) +{ + if (automask_data->have_orig_data) { + SCULPT_orig_vert_data_update(&automask_data->orig_data, vd); + } +} + +bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original) +{ + float ray_start[3], ray_end[3], ray_normal[3], face_normal[3]; + float co[3]; + + copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex)); + float mouse[2]; + + ED_view3d_project_float_v2_m4(ss->cache->vc->region, co, mouse, ss->cache->projection_mat); + + int depth = SCULPT_raycast_init(ss->cache->vc, mouse, ray_end, ray_start, ray_normal, original); + + negate_v3(ray_normal); + + copy_v3_v3(ray_start, SCULPT_vertex_co_get(ss, vertex)); + madd_v3_v3fl(ray_start, ray_normal, 0.002); + + SculptRaycastData srd = {0}; + srd.original = original; + srd.ss = ss; + srd.hit = false; + srd.ray_start = ray_start; + srd.ray_normal = ray_normal; + srd.depth = depth; + srd.face_normal = face_normal; + + isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); + BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + + return srd.hit; +} + void SCULPT_stroke_id_next(Object *ob) { /* Manually wrap in int32 space to avoid tripping up undefined behavior @@ -6024,11 +6089,14 @@ void SCULPT_stroke_id_ensure(Object *ob) { SculptSession *ss = ob->sculpt; - if (!ss->attrs.stroke_id) { + if (!ss->attrs.automasking_stroke_id) { SculptAttributeParams params = {0}; - - ss->attrs.stroke_id = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(stroke_id), ¶ms); + ss->attrs.automasking_stroke_id = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), + ¶ms); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 43225443ea0..100eb69ac48 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -98,6 +98,12 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { return true; } + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BRUSH_NORMAL)) { + return true; + } + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_VIEW_NORMAL)) { + return true; + } if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CAVITY_ALL)) { return true; } @@ -127,6 +133,50 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br return sculpt->automasking_flags; } +bool SCULPT_automasking_needs_normal(const SculptSession *UNUSED(ss), + const Sculpt *sculpt, + const Brush *brush) +{ + int flags = sculpt_automasking_mode_effective_bits(sculpt, brush); + + return flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); +} + +static float sculpt_automasking_normal_calc(SculptSession *ss, + PBVHVertRef vertex, + float3 &normal, + float limit_lower, + float limit_upper, + AutomaskingNodeData *automask_data) +{ + float3 normal_v; + + if (automask_data->have_orig_data) { + normal_v = automask_data->orig_data.no; + } + else { + SCULPT_vertex_normal_get(ss, vertex, normal_v); + } + + float angle = saacos(dot_v3v3(normal, normal_v)); + + /* note that limit is pre-divided by M_PI */ + + if (angle > limit_lower && angle < limit_upper) { + float t = 1.0f - (angle - limit_lower) / (limit_upper - limit_lower); + + /* smoothstep */ + t = t * t * (3.0 - 2.0 * t); + + return t; + } + else if (angle > limit_upper) { + return 0.0f; + } + + return 1.0f; +} + static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush) { @@ -134,13 +184,93 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { return true; } + if (automasking_flags & - (BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS | BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { + (BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS | + BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL)) { return brush && brush->automasking_boundary_edges_propagation_steps != 1; } return false; } +static float automasking_brush_normal_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex, + AutomaskingNodeData *automask_data) +{ + float falloff = automasking->settings.start_normal_falloff * M_PI; + float3 initial_normal; + + if (ss->cache) { + initial_normal = ss->cache->initial_normal; + } + else { + initial_normal = ss->filter_cache->initial_normal; + } + + return sculpt_automasking_normal_calc(ss, + vertex, + initial_normal, + automasking->settings.start_normal_limit - falloff * 0.5f, + automasking->settings.start_normal_limit + falloff * 0.5f, + automask_data); +} + +static float automasking_view_normal_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex, + AutomaskingNodeData *automask_data) +{ + float falloff = automasking->settings.view_normal_falloff * M_PI; + + float3 view_normal; + + if (ss->cache) { + view_normal = ss->cache->view_normal; + } + else { + view_normal = ss->filter_cache->view_normal; + } + + return sculpt_automasking_normal_calc(ss, + vertex, + view_normal, + automasking->settings.view_normal_limit, + automasking->settings.view_normal_limit + falloff, + automask_data); +} + +static float automasking_view_occlusion_factor(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vertex, + uchar stroke_id, + AutomaskingNodeData *UNUSED(automask_data)) +{ + char f = *(char *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion); + + if (stroke_id != automasking->current_stroke_id) { + f = *(char *)SCULPT_vertex_attr_get( + vertex, + ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1; + } + + return f == 2; +} + +/* Updates vertex stroke id. */ +static float automasking_factor_end(SculptSession *ss, + AutomaskingCache *automasking, + PBVHVertRef vertex, + float value) +{ + if (ss->attrs.automasking_stroke_id) { + *(uchar *)SCULPT_vertex_attr_get( + vertex, ss->attrs.automasking_stroke_id) = automasking->current_stroke_id; + } + + return value; +} + static float sculpt_cavity_calc_factor(AutomaskingCache *automasking, float factor) { float sign = signf(factor); @@ -297,8 +427,7 @@ static void sculpt_calc_blurred_cavity(SculptSession *ss, factor_sum = sculpt_cavity_calc_factor(automasking, factor_sum); - *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.cavity) = factor_sum; - *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id) = automasking->cavity_stroke_id; + *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum; } int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking) @@ -331,6 +460,20 @@ int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking) hash = BLI_hash_int_2d(hash, automasking->settings.initial_face_set); } + if (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL) { + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.view_normal_falloff)); + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.view_normal_limit)); + } + + if (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL) { + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.start_normal_falloff)); + hash = BLI_hash_int_2d(hash, + *reinterpret_cast<uint *>(&automasking->settings.start_normal_limit)); + } + return hash; } @@ -338,13 +481,13 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, SculptSession *ss, PBVHVertRef vertex) { - uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id); + uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id); - if (stroke_id != automasking->cavity_stroke_id) { + if (stroke_id != automasking->current_stroke_id) { sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex); } - float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.cavity); + float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity); bool inverted = automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED; if ((automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) && @@ -359,7 +502,8 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, - PBVHVertRef vert) + PBVHVertRef vert, + AutomaskingNodeData *automask_data) { if (!automasking) { return 1.0f; @@ -378,6 +522,18 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, return factor; } + uchar stroke_id = ss->attrs.automasking_stroke_id ? + *(uchar *)(SCULPT_vertex_attr_get(vert, ss->attrs.automasking_stroke_id)) : + -1; + + bool do_occlusion = (automasking->settings.flags & + (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL)) == + (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL); + if (do_occlusion && + automasking_view_occlusion_factor(automasking, ss, vert, stroke_id, automask_data)) { + return automasking_factor_end(ss, automasking, vert, 0.0f); + } + if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) { return 0.0f; @@ -396,11 +552,23 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, } } + float mask = 1.0f; + + if ((ss->cache || ss->filter_cache) && + (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL)) { + mask *= automasking_brush_normal_factor(automasking, ss, vert, automask_data); + } + + if ((ss->cache || ss->filter_cache) && + (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) { + mask *= automasking_view_normal_factor(automasking, ss, vert, automask_data); + } + if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { - return sculpt_automasking_cavity_factor(automasking, ss, vert); + mask *= sculpt_automasking_cavity_factor(automasking, ss, vert); } - return 1.0f; + return automasking_factor_end(ss, automasking, vert, mask); } void SCULPT_automasking_cache_free(AutomaskingCache *automasking) @@ -582,6 +750,11 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); + automasking->settings.view_normal_limit = sd->automasking_view_normal_limit; + automasking->settings.view_normal_falloff = sd->automasking_view_normal_falloff; + automasking->settings.start_normal_limit = sd->automasking_start_normal_limit; + automasking->settings.start_normal_falloff = sd->automasking_start_normal_falloff; + if (brush && (brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) { automasking->settings.cavity_curve = brush->automasking_cavity_curve; automasking->settings.cavity_factor = brush->automasking_cavity_factor; @@ -594,7 +767,42 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski } } -bool SCULPT_tool_can_reuse_cavity_mask(int sculpt_tool) +static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automasking, + Object *ob, + eAutomasking_flag mode) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + /* No need to build original data since this is only called at the beginning of strokes.*/ + AutomaskingNodeData nodedata; + nodedata.have_orig_data = false; + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + float f = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor); + + if ((int)mode & BRUSH_AUTOMASKING_BRUSH_NORMAL) { + f *= automasking_brush_normal_factor(automasking, ss, vertex, &nodedata); + } + if ((int)mode & BRUSH_AUTOMASKING_VIEW_NORMAL) { + if ((int)mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) { + f *= automasking_view_occlusion_factor(automasking, ss, vertex, -1, &nodedata); + } + + f *= automasking_view_normal_factor(automasking, ss, vertex, &nodedata); + } + + if (ss->attrs.automasking_stroke_id) { + *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id) = ss->stroke_id; + } + + *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f; + } +} + +bool SCULPT_tool_can_reuse_automask(int sculpt_tool) { return ELEM(sculpt_tool, SCULPT_TOOL_PAINT, @@ -617,32 +825,61 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush); SCULPT_boundary_info_ensure(ob); - if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CAVITY_ALL)) { + automasking->current_stroke_id = ss->stroke_id; + + bool use_stroke_id = false; + int mode = sculpt_automasking_mode_effective_bits(sd, brush); + + if ((mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && (mode & BRUSH_AUTOMASKING_VIEW_NORMAL)) { + use_stroke_id = true; + + if (!ss->attrs.automasking_occlusion) { + SculptAttributeParams params = {0}; + ss->attrs.automasking_occlusion = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + SCULPT_ATTRIBUTE_NAME(automasking_occlusion), + ¶ms); + } + } + + if (mode & BRUSH_AUTOMASKING_CAVITY_ALL) { + use_stroke_id = true; + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CAVITY_USE_CURVE)) { BKE_curvemapping_init(brush->automasking_cavity_curve); BKE_curvemapping_init(sd->automasking_cavity_curve); } - SCULPT_stroke_id_ensure(ob); - automasking->cavity_stroke_id = ss->stroke_id; - - if (!ss->attrs.cavity) { + if (!ss->attrs.automasking_cavity) { SculptAttributeParams params = {0}; - ss->attrs.cavity = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(cavity), ¶ms); + ss->attrs.automasking_cavity = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + SCULPT_ATTRIBUTE_NAME(automasking_cavity), + ¶ms); } - /* Can we reuse the previous stroke's cavity mask? */ - else if (brush && SCULPT_tool_can_reuse_cavity_mask(brush->sculpt_tool)) { + } + + if (use_stroke_id) { + SCULPT_stroke_id_ensure(ob); + + bool have_occlusion = (mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && + (mode & BRUSH_AUTOMASKING_VIEW_NORMAL); + + if (brush && SCULPT_tool_can_reuse_automask(brush->sculpt_tool) && !have_occlusion) { int hash = SCULPT_automasking_settings_hash(ob, automasking); if (hash == ss->last_automasking_settings_hash) { - automasking->cavity_stroke_id = ss->last_automasking_settings_hash; - automasking->can_reuse_cavity = true; + automasking->current_stroke_id = ss->last_automask_stroke_id; + automasking->can_reuse_mask = true; } } - if (!automasking->can_reuse_cavity) { - ss->last_cavity_stroke_id = ss->stroke_id; + if (!automasking->can_reuse_mask) { + ss->last_automask_stroke_id = ss->stroke_id; } } @@ -675,6 +912,14 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object sculpt_face_sets_automasking_init(sd, ob); } + int normal_bits = sculpt_automasking_mode_effective_bits(sd, brush) & + (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL | + BRUSH_AUTOMASKING_VIEW_OCCLUSION); + + if (normal_bits) { + sculpt_normal_occlusion_automasking_fill(automasking, ob, (eAutomasking_flag)normal_bits); + } + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps); @@ -690,5 +935,8 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object bool SCULPT_automasking_needs_original(const Sculpt *sd, const Brush *brush) { - return sculpt_automasking_mode_effective_bits(sd, brush) & BRUSH_AUTOMASKING_CAVITY_ALL; + + return sculpt_automasking_mode_effective_bits(sd, brush) & + (BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_BRUSH_NORMAL | + BRUSH_AUTOMASKING_VIEW_NORMAL); } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 005892b88a0..f6da40065e0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -681,12 +681,16 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, angle_factor = floorf(angle_factor * 10) / 10.0f; } 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]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -694,7 +698,8 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]); @@ -729,12 +734,16 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], 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]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -742,7 +751,8 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -773,6 +783,9 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); @@ -781,6 +794,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -788,7 +802,8 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -819,12 +834,16 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].propagation_steps_num == -1) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -832,7 +851,8 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -862,6 +882,9 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; @@ -876,6 +899,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); SCULPT_orig_vert_data_update(&orig_data, &vd); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { @@ -883,7 +907,8 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c index 00ad77e48cf..f2f7eac072e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c @@ -302,10 +302,17 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* Offset vertex. */ const float fade = SCULPT_brush_strength_factor(ss, brush, @@ -315,7 +322,8 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -387,6 +395,10 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -405,6 +417,8 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -413,7 +427,8 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -485,6 +500,10 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -503,6 +522,8 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -511,7 +532,8 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -601,6 +623,10 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, /* Tilted plane (front part of the brush). */ plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -621,6 +647,8 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix); sub_v3_v3v3(val, intr_tilt, vd.co); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -629,7 +657,8 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -764,6 +793,10 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -776,6 +809,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); if (SCULPT_plane_trim(ss->cache, brush, val)) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -784,7 +819,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -922,6 +958,10 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -933,6 +973,8 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -941,7 +983,8 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -1041,6 +1084,10 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { continue; @@ -1058,6 +1105,9 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, if (!SCULPT_plane_trim(ss->cache, brush, val)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */ const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, @@ -1067,7 +1117,8 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -1202,6 +1253,10 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, KelvinletParams params; BKE_kelvinlet_init_params(¶ms, ss->cache->radius, bstrength, 1.0f, 0.4f); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -1212,6 +1267,8 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, fade = 1.0f; } else { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1220,7 +1277,8 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); } mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1267,7 +1325,9 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, if (vd.mask) { mul_v3_fl(disp, 1.0f - *vd.mask); } - mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); + mul_v3_fl( + disp, + SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data)); copy_v3_v3(proxy[vd.i], disp); } @@ -1339,12 +1399,18 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -1353,7 +1419,8 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1412,6 +1479,10 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -1427,7 +1498,8 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); @@ -1485,12 +1557,18 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1499,7 +1577,8 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); const int vi = vd.index; float *disp_factor; @@ -1600,10 +1679,16 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1612,7 +1697,8 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float val[3]; if (vd.fno) { @@ -1668,10 +1754,16 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1680,7 +1772,8 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1746,11 +1839,17 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } /* Offset vertex. */ + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1759,7 +1858,8 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float val1[3]; float val2[3]; @@ -1863,10 +1963,16 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(x_object_space, stroke_xz[0]); copy_v3_v3(z_object_space, stroke_xz[1]); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -1875,7 +1981,8 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float disp_center[3]; float x_disp[3]; float z_disp[3]; @@ -1977,12 +2084,18 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -1991,7 +2104,8 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); if (grab_silhouette) { float silhouette_test_dir[3]; @@ -2055,6 +2169,9 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, const float bstrength = ss->cache->bstrength; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -2110,7 +2227,9 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(final_disp, 1.0f - *vd.mask); } - mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); + mul_v3_fl( + final_disp, + SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data)); copy_v3_v3(proxy[vd.i], final_disp); @@ -2174,12 +2293,18 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } /* Offset vertex. */ + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -2188,7 +2313,8 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -2258,11 +2384,17 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -2271,7 +2403,8 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float current_disp[3]; float current_disp_norm[3]; float final_disp[3] = {0.0f, 0.0f, 0.0f}; @@ -2415,11 +2548,17 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, brush, orig_data.co, @@ -2428,7 +2567,8 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); if (vd.mvert) { @@ -2491,11 +2631,17 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -2504,7 +2650,8 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float limit_co[3]; float disp[3]; @@ -2557,11 +2704,17 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -2570,7 +2723,8 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float current_disp[3]; float current_disp_norm[3]; @@ -2718,16 +2872,29 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - const float fade = - bstrength * - SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) * - ss->cache->pressure; + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + const float fade = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + *vd.mask, + vd.vertex, + thread_id, + &automask_data) * + ss->cache->pressure; float avg[3], val[3]; @@ -2797,13 +2964,26 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + 0.0f, + vd.vertex, + thread_id, + &automask_data); if (bstrength > 0.0f) { (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index b354e2cb182..2b365a661ee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -504,6 +504,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor); } + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float force[3]; float sim_location[3]; @@ -544,7 +548,8 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float brush_disp[3]; @@ -765,6 +770,9 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( } 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]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float sim_location[3]; @@ -788,7 +796,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor); const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) * - SCULPT_automasking_factor_get(automasking, ss, vd.vertex); + SCULPT_automasking_factor_get(automasking, ss, vd.vertex, &automask_data); madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); @@ -821,6 +829,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, { AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss); + AutomaskingNodeData automask_data = {0}; + + automask_data.have_orig_data = true; for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) { for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { @@ -860,9 +871,11 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2); const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, vertex1)) * - SCULPT_automasking_factor_get(automasking, ss, vertex1); + SCULPT_automasking_factor_get( + automasking, ss, vertex1, &automask_data); const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, vertex2)) * - SCULPT_automasking_factor_get(automasking, ss, vertex2); + SCULPT_automasking_factor_get( + automasking, ss, vertex2, &automask_data); float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -1434,11 +1447,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, sculpt_gravity[2] = -1.0f; } mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->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) { float fade = vd.mask ? *vd.mask : 0.0f; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); fade = 1.0f - fade; float force[3] = {0.0f, 0.0f, 0.0f}; float disp[3], temp[3], transform[3][3]; @@ -1581,7 +1598,8 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); SCULPT_undo_push_begin(ob, op); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + SCULPT_filter_cache_init( + C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); @@ -1644,14 +1662,14 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* RNA. */ + SCULPT_mesh_filter_properties(ot); + RNA_def_enum(ot->srna, "type", prop_cloth_filter_type, CLOTH_FILTER_GRAVITY, "Filter Type", "Operation that is going to be applied to the mesh"); - RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); RNA_def_enum_flag(ot->srna, "force_axis", prop_cloth_filter_force_axis_items, diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 3f65248bc78..be52aaf0aa7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -129,6 +129,9 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { @@ -154,7 +157,8 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); if (fade > 0.05f) { ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set; @@ -173,7 +177,8 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); if (fade > 0.05f) { SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); @@ -205,6 +210,9 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, } const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -222,7 +230,8 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); if (vd.mvert) { diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 6d1fad1fc65..42ea02caff8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -95,6 +95,10 @@ static void color_filter_task_cb(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[n]); + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -104,7 +108,8 @@ static void color_filter_task_cb(void *__restrict userdata, float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); if (fade == 0.0f) { continue; } @@ -290,6 +295,8 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } + SCULPT_stroke_id_next(ob); + const float len = event->prev_press_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f; @@ -361,7 +368,8 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR); + SCULPT_filter_cache_init( + C, ob, sd, SCULPT_UNDO_COLOR, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); FilterCache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); @@ -386,9 +394,9 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* rna */ + SCULPT_mesh_filter_properties(ot); + RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter Type", ""); - RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); PropertyRNA *prop = RNA_def_float_color( ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 0b68a527b59..4e481da03bc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -101,7 +101,12 @@ static void filter_cache_init_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, data->filter_undo_type); } -void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int undo_type) +void SCULPT_filter_cache_init(bContext *C, + Object *ob, + Sculpt *sd, + const int undo_type, + const int mval[2], + float area_normal_radius) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; @@ -159,6 +164,79 @@ void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int und ED_view3d_viewcontext_init(C, &vc, depsgraph); copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat); copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv); + + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + float co[3]; + float mval_fl[2] = {(float)mval[0], (float)mval[1]}; + + if (SCULPT_stroke_get_location(C, co, mval_fl, false)) { + PBVHNode **nodes; + int totnode; + + /* Get radius from brush. */ + Brush *brush = BKE_paint_brush(&sd->paint); + float radius; + + if (brush) { + if (BKE_brush_use_locked_size(scene, brush)) { + radius = paint_calc_object_space_radius( + &vc, co, (float)BKE_brush_size_get(scene, brush) * area_normal_radius); + } + else { + radius = BKE_brush_unprojected_radius_get(scene, brush) * area_normal_radius; + } + } + else { + radius = paint_calc_object_space_radius(&vc, co, (float)ups->size * area_normal_radius); + } + + SculptSearchSphereData search_data = { + .original = true, + .center = co, + .radius_squared = radius * radius, + .ignore_fully_ineffective = true, + }; + + BKE_pbvh_search_gather(pbvh, SCULPT_search_sphere_cb, &search_data, &nodes, &totnode); + + if (SCULPT_pbvh_calc_area_normal( + brush, ob, nodes, totnode, true, ss->filter_cache->initial_normal)) { + copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal); + } + else { + copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal); + } + + MEM_SAFE_FREE(nodes); + + /* Update last stroke location */ + + mul_m4_v3(ob->obmat, co); + + add_v3_v3(ups->average_stroke_accum, co); + ups->average_stroke_counter++; + ups->last_stroke_valid = true; + } + else { + /* Use last normal. */ + copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal); + } + + /* Update view normal */ + float projection_mat[4][4]; + float mat[3][3]; + float viewDir[3] = {0.0f, 0.0f, 1.0f}; + + ED_view3d_ob_project_mat_get(vc.rv3d, ob, projection_mat); + + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, vc.rv3d->viewinv); + mul_m3_v3(mat, viewDir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, viewDir); + normalize_v3_v3(ss->filter_cache->view_normal, viewDir); } void SCULPT_filter_cache_free(SculptSession *ss) @@ -288,15 +366,20 @@ static void mesh_filter_task_cb(void *__restrict userdata, /* This produces better results as the relax operation is no completely focused on the * 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); PBVHVertexIter vd; 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); + 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 *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) { /* Surface Smooth can't skip the loop for this vertex as it needs to calculate its @@ -580,11 +663,18 @@ static void mesh_filter_surface_smooth_displace_task_cb( PBVHNode *node = data->nodes[i]; PBVHVertexIter vd; + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[i]); + 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 *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); if (fade == 0.0f) { continue; } @@ -622,6 +712,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * const float len = event->prev_press_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; + SCULPT_stroke_id_next(ob); SCULPT_vertex_random_access_ensure(ss); bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type); @@ -699,7 +790,8 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_undo_push_begin(ob, op); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + SCULPT_filter_cache_init( + C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); FilterCache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; @@ -746,6 +838,22 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } +void SCULPT_mesh_filter_properties(struct wmOperatorType *ot) +{ + RNA_def_float( + ot->srna, + "area_normal_radius", + 0.25, + 0.001, + 5.0, + "Normal Radius", + "Radius used for calculating area normal on initial click,\nin percentage of brush radius.", + 0.01, + 1.0); + RNA_def_float( + ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); +} + void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) { /* Identifiers. */ @@ -761,14 +869,14 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* RNA. */ + SCULPT_mesh_filter_properties(ot); + RNA_def_enum(ot->srna, "type", prop_mesh_filter_types, MESH_FILTER_INFLATE, "Filter Type", "Operation that is going to be applied to the mesh"); - RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); RNA_def_enum_flag(ot->srna, "deform_axis", prop_mesh_filter_deform_axis_items, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index a28c18359e5..4817591a5c3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -27,6 +27,7 @@ extern "C" { #endif struct AutomaskingCache; +struct AutomaskingNodeData; struct Image; struct ImageUser; struct KeyBlock; @@ -394,16 +395,20 @@ typedef struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + float cavity_factor; int cavity_blur_steps; struct CurveMapping *cavity_curve; + + float start_normal_limit, start_normal_falloff; + float view_normal_limit, view_normal_falloff; } AutomaskingSettings; typedef struct AutomaskingCache { AutomaskingSettings settings; - bool can_reuse_cavity; - uchar cavity_stroke_id; + bool can_reuse_mask; + uchar current_stroke_id; } AutomaskingCache; typedef struct FilterCache { @@ -463,6 +468,8 @@ typedef struct FilterCache { /* Auto-masking. */ AutomaskingCache *automasking; + float initial_normal[3]; + float view_normal[3]; /* Pre-smoothed colors used by sharpening. Colors are HSL. */ float (*pre_smoothed_color)[4]; @@ -913,6 +920,8 @@ float SCULPT_vertex_mask_get(struct SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]); void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]); +bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original); + /** Returns true if a color attribute exists in the current sculpt session. */ bool SCULPT_has_colors(const SculptSession *ss); @@ -1194,7 +1203,8 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss, const float fno[3], float mask, const PBVHVertRef vertex, - int thread_id); + int thread_id, + struct AutomaskingNodeData *automask_data); /** * Tilts a normal by the x and y tilt values using the view axis. @@ -1282,9 +1292,30 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); /** \name Auto-masking. * \{ */ +typedef struct AutomaskingNodeData { + PBVHNode *node; + SculptOrigVertData orig_data; + bool have_orig_data; +} AutomaskingNodeData; + +/** Call before PBVH vertex iteration. + * \param automask_data: pointer to an uninitialized AutomaskingNodeData struct. + */ +void SCULPT_automasking_node_begin(struct Object *ob, + const SculptSession *ss, + struct AutomaskingCache *automasking, + AutomaskingNodeData *automask_data, + PBVHNode *node); + +/* Call before SCULPT_automasking_factor_get and SCULPT_brush_strength_factor. */ +void SCULPT_automasking_node_update(SculptSession *ss, + AutomaskingNodeData *automask_data, + PBVHVertexIter *vd); + float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking, SculptSession *ss, - PBVHVertRef vertex); + PBVHVertRef vertex, + AutomaskingNodeData *automask_data); /* Returns the automasking cache depending on the active tool. Used for code that can run both for * brushes and filter. */ @@ -1300,6 +1331,9 @@ float *SCULPT_boundary_automasking_init(Object *ob, eBoundaryAutomaskMode mode, int propagation_steps, float *automask_factor); +bool SCULPT_automasking_needs_normal(const SculptSession *ss, + const Sculpt *sculpt, + const Brush *brush); bool SCULPT_automasking_needs_original(const struct Sculpt *sd, const struct Brush *brush); int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking); @@ -1329,8 +1363,14 @@ float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_r /** \name Filter API * \{ */ -void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, int undo_type); +void SCULPT_filter_cache_init(struct bContext *C, + Object *ob, + Sculpt *sd, + int undo_type, + const int mval[2], + float area_normal_radius); void SCULPT_filter_cache_free(SculptSession *ss); +void SCULPT_mesh_filter_properties(struct wmOperatorType *ot); void SCULPT_mask_filter_smooth_apply( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, int smooth_iterations); @@ -1849,7 +1889,7 @@ BLI_INLINE bool SCULPT_tool_is_face_sets(int tool) void SCULPT_stroke_id_ensure(struct Object *ob); void SCULPT_stroke_id_next(struct Object *ob); -bool SCULPT_tool_can_reuse_cavity_mask(int sculpt_tool); +bool SCULPT_tool_can_reuse_automask(int sculpt_tool); #ifdef __cplusplus } diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index 1e8731e54c0..9f57f9d6713 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -69,6 +69,10 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, test_radius *= brush->normal_radius_factor; 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]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -78,6 +82,9 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, float normal[3]; copy_v3_v3(normal, vd.no ? vd.no : vd.fno); mul_v3_m4v3(local_co, mat, vd.co); + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* Use the brush falloff to weight the sampled normals. */ const float fade = SCULPT_brush_strength_factor(ss, brush, @@ -87,7 +94,8 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); /* Sample the normal and area of the +X and -X axis individually. */ if (local_co[0] > 0.0f) { @@ -144,6 +152,10 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -184,6 +196,9 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, if (!SCULPT_plane_trim(ss->cache, brush, val)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + /* Deform the local space along the Y axis to avoid artifacts on curved strokes. */ /* This produces a not round brush tip. */ local_co[1] *= 2.0f; @@ -195,7 +210,8 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); mul_v3_v3fl(proxy[vd.i], val, fade); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index e56d967808f..64a55168977 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -1033,8 +1033,15 @@ static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata, SCULPT_undo_push_node(tdata->ob, node, SCULPT_UNDO_MASK); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + tdata->ob, ss, ss->cache->automasking, &automask_data, node); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - float automask = SCULPT_automasking_factor_get(tdata->automasking, ss, vd.vertex); + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + float automask = SCULPT_automasking_factor_get( + tdata->automasking, ss, vd.vertex, &automask_data); float mask; switch (mode) { diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index e717c15d6dc..7946affdec5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -70,10 +70,17 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -82,7 +89,8 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float smooth_color[4]; SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); @@ -125,6 +133,10 @@ 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]); + if (brush->flag & BRUSH_USE_GRADIENT) { switch (brush->gradient_stroke_mode) { case BRUSH_GRADIENT_PRESSURE: @@ -161,6 +173,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -169,7 +183,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); /* Density. */ float noise = 1.0f; @@ -197,7 +212,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, /* Final mix over the original color using brush alpha. We apply auto-making again * at this point to avoid washing out non-binary masking modes like cavity masking. */ - float automasking = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + float automasking = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha * automasking); float col[4]; @@ -404,10 +420,17 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, sub_v3_v3v3(brush_delta, ss->cache->location, ss->cache->last_location); } + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -416,7 +439,8 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float current_disp[3]; float current_disp_norm[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 8a3a3fe7adc..6dfb01cec20 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -153,7 +153,10 @@ template<typename ImageBuffer> class PaintingKernel { init_brush_test(); } - bool paint(const Triangles &triangles, const PackedPixelRow &pixel_row, ImBuf *image_buffer) + bool paint(const Triangles &triangles, + const PackedPixelRow &pixel_row, + ImBuf *image_buffer, + AutomaskingNodeData *automask_data) { image_accessor.set_image_position(image_buffer, pixel_row.start_image_coordinate); const TrianglePaintInput triangle = triangles.get_paint_input(pixel_row.triangle_index); @@ -171,6 +174,7 @@ template<typename ImageBuffer> class PaintingKernel { const float3 normal(0.0f, 0.0f, 0.0f); const float3 face_normal(0.0f, 0.0f, 0.0f); const float mask = 0.0f; + const float falloff_strength = SCULPT_brush_strength_factor( ss, brush, @@ -180,7 +184,8 @@ template<typename ImageBuffer> class PaintingKernel { face_normal, mask, BKE_pbvh_make_vref(PBVH_REF_NONE), - thread_id); + thread_id, + automask_data); float4 paint_color = brush_color * falloff_strength * brush_strength; float4 buffer_color; blend_color_mix_float(buffer_color, color, paint_color); @@ -321,6 +326,9 @@ static void do_paint_pixels(void *__restrict userdata, PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, mvert); PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, mvert); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + ImageUser image_user = *data->image_data.image_user; bool pixels_updated = false; for (UDIMTilePixels &tile_data : node_data.tiles) { @@ -347,10 +355,12 @@ static void do_paint_pixels(void *__restrict userdata, } bool pixels_painted = false; if (image_buffer->rect_float != nullptr) { - pixels_painted = kernel_float4.paint(node_data.triangles, pixel_row, image_buffer); + pixels_painted = kernel_float4.paint( + node_data.triangles, pixel_row, image_buffer, &automask_data); } else { - pixels_painted = kernel_byte4.paint(node_data.triangles, pixel_row, image_buffer); + pixels_painted = kernel_byte4.paint( + node_data.triangles, pixel_row, image_buffer, &automask_data); } if (pixels_painted) { diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index d1418c8dc35..fa33417faba 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -157,9 +157,13 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_automasking_node_update(ss, &automask_data, &vd); float total_disp[3]; zero_v3(total_disp); @@ -182,7 +186,8 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, /* Apply the vertex mask to the displacement. */ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); mul_v3_fl(disp, mask * automask); /* Accumulate the displacement. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 2ef3c28ba0c..08a6c037608 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -217,11 +217,17 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -230,7 +236,8 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float disp[3]; madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade); @@ -300,11 +307,17 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor( ss, brush, @@ -314,7 +327,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), vd.vertex, - thread_id); + thread_id, + &automask_data); if (smooth_mask) { float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; val *= fade * bstrength; @@ -470,12 +484,18 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( const int thread_id = BLI_task_parallel_thread_id(tls); SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -484,7 +504,8 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); float disp[3]; SCULPT_surface_smooth_laplacian_step( @@ -512,11 +533,17 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( 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); + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -525,7 +552,8 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( vd.fno, vd.mask ? *vd.mask : 0.0f, vd.vertex, - thread_id); + thread_id, + &automask_data); SCULPT_surface_smooth_displace_step( ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index dfaa0bd4daa..f841cd14386 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -46,7 +46,7 @@ #include <math.h> #include <stdlib.h> -void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_name) +void ED_sculpt_init_transform(struct bContext *C, Object *ob, const int mval[2], const char *undo_name) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; SculptSession *ss = ob->sculpt; @@ -66,7 +66,8 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_n ss->pivot_rot[3] = 1.0f; SCULPT_vertex_random_access_ensure(ss); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0); if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) { ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; diff --git a/source/blender/editors/transform/transform_convert_sculpt.c b/source/blender/editors/transform/transform_convert_sculpt.c index 3792cfefe06..f4f2e2a1c6e 100644 --- a/source/blender/editors/transform/transform_convert_sculpt.c +++ b/source/blender/editors/transform/transform_convert_sculpt.c @@ -87,7 +87,7 @@ static void createTransSculpt(bContext *C, TransInfo *t) copy_m3_m4(td->axismtx, ob->obmat); BLI_assert(!(t->options & CTX_PAINT_CURVE)); - ED_sculpt_init_transform(C, ob, t->undo_name); + ED_sculpt_init_transform(C, ob, t->mval, t->undo_name); } /** \} */ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index e0567151aa4..9b2dad915ed 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -338,6 +338,10 @@ typedef enum eAutomasking_flag { BRUSH_AUTOMASKING_CAVITY_INVERTED = (1 << 5), BRUSH_AUTOMASKING_CAVITY_ALL = (1 << 4) | (1 << 5), BRUSH_AUTOMASKING_CAVITY_USE_CURVE = (1 << 6), + /* (1 << 7) - unused. */ + BRUSH_AUTOMASKING_BRUSH_NORMAL = (1 << 8), + BRUSH_AUTOMASKING_VIEW_NORMAL = (1 << 9), + BRUSH_AUTOMASKING_VIEW_OCCLUSION = (1 << 10), } eAutomasking_flag; typedef enum ePaintBrush_flag { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index cf163ab7019..80b1049ca23 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1025,6 +1025,9 @@ typedef struct Sculpt { float automasking_cavity_factor; char _pad[4]; + float automasking_start_normal_limit, automasking_start_normal_falloff; + float automasking_view_normal_limit, automasking_view_normal_falloff; + struct CurveMapping *automasking_cavity_curve; struct CurveMapping *automasking_cavity_curve_op; /* For use by operators */ struct Object *gravity_object; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 3f4542a0f64..f927a3dab89 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -3270,6 +3270,28 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_automasking_start_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_BRUSH_NORMAL); + RNA_def_property_ui_text(prop, + "Area Normal", + "Affect only vertices with a similar normal to where the stroke starts"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_automasking_view_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_NORMAL); + RNA_def_property_ui_text(prop, + "View Normal", + "Affect only vertices with a normal that faces the viewer"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_automasking_view_occlusion", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_OCCLUSION); + RNA_def_property_ui_text(prop, "Occlusion", "Only affect vertices that are not occluded by other faces. (Slower performance)"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_scene_spacing", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, brush_spacing_unit_items); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 60f2b289af0..5348cf80d22 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -953,7 +953,64 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Cavity Curve", "Curve used for the sensitivity"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_start_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_BRUSH_NORMAL); + RNA_def_property_ui_text(prop, + "Area Normal", + "Affect only vertices with a similar normal to where the stroke starts"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_view_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_NORMAL); + RNA_def_property_ui_text(prop, + "View Normal", + "Affect only vertices with a normal that faces the viewer"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_view_occlusion", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_OCCLUSION); + RNA_def_property_ui_text(prop, "Occlusion", "Only affect vertices that are not occluded by other faces. (Slower performance)"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "automasking_start_normal_limit", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna( + prop, NULL, "automasking_start_normal_limit"); + RNA_def_property_range(prop, 0.0001f, M_PI); + RNA_def_property_ui_text(prop, + "Area Normal Limit", + "The range of angles that will be affected"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "automasking_start_normal_falloff", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna( + prop, NULL, "automasking_start_normal_falloff"); + RNA_def_property_range(prop, 0.0001f, 1.0f); + RNA_def_property_ui_text(prop, + "Area Normal Falloff", + "Extend the angular range with a falloff gradient"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "automasking_view_normal_limit", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna( + prop, NULL, "automasking_view_normal_limit"); + RNA_def_property_range(prop, 0.0001f, M_PI); + RNA_def_property_ui_text(prop, + "View Normal Limit", + "The range of angles that will be affected"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "automasking_view_normal_falloff", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna( + prop, NULL, "automasking_view_normal_falloff"); + RNA_def_property_range(prop, 0.0001f, 1.0f); + RNA_def_property_ui_text(prop, + "View Normal Falloff", + "Extend the angular range with a falloff gradient"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "symmetrize_direction", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_symmetrize_direction_items); RNA_def_property_ui_text(prop, "Direction", "Source and destination for symmetrize operator"); |