diff options
author | Pablo Dobarro <pablodp606@gmail.com> | 2020-04-03 22:46:08 +0300 |
---|---|---|
committer | Pablo Dobarro <pablodp606@gmail.com> | 2020-04-03 22:46:08 +0300 |
commit | 17931f3b5125528e3d763ae8a80eae5e4730dcc3 (patch) | |
tree | f0917bab236dd01a2c0929492bfa0b013f956e80 /source/blender/editors/sculpt_paint/sculpt.c | |
parent | f2f30db98dacf2821fce3389952798c597bffe11 (diff) |
Cleanup: Move Mask Filter and Mask Expand to their own files
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 893 |
1 files changed, 2 insertions, 891 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index a53c9aed36d..5bd0f3f2f48 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -153,7 +153,7 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index) return NULL; } -static void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -209,7 +209,7 @@ const float *SCULPT_active_vertex_co_get(SculptSession *ss) return SCULPT_vertex_co_get(ss, SCULPT_active_vertex_get(ss)); } -static void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]) +void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]) { SCULPT_vertex_normal_get(ss, SCULPT_active_vertex_get(ss), normal); } @@ -8463,895 +8463,6 @@ static void SCULPT_OT_set_detail_size(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -typedef enum eSculptMaskFilterTypes { - MASK_FILTER_SMOOTH = 0, - MASK_FILTER_SHARPEN = 1, - MASK_FILTER_GROW = 2, - MASK_FILTER_SHRINK = 3, - MASK_FILTER_CONTRAST_INCREASE = 5, - MASK_FILTER_CONTRAST_DECREASE = 6, -} eSculptMaskFilterTypes; - -static EnumPropertyItem prop_mask_filter_types[] = { - {MASK_FILTER_SMOOTH, "SMOOTH", 0, "Smooth Mask", "Smooth mask"}, - {MASK_FILTER_SHARPEN, "SHARPEN", 0, "Sharpen Mask", "Sharpen mask"}, - {MASK_FILTER_GROW, "GROW", 0, "Grow Mask", "Grow mask"}, - {MASK_FILTER_SHRINK, "SHRINK", 0, "Shrink Mask", "Shrink mask"}, - {MASK_FILTER_CONTRAST_INCREASE, - "CONTRAST_INCREASE", - 0, - "Increase contrast", - "Increase the contrast of the paint mask"}, - {MASK_FILTER_CONTRAST_DECREASE, - "CONTRAST_DECREASE", - 0, - "Decrease contrast", - "Decrease the contrast of the paint mask"}, - {0, NULL, 0, NULL, NULL}, -}; - -static void mask_filter_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]; - bool update = false; - - const int mode = data->filter_type; - float contrast = 0.0f; - - PBVHVertexIter vd; - - if (mode == MASK_FILTER_CONTRAST_INCREASE) { - contrast = 0.1f; - } - - if (mode == MASK_FILTER_CONTRAST_DECREASE) { - contrast = -0.1f; - } - - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { - float delta, gain, offset, max, min; - float prev_val = *vd.mask; - SculptVertexNeighborIter ni; - switch (mode) { - case MASK_FILTER_SMOOTH: - case MASK_FILTER_SHARPEN: { - float val = SCULPT_neighbor_mask_average(ss, vd.index); - - val -= *vd.mask; - - if (mode == MASK_FILTER_SMOOTH) { - *vd.mask += val; - } - else if (mode == MASK_FILTER_SHARPEN) { - if (*vd.mask > 0.5f) { - *vd.mask += 0.05f; - } - else { - *vd.mask -= 0.05f; - } - *vd.mask += val / 2.0f; - } - break; - } - case MASK_FILTER_GROW: - max = 0.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { - float vmask_f = data->prev_mask[ni.index]; - if (vmask_f > max) { - max = vmask_f; - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - *vd.mask = max; - break; - case MASK_FILTER_SHRINK: - min = 1.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { - float vmask_f = data->prev_mask[ni.index]; - if (vmask_f < min) { - min = vmask_f; - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - *vd.mask = min; - break; - case MASK_FILTER_CONTRAST_INCREASE: - case MASK_FILTER_CONTRAST_DECREASE: - delta = contrast / 2.0f; - gain = 1.0f - delta * 2.0f; - if (contrast > 0) { - gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON); - offset = gain * (-delta); - } - else { - delta *= -1.0f; - offset = gain * (delta); - } - *vd.mask = gain * (*vd.mask) + offset; - break; - } - CLAMP(*vd.mask, 0.0f, 1.0f); - if (*vd.mask != prev_val) { - update = true; - } - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; - - if (update) { - BKE_pbvh_node_mark_update_mask(node); - } -} - -static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - PBVH *pbvh = ob->sculpt->pbvh; - PBVHNode **nodes; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int totnode; - int filter_type = RNA_enum_get(op->ptr, "filter_type"); - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); - - SCULPT_vertex_random_access_init(ss); - - if (!ob->sculpt->pmap) { - return OPERATOR_CANCELLED; - } - - int num_verts = SCULPT_vertex_count_get(ss); - - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin("Mask filter"); - - for (int i = 0; i < totnode; i++) { - SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); - } - - float *prev_mask = NULL; - int iterations = RNA_int_get(op->ptr, "iterations"); - - /* Auto iteration count calculates the number of iteration based on the vertices of the mesh to - * avoid adding an unnecessary amount of undo steps when using the operator from a shortcut. - * One iteration per 50000 vertices in the mesh should be fine in most cases. - * Maybe we want this to be configurable. */ - if (RNA_boolean_get(op->ptr, "auto_iteration_count")) { - iterations = (int)(num_verts / 50000.0f) + 1; - } - - for (int i = 0; i < iterations; i++) { - if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { - prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask"); - for (int j = 0; j < num_verts; j++) { - prev_mask[j] = SCULPT_vertex_mask_get(ss, j); - } - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = nodes, - .filter_type = filter_type, - .prev_mask = prev_mask, - }; - - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); - - if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { - MEM_freeN(prev_mask); - } - } - - MEM_SAFE_FREE(nodes); - - SCULPT_undo_push_end(); - - ED_region_tag_redraw(region); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_mask_filter(struct wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Mask Filter"; - ot->idname = "SCULPT_OT_mask_filter"; - ot->description = "Applies a filter to modify the current mask"; - - /* API callbacks. */ - ot->exec = sculpt_mask_filter_exec; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER; - - /* RNA. */ - RNA_def_enum(ot->srna, - "filter_type", - prop_mask_filter_types, - MASK_FILTER_SMOOTH, - "Type", - "Filter that is going to be applied to the mask"); - RNA_def_int(ot->srna, - "iterations", - 1, - 1, - 100, - "Iterations", - "Number of times that the filter is going to be applied", - 1, - 100); - RNA_def_boolean( - ot->srna, - "auto_iteration_count", - false, - "Auto Iteration Count", - "Use a automatic number of iterations based on the number of vertices of the sculpt"); -} - -static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) -{ - int total = 0; - float avg[3]; - zero_v3(avg); - - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { - float normalized[3]; - sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co); - normalize_v3(normalized); - add_v3_v3(avg, normalized); - total++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - float normal[3]; - if (vd->no) { - normal_short_to_float_v3(normal, vd->no); - } - else { - copy_v3_v3(normal, vd->fno); - } - float dot = dot_v3v3(avg, normal); - float angle = max_ff(saacosf(dot), 0.0f); - return angle; - } - return 0.0f; -} - -typedef struct DirtyMaskRangeData { - float min, max; -} DirtyMaskRangeData; - -static void dirty_mask_compute_range_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - PBVHNode *node = data->nodes[i]; - DirtyMaskRangeData *range = tls->userdata_chunk; - PBVHVertexIter vd; - - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { - float dirty_mask = neighbor_dirty_mask(ss, &vd); - range->min = min_ff(dirty_mask, range->min); - range->max = max_ff(dirty_mask, range->max); - } - BKE_pbvh_vertex_iter_end; -} - -static void dirty_mask_compute_range_reduce(const void *__restrict UNUSED(userdata), - void *__restrict chunk_join, - void *__restrict chunk) -{ - DirtyMaskRangeData *join = chunk_join; - DirtyMaskRangeData *range = chunk; - join->min = min_ff(range->min, join->min); - join->max = max_ff(range->max, join->max); -} - -static void dirty_mask_apply_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; - - const bool dirty_only = data->dirty_mask_dirty_only; - const float min = data->dirty_mask_min; - const float max = data->dirty_mask_max; - - float range = max - min; - if (range < 0.0001f) { - range = 0.0f; - } - else { - range = 1.0f / range; - } - - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { - float dirty_mask = neighbor_dirty_mask(ss, &vd); - float mask = *vd.mask + (1.0f - ((dirty_mask - min) * range)); - if (dirty_only) { - mask = fminf(mask, 0.5f) * 2.0f; - } - *vd.mask = CLAMPIS(mask, 0.0f, 1.0f); - - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_update_mask(node); -} - -static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) -{ - ARegion *region = CTX_wm_region(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - PBVH *pbvh = ob->sculpt->pbvh; - PBVHNode **nodes; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int totnode; - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); - - SCULPT_vertex_random_access_init(ss); - - if (!ob->sculpt->pmap) { - return OPERATOR_CANCELLED; - } - - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - SCULPT_undo_push_begin("Dirty Mask"); - - for (int i = 0; i < totnode; i++) { - SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = nodes, - .dirty_mask_dirty_only = RNA_boolean_get(op->ptr, "dirty_only"), - }; - DirtyMaskRangeData range = { - .min = FLT_MAX, - .max = -FLT_MAX, - }; - - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - - settings.func_reduce = dirty_mask_compute_range_reduce; - settings.userdata_chunk = ⦥ - settings.userdata_chunk_size = sizeof(DirtyMaskRangeData); - - BKE_pbvh_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); - data.dirty_mask_min = range.min; - data.dirty_mask_max = range.max; - BKE_pbvh_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings); - - MEM_SAFE_FREE(nodes); - - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); - - SCULPT_undo_push_end(); - - ED_region_tag_redraw(region); - - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_dirty_mask(struct wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Dirty Mask"; - ot->idname = "SCULPT_OT_dirty_mask"; - ot->description = "Generates a mask based on the geometry cavity and pointiness"; - - /* API callbacks. */ - ot->exec = sculpt_dirty_mask_exec; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER; - - /* RNA. */ - RNA_def_boolean( - ot->srna, "dirty_only", false, "Dirty Only", "Don't calculate cleans for convex areas"); -} - -static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set"); - - MEM_freeN(op->customdata); - - for (int n = 0; n < ss->filter_cache->totnode; n++) { - PBVHNode *node = ss->filter_cache->nodes[n]; - if (create_face_set) { - for (int i = 0; i < ss->totfaces; i++) { - ss->face_sets[i] = ss->filter_cache->prev_face_set[i]; - } - } - else { - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { - *vd.mask = ss->filter_cache->prev_mask[vd.index]; - } - BKE_pbvh_vertex_iter_end; - } - - BKE_pbvh_node_mark_redraw(node); - } - - if (!create_face_set) { - SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); - } - SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); - SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); - ED_workspace_status_text(C, NULL); -} - -static void sculpt_expand_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; - - 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; - } - else { - final_mask = 0.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); - } - - if (data->mask_expand_invert_mask) { - final_mask = 1.0f - final_mask; - } - - if (*vd.mask != final_mask) { - 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); - 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]; - SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); - mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)]; - } - - 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. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = ss->filter_cache->nodes, - .filter_type = MASK_FILTER_SMOOTH, - }; - - int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations"); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false); - for (int i = 0; i < smooth_iterations; i++) { - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings( - &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, mask_filter_task_cb, &settings); - } - - /* Pivot position. */ - if (RNA_boolean_get(op->ptr, "update_pivot")) { - const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - 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"), - }; - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings( - &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_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; -} - -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_init(ss); - - op->customdata = MEM_mallocN(2 * sizeof(float), "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); - - 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("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"), - }; - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings( - &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_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; -} - -static void SCULPT_OT_mask_expand(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Mask Expand"; - ot->idname = "SCULPT_OT_mask_expand"; - ot->description = "Expands a mask from the initial active vertex under the cursor"; - - /* API callbacks. */ - ot->invoke = sculpt_mask_expand_invoke; - ot->modal = sculpt_mask_expand_modal; - ot->cancel = sculpt_mask_expand_cancel; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_boolean(ot->srna, "invert", true, "Invert", "Invert the new mask"); - ot->prop = RNA_def_boolean( - ot->srna, "use_cursor", true, "Use Cursor", "Expand the mask to the cursor position"); - ot->prop = RNA_def_boolean(ot->srna, - "update_pivot", - true, - "Update Pivot Position", - "Set the pivot position to the mask border after creating the mask"); - ot->prop = RNA_def_int(ot->srna, "smooth_iterations", 2, 0, 10, "Smooth iterations", "", 0, 10); - ot->prop = RNA_def_int(ot->srna, "mask_speed", 5, 1, 10, "Mask speed", "", 1, 10); - - ot->prop = RNA_def_boolean(ot->srna, - "use_normals", - true, - "Use Normals", - "Generate the mask using the normals and curvature of the model"); - ot->prop = RNA_def_boolean(ot->srna, - "keep_previous_mask", - false, - "Keep Previous Mask", - "Generate the new mask on top of the current one"); - ot->prop = RNA_def_int(ot->srna, - "edge_sensitivity", - 300, - 0, - 2000, - "Edge Detection Sensitivity", - "Sensitivity for expanding the mask across sculpted sharp edges when " - "using normals to generate the mask", - 0, - 2000); - ot->prop = RNA_def_boolean(ot->srna, - "create_face_set", - false, - "Expand Face Mask", - "Expand a new Face Mask instead of the sculpt mask"); -} - void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); |