Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_filter_mask.c
| Show First 20 Lines • Show All 297 Lines • ▼ Show 20 Lines | RNA_def_int(ot->srna, | ||||
| 100); | 100); | ||||
| RNA_def_boolean( | RNA_def_boolean( | ||||
| ot->srna, | ot->srna, | ||||
| "auto_iteration_count", | "auto_iteration_count", | ||||
| true, | true, | ||||
| "Auto Iteration Count", | "Auto Iteration Count", | ||||
| "Use a automatic number of iterations based on the number of vertices of the sculpt"); | "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->vertex, ni) { | |||||
| float normalized[3]; | |||||
| sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), 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 dot = dot_v3v3(avg, vd->no ? vd->no : vd->fno); | |||||
| 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); | |||||
| } | |||||
| 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, false); | |||||
| SCULPT_vertex_random_access_ensure(ss); | |||||
| if (!ob->sculpt->pmap) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); | |||||
| SCULPT_undo_push_begin(ob, op); | |||||
| 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, | |||||
| }; | |||||
| TaskParallelSettings settings; | |||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | |||||
| settings.func_reduce = dirty_mask_compute_range_reduce; | |||||
| settings.userdata_chunk = ⦥ | |||||
| settings.userdata_chunk_size = sizeof(DirtyMaskRangeData); | |||||
| BLI_task_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); | |||||
| data.dirty_mask_min = range.min; | |||||
| data.dirty_mask_max = range.max; | |||||
| BLI_task_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(ob); | |||||
| ED_region_tag_redraw(region); | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| 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"); | |||||
| } | |||||