Sculpt: Add global automasking settings support in filters

When using the sculpt filters, global automasking settings that affect
all brushes were ignored because the automasking system was not
implemented for filters, making filters and brushes react differently
to the global sculpt settings which creates confusion.

This makes all filter tools (mesh, cloth, color) use the same general
automasking settings and features as the brush tools. Filters will now
use the settings in the options panel to limit their effect.

This also removes the "use Face Sets" option from the Mesh filter code,
as it was duplicated from the automasking code just to have that
funcitonality. This is now handled by the regular automasking system.

The "Use Face Sets" option is still available in the cloth filter as that
option limits the action of the forces, not the displacement.

After this, it is possible to initialize the automasking system
independently from the StrokeCache and Brush settings, so it can also be
added to more tools and features in the future.

Fixes T81619

Reviewed By: dbystedt, sergey

Maniphest Tasks: T81619

Differential Revision: https://developer.blender.org/D9171
This commit is contained in:
Pablo Dobarro 2020-10-15 19:34:54 +02:00
parent da7ace00d5
commit 6fe3521481
Notes: blender-bot 2023-02-14 04:39:18 +01:00
Referenced by issue #81619, Face Sets Boundary Automasking and Face Sets Automasking not working with Cloth Filter as expected
7 changed files with 150 additions and 122 deletions

View File

@ -1351,7 +1351,6 @@ class _defs_sculpt:
row = layout.row(align=True)
row.prop(props, "deform_axis")
layout.prop(props, "orientation", expand=False)
layout.prop(props, "use_face_sets")
if props.type == 'SURFACE_SMOOTH':
layout.prop(props, "surface_smooth_shape_preservation", expand=False)
layout.prop(props, "surface_smooth_current_vertex", expand=False)

View File

@ -2506,7 +2506,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
avg *= 1.0f - mask;
/* Auto-masking. */
avg *= SCULPT_automasking_factor_get(ss, vertex_index);
avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex_index);
return avg;
}
@ -3908,7 +3908,7 @@ 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, vd.index));
mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
copy_v3_v3(proxy[vd.i], final_disp);
@ -5760,7 +5760,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
SCULPT_automasking_init(sd, ob);
ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
}
}
@ -7764,7 +7764,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
}
if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
SCULPT_automasking_end(ob);
SCULPT_automasking_cache_free(ss->cache->automasking);
}
BKE_pbvh_node_color_buffer_free(ss->pbvh);

View File

@ -62,16 +62,30 @@
#include <math.h>
#include <stdlib.h>
AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
{
if (ss->cache) {
return ss->cache->automasking;
}
else if (ss->filter_cache) {
return ss->filter_cache->automasking;
}
return NULL;
}
bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
const Brush *br,
const eAutomasking_flag mode)
{
return br->automasking_flags & mode || sd->automasking_flags & mode;
if (br) {
return br->automasking_flags & mode || sd->automasking_flags & mode;
}
return sd->automasking_flags & mode;
}
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
{
if (SCULPT_stroke_is_dynamic_topology(ss, br)) {
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
return false;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
@ -91,10 +105,13 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
{
return sculpt->automasking_flags | brush->automasking_flags;
if (brush) {
return sculpt->automasking_flags | brush->automasking_flags;
}
return sculpt->automasking_flags;
}
static bool SCULPT_automasking_needs_cache(const Sculpt *sd, const Brush *brush)
static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
{
const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
@ -102,39 +119,39 @@ static bool SCULPT_automasking_needs_cache(const Sculpt *sd, const Brush *brush)
return true;
}
if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
return brush->automasking_boundary_edges_propagation_steps != 1;
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
}
if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
return brush->automasking_boundary_edges_propagation_steps != 1;
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
}
return false;
}
float SCULPT_automasking_factor_get(SculptSession *ss, int vert)
float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
{
if (!ss->cache) {
if (!automasking) {
return 1.0f;
}
/* If the cache is initialized with valid info, use the cache. This is used when the
* automasking information can't be computed in real time per vertex and needs to be
* initialized for the whole mesh when the stroke starts. */
if (ss->cache->automask_factor) {
return ss->cache->automask_factor[vert];
if (automasking->factor) {
return automasking->factor[vert];
}
if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
if (!SCULPT_vertex_has_face_set(ss, vert, ss->cache->automask_settings.initial_face_set)) {
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) {
return 0.0f;
}
}
if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
if (SCULPT_vertex_is_boundary(ss, vert)) {
return 0.0f;
}
}
if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
if (!SCULPT_vertex_has_unique_face_set(ss, vert)) {
return 0.0f;
}
@ -143,12 +160,14 @@ float SCULPT_automasking_factor_get(SculptSession *ss, int vert)
return 1.0f;
}
void SCULPT_automasking_end(Object *ob)
void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
{
SculptSession *ss = ob->sculpt;
if (ss->cache && ss->cache->automask_factor) {
MEM_freeN(ss->cache->automask_factor);
if (!automasking) {
return;
}
MEM_SAFE_FREE(automasking->factor);
MEM_SAFE_FREE(automasking);
}
static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
@ -189,10 +208,6 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
return NULL;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert(!"Topology masking: pmap missing");
return NULL;
@ -200,19 +215,20 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
ss->cache->automask_factor[i] = 0.0f;
automask_factor[i] = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
* the brush radius if the tool requires it. */
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
SCULPT_floodfill_add_active(sd, ob, ss, &flood, ss->cache->radius);
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
AutomaskFloodFillData fdata = {
.automask_factor = automask_factor,
.radius = ss->cache->radius,
.use_radius = sculpt_automasking_is_constrained_by_radius(brush),
.radius = radius,
.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
.symm = SCULPT_mesh_symmetry_xyz_get(ob),
};
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
@ -306,58 +322,60 @@ float *SCULPT_boundary_automasking_init(Object *ob,
return automask_factor;
}
static void SCULPT_stroke_automasking_settings_update(SculptSession *ss, Sculpt *sd, Brush *brush)
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
SculptSession *ss,
Sculpt *sd,
Brush *brush)
{
BLI_assert(ss->cache);
ss->cache->automask_settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
ss->cache->automask_settings.initial_face_set = SCULPT_active_face_set_get(ss);
automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
}
void SCULPT_automasking_init(Sculpt *sd, Object *ob)
AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
const int totvert = SCULPT_vertex_count_get(ss);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
return;
return NULL;
}
SCULPT_stroke_automasking_settings_update(ss, sd, brush);
AutomaskingCache *automasking = MEM_callocN(sizeof(AutomaskingCache), "automasking cache");
SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush);
SCULPT_boundary_info_ensure(ob);
if (!SCULPT_automasking_needs_cache(sd, brush)) {
return;
if (!SCULPT_automasking_needs_factors_cache(sd, brush)) {
return automasking;
}
ss->cache->automask_factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
for (int i = 0; i < totvert; i++) {
ss->cache->automask_factor[i] = 1.0f;
automasking->factor[i] = 1.0f;
}
const int boundary_propagation_steps = brush ?
brush->automasking_boundary_edges_propagation_steps :
1;
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_topology_automasking_init(sd, ob, ss->cache->automask_factor);
SCULPT_topology_automasking_init(sd, ob, automasking->factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
sculpt_face_sets_automasking_init(sd, ob, ss->cache->automask_factor);
sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
}
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,
brush->automasking_boundary_edges_propagation_steps,
ss->cache->automask_factor);
SCULPT_boundary_automasking_init(
ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(ob,
AUTOMASK_INIT_BOUNDARY_FACE_SETS,
brush->automasking_boundary_edges_propagation_steps,
ss->cache->automask_factor);
SCULPT_boundary_automasking_init(
ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
}
return automasking;
}

View File

@ -723,6 +723,8 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
return;
}
AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
float sim_location[3];
@ -743,7 +745,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(ss, vd.index);
SCULPT_automasking_factor_get(automasking, ss, vd.index);
madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v);
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
@ -775,6 +777,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
Brush *brush,
SculptClothSimulation *cloth_sim)
{
AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) {
for (int i = 0; i < cloth_sim->tot_length_constraints; i++) {
const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i];
@ -807,9 +812,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f);
const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) *
SCULPT_automasking_factor_get(ss, v1);
SCULPT_automasking_factor_get(automasking, ss, v1);
const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
SCULPT_automasking_factor_get(ss, v2);
SCULPT_automasking_factor_get(automasking, ss, v2);
float sim_location[3];
cloth_brush_simulation_location_get(ss, brush, sim_location);
@ -1354,6 +1359,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
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.index);
fade = 1.0f - fade;
float force[3] = {0.0f, 0.0f, 0.0f};
float disp[3], temp[3], transform[3][3];
@ -1498,6 +1504,8 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_undo_push_begin("Cloth filter");
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");

View File

@ -119,6 +119,10 @@ 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.index);
if (fade == 0.0f) {
continue;
}
copy_v3_v3(orig_color, orig_data.col);
@ -255,7 +259,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_RUNNING_MODAL;
}
static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@ -263,6 +267,17 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
int mode = RNA_enum_get(op->ptr, "type");
PBVH *pbvh = ob->sculpt->pbvh;
const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL);
if (use_automasking) {
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
* Filter Tool. */
float mouse[2];
SculptCursorGeometryInfo sgi;
mouse[0] = event->mval[0];
mouse[1] = event->mval[1];
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
}
/* Disable for multires and dyntopo for now */
if (!ss->pbvh) {
return OPERATOR_CANCELLED;
@ -282,14 +297,17 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
/* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
* earlier steps modifying the data. */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
bool needs_pmap = mode == COLOR_FILTER_SMOOTH;
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, true);
const bool needs_topology_info = mode == COLOR_FILTER_SMOOTH || use_automasking;
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, true);
if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) {
return OPERATOR_CANCELLED;
}
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR);
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);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;

View File

@ -179,12 +179,14 @@ void SCULPT_filter_cache_free(SculptSession *ss)
if (ss->filter_cache->cloth_sim) {
SCULPT_cloth_simulation_free(ss->filter_cache->cloth_sim);
}
if (ss->filter_cache->automasking) {
SCULPT_automasking_cache_free(ss->filter_cache->automasking);
}
MEM_SAFE_FREE(ss->filter_cache->nodes);
MEM_SAFE_FREE(ss->filter_cache->mask_update_it);
MEM_SAFE_FREE(ss->filter_cache->prev_mask);
MEM_SAFE_FREE(ss->filter_cache->normal_factor);
MEM_SAFE_FREE(ss->filter_cache->prev_face_set);
MEM_SAFE_FREE(ss->filter_cache->automask);
MEM_SAFE_FREE(ss->filter_cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(ss->filter_cache->sharpen_factor);
MEM_SAFE_FREE(ss->filter_cache->detail_directions);
@ -269,15 +271,15 @@ static EnumPropertyItem prop_mesh_filter_orientation_items[] = {
{0, NULL, 0, NULL, NULL},
};
static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets)
static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type)
{
return use_face_sets || ELEM(filter_type,
MESH_FILTER_SMOOTH,
MESH_FILTER_RELAX,
MESH_FILTER_RELAX_FACE_SETS,
MESH_FILTER_SURFACE_SMOOTH,
MESH_FILTER_ENHANCE_DETAILS,
MESH_FILTER_SHARPEN);
return ELEM(filter_type,
MESH_FILTER_SMOOTH,
MESH_FILTER_RELAX,
MESH_FILTER_RELAX_FACE_SETS,
MESH_FILTER_SURFACE_SMOOTH,
MESH_FILTER_ENHANCE_DETAILS,
MESH_FILTER_SHARPEN);
}
static void mesh_filter_task_cb(void *__restrict userdata,
@ -307,33 +309,16 @@ static void mesh_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.index);
if (fade == 0.0f) {
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
* laplacian_disp. This value is accessed from the vertex neighbors when deforming the
* vertices, so it is needed for all vertices even if they are not going to be displaced.
*/
continue;
}
if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) {
if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) {
/* Surface Smooth can't skip the loop for this vertex as it needs to calculate its
* laplacian_disp. This value is accessed from the vertex neighbors when deforming the
* vertices, so it is needed for all vertices even if they are not going to be displaced.
*/
if (filter_type == MESH_FILTER_SURFACE_SMOOTH) {
fade = 0.0f;
}
else {
continue;
}
}
/* Skip the edges of the face set when relaxing or smoothing.
* There is a relax face set option to relax the boundaries independently. */
if (filter_type == MESH_FILTER_RELAX) {
if (!SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
continue;
}
}
}
if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) {
copy_v3_v3(orig_co, vd.co);
}
@ -606,16 +591,11 @@ static void mesh_filter_surface_smooth_displace_task_cb(
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.index);
if (fade == 0.0f) {
continue;
}
if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) {
if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) {
continue;
}
}
SCULPT_surface_smooth_displace_step(ss,
vd.co,
ss->filter_cache->surface_smooth_laplacian_disp,
@ -634,7 +614,6 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type");
float filter_strength = RNA_float_get(op->ptr, "strength");
const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
SCULPT_filter_cache_free(ss);
@ -652,7 +631,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
SCULPT_vertex_random_access_ensure(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
SculptThreadedTaskData data = {
@ -700,15 +679,15 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
const eMeshFilterDeformAxis deform_axis = RNA_enum_get(op->ptr, "deform_axis");
const eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type");
const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets");
const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL);
const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking;
if (deform_axis == 0) {
/* All axis are disabled, so the filter is not going to produce any deformation. */
return OPERATOR_CANCELLED;
}
if (use_face_sets) {
if (use_automasking) {
/* Update the active face set manually as the paint cursor is not enabled when using the Mesh
* Filter Tool. */
float mouse[2];
@ -729,8 +708,8 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
FilterCache *filter_cache = ss->filter_cache;
filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) :
SCULPT_FACE_SET_NONE;
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
switch (filter_type) {
case MESH_FILTER_SURFACE_SMOOTH: {
@ -808,11 +787,6 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
SCULPT_FILTER_ORIENTATION_LOCAL,
"Orientation",
"Orientation of the axis to limit the filter displacement");
ot->prop = RNA_def_boolean(ot->srna,
"use_face_sets",
false,
"Use Face Sets",
"Apply the filter only to the Face Mask under the cursor");
/* Surface Smooth Mesh Filter properties. */
RNA_def_float(ot->srna,

View File

@ -36,6 +36,7 @@
#include "BKE_paint.h"
#include "BKE_pbvh.h"
struct AutomaskingCache;
struct KeyBlock;
struct Object;
struct SculptPoseIKChainSegment;
@ -329,10 +330,16 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
void SCULPT_pbvh_clear(Object *ob);
/* Automasking. */
float SCULPT_automasking_factor_get(SculptSession *ss, int vert);
float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking,
SculptSession *ss,
int vert);
void SCULPT_automasking_init(Sculpt *sd, Object *ob);
void SCULPT_automasking_end(Object *ob);
/* Returns the automasking cache depending on the active tool. Used for code that can run both for
* brushes and filter. */
struct AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss);
struct AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob);
void SCULPT_automasking_cache_free(struct AutomaskingCache *automasking);
bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
const Brush *br,
@ -826,6 +833,13 @@ typedef struct AutomaskingSettings {
int initial_face_set;
} AutomaskingSettings;
typedef struct AutomaskingCache {
AutomaskingSettings settings;
/* Precomputed automask factor indexed by vertex, owned by the automasking system and initialized
* in SCULPT_automasking_cache_init when needed. */
float *factor;
} AutomaskingCache;
typedef struct StrokeCache {
/* Invariants */
float initial_radius;
@ -966,10 +980,7 @@ typedef struct StrokeCache {
float gravity_direction[3];
/* Automasking. */
AutomaskingSettings automask_settings;
/* Precomputed automask factor indexed by vertex, owned by the automasking system and initialized
* in SCULPT_automasking_init when needed. */
float *automask_factor;
AutomaskingCache *automasking;
float stroke_local_mat[4][4];
float multiplane_scrape_angle;
@ -1041,13 +1052,13 @@ typedef struct FilterCache {
float *prev_mask;
float mask_expand_initial_co[3];
/* Used to prevent undesired results on certain mesh filters. */
float *automask;
int new_face_set;
int *prev_face_set;
int active_face_set;
/* Automasking. */
AutomaskingCache *automasking;
} FilterCache;
void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,