Sculpt/Paint: Brush curve presets
authorPablo Dobarro <pablodp606@gmail.com>
Wed, 31 Jul 2019 10:58:50 +0000 (12:58 +0200)
committerPablo Dobarro <pablodp606@gmail.com>
Wed, 31 Jul 2019 11:04:55 +0000 (13:04 +0200)
This patch introduces the same presets that are used for proportional editing in the brush falloff menu. The user can select any of these presets or use the regular custom falloff curve. The presets are hardcoded formulas, so the falloff curve is not used when they are active.

This change improves the general feeling of the brushes and it is more convenient and simpler to use. The CUSTOM curve option should now be used in the case that an unusual deformation is needed, in other cases, the hardcoded curve presets should be the default.

The smooth curve presets is a must in the grab brush, as it fixes the deformation issue with the current custom curve setting. The user may try to adjust the deformation by tweaking the curve, but it is nearly impossible to replicate this desired behavior.

{F7636217}

Other brushes that are included in the sculpt branch also rely on this as they need specific hardcoded falloffs to produce the desired effect.

Reviewers: brecht, billreynish

Reviewed By: brecht

Subscribers: JulienKaspar

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

release/scripts/startup/bl_ui/space_image.py
release/scripts/startup/bl_ui/space_view3d.py
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/intern/brush.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/makesdna/DNA_brush_types.h
source/blender/makesrna/intern/rna_brush.c

index bd003063e16e5bdd779934c9aa97bc58f08685f8..a483ff3f29138f00fe0237215b16eba31fc3076c 100644 (file)
@@ -1438,16 +1438,21 @@ class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel):
         tool_settings = context.tool_settings.image_paint
         brush = tool_settings.brush
 
-        layout.template_curve_mapping(brush, "curve")
-
         col = layout.column(align=True)
         row = col.row(align=True)
-        row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
-        row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
-        row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
-        row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
-        row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
-        row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
+        row.prop(brush, "curve_preset", text="")
+
+        if brush.curve_preset == 'CUSTOM':
+            layout.template_curve_mapping(brush, "curve")
+
+            col = layout.column(align=True)
+            row = col.row(align=True)
+            row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
+            row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
+            row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
+            row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
+            row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
+            row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
 
 
 class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel):
@@ -1538,15 +1543,20 @@ class IMAGE_PT_uv_sculpt_curve(Panel):
         brush = uvsculpt.brush
 
         if brush is not None:
-            layout.template_curve_mapping(brush, "curve")
-
-            row = layout.row(align=True)
-            row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
-            row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
-            row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
-            row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
-            row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
-            row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
+            col = layout.column(align=True)
+            row = col.row(align=True)
+            row.prop(brush, "curve_preset", text="")
+
+            if brush.curve_preset == 'CUSTOM':
+                layout.template_curve_mapping(brush, "curve")
+
+                row = layout.row(align=True)
+                row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
+                row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
+                row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
+                row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
+                row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
+                row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
 
 
 class ImageScopesPanel:
index 40e4f97a3955aba957a3a385de8dc892872f5320..ee3503763af2ef8784cb5fce67fdf2dd214e55ef 100644 (file)
@@ -2687,7 +2687,7 @@ class VIEW3D_MT_brush(Menu):
             sculpt_tool = brush.sculpt_tool
 
             layout.separator()
-            layout.operator_menu_enum("brush.curve_preset", "shape", text="Curve Preset")
+            layout.prop_menu_enum(brush, "curve_preset")
             layout.separator()
 
             if sculpt_tool != 'GRAB':
index 730bad796db5e5ff6feed4a7a37c5a113aaedb9f..0d871e8fcecd871c6ac395016bcfc06b0128e0c6 100644 (file)
@@ -958,16 +958,21 @@ class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel):
         settings = self.paint_settings(context)
         brush = settings.brush
 
-        layout.template_curve_mapping(brush, "curve", brush=True)
-
         col = layout.column(align=True)
         row = col.row(align=True)
-        row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
-        row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
-        row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
-        row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
-        row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
-        row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
+        row.prop(brush, "curve_preset", text="")
+
+        if brush.curve_preset == 'CUSTOM':
+            layout.template_curve_mapping(brush, "curve", brush=True)
+
+            col = layout.column(align=True)
+            row = col.row(align=True)
+            row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
+            row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
+            row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
+            row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
+            row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
+            row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
 
 
 class VIEW3D_PT_tools_brush_falloff_frontface(View3DPaintPanel, Panel):
index ec9a774a65cbe1d600403c6607637f45348834be..709f74808a3a4fa89c45af9fadc22a029dfe8d4e 100644 (file)
@@ -1400,16 +1400,45 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
 /* Uses the brush curve control to find a strength value */
 float BKE_brush_curve_strength(const Brush *br, float p, const float len)
 {
-  float strength;
+  float strength = 1.0f;
 
   if (p >= len) {
     return 0;
   }
   else {
     p = p / len;
+    p = 1.0f - p;
   }
 
-  strength = curvemapping_evaluateF(br->curve, 0, p);
+  switch (br->curve_preset) {
+    case BRUSH_CURVE_CUSTOM:
+      strength = curvemapping_evaluateF(br->curve, 0, 1.0f - p);
+      break;
+    case BRUSH_CURVE_SHARP:
+      strength = p * p;
+      break;
+    case BRUSH_CURVE_SMOOTH:
+      strength = 3.0f * p * p - 2.0f * p * p * p;
+      break;
+    case BRUSH_CURVE_ROOT:
+      strength = sqrtf(p);
+      break;
+    case BRUSH_CURVE_LIN:
+      strength = p;
+      break;
+    case BRUSH_CURVE_CONSTANT:
+      strength = 1.0f;
+      break;
+    case BRUSH_CURVE_SPHERE:
+      strength = sqrtf(2 * p - p * p);
+      break;
+    case BRUSH_CURVE_POW4:
+      strength = p * p * p * p;
+      break;
+    case BRUSH_CURVE_INVSQUARE:
+      strength = p * (2.0f - p);
+      break;
+  }
 
   return strength;
 }
index b9c1c05b3288f922f3f1f19b99cab61212e2ca92..925a14496a311c684f62ad7fbd9b993bf5d8d505 100644 (file)
@@ -1262,7 +1262,6 @@ float tex_strength(SculptSession *ss,
 
   /* Falloff curve */
   avg *= BKE_brush_curve_strength(br, len, cache->radius);
-
   avg *= frontface(br, cache->view_normal, vno, fno);
 
   /* Paint mask */
index 6f775e48d58289116d224e693c3df98121aea0a9..93ce3d9769b6bf68764ebc708876089c572c75c1 100644 (file)
@@ -186,6 +186,18 @@ typedef enum eGP_BrushIcons {
   GP_BRUSH_ICON_ERASE_STROKE = 10,
 } eGP_BrushIcons;
 
+typedef enum eBrushCurvePreset {
+  BRUSH_CURVE_CUSTOM = 0,
+  BRUSH_CURVE_SMOOTH = 1,
+  BRUSH_CURVE_SPHERE = 2,
+  BRUSH_CURVE_ROOT = 3,
+  BRUSH_CURVE_SHARP = 4,
+  BRUSH_CURVE_LIN = 5,
+  BRUSH_CURVE_POW4 = 6,
+  BRUSH_CURVE_INVSQUARE = 7,
+  BRUSH_CURVE_CONSTANT = 8,
+} eBrushCurvePreset;
+
 typedef struct Brush {
   ID id;
 
@@ -289,6 +301,9 @@ typedef struct Brush {
 
   float texture_sample_bias;
 
+  int curve_preset;
+  char _pad1[4];
+
   /* overlay */
   int texture_overlay_alpha;
   int mask_overlay_alpha;
index fb60f092d16577714e11dd52c72554fca225dae2..eeba716130934a63cdc91929bfaf10cfb931baec 100644 (file)
@@ -1565,6 +1565,19 @@ static void rna_def_brush(BlenderRNA *brna)
       {0, NULL, 0, NULL, NULL},
   };
 
+  static const EnumPropertyItem brush_curve_preset_items[] = {
+      {BRUSH_CURVE_CUSTOM, "CUSTOM", ICON_RNDCURVE, "Custom", ""},
+      {BRUSH_CURVE_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""},
+      {BRUSH_CURVE_SPHERE, "SPHERE", ICON_SPHERECURVE, "Sphere", ""},
+      {BRUSH_CURVE_ROOT, "ROOT", ICON_ROOTCURVE, "Root", ""},
+      {BRUSH_CURVE_SHARP, "SHARP", ICON_SHARPCURVE, "Sharp", ""},
+      {BRUSH_CURVE_LIN, "LIN", ICON_LINCURVE, "Linear", ""},
+      {BRUSH_CURVE_POW4, "POW4", ICON_SHARPCURVE, "Sharper", ""},
+      {BRUSH_CURVE_INVSQUARE, "INVSQUARE", ICON_INVERSESQUARECURVE, "Inverse square", ""},
+      {BRUSH_CURVE_CONSTANT, "CONSTANT", ICON_NOCURVE, "Constant", ""},
+      {0, NULL, 0, NULL, NULL},
+  };
+
   srna = RNA_def_struct(brna, "Brush", "ID");
   RNA_def_struct_ui_text(
       srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting");
@@ -1640,6 +1653,11 @@ static void rna_def_brush(BlenderRNA *brna)
   RNA_def_property_ui_text(prop, "Mask Tool", "");
   RNA_def_property_update(prop, 0, "rna_Brush_update");
 
+  prop = RNA_def_property(srna, "curve_preset", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_items(prop, brush_curve_preset_items);
+  RNA_def_property_ui_text(prop, "Curve Preset", "");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
   /* number values */
   prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL);
   RNA_def_property_int_funcs(prop, NULL, "rna_Brush_set_size", NULL);