Basic mask expand operation working

This commit is contained in:
Pablo Dobarro 2021-01-14 01:05:11 +01:00
parent df9a5825e1
commit a9f6ab7357
3 changed files with 232 additions and 390 deletions

View File

@ -10561,6 +10561,8 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_project_lasso_gesture);
WM_operatortype_append(SCULPT_OT_project_box_gesture);
WM_operatortype_append(SCULPT_OT_expand);
WM_operatortype_append(SCULPT_OT_sample_color);
WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);

View File

@ -74,25 +74,149 @@ static EnumPropertyItem prop_sculpt_expand_faloff_type_items[] = {
{SCULPT_EXPAND_FALLOFF_TOPOLOGY, "TOPOLOGY", 0, "Topology", ""},
{SCULPT_EXPAND_FALLOFF_NORMALS, "NORMALS", 0, "Normals", ""},
{SCULPT_EXPAND_FALLOFF_SPHERICAL, "SPHERICAL", 0, "Spherical", ""},
{SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY, "BOUNDARY_TOPOLOGY`", 0, "Boundary Topology", ""},
{0, NULL, 0, NULL, NULL},
};
static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int vertex) {
return SCULPT_geodesic_from_vertex_and_symm(sd, ob, vertex, FLT_MAX);
}
static float *sculpt_expand_topology_falloff_create() {
return NULL;
typedef struct ExpandFloodFillData {
float original_normal[3];
float edge_sensitivity;
float *dists;
float *edge_factor;
} ExpandFloodFillData;
static bool mask_expand_topology_floodfill_cb(
SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata)
{
ExpandFloodFillData *data = userdata;
if (!is_duplicate) {
const int to_it = data->dists[from_v] + 1;
data->dists[to_v] = to_it;
}
else {
data->dists[to_v] = data->dists[from_v];
}
return true;
}
static float *sculpt_expand_spherical_falloff_create() {
return NULL;
static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int vertex) {
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_malloc_arrayN(sizeof (float), totvert, "spherical dist");
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, vertex, FLT_MAX);
ExpandFloodFillData fdata;
fdata.dists = dists;
SCULPT_floodfill_execute(ss, &flood, mask_expand_topology_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
return dists;
}
static float *sculpt_expand_normal_falloff_create() {
return NULL;
static bool mask_expand_normals_floodfill_cb(
SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
{
ExpandFloodFillData *data = userdata;
if (!is_duplicate) {
float current_normal[3], prev_normal[3];
SCULPT_vertex_normal_get(ss, to_v, current_normal);
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
const float from_edge_factor = data->edge_factor[from_v];
data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) *
from_edge_factor;
data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) *
powf(from_edge_factor, data->edge_sensitivity);
CLAMP(data->dists[to_v], 0.0f, 1.0f);
}
else {
/* PBVH_GRIDS duplicate handling. */
data->edge_factor[to_v] = data->edge_factor[from_v];
data->dists[to_v] = data->dists[from_v];
}
return true;
}
static float *sculpt_expand_normal_falloff_create(Sculpt *sd, Object *ob, const int vertex, const float edge_sensitivity) {
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_malloc_arrayN(sizeof (float), totvert, "normal dist");
float *edge_factor = MEM_callocN(sizeof(float) * totvert,
"mask edge factor");
for (int i = 0; i < totvert; i++) {
edge_factor[i] = 1.0f;
}
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, vertex, FLT_MAX);
ExpandFloodFillData fdata;
fdata.dists = dists;
fdata.edge_factor = edge_factor;
fdata.edge_sensitivity = edge_sensitivity;
SCULPT_vertex_normal_get(ss, vertex, fdata.original_normal);
SCULPT_floodfill_execute(ss, &flood, mask_expand_topology_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < totvert; i++) {
float avg = 0.0f;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
avg += dists[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
dists[i] = avg / ni.size;
}
}
MEM_SAFE_FREE(edge_factor);
return dists;
}
static float *sculpt_expand_spherical_falloff_create(Sculpt *sd, Object *ob, const int vertex) {
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_malloc_arrayN(sizeof (float), totvert, "spherical dist");
for (int i = 0; i < totvert; i++) {
dists[i] = FLT_MAX;
}
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char symm_it = 0; symm_it <= symm; symm_it++) {
if (SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
int v = -1;
if (symm_it == 0) {
v = vertex;
}
else {
float location[3];
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), symm_it);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
if (v != -1) {
const float *co = SCULPT_vertex_co_get(ss, v);
for (int i = 0; i < totvert; i++) {
dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i)));
}
}
}
}
return dists;
}
static void sculpt_expand_update_max_falloff_factor(SculptSession *ss, ExpandCache *expand_cache) {
@ -110,19 +234,22 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(ExpandCach
return;
}
if (expand_cache->falloff_factor) {
MEM_freeN(expand_cache->falloff_factor);
}
switch (falloff_type) {
case SCULPT_EXPAND_FALLOFF_GEODESICS:
expand_cache->falloff_factor = sculpt_expand_geodesic_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_TOPOLOGY:
expand_cache->falloff_factor = sculpt_expand_topology_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_NORMALS:
expand_cache->falloff_factor = sculpt_expand_normal_falloff_create(sd, ob, vertex, 300.0f);
break;
case SCULPT_EXPAND_FALLOFF_SPHERICAL:
expand_cache->falloff_factor = sculpt_expand_spherical_falloff_create(sd, ob, vertex);
break;
case SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY:
@ -135,15 +262,11 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(ExpandCach
sculpt_expand_update_max_falloff_factor(ss, expand_cache);
}
float *sculpt_expand_falloff_factors_update() {
return NULL;
}
void sculpt_expand_cache_free(ExpandCache *expand_cache) {
MEM_SAFE_FREE(expand_cache->nodes);
MEM_SAFE_FREE(expand_cache->falloff_factor);
MEM_SAFE_FREE(expand_cache->initial_mask);
MEM_SAFE_FREE(expand_cache->initial_face_sets);
MEM_SAFE_FREE(expand_cache);
}
@ -187,263 +310,96 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op)
static void sculpt_expand_cancel(bContext *C, wmOperator *op) {
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
sculpt_expand_cache_free(ss->expand_cache);
ED_workspace_status_text(C, NULL);
}
static void sculpt_expand_task_cb(void *__restrict userdata,
static bool sculpt_expand_state_get(ExpandCache *expand_cache, int i) {
bool enabled = expand_cache->falloff_factor[i] <= expand_cache->active_factor;
if (expand_cache->invert) {
enabled = !enabled;
}
return enabled;
}
static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
PBVHVertexIter vd;
int update_it = data->mask_expand_update_it;
ExpandCache *expand_cache = ss->expand_cache;
bool any_changed = false;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL)
{
int vi = vd.index;
float final_mask = *vd.mask;
if (data->mask_expand_use_normals) {
if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] <
ss->filter_cache->normal_factor[vd.index]) {
final_mask = 1.0f;
const float initial_mask = *vd.mask;
const bool enabled = sculpt_expand_state_get(expand_cache, vd.index);
if (enabled){
if (expand_cache->mask_preserve_previous) {
*vd.mask = max_ff(initial_mask, expand_cache->initial_mask[vd.index]);
}
else {
final_mask = 0.0f;
*vd.mask = 1.0f;
}
}
else {
if (ss->filter_cache->mask_update_it[vi] <= update_it &&
ss->filter_cache->mask_update_it[vi] != 0) {
final_mask = 1.0f;
}
else {
final_mask = 0.0f;
}
}
if (data->mask_expand_create_face_set) {
if (final_mask == 1.0f) {
SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set);
}
BKE_pbvh_node_mark_redraw(node);
}
else {
if (data->mask_expand_keep_prev_mask) {
final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask);
*vd.mask = 0.0f;
}
if (data->mask_expand_invert_mask) {
final_mask = 1.0f - final_mask;
}
if (*vd.mask != final_mask) {
if (*vd.mask != initial_mask) {
any_changed = true;
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
*vd.mask = final_mask;
BKE_pbvh_node_mark_update_mask(node);
}
}
}
BKE_pbvh_vertex_iter_end;
}
static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ARegion *region = CTX_wm_region(C);
float prevclick_f[2];
copy_v2_v2(prevclick_f, op->customdata);
const int prevclick[2] = {(int)prevclick_f[0], (int)prevclick_f[1]};
int len = (int)len_v2v2_int(prevclick, event->mval);
len = abs(len);
int mask_speed = RNA_int_get(op->ptr, "mask_speed");
int mask_expand_update_it = len / mask_speed;
mask_expand_update_it = mask_expand_update_it + 1;
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
if (RNA_boolean_get(op->ptr, "use_cursor")) {
SculptCursorGeometryInfo sgi;
float mouse[2];
mouse[0] = event->mval[0];
mouse[1] = event->mval[1];
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
/* The cursor is over the mesh, get the update iteration from the updated active vertex. */
mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)];
}
else {
/* When the cursor is outside the mesh, affect the entire connected component. */
mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1;
}
if (any_changed) {
BKE_pbvh_node_mark_update_mask(node);
}
if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
(event->type == RIGHTMOUSE && event->val == KM_PRESS)) {
/* Returning OPERATOR_CANCELLED will leak memory due to not finishing
* undo. Better solution could be to make paint_mesh_restore_co work
* for this case. */
sculpt_mask_expand_cancel(C, op);
return OPERATOR_FINISHED;
}
if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
(event->type == EVT_RETKEY && event->val == KM_PRESS) ||
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
/* Smooth iterations. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
SCULPT_mask_filter_smooth_apply(
sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations);
/* Pivot position. */
if (RNA_boolean_get(op->ptr, "update_pivot")) {
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
const float threshold = 0.2f;
float avg[3];
int total = 0;
zero_v3(avg);
for (int n = 0; n < ss->filter_cache->totnode; n++) {
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, ss->filter_cache->nodes[n], vd, PBVH_ITER_UNIQUE)
{
const float mask = (vd.mask) ? *vd.mask : 0.0f;
if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) {
if (SCULPT_check_vertex_pivot_symmetry(
vd.co, ss->filter_cache->mask_expand_initial_co, symm)) {
add_v3_v3(avg, vd.co);
total++;
}
}
}
BKE_pbvh_vertex_iter_end;
}
if (total > 0) {
mul_v3_fl(avg, 1.0f / total);
copy_v3_v3(ss->pivot_pos, avg);
}
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
}
MEM_freeN(op->customdata);
for (int i = 0; i < ss->filter_cache->totnode; i++) {
BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]);
}
SCULPT_filter_cache_free(ss);
SCULPT_undo_push_end();
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
}
/* When pressing Ctrl, expand directly to the max number of iterations. This allows to flood fill
* mask and face sets by connectivity directly. */
if (event->ctrl) {
mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1;
}
if (!ELEM(event->type, MOUSEMOVE, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
return OPERATOR_RUNNING_MODAL;
}
if (mask_expand_update_it == ss->filter_cache->mask_update_current_it) {
ED_region_tag_redraw(region);
return OPERATOR_RUNNING_MODAL;
}
if (mask_expand_update_it < ss->filter_cache->mask_update_last_it) {
if (create_face_set) {
for (int i = 0; i < ss->totfaces; i++) {
ss->face_sets[i] = ss->filter_cache->prev_face_set[i];
}
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
.mask_expand_update_it = mask_expand_update_it,
.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"),
.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"),
.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"),
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
ss->filter_cache->mask_update_current_it = mask_expand_update_it;
}
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
return OPERATOR_RUNNING_MODAL;
}
typedef struct MaskExpandFloodFillData {
float original_normal[3];
float edge_sensitivity;
bool use_normals;
} MaskExpandFloodFillData;
static bool mask_expand_floodfill_cb(
SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
{
MaskExpandFloodFillData *data = userdata;
if (!is_duplicate) {
int to_it = ss->filter_cache->mask_update_it[from_v] + 1;
ss->filter_cache->mask_update_it[to_v] = to_it;
if (to_it > ss->filter_cache->mask_update_last_it) {
ss->filter_cache->mask_update_last_it = to_it;
}
if (data->use_normals) {
float current_normal[3], prev_normal[3];
SCULPT_vertex_normal_get(ss, to_v, current_normal);
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
const float from_edge_factor = ss->filter_cache->edge_factor[from_v];
ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) *
from_edge_factor;
ss->filter_cache->normal_factor[to_v] = dot_v3v3(data->original_normal, current_normal) *
powf(from_edge_factor, data->edge_sensitivity);
CLAMP(ss->filter_cache->normal_factor[to_v], 0.0f, 1.0f);
}
}
else {
/* PBVH_GRIDS duplicate handling. */
ss->filter_cache->mask_update_it[to_v] = ss->filter_cache->mask_update_it[from_v];
if (data->use_normals) {
ss->filter_cache->edge_factor[to_v] = ss->filter_cache->edge_factor[from_v];
ss->filter_cache->normal_factor[to_v] = ss->filter_cache->normal_factor[from_v];
}
}
return true;
}
#define SCULPT_EXPAND_VERTEX_NONE -1
static void sculpt_expand_flush_updates(bContext *C) {
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
for (int i = 0; i < ss->expand_cache->totnode; i++) {
BKE_pbvh_node_mark_redraw(ss->expand_cache->nodes[i]);
}
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
}
static void sculpt_expand_initial_state_store(Object *ob, ExpandCache *expand_cache) {
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
const int totface = ss->totfaces;
expand_cache->initial_mask = MEM_malloc_arrayN(totvert, sizeof (float), "initial mask");
for (int i = 0; i < totvert; i++) {
expand_cache->initial_mask[i] = SCULPT_vertex_mask_get(ss, i);
}
expand_cache->initial_face_sets = MEM_malloc_arrayN(totvert, sizeof (int), "initial face set");
for (int i = 0; i < totface; i++) {
expand_cache->initial_face_sets[i] = ss->face_sets[i];
}
}
static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex) {
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ExpandCache *expand_cache = ss->expand_cache;
/* Update the active factor in the cache. */
@ -454,12 +410,18 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
expand_cache->active_factor = expand_cache->falloff_factor[vertex];
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = expand_cache->nodes,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, expand_cache->totnode);
BLI_task_parallel_range(0, expand_cache->totnode, &data, sculpt_expand_mask_update_task_cb, &settings);
sculpt_expand_flush_updates(C);
sculpt_expand_flush_updates(C);
}
@ -477,20 +439,21 @@ static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, c
static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, event);
sculpt_expand_update_for_vertex(C, ob, target_expand_vertex);
if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
(event->type == EVT_RETKEY && event->val == KM_PRESS) ||
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
sculpt_expand_cache_free(ss->expand_cache);
SCULPT_undo_push_end();
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
}
return OPERATOR_RUNNING_MODAL;
}
@ -501,17 +464,15 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
/* Update object. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_ensure(ss);
SCULPT_undo_push_begin(ob, "expand");
/* Create the Expand Cache. */
ss->expand_cache = MEM_callocN(sizeof(ExpandCache), "expand cache");
/* Set the initial element for expand. */
int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, event);
if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) {
@ -520,154 +481,23 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
}
ss->expand_cache->initial_active_vertex = initial_vertex;
/* Cache PBVH nodes. */
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &ss->expand_cache->nodes, &ss->expand_cache->totnode);
/* Store initial state. */
sculpt_expand_initial_state_store(ob, ss->expand_cache);
/* Initialize the factors. */
sculpt_expand_falloff_factors_from_vertex_and_symm_create(ss->expand_cache, sd, ob, initial_vertex, SCULPT_EXPAND_FALLOFF_GEODESICS);
/* Initial update. */
sculpt_expand_update_for_vertex(C, ob, initial_vertex);
const char *status_str = TIP_(
"Move the mouse to expand from the active vertex. LMB: confirm, ESC/RMB: "
"cancel");
ED_workspace_status_text(C, status_str);
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
const bool use_normals = RNA_boolean_get(op->ptr, "use_normals");
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
SculptCursorGeometryInfo sgi;
float mouse[2];
mouse[0] = event->mval[0];
mouse[1] = event->mval[1];
SCULPT_vertex_random_access_ensure(ss);
op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position");
copy_v2_v2(op->customdata, mouse);
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
int vertex_count = SCULPT_vertex_count_get(ss);
ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache");
BKE_pbvh_search_gather(pbvh, NULL, NULL, &ss->filter_cache->nodes, &ss->filter_cache->totnode);
SCULPT_undo_push_begin(ob, "Mask Expand");
if (create_face_set) {
SCULPT_undo_push_node(ob, ss->filter_cache->nodes[0], SCULPT_UNDO_FACE_SETS);
for (int i = 0; i < ss->filter_cache->totnode; i++) {
BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]);
}
}
else {
for (int i = 0; i < ss->filter_cache->totnode; i++) {
SCULPT_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]);
}
}
ss->filter_cache->mask_update_it = MEM_callocN(sizeof(int) * vertex_count,
"mask update iteration");
if (use_normals) {
ss->filter_cache->normal_factor = MEM_callocN(sizeof(float) * vertex_count,
"mask update normal factor");
ss->filter_cache->edge_factor = MEM_callocN(sizeof(float) * vertex_count,
"mask update normal factor");
for (int i = 0; i < vertex_count; i++) {
ss->filter_cache->edge_factor[i] = 1.0f;
}
}
if (create_face_set) {
ss->filter_cache->prev_face_set = MEM_callocN(sizeof(float) * ss->totfaces, "prev face mask");
for (int i = 0; i < ss->totfaces; i++) {
ss->filter_cache->prev_face_set[i] = ss->face_sets[i];
}
ss->filter_cache->new_face_set = SCULPT_face_set_next_available_get(ss);
}
else {
ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask");
for (int i = 0; i < vertex_count; i++) {
ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i);
}
}
ss->filter_cache->mask_update_last_it = 1;
ss->filter_cache->mask_update_current_it = 1;
ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0;
copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss));
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
SCULPT_floodfill_add_active(sd, ob, ss, &flood, FLT_MAX);
MaskExpandFloodFillData fdata = {
.use_normals = use_normals,
.edge_sensitivity = RNA_int_get(op->ptr, "edge_sensitivity"),
};
SCULPT_active_vertex_normal_get(ss, fdata.original_normal);
SCULPT_floodfill_execute(ss, &flood, mask_expand_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
if (use_normals) {
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < vertex_count; i++) {
float avg = 0.0f;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
avg += ss->filter_cache->normal_factor[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
ss->filter_cache->normal_factor[i] = avg / ni.size;
}
}
MEM_SAFE_FREE(ss->filter_cache->edge_factor);
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
.mask_expand_update_it = 0,
.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"),
.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"),
.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"),
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
const char *status_str = TIP_(
"Move the mouse to expand the mask from the active vertex. LMB: confirm mask, ESC/RMB: "
"cancel");
ED_workspace_status_text(C, status_str);
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}

View File

@ -1171,6 +1171,13 @@ typedef struct ExpandCache {
int initial_active_vertex;
float active_factor;
PBVHNode **nodes;
int totnode;
bool invert;
bool mask_preserve_previous;
eSculptExpandTargetType target;
float *initial_mask;
int *initial_face_sets;
@ -1304,6 +1311,9 @@ void SCULPT_OT_dirty_mask(struct wmOperatorType *ot);
/* Mask and Face Sets Expand. */
void SCULPT_OT_mask_expand(struct wmOperatorType *ot);
/* Expand. */
void SCULPT_OT_expand(struct wmOperatorType *ot);
/* Detail size. */
void SCULPT_OT_detail_flood_fill(struct wmOperatorType *ot);
void SCULPT_OT_sample_detail_size(struct wmOperatorType *ot);