Sculpt: Clay Strips Tip Roundness property
authorPablo Dobarro <pablodp606@gmail.com>
Tue, 11 Feb 2020 19:04:41 +0000 (20:04 +0100)
committerPablo Dobarro <pablodp606@gmail.com>
Tue, 11 Feb 2020 19:11:44 +0000 (20:11 +0100)
This patch allow to change the brush tip shape between a square and a
circle using a brush property.

After this change we are no longer testing the distance against a cube
(the Z axis is not used). I did not test this in depth, but if it does
not produce any artifacts I think we can keep it this way instead of
adding more complexity to the code.

In this new distance test the brush falloff is only applied on the
rounded parts of the square to avoid sharp artifacts in the
diagonals. Because of this, the round version is much softer than
the square one. The planned hardness property will fix this, but
this can also be avoided by setting the fallof to a custom curve.

Reviewed By: jbakker

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

release/scripts/startup/bl_ui/properties_paint_common.py
source/blender/blenkernel/intern/brush.c
source/blender/blenkernel/intern/colortools.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_types.h
source/blender/makesrna/intern/rna_brush.c

index 7d2846acb72aed3353778b6b3cdd87f988bea359..2031546f4d0b3fc17a3132053997850c590eab96 100644 (file)
@@ -609,6 +609,10 @@ def brush_settings(layout, context, brush, popover=False):
                 layout.operator("sculpt.set_persistent_base")
                 layout.separator()
 
+        if brush.sculpt_tool == 'CLAY_STRIPS':
+            row = layout.row()
+            row.prop(brush, "tip_roundness")
+
         if brush.sculpt_tool == 'ELASTIC_DEFORM':
             layout.separator()
             layout.prop(brush, "elastic_deform_type")
index effb3002630ba2fb8703cbf440f86b04348753ff..69831f33ce5e7a5f3cbbf1c8c08b4ea9e4c26a9d 100644 (file)
@@ -949,9 +949,10 @@ void BKE_brush_sculpt_reset(Brush *br)
       br->flag |= BRUSH_ACCUMULATE | BRUSH_SIZE_PRESSURE;
       br->flag &= ~BRUSH_SPACE_ATTEN;
       br->alpha = 0.6f;
+      br->spacing = 5;
       br->normal_radius_factor = 1.55f;
-      br->curve_preset = BRUSH_CURVE_SPHERE;
-      br->spacing = 6;
+      br->tip_roundness = 0.18f;
+      br->curve_preset = BRUSH_CURVE_SMOOTHER;
       break;
     case SCULPT_TOOL_MULTIPLANE_SCRAPE:
       br->flag2 |= BRUSH_MULTIPLANE_SCRAPE_DYNAMIC | BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW;
index 2ec04ee2747a16912057328e2c800ebca49df388..e2080f04d3a7a4e7959b0ac98e5225c9bde93907 100644 (file)
@@ -391,7 +391,7 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope
       cuma->curve[6].y = 0.025f;
       break;
     case CURVE_PRESET_BELL:
-      cuma->curve[0].x = 0;
+      cuma->curve[0].x = 0.0f;
       cuma->curve[0].y = 0.025f;
 
       cuma->curve[1].x = 0.50f;
index b570947739a2e65dc183cb4f1ef10cff45b6e016..6062d9716295784d50dd83fde2b91cc2bc2d540a 100644 (file)
@@ -4428,7 +4428,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
       }
     }
 
-    /* Brush cursor alpha */
     for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
       br->add_col[3] = 0.9f;
       br->sub_col[3] = 0.9f;
@@ -4447,5 +4446,14 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
         br->flag2 |= BRUSH_POSE_IK_ANCHORED;
       }
     }
+
+    /* Tip Roundness. */
+    if (!DNA_struct_elem_find(fd->filesdna, "Brush", "float", "tip_roundness")) {
+      for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
+        if (br->ob_mode & OB_MODE_SCULPT && br->sculpt_tool == SCULPT_TOOL_CLAY_STRIPS) {
+          br->tip_roundness = 0.18f;
+        }
+      }
+    }
   }
 }
index e0c75e1e64f389f8ca8c412331ea445764e610a5..4fae136d133a0a657ca5b12d867041fc73f20e59 100644 (file)
@@ -1109,7 +1109,10 @@ bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3])
   }
 }
 
-bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4])
+bool sculpt_brush_test_cube(SculptBrushTest *test,
+                            const float co[3],
+                            float local[4][4],
+                            const float roundness)
 {
   float side = M_SQRT1_2;
   float local_co[3];
@@ -1124,14 +1127,32 @@ bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float loca
   local_co[1] = fabsf(local_co[1]);
   local_co[2] = fabsf(local_co[2]);
 
-  const float p = 8.0f;
-  if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
-    test->dist = ((powf(local_co[0], p) + powf(local_co[1], p) + powf(local_co[2], p)) /
-                  powf(side, p));
+  /* Keep the square and circular brush tips the same size. */
+  side += (1.0f - side) * roundness;
+
+  const float hardness = 1.0f - roundness;
+  const float constant_side = hardness * side;
+  const float falloff_side = roundness * side;
 
+  if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
+    /* Corner, distance to the center of the corner circle. */
+    if (min_ff(local_co[0], local_co[1]) > constant_side) {
+      float r_point[3];
+      copy_v3_fl(r_point, constant_side);
+      test->dist = len_v2v2(r_point, local_co) / falloff_side;
+      return true;
+    }
+    /* Side, distance to the square XY axis. */
+    if (max_ff(local_co[0], local_co[1]) > constant_side) {
+      test->dist = (max_ff(local_co[0], local_co[1]) - constant_side) / falloff_side;
+      return true;
+    }
+    /* Inside the square, constant distance. */
+    test->dist = 0.0f;
     return true;
   }
   else {
+    /* Outside the square. */
     return false;
   }
 }
@@ -5467,7 +5488,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
 
   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
   {
-    if (sculpt_brush_test_cube(&test, vd.co, mat)) {
+    if (sculpt_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) {
       if (plane_point_side_flip(vd.co, test.plane_tool, flip)) {
         float intr[3];
         float val[3];
@@ -7047,12 +7068,11 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
     case SCULPT_TOOL_CLAY:
       return max_ff(initial_size * 0.20f, initial_size * pow3f(cache->pressure));
     case SCULPT_TOOL_CLAY_STRIPS:
-      return max_ff(initial_size * 0.35f, initial_size * pow2f(cache->pressure));
+      return max_ff(initial_size * 0.30f, initial_size * pow2f(cache->pressure));
     case SCULPT_TOOL_CLAY_THUMB: {
       float clay_stabilized_pressure = sculpt_clay_thumb_get_stabilized_pressure(cache);
       return initial_size * clay_stabilized_pressure;
     }
-
     default:
       return initial_size * cache->pressure;
   }
index 0b5bb94203294838fa61ec9695757304190ae564..9d1795dec8f46cfcf94f7d1d837bfd457d309707 100644 (file)
@@ -280,7 +280,10 @@ void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test);
 bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]);
 bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]);
 bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]);
-bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]);
+bool sculpt_brush_test_cube(SculptBrushTest *test,
+                            const float co[3],
+                            float local[4][4],
+                            const float roundness);
 bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]);
 bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v);
 bool sculpt_search_circle_cb(PBVHNode *node, void *data_v);
index c5bf026983b4de41aa4d5e232a5834ad4e667c18..a9e15a5baf91a660adba5b2c8c53cf9aaec8bcc8 100644 (file)
@@ -311,7 +311,7 @@ typedef struct Brush {
   char mask_tool;
   /** Active grease pencil tool. */
   char gpencil_tool;
-  char _pad1[5];
+  char _pad1[1];
 
   float autosmooth_factor;
 
@@ -330,6 +330,10 @@ typedef struct Brush {
   int curve_preset;
   int automasking_flags;
 
+  /* Factor that controls the shape of the brush tip by rounding the corners of a square. */
+  /* 0.0 value produces a square, 1.0 produces a circle. */
+  float tip_roundness;
+
   int elastic_deform_type;
   float elastic_deform_volume_preservation;
 
index 0096aa3f29f178e12eaf07fe86c479cf86c39b71..04b6ff559787cd4ca8ccbc2f23b56b7cbe9fb9ec 100644 (file)
@@ -1943,6 +1943,12 @@ static void rna_def_brush(BlenderRNA *brna)
       "Number of segments of the inverse kinematics chain that will deform the mesh");
   RNA_def_property_update(prop, 0, "rna_Brush_update");
 
+  prop = RNA_def_property(srna, "tip_roundness", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "tip_roundness");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_text(prop, "Tip Roundness", "Roundness of the brush tip");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
   prop = RNA_def_property(srna, "auto_smooth_factor", PROP_FLOAT, PROP_FACTOR);
   RNA_def_property_float_sdna(prop, NULL, "autosmooth_factor");
   RNA_def_property_float_default(prop, 0);