Blender  V3.3
sculpt_filter_mask.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2020 Blender Foundation. All rights reserved. */
3 
8 #include "MEM_guardedalloc.h"
9 
10 #include "BLI_blenlib.h"
11 #include "BLI_hash.h"
12 #include "BLI_math.h"
13 #include "BLI_task.h"
14 
15 #include "DNA_mesh_types.h"
16 #include "DNA_meshdata_types.h"
17 
18 #include "BKE_brush.h"
19 #include "BKE_context.h"
20 #include "BKE_mesh.h"
21 #include "BKE_mesh_mapping.h"
22 #include "BKE_object.h"
23 #include "BKE_paint.h"
24 #include "BKE_pbvh.h"
25 #include "BKE_scene.h"
26 
27 #include "DEG_depsgraph.h"
28 
29 #include "WM_api.h"
30 #include "WM_message.h"
31 #include "WM_toolsystem.h"
32 #include "WM_types.h"
33 
34 #include "ED_object.h"
35 #include "ED_screen.h"
36 #include "ED_sculpt.h"
37 #include "paint_intern.h"
38 #include "sculpt_intern.h"
39 
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42 
43 #include "UI_interface.h"
44 
45 #include "bmesh.h"
46 
47 #include <math.h>
48 #include <stdlib.h>
49 
50 typedef enum eSculptMaskFilterTypes {
58 
60  {MASK_FILTER_SMOOTH, "SMOOTH", 0, "Smooth Mask", "Smooth mask"},
61  {MASK_FILTER_SHARPEN, "SHARPEN", 0, "Sharpen Mask", "Sharpen mask"},
62  {MASK_FILTER_GROW, "GROW", 0, "Grow Mask", "Grow mask"},
63  {MASK_FILTER_SHRINK, "SHRINK", 0, "Shrink Mask", "Shrink mask"},
65  "CONTRAST_INCREASE",
66  0,
67  "Increase Contrast",
68  "Increase the contrast of the paint mask"},
70  "CONTRAST_DECREASE",
71  0,
72  "Decrease Contrast",
73  "Decrease the contrast of the paint mask"},
74  {0, NULL, 0, NULL, NULL},
75 };
76 
77 static void mask_filter_task_cb(void *__restrict userdata,
78  const int i,
79  const TaskParallelTLS *__restrict UNUSED(tls))
80 {
81  SculptThreadedTaskData *data = userdata;
82  SculptSession *ss = data->ob->sculpt;
83  PBVHNode *node = data->nodes[i];
84  bool update = false;
85 
86  const int mode = data->filter_type;
87  float contrast = 0.0f;
88 
89  PBVHVertexIter vd;
90 
91  if (mode == MASK_FILTER_CONTRAST_INCREASE) {
92  contrast = 0.1f;
93  }
94 
95  if (mode == MASK_FILTER_CONTRAST_DECREASE) {
96  contrast = -0.1f;
97  }
98 
100  float delta, gain, offset, max, min;
101  float prev_val = *vd.mask;
103  switch (mode) {
104  case MASK_FILTER_SMOOTH:
105  case MASK_FILTER_SHARPEN: {
106  float val = SCULPT_neighbor_mask_average(ss, vd.index);
107 
108  val -= *vd.mask;
109 
110  if (mode == MASK_FILTER_SMOOTH) {
111  *vd.mask += val;
112  }
113  else if (mode == MASK_FILTER_SHARPEN) {
114  if (*vd.mask > 0.5f) {
115  *vd.mask += 0.05f;
116  }
117  else {
118  *vd.mask -= 0.05f;
119  }
120  *vd.mask += val / 2.0f;
121  }
122  break;
123  }
124  case MASK_FILTER_GROW:
125  max = 0.0f;
127  float vmask_f = data->prev_mask[ni.index];
128  if (vmask_f > max) {
129  max = vmask_f;
130  }
131  }
133  *vd.mask = max;
134  break;
135  case MASK_FILTER_SHRINK:
136  min = 1.0f;
138  float vmask_f = data->prev_mask[ni.index];
139  if (vmask_f < min) {
140  min = vmask_f;
141  }
142  }
144  *vd.mask = min;
145  break;
148  delta = contrast / 2.0f;
149  gain = 1.0f - delta * 2.0f;
150  if (contrast > 0) {
151  gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
152  offset = gain * (-delta);
153  }
154  else {
155  delta *= -1.0f;
156  offset = gain * (delta);
157  }
158  *vd.mask = gain * (*vd.mask) + offset;
159  break;
160  }
161  *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f);
162  if (*vd.mask != prev_val) {
163  update = true;
164  }
165  }
167 
168  if (update) {
170  }
171 }
172 
174 {
177  PBVHNode **nodes;
179  int totnode;
180  int filter_type = RNA_enum_get(op->ptr, "filter_type");
181 
182  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
183 
184  SculptSession *ss = ob->sculpt;
185  PBVH *pbvh = ob->sculpt->pbvh;
186 
188 
189  if (!ob->sculpt->pmap) {
190  return OPERATOR_CANCELLED;
191  }
192 
193  int num_verts = SCULPT_vertex_count_get(ss);
194 
195  BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
196  SCULPT_undo_push_begin(ob, "Mask Filter");
197 
198  for (int i = 0; i < totnode; i++) {
200  }
201 
202  float *prev_mask = NULL;
203  int iterations = RNA_int_get(op->ptr, "iterations");
204 
205  /* Auto iteration count calculates the number of iteration based on the vertices of the mesh to
206  * avoid adding an unnecessary amount of undo steps when using the operator from a shortcut.
207  * One iteration per 50000 vertices in the mesh should be fine in most cases.
208  * Maybe we want this to be configurable. */
209  if (RNA_boolean_get(op->ptr, "auto_iteration_count")) {
210  iterations = (int)(num_verts / 50000.0f) + 1;
211  }
212 
213  for (int i = 0; i < iterations; i++) {
214  if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
215  prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask");
216  for (int j = 0; j < num_verts; j++) {
217  prev_mask[j] = SCULPT_vertex_mask_get(ss, j);
218  }
219  }
220 
222  .sd = sd,
223  .ob = ob,
224  .nodes = nodes,
225  .filter_type = filter_type,
226  .prev_mask = prev_mask,
227  };
228 
229  TaskParallelSettings settings;
230  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
231  BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
232 
233  if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
234  MEM_freeN(prev_mask);
235  }
236  }
237 
238  MEM_SAFE_FREE(nodes);
239 
241 
243 
244  return OPERATOR_FINISHED;
245 }
246 
248  Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, const int smooth_iterations)
249 {
251  .sd = sd,
252  .ob = ob,
253  .nodes = nodes,
254  .filter_type = MASK_FILTER_SMOOTH,
255  };
256 
257  for (int i = 0; i < smooth_iterations; i++) {
258  TaskParallelSettings settings;
259  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
260  BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
261  }
262 }
263 
265 {
266  /* Identifiers. */
267  ot->name = "Mask Filter";
268  ot->idname = "SCULPT_OT_mask_filter";
269  ot->description = "Applies a filter to modify the current mask";
270 
271  /* API callbacks. */
274 
276 
277  /* RNA. */
279  "filter_type",
282  "Type",
283  "Filter that is going to be applied to the mask");
285  "iterations",
286  1,
287  1,
288  100,
289  "Iterations",
290  "Number of times that the filter is going to be applied",
291  1,
292  100);
294  ot->srna,
295  "auto_iteration_count",
296  true,
297  "Auto Iteration Count",
298  "Use a automatic number of iterations based on the number of vertices of the sculpt");
299 }
300 
302 {
303  int total = 0;
304  float avg[3];
305  zero_v3(avg);
306 
309  float normalized[3];
312  add_v3_v3(avg, normalized);
313  total++;
314  }
316 
317  if (total > 0) {
318  mul_v3_fl(avg, 1.0f / total);
319  float dot = dot_v3v3(avg, vd->no ? vd->no : vd->fno);
320  float angle = max_ff(saacosf(dot), 0.0f);
321  return angle;
322  }
323  return 0.0f;
324 }
325 
326 typedef struct DirtyMaskRangeData {
327  float min, max;
329 
330 static void dirty_mask_compute_range_task_cb(void *__restrict userdata,
331  const int i,
332  const TaskParallelTLS *__restrict tls)
333 {
334  SculptThreadedTaskData *data = userdata;
335  SculptSession *ss = data->ob->sculpt;
336  PBVHNode *node = data->nodes[i];
337  DirtyMaskRangeData *range = tls->userdata_chunk;
338  PBVHVertexIter vd;
339 
341  float dirty_mask = neighbor_dirty_mask(ss, &vd);
342  range->min = min_ff(dirty_mask, range->min);
343  range->max = max_ff(dirty_mask, range->max);
344  }
346 }
347 
348 static void dirty_mask_compute_range_reduce(const void *__restrict UNUSED(userdata),
349  void *__restrict chunk_join,
350  void *__restrict chunk)
351 {
352  DirtyMaskRangeData *join = chunk_join;
353  DirtyMaskRangeData *range = chunk;
354  join->min = min_ff(range->min, join->min);
355  join->max = max_ff(range->max, join->max);
356 }
357 
358 static void dirty_mask_apply_task_cb(void *__restrict userdata,
359  const int i,
360  const TaskParallelTLS *__restrict UNUSED(tls))
361 {
362  SculptThreadedTaskData *data = userdata;
363  SculptSession *ss = data->ob->sculpt;
364  PBVHNode *node = data->nodes[i];
365  PBVHVertexIter vd;
366 
367  const bool dirty_only = data->dirty_mask_dirty_only;
368  const float min = data->dirty_mask_min;
369  const float max = data->dirty_mask_max;
370 
371  float range = max - min;
372  if (range < 0.0001f) {
373  range = 0.0f;
374  }
375  else {
376  range = 1.0f / range;
377  }
378 
380  float dirty_mask = neighbor_dirty_mask(ss, &vd);
381  float mask = *vd.mask + (1.0f - ((dirty_mask - min) * range));
382  if (dirty_only) {
383  mask = fminf(mask, 0.5f) * 2.0f;
384  }
385  *vd.mask = CLAMPIS(mask, 0.0f, 1.0f);
386  }
389 }
390 
392 {
393  ARegion *region = CTX_wm_region(C);
395  SculptSession *ss = ob->sculpt;
397  PBVH *pbvh = ob->sculpt->pbvh;
398  PBVHNode **nodes;
400  int totnode;
401 
402  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
403 
405 
406  if (!ob->sculpt->pmap) {
407  return OPERATOR_CANCELLED;
408  }
409 
410  BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
411  SCULPT_undo_push_begin(ob, "Dirty Mask");
412 
413  for (int i = 0; i < totnode; i++) {
415  }
416 
418  .sd = sd,
419  .ob = ob,
420  .nodes = nodes,
421  .dirty_mask_dirty_only = RNA_boolean_get(op->ptr, "dirty_only"),
422  };
423  DirtyMaskRangeData range = {
424  .min = FLT_MAX,
425  .max = -FLT_MAX,
426  };
427 
428  TaskParallelSettings settings;
429  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
430 
432  settings.userdata_chunk = &range;
433  settings.userdata_chunk_size = sizeof(DirtyMaskRangeData);
434 
436  data.dirty_mask_min = range.min;
437  data.dirty_mask_max = range.max;
438  BLI_task_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings);
439 
440  MEM_SAFE_FREE(nodes);
441 
443 
445 
446  ED_region_tag_redraw(region);
447 
449 
450  return OPERATOR_FINISHED;
451 }
452 
454 {
455  /* Identifiers. */
456  ot->name = "Dirty Mask";
457  ot->idname = "SCULPT_OT_dirty_mask";
458  ot->description = "Generates a mask based on the geometry cavity and pointiness";
459 
460  /* API callbacks. */
463 
465 
466  /* RNA. */
468  ot->srna, "dirty_only", false, "Dirty Only", "Don't calculate cleans for convex areas");
469 }
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Definition: context.c:1505
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
General operations, lookup, etc. for blender objects.
void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph, struct Object *ob_orig, bool need_pmap, bool need_mask, bool is_paint_tool)
Definition: paint.c:1914
A BVH for high poly meshes.
#define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode)
Definition: BKE_pbvh.h:439
#define BKE_pbvh_vertex_iter_end
Definition: BKE_pbvh.h:509
#define PBVH_ITER_UNIQUE
Definition: BKE_pbvh.h:391
void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, bool use_threading, int totnode)
Definition: pbvh.c:3211
void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flags)
Definition: pbvh.c:1567
void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
Definition: pbvh.c:1875
void BKE_pbvh_search_gather(PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot)
Definition: pbvh.c:838
@ PBVH_UpdateMask
Definition: BKE_pbvh.h:71
MINLINE float max_ff(float a, float b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE float saacosf(float f)
MINLINE float normalize_v3(float r[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition: task_range.cc:94
#define CLAMPIS(a, b, c)
#define UNUSED(x)
#define ELEM(...)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition: RandGen.cpp:25
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define ND_DRAW
Definition: WM_types.h:410
#define NC_OBJECT
Definition: WM_types.h:329
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
SIMD_FORCE_INLINE btVector3 normalized() const
Return a normalized version of this vector.
OperationNode * node
const Depsgraph * depsgraph
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
#define fminf(x, y)
Definition: metal/compat.h:229
static void update(bNodeTree *ntree)
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3597
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
const float * SCULPT_vertex_co_get(SculptSession *ss, int index)
Definition: sculpt.c:125
int SCULPT_vertex_count_get(SculptSession *ss)
Definition: sculpt.c:111
void SCULPT_vertex_random_access_ensure(SculptSession *ss)
Definition: sculpt.c:103
bool SCULPT_mode_poll(bContext *C)
Definition: sculpt.c:3957
float SCULPT_vertex_mask_get(SculptSession *ss, int index)
Definition: sculpt.c:248
void SCULPT_tag_update_overlays(bContext *C)
Definition: sculpt.c:1048
static EnumPropertyItem prop_mask_filter_types[]
static void dirty_mask_apply_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls))
void SCULPT_OT_mask_filter(struct wmOperatorType *ot)
static void mask_filter_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls))
static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd)
void SCULPT_mask_filter_smooth_apply(Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, const int smooth_iterations)
static void dirty_mask_compute_range_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
static void dirty_mask_compute_range_reduce(const void *__restrict UNUSED(userdata), void *__restrict chunk_join, void *__restrict chunk)
void SCULPT_OT_dirty_mask(struct wmOperatorType *ot)
struct DirtyMaskRangeData DirtyMaskRangeData
eSculptMaskFilterTypes
@ MASK_FILTER_CONTRAST_DECREASE
@ MASK_FILTER_CONTRAST_INCREASE
@ MASK_FILTER_GROW
@ MASK_FILTER_SMOOTH
@ MASK_FILTER_SHRINK
@ MASK_FILTER_SHARPEN
void SCULPT_undo_push_begin(struct Object *ob, const char *name)
Definition: sculpt_undo.c:1545
void SCULPT_undo_push_end(struct Object *ob)
Definition: sculpt_undo.c:1575
#define SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator)
float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
#define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator)
SculptUndoNode * SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type)
Definition: sculpt_undo.c:1419
@ SCULPT_UNDO_MASK
#define min(a, b)
Definition: sort.c:35
struct SculptSession * sculpt
float * co
Definition: BKE_pbvh.h:430
float * fno
Definition: BKE_pbvh.h:432
float * no
Definition: BKE_pbvh.h:431
float * mask
Definition: BKE_pbvh.h:433
struct MeshElemMap * pmap
Definition: BKE_paint.h:516
struct PBVH * pbvh
Definition: BKE_paint.h:550
TaskParallelReduceFunc func_reduce
Definition: BLI_task.h:181
size_t userdata_chunk_size
Definition: BLI_task.h:169
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
struct PointerRNA * ptr
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3479