Sculpt: Split normal radius and area radius
authorPablo Dobarro <pablodp606@gmail.com>
Mon, 16 Dec 2019 22:16:21 +0000 (23:16 +0100)
committerPablo Dobarro <pablodp606@gmail.com>
Tue, 11 Feb 2020 19:48:01 +0000 (20:48 +0100)
This enables an extra layer of control in the sculpt brushes.
For now it is enabled only in Scrape, but it should work in all brushes (like normal radius). In the future it may also be enabled in other brushes.
You can tweak in this property in the scrape brush to achieve a much better behavior when working on curve surfaces and control how much volume you want to trim. In most cases, it also fixes the bug where the brush keeps trimming in the same area without disabling accumulate.
It should be possible to fix some other artifacts in other brushes by tweaking this default property.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D5993

release/scripts/startup/bl_ui/properties_paint_common.py
source/blender/blenkernel/intern/brush.c
source/blender/blenlib/BLI_math_vector.h
source/blender/blenlib/intern/math_vector_inline.c
source/blender/blenloader/intern/versioning_280.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/sculpt_paint/sculpt_intern.h
source/blender/makesdna/DNA_brush_defaults.h
source/blender/makesdna/DNA_brush_types.h
source/blender/makesrna/intern/rna_brush.c

index 2031546f4d0b3fc17a3132053997850c590eab96..e82caccdfe152d3c1bfccb278cb02632bfdb75d7 100644 (file)
@@ -628,10 +628,14 @@ def brush_settings(layout, context, brush, popover=False):
             layout.separator()
         
         if brush.sculpt_tool == 'SCRAPE':
+            row = layout.row()
+            row.prop(brush, "area_radius_factor", slider=True)
             row = layout.row()
             row.prop(brush, "invert_to_scrape_fill", text="Invert to Fill")
 
         if brush.sculpt_tool == 'FILL':
+            row = layout.row()
+            row.prop(brush, "area_radius_factor", slider=True)
             row = layout.row()
             row.prop(brush, "invert_to_scrape_fill", text="Invert to Scrape")
 
index 69831f33ce5e7a5f3cbbf1c8c08b4ea9e4c26a9d..6a40d126352b1bcb08db959f0ddd9579f6242950 100644 (file)
@@ -82,6 +82,7 @@ static void brush_defaults(Brush *brush)
   FROM_DEFAULT(topology_rake_factor);
   FROM_DEFAULT(crease_pinch_factor);
   FROM_DEFAULT(normal_radius_factor);
+  FROM_DEFAULT(area_radius_factor);
   FROM_DEFAULT(sculpt_plane);
   FROM_DEFAULT(plane_offset);
   FROM_DEFAULT(clone.alpha);
index b39e979ec4745bdf99b766e6a3c8dd95ace13759..19a378269feb85b5d457e422ac33ea30bad74734 100644 (file)
@@ -105,6 +105,7 @@ MINLINE void add_v4_fl(float r[4], float f);
 MINLINE void add_v2_v2(float r[2], const float a[2]);
 MINLINE void add_v2_v2_db(double r[2], const double a[2]);
 MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2]);
+MINLINE void add_v2_v2_int(int r[2], const int a[2]);
 MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2]);
 MINLINE void add_v3_v3(float r[3], const float a[3]);
 MINLINE void add_v3_v3_db(double r[3], const double a[3]);
index caa38c9cf08d2f9e828ebe13908b26f53f2c064e..a304042a6059fcf960afbb2ef04f4eff0ed772da 100644 (file)
@@ -373,6 +373,12 @@ MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
   r[1] = a[1] + b[1];
 }
 
+MINLINE void add_v2_v2_int(int r[2], const int a[2])
+{
+  r[0] = r[0] + a[0];
+  r[1] = r[1] + a[1];
+}
+
 MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2])
 {
   r[0] = a[0] + b[0];
index 6062d9716295784d50dd83fde2b91cc2bc2d540a..dd67a4d72cbc170b7f809eb19c5f00ec4020c1ac 100644 (file)
@@ -4254,6 +4254,12 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
         }
       }
     }
+
+    for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
+      if (br->ob_mode & OB_MODE_SCULPT && br->area_radius_factor == 0.0f) {
+        br->area_radius_factor = 0.5f;
+      }
+    }
   }
 
   if (!MAIN_VERSION_ATLEAST(bmain, 282, 2)) {
index 4fae136d133a0a657ca5b12d867041fc73f20e59..92c74538fd17d28c8710517f2881bacf15af3e37 100644 (file)
@@ -1439,7 +1439,8 @@ typedef struct AreaNormalCenterTLSData {
   /* 0 = towards view, 1 = flipped */
   float area_cos[2][3];
   float area_nos[2][3];
-  int area_count[2];
+  int count_no[2];
+  int count_co[2];
 } AreaNormalCenterTLSData;
 
 static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
@@ -1456,24 +1457,45 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
   SculptUndoNode *unode = NULL;
 
   bool use_original = false;
+  bool normal_test_r, area_test_r;
 
   if (ss->cache && ss->cache->original) {
     unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
     use_original = (unode->co || unode->bm_entry);
   }
 
-  SculptBrushTest test;
-  SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
-      ss, &test, data->brush->falloff_shape);
+  SculptBrushTest normal_test;
+  SculptBrushTestFn sculpt_brush_normal_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
+      ss, &normal_test, data->brush->falloff_shape);
 
   /* Update the test radius to sample the normal using the normal radius of the brush. */
   if (data->brush->ob_mode == OB_MODE_SCULPT) {
-    float test_radius = sqrtf(test.radius_squared);
-    /* Layer brush produces artifacts with normal radius. */
+    float test_radius = sqrtf(normal_test.radius_squared);
+    /* Layer brush produces artifacts with normal and area radius. */
     if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) {
       test_radius *= data->brush->normal_radius_factor;
     }
-    test.radius_squared = test_radius * test_radius;
+    normal_test.radius_squared = test_radius * test_radius;
+  }
+
+  SculptBrushTest area_test;
+  SculptBrushTestFn sculpt_brush_area_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
+      ss, &area_test, data->brush->falloff_shape);
+
+  if (data->brush->ob_mode == OB_MODE_SCULPT) {
+    float test_radius = sqrtf(area_test.radius_squared);
+    /* Layer brush produces artifacts with normal and area radius */
+    if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) {
+      /* Enable area radius control only on Scrape for now */
+      if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) &&
+          data->brush->area_radius_factor > 0.0f) {
+        test_radius *= data->brush->area_radius_factor;
+      }
+      else {
+        test_radius *= data->brush->normal_radius_factor;
+      }
+    }
+    area_test.radius_squared = test_radius * test_radius;
   }
 
   /* When the mesh is edited we can't rely on original coords
@@ -1493,22 +1515,26 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
       };
       float co[3];
 
-      closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri));
+      closest_on_tri_to_point_v3(co, normal_test.location, UNPACK3(co_tri));
+
+      normal_test_r = sculpt_brush_normal_test_sq_fn(&normal_test, co);
+      area_test_r = sculpt_brush_area_test_sq_fn(&area_test, co);
 
-      if (sculpt_brush_test_sq_fn(&test, co)) {
+      if (normal_test_r || area_test_r) {
         float no[3];
         int flip_index;
 
         normal_tri_v3(no, UNPACK3(co_tri));
 
         flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f);
-        if (use_area_cos) {
+        if (use_area_cos && area_test_r) {
           add_v3_v3(anctd->area_cos[flip_index], co);
+          anctd->count_co[flip_index] += 1;
         }
-        if (use_area_nos) {
+        if (use_area_nos && normal_test_r) {
           add_v3_v3(anctd->area_nos[flip_index], no);
+          anctd->count_no[flip_index] += 1;
         }
-        anctd->area_count[flip_index] += 1;
       }
     }
   }
@@ -1532,7 +1558,10 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
         co = vd.co;
       }
 
-      if (sculpt_brush_test_sq_fn(&test, co)) {
+      normal_test_r = sculpt_brush_normal_test_sq_fn(&normal_test, co);
+      area_test_r = sculpt_brush_area_test_sq_fn(&area_test, co);
+
+      if (normal_test_r || area_test_r) {
         float no_buf[3];
         const float *no;
         int flip_index;
@@ -1555,13 +1584,14 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
 
         flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <=
                       0.0f);
-        if (use_area_cos) {
+        if (use_area_cos && area_test_r) {
           add_v3_v3(anctd->area_cos[flip_index], co);
+          anctd->count_co[flip_index] += 1;
         }
-        if (use_area_nos) {
+        if (use_area_nos && normal_test_r) {
           add_v3_v3(anctd->area_nos[flip_index], no);
+          anctd->count_no[flip_index] += 1;
         }
-        anctd->area_count[flip_index] += 1;
       }
     }
     BKE_pbvh_vertex_iter_end;
@@ -1584,8 +1614,8 @@ static void calc_area_normal_and_center_reduce(const void *__restrict UNUSED(use
   add_v3_v3(join->area_nos[1], anctd->area_nos[1]);
 
   /* Weights. */
-  join->area_count[0] += anctd->area_count[0];
-  join->area_count[1] += anctd->area_count[1];
+  add_v2_v2_int(join->count_no, anctd->count_no);
+  add_v2_v2_int(join->count_co, anctd->count_co);
 }
 
 static void calc_area_center(
@@ -1607,7 +1637,7 @@ static void calc_area_center(
       .use_area_cos = true,
   };
 
-  AreaNormalCenterTLSData anctd = {{{0}}};
+  AreaNormalCenterTLSData anctd = {0};
 
   PBVHParallelSettings settings;
   BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
@@ -1618,14 +1648,21 @@ static void calc_area_center(
 
   /* For flatten center. */
   for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
-    if (anctd.area_count[n] != 0) {
-      mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.area_count[n]);
+    if (anctd.count_co[n] != 0) {
+      mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.count_co[n]);
       break;
     }
   }
+
   if (n == 2) {
     zero_v3(r_area_co);
   }
+
+  if (anctd.count_co[0] == 0 && anctd.count_co[1] == 0) {
+    if (ss->cache) {
+      copy_v3_v3(r_area_co, ss->cache->location);
+    }
+  }
 }
 
 static void calc_area_normal(
@@ -1711,15 +1748,22 @@ static void calc_area_normal_and_center(
 
   /* For flatten center. */
   for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
-    if (anctd.area_count[n] != 0) {
-      mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.area_count[n]);
+    if (anctd.count_co[n] != 0) {
+      mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.count_co[n]);
       break;
     }
   }
+
   if (n == 2) {
     zero_v3(r_area_co);
   }
 
+  if (anctd.count_co[0] == 0 && anctd.count_co[1] == 0) {
+    if (ss->cache) {
+      copy_v3_v3(r_area_co, ss->cache->location);
+    }
+  }
+
   /* For area normal. */
   for (n = 0; n < ARRAY_SIZE(anctd.area_nos); n++) {
     if (normalize_v3_v3(r_area_no, anctd.area_nos[n]) != 0.0f) {
index 9d1795dec8f46cfcf94f7d1d837bfd457d309707..0edac0f1b169a65634bac5d5ca93285130f63224 100644 (file)
@@ -207,6 +207,13 @@ typedef struct SculptThreadedTaskData {
 
   bool use_area_cos;
   bool use_area_nos;
+
+  /* 0=towards view, 1=flipped */
+  float (*area_cos)[3];
+  float (*area_nos)[3];
+  int *count_no;
+  int *count_co;
+
   bool any_vertex_sampled;
 
   float *prev_mask;
index eec154ea09d9c84568ef7e3f3e182a4423c4b8ce..03129bf673441f03268259f04591ae93a1452a6b 100644 (file)
@@ -46,6 +46,7 @@
     .topology_rake_factor = 0.0f, \
     .crease_pinch_factor = 0.5f, \
     .normal_radius_factor = 0.5f, \
+    .area_radius_factor = 0.5f, \
     .sculpt_plane = SCULPT_DISP_DIR_AREA, \
     /* How far above or below the plane that is found by averaging the faces. */ \
     .plane_offset = 0.0f, \
index a9e15a5baf91a660adba5b2c8c53cf9aaec8bcc8..a5baa7a5c756187c0ac6495e751d1422c7a9a2c6 100644 (file)
@@ -311,7 +311,7 @@ typedef struct Brush {
   char mask_tool;
   /** Active grease pencil tool. */
   char gpencil_tool;
-  char _pad1[1];
+  char _pad1[5];
 
   float autosmooth_factor;
 
@@ -320,6 +320,7 @@ typedef struct Brush {
   float crease_pinch_factor;
 
   float normal_radius_factor;
+  float area_radius_factor;
 
   float plane_trim;
   /** Affectable height of brush (layer height for layer tool, i.e.). */
index 04b6ff559787cd4ca8ccbc2f23b56b7cbe9fb9ec..42c4e249aae1fa6b3dc472389c75fff64b2501b8 100644 (file)
@@ -1980,6 +1980,16 @@ static void rna_def_brush(BlenderRNA *brna)
                            "used to sample the normal");
   RNA_def_property_update(prop, 0, "rna_Brush_update");
 
+  prop = RNA_def_property(srna, "area_radius_factor", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "area_radius_factor");
+  RNA_def_property_range(prop, 0.0f, 2.0f);
+  RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.001, 3);
+  RNA_def_property_ui_text(prop,
+                           "Area Radius",
+                           "Ratio between the brush radius and the radius that is going to be "
+                           "used to sample the area center");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
   prop = RNA_def_property(srna, "stencil_pos", PROP_FLOAT, PROP_XYZ);
   RNA_def_property_float_sdna(prop, NULL, "stencil_pos");
   RNA_def_property_array(prop, 2);