Blender  V2.93
sculpt_filter_mask.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include "MEM_guardedalloc.h"
25 
26 #include "BLI_blenlib.h"
27 #include "BLI_hash.h"
28 #include "BLI_math.h"
29 #include "BLI_task.h"
30 
31 #include "DNA_mesh_types.h"
32 #include "DNA_meshdata_types.h"
33 
34 #include "BKE_brush.h"
35 #include "BKE_context.h"
36 #include "BKE_mesh.h"
37 #include "BKE_mesh_mapping.h"
38 #include "BKE_object.h"
39 #include "BKE_paint.h"
40 #include "BKE_pbvh.h"
41 #include "BKE_scene.h"
42 
43 #include "DEG_depsgraph.h"
44 
45 #include "WM_api.h"
46 #include "WM_message.h"
47 #include "WM_toolsystem.h"
48 #include "WM_types.h"
49 
50 #include "ED_object.h"
51 #include "ED_screen.h"
52 #include "ED_sculpt.h"
53 #include "paint_intern.h"
54 #include "sculpt_intern.h"
55 
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58 
59 #include "UI_interface.h"
60 
61 #include "bmesh.h"
62 
63 #include <math.h>
64 #include <stdlib.h>
65 
66 typedef enum eSculptMaskFilterTypes {
74 
76  {MASK_FILTER_SMOOTH, "SMOOTH", 0, "Smooth Mask", "Smooth mask"},
77  {MASK_FILTER_SHARPEN, "SHARPEN", 0, "Sharpen Mask", "Sharpen mask"},
78  {MASK_FILTER_GROW, "GROW", 0, "Grow Mask", "Grow mask"},
79  {MASK_FILTER_SHRINK, "SHRINK", 0, "Shrink Mask", "Shrink mask"},
81  "CONTRAST_INCREASE",
82  0,
83  "Increase Contrast",
84  "Increase the contrast of the paint mask"},
86  "CONTRAST_DECREASE",
87  0,
88  "Decrease Contrast",
89  "Decrease the contrast of the paint mask"},
90  {0, NULL, 0, NULL, NULL},
91 };
92 
93 static void mask_filter_task_cb(void *__restrict userdata,
94  const int i,
95  const TaskParallelTLS *__restrict UNUSED(tls))
96 {
97  SculptThreadedTaskData *data = userdata;
98  SculptSession *ss = data->ob->sculpt;
99  PBVHNode *node = data->nodes[i];
100  bool update = false;
101 
102  const int mode = data->filter_type;
103  float contrast = 0.0f;
104 
105  PBVHVertexIter vd;
106 
107  if (mode == MASK_FILTER_CONTRAST_INCREASE) {
108  contrast = 0.1f;
109  }
110 
111  if (mode == MASK_FILTER_CONTRAST_DECREASE) {
112  contrast = -0.1f;
113  }
114 
116  float delta, gain, offset, max, min;
117  float prev_val = *vd.mask;
119  switch (mode) {
120  case MASK_FILTER_SMOOTH:
121  case MASK_FILTER_SHARPEN: {
122  float val = SCULPT_neighbor_mask_average(ss, vd.index);
123 
124  val -= *vd.mask;
125 
126  if (mode == MASK_FILTER_SMOOTH) {
127  *vd.mask += val;
128  }
129  else if (mode == MASK_FILTER_SHARPEN) {
130  if (*vd.mask > 0.5f) {
131  *vd.mask += 0.05f;
132  }
133  else {
134  *vd.mask -= 0.05f;
135  }
136  *vd.mask += val / 2.0f;
137  }
138  break;
139  }
140  case MASK_FILTER_GROW:
141  max = 0.0f;
143  float vmask_f = data->prev_mask[ni.index];
144  if (vmask_f > max) {
145  max = vmask_f;
146  }
147  }
149  *vd.mask = max;
150  break;
151  case MASK_FILTER_SHRINK:
152  min = 1.0f;
154  float vmask_f = data->prev_mask[ni.index];
155  if (vmask_f < min) {
156  min = vmask_f;
157  }
158  }
160  *vd.mask = min;
161  break;
164  delta = contrast / 2.0f;
165  gain = 1.0f - delta * 2.0f;
166  if (contrast > 0) {
167  gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
168  offset = gain * (-delta);
169  }
170  else {
171  delta *= -1.0f;
172  offset = gain * (delta);
173  }
174  *vd.mask = gain * (*vd.mask) + offset;
175  break;
176  }
177  *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f);
178  if (*vd.mask != prev_val) {
179  update = true;
180  }
181  if (vd.mvert) {
183  }
184  }
186 
187  if (update) {
189  }
190 }
191 
193 {
195  SculptSession *ss = ob->sculpt;
197  PBVH *pbvh = ob->sculpt->pbvh;
198  PBVHNode **nodes;
200  int totnode;
201  int filter_type = RNA_enum_get(op->ptr, "filter_type");
202 
203  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
204 
206 
207  if (!ob->sculpt->pmap) {
208  return OPERATOR_CANCELLED;
209  }
210 
211  int num_verts = SCULPT_vertex_count_get(ss);
212 
213  BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
214  SCULPT_undo_push_begin(ob, "Mask filter");
215 
216  for (int i = 0; i < totnode; i++) {
218  }
219 
220  float *prev_mask = NULL;
221  int iterations = RNA_int_get(op->ptr, "iterations");
222 
223  /* Auto iteration count calculates the number of iteration based on the vertices of the mesh to
224  * avoid adding an unnecessary amount of undo steps when using the operator from a shortcut.
225  * One iteration per 50000 vertices in the mesh should be fine in most cases.
226  * Maybe we want this to be configurable. */
227  if (RNA_boolean_get(op->ptr, "auto_iteration_count")) {
228  iterations = (int)(num_verts / 50000.0f) + 1;
229  }
230 
231  for (int i = 0; i < iterations; i++) {
232  if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
233  prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask");
234  for (int j = 0; j < num_verts; j++) {
235  prev_mask[j] = SCULPT_vertex_mask_get(ss, j);
236  }
237  }
238 
240  .sd = sd,
241  .ob = ob,
242  .nodes = nodes,
243  .filter_type = filter_type,
244  .prev_mask = prev_mask,
245  };
246 
247  TaskParallelSettings settings;
248  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
249  BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
250 
251  if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
252  MEM_freeN(prev_mask);
253  }
254  }
255 
256  MEM_SAFE_FREE(nodes);
257 
259 
261 
262  return OPERATOR_FINISHED;
263 }
264 
266  Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, const int smooth_iterations)
267 {
269  .sd = sd,
270  .ob = ob,
271  .nodes = nodes,
272  .filter_type = MASK_FILTER_SMOOTH,
273  };
274 
275  for (int i = 0; i < smooth_iterations; i++) {
276  TaskParallelSettings settings;
277  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
278  BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
279  }
280 }
281 
283 {
284  /* Identifiers. */
285  ot->name = "Mask Filter";
286  ot->idname = "SCULPT_OT_mask_filter";
287  ot->description = "Applies a filter to modify the current mask";
288 
289  /* API callbacks. */
292 
294 
295  /* RNA. */
297  "filter_type",
300  "Type",
301  "Filter that is going to be applied to the mask");
303  "iterations",
304  1,
305  1,
306  100,
307  "Iterations",
308  "Number of times that the filter is going to be applied",
309  1,
310  100);
312  ot->srna,
313  "auto_iteration_count",
314  false,
315  "Auto Iteration Count",
316  "Use a automatic number of iterations based on the number of vertices of the sculpt");
317 }
318 
320 {
321  int total = 0;
322  float avg[3];
323  zero_v3(avg);
324 
327  float normalized[3];
330  add_v3_v3(avg, normalized);
331  total++;
332  }
334 
335  if (total > 0) {
336  mul_v3_fl(avg, 1.0f / total);
337  float normal[3];
338  if (vd->no) {
340  }
341  else {
342  copy_v3_v3(normal, vd->fno);
343  }
344  float dot = dot_v3v3(avg, normal);
345  float angle = max_ff(saacosf(dot), 0.0f);
346  return angle;
347  }
348  return 0.0f;
349 }
350 
351 typedef struct DirtyMaskRangeData {
352  float min, max;
354 
355 static void dirty_mask_compute_range_task_cb(void *__restrict userdata,
356  const int i,
357  const TaskParallelTLS *__restrict tls)
358 {
359  SculptThreadedTaskData *data = userdata;
360  SculptSession *ss = data->ob->sculpt;
361  PBVHNode *node = data->nodes[i];
362  DirtyMaskRangeData *range = tls->userdata_chunk;
363  PBVHVertexIter vd;
364 
366  float dirty_mask = neighbor_dirty_mask(ss, &vd);
367  range->min = min_ff(dirty_mask, range->min);
368  range->max = max_ff(dirty_mask, range->max);
369  }
371 }
372 
373 static void dirty_mask_compute_range_reduce(const void *__restrict UNUSED(userdata),
374  void *__restrict chunk_join,
375  void *__restrict chunk)
376 {
377  DirtyMaskRangeData *join = chunk_join;
378  DirtyMaskRangeData *range = chunk;
379  join->min = min_ff(range->min, join->min);
380  join->max = max_ff(range->max, join->max);
381 }
382 
383 static void dirty_mask_apply_task_cb(void *__restrict userdata,
384  const int i,
385  const TaskParallelTLS *__restrict UNUSED(tls))
386 {
387  SculptThreadedTaskData *data = userdata;
388  SculptSession *ss = data->ob->sculpt;
389  PBVHNode *node = data->nodes[i];
390  PBVHVertexIter vd;
391 
392  const bool dirty_only = data->dirty_mask_dirty_only;
393  const float min = data->dirty_mask_min;
394  const float max = data->dirty_mask_max;
395 
396  float range = max - min;
397  if (range < 0.0001f) {
398  range = 0.0f;
399  }
400  else {
401  range = 1.0f / range;
402  }
403 
405  float dirty_mask = neighbor_dirty_mask(ss, &vd);
406  float mask = *vd.mask + (1.0f - ((dirty_mask - min) * range));
407  if (dirty_only) {
408  mask = fminf(mask, 0.5f) * 2.0f;
409  }
410  *vd.mask = CLAMPIS(mask, 0.0f, 1.0f);
411 
412  if (vd.mvert) {
414  }
415  }
418 }
419 
421 {
422  ARegion *region = CTX_wm_region(C);
424  SculptSession *ss = ob->sculpt;
426  PBVH *pbvh = ob->sculpt->pbvh;
427  PBVHNode **nodes;
429  int totnode;
430 
431  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
432 
434 
435  if (!ob->sculpt->pmap) {
436  return OPERATOR_CANCELLED;
437  }
438 
439  BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
440  SCULPT_undo_push_begin(ob, "Dirty Mask");
441 
442  for (int i = 0; i < totnode; i++) {
444  }
445 
447  .sd = sd,
448  .ob = ob,
449  .nodes = nodes,
450  .dirty_mask_dirty_only = RNA_boolean_get(op->ptr, "dirty_only"),
451  };
452  DirtyMaskRangeData range = {
453  .min = FLT_MAX,
454  .max = -FLT_MAX,
455  };
456 
457  TaskParallelSettings settings;
458  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
459 
461  settings.userdata_chunk = &range;
462  settings.userdata_chunk_size = sizeof(DirtyMaskRangeData);
463 
465  data.dirty_mask_min = range.min;
466  data.dirty_mask_max = range.max;
467  BLI_task_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings);
468 
469  MEM_SAFE_FREE(nodes);
470 
472 
474 
475  ED_region_tag_redraw(region);
476 
478 
479  return OPERATOR_FINISHED;
480 }
481 
483 {
484  /* Identifiers. */
485  ot->name = "Dirty Mask";
486  ot->idname = "SCULPT_OT_dirty_mask";
487  ot->description = "Generates a mask based on the geometry cavity and pointiness";
488 
489  /* API callbacks. */
492 
494 
495  /* RNA. */
497  ot->srna, "dirty_only", false, "Dirty Only", "Don't calculate cleans for convex areas");
498 }
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1279
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Definition: context.c:1401
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1208
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 need_colors)
Definition: paint.c:1817
A BVH for high poly meshes.
#define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode)
Definition: BKE_pbvh.h:384
#define BKE_pbvh_vertex_iter_end
Definition: BKE_pbvh.h:457
#define PBVH_ITER_UNIQUE
Definition: BKE_pbvh.h:335
void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, bool use_threading, int totnode)
Definition: pbvh.c:3042
void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flags)
Definition: pbvh.c:1432
void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
Definition: pbvh.c:1738
void BKE_pbvh_search_gather(PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot)
Definition: pbvh.c:843
@ 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 normal_short_to_float_v3(float r[3], const short n[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
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(const int start, const int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition: task_range.cc:110
#define CLAMPIS(a, b, c)
#define UNUSED(x)
#define ELEM(...)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
@ ME_VERT_PBVH_UPDATE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:667
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition: RandGen.cpp:39
#define ND_DRAW
Definition: WM_types.h:362
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define NC_OBJECT
Definition: WM_types.h:280
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
IconTextureDrawCall normal
#define fminf(x, y)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static void update(bNodeTree *ntree)
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
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:3585
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:3771
const float * SCULPT_vertex_co_get(SculptSession *ss, int index)
Definition: sculpt.c:134
int SCULPT_vertex_count_get(SculptSession *ss)
Definition: sculpt.c:120
void SCULPT_vertex_random_access_ensure(SculptSession *ss)
Definition: sculpt.c:112
bool SCULPT_mode_poll(bContext *C)
Definition: sculpt.c:6601
float SCULPT_vertex_mask_get(SculptSession *ss, int index)
Definition: sculpt.c:254
void SCULPT_tag_update_overlays(bContext *C)
Definition: sculpt.c:1066
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:1383
#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)
void SCULPT_undo_push_end(void)
Definition: sculpt_undo.c:1400
SculptUndoNode * SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type)
Definition: sculpt_undo.c:1292
@ SCULPT_UNDO_MASK
#define min(a, b)
Definition: sort.c:51
struct SculptSession * sculpt
struct MVert * mvert
Definition: BKE_pbvh.h:372
short * no
Definition: BKE_pbvh.h:375
float * co
Definition: BKE_pbvh.h:374
float * fno
Definition: BKE_pbvh.h:376
float * mask
Definition: BKE_pbvh.h:377
struct MeshElemMap * pmap
Definition: BKE_paint.h:474
struct PBVH * pbvh
Definition: BKE_paint.h:504
TaskParallelReduceFunc func_reduce
Definition: BLI_task.h:158
size_t userdata_chunk_size
Definition: BLI_task.h:150
const char * name
Definition: WM_types.h:721
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
struct PointerRNA * ptr
float max
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3156