From a2b3fe5e013718d922d9cb3dd146e3d2e6af1419 Mon Sep 17 00:00:00 2001 From: Sean Kim Date: Tue, 30 Jan 2024 05:08:23 +0100 Subject: [PATCH] Sculpt: Add per-brush input samples This pull request adds the ability for users to specify input samples on a per brush basis. The existing field in the main `Paint` struct forces all brushes of a particular tool type to use the same value. A new field was added to the `Brush` struct to allow for this value to be specified there instead, and a corresponding unified value in `UnifiedPaintSettings` has been created to allow users to use the same value across all brushes. Addresses #108109 Pull Request: https://projects.blender.org/blender/blender/pulls/117080 --- .../startup/bl_ui/properties_paint_common.py | 9 +++- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_brush.hh | 3 ++ source/blender/blenkernel/intern/brush.cc | 19 +++++++ source/blender/blenkernel/intern/paint.cc | 4 -- .../blenloader/intern/versioning_400.cc | 49 +++++++++++++++++++ .../blenloader/intern/versioning_defaults.cc | 8 +++ .../editors/sculpt_paint/paint_stroke.cc | 8 +-- source/blender/makesdna/DNA_brush_defaults.h | 1 + source/blender/makesdna/DNA_brush_types.h | 5 +- source/blender/makesdna/DNA_scene_defaults.h | 1 + source/blender/makesdna/DNA_scene_types.h | 13 ++++- source/blender/makesrna/intern/rna_brush.cc | 10 ++++ source/blender/makesrna/intern/rna_scene.cc | 18 +++++++ .../makesrna/intern/rna_sculpt_paint.cc | 7 --- 15 files changed, 138 insertions(+), 19 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 077fa936cfa..7f3f0f70d35 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -362,7 +362,14 @@ class StrokePanel(BrushPanel): col.row().prop(brush, "jitter_unit", expand=True) col.separator() - col.prop(settings, "input_samples") + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "input_samples", + unified_name="use_unified_input_samples", + slider=True, + ) class SmoothStrokePanel(BrushPanel): diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index ba83cf6f5b9..fee9335239a 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 16 +#define BLENDER_FILE_SUBVERSION 17 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/BKE_brush.hh b/source/blender/blenkernel/BKE_brush.hh index aad20ab6b55..ec7d758057d 100644 --- a/source/blender/blenkernel/BKE_brush.hh +++ b/source/blender/blenkernel/BKE_brush.hh @@ -152,6 +152,9 @@ void BKE_brush_alpha_set(Scene *scene, Brush *brush, float alpha); float BKE_brush_weight_get(const Scene *scene, const Brush *brush); void BKE_brush_weight_set(const Scene *scene, Brush *brush, float value); +int BKE_brush_input_samples_get(const Scene *scene, const Brush *brush); +void BKE_brush_input_samples_set(const Scene *scene, Brush *brush, int value); + bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush); bool BKE_brush_use_alpha_pressure(const Brush *brush); bool BKE_brush_use_size_pressure(const Brush *brush); diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 5f153d3d498..3e45f6db5e5 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -2451,6 +2451,25 @@ void BKE_brush_weight_set(const Scene *scene, Brush *brush, float value) } } +int BKE_brush_input_samples_get(const Scene *scene, const Brush *brush) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + return (ups->flag & UNIFIED_PAINT_INPUT_SAMPLES) ? ups->input_samples : brush->input_samples; +} + +void BKE_brush_input_samples_set(const Scene *scene, Brush *brush, int value) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + if (ups->flag & UNIFIED_PAINT_INPUT_SAMPLES) { + ups->input_samples = value; + } + else { + brush->input_samples = value; + } +} + void BKE_brush_scale_unprojected_radius(float *unprojected_radius, int new_brush_size, int old_brush_size) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index fe4e929491c..2d8c79fff62 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1248,10 +1248,6 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p) void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p) { - if (p->num_input_samples < 1) { - p->num_input_samples = 1; - } - BLO_read_data_address(reader, &p->cavity_curve); if (p->cavity_curve) { BKE_curvemapping_blend_read(reader, p->cavity_curve); diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 3a7d905acc7..0f0ff9134a7 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -2789,6 +2789,55 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 401, 17)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *ts = scene->toolsettings; + int input_sample_values[10]; + + input_sample_values[0] = ts->imapaint.paint.num_input_samples; + input_sample_values[1] = ts->sculpt != nullptr ? ts->sculpt->paint.num_input_samples : 1; + input_sample_values[2] = ts->curves_sculpt != nullptr ? + ts->curves_sculpt->paint.num_input_samples : + 1; + input_sample_values[3] = ts->uvsculpt != nullptr ? ts->uvsculpt->paint.num_input_samples : 1; + + input_sample_values[4] = ts->gp_paint != nullptr ? ts->gp_paint->paint.num_input_samples : 1; + input_sample_values[5] = ts->gp_vertexpaint != nullptr ? + ts->gp_vertexpaint->paint.num_input_samples : + 1; + input_sample_values[6] = ts->gp_sculptpaint != nullptr ? + ts->gp_sculptpaint->paint.num_input_samples : + 1; + input_sample_values[7] = ts->gp_weightpaint != nullptr ? + ts->gp_weightpaint->paint.num_input_samples : + 1; + + input_sample_values[8] = ts->vpaint != nullptr ? ts->vpaint->paint.num_input_samples : 1; + input_sample_values[9] = ts->wpaint != nullptr ? ts->wpaint->paint.num_input_samples : 1; + + int unified_value = 1; + for (int i = 0; i < 10; i++) { + if (input_sample_values[i] != 1) { + if (unified_value == 1) { + unified_value = input_sample_values[i]; + } + else { + /* In the case of a user having multiple tools with different num_input_value values + * set we cannot support this in the single UnifiedPaintSettings value, so fallback + * to 1 instead of deciding that one value is more canonical than the other. + */ + break; + } + } + } + + ts->unified_paint_settings.input_samples = unified_value; + } + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->input_samples = 1; + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 9e4cc1a7c5c..dea58b06018 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -387,6 +387,11 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->sculpt) { ts->sculpt->automasking_boundary_edges_propagation_steps = 1; } + + /* Ensure input_samples has a correct default value of 1. */ + if (ts->unified_paint_settings.input_samples == 0) { + ts->unified_paint_settings.input_samples = 1; + } } void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) @@ -679,6 +684,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Enable anti-aliasing by default. */ brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING; + + /* By default, each brush should use a single input sample. */ + brush->input_samples = 1; } { diff --git a/source/blender/editors/sculpt_paint/paint_stroke.cc b/source/blender/editors/sculpt_paint/paint_stroke.cc index 624af63bc97..b21619613f5 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.cc +++ b/source/blender/editors/sculpt_paint/paint_stroke.cc @@ -1158,10 +1158,10 @@ wmKeyMap *paint_stroke_modal_keymap(wmKeyConfig *keyconf) } static void paint_stroke_add_sample( - const Paint *paint, PaintStroke *stroke, float x, float y, float pressure) + PaintStroke *stroke, int input_samples, float x, float y, float pressure) { PaintSample *sample = &stroke->samples[stroke->cur_sample]; - int max_samples = std::clamp(paint->num_input_samples, 1, PAINT_MAX_INPUT_SAMPLES); + int max_samples = std::clamp(input_samples, 1, PAINT_MAX_INPUT_SAMPLES); sample->mouse[0] = x; sample->mouse[1] = y; @@ -1431,6 +1431,7 @@ static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2]) int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p) { + Scene *scene = CTX_data_scene(C); Paint *p = BKE_paint_get_active_from_context(C); PaintMode mode = BKE_paintmode_get_active_from_context(C); PaintStroke *stroke = *stroke_p; @@ -1460,7 +1461,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS stroke->last_tablet_event_pressure = pressure; } - paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); + int input_samples = BKE_brush_input_samples_get(scene, br); + paint_stroke_add_sample(stroke, input_samples, event->mval[0], event->mval[1], pressure); paint_stroke_sample_average(stroke, &sample_average); /* Tilt. */ diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 7b65d99e541..bd88d4ac6d7 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -66,6 +66,7 @@ \ .jitter = 0.0f, \ \ + .input_samples = 1, \ /* Dash */ \ .dash_ratio = 1.0f, \ .dash_samples = 20, \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 37ff07d017b..f4800083f53 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -204,6 +204,9 @@ typedef struct Brush { int flag2; int sampling_flag; + /** Number of samples used to smooth the stroke. */ + int input_samples; + /** Pressure influence for mask. */ int mask_pressure; /** Jitter the position of the brush. */ @@ -260,7 +263,7 @@ typedef struct Brush { /** Source for fill tool color gradient application. */ char gradient_fill_mode; - char _pad0[5]; + char _pad0; /** Projection shape (sphere, circle). */ char falloff_shape; diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 8fcf30aa25d..29661489c52 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -308,6 +308,7 @@ #define _DNA_DEFAULTS_UnifiedPaintSettings \ { \ .size = 50, \ + .input_samples = 1, \ .unprojected_radius = 0.29, \ .alpha = 0.5f, \ .weight = 0.5f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 1ffbd20c725..0fca1de44ed 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -967,8 +967,12 @@ typedef struct Paint { /** Enum #ePaintFlags. */ int flags; - /** Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES inputs to smooth the stroke. */ - int num_input_samples; + /** + * Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES inputs to smooth the stroke. + * This value is now DEPRECATED, please refer to the Brush and UnifiedPaintSetting values + * instead. + */ + int num_input_samples DNA_DEPRECATED; /** Flags used for symmetry. */ int symmetry_flags; @@ -1338,8 +1342,12 @@ typedef struct UnifiedPaintSettings { /** Unified brush secondary color. */ float secondary_rgb[3]; + /** Unified brush stroke input samples. */ + int input_samples; + /** User preferences for sculpt and paint. */ int flag; + char _pad[4]; /* Rake rotation. */ @@ -1411,6 +1419,7 @@ typedef enum { UNIFIED_PAINT_ALPHA = (1 << 1), UNIFIED_PAINT_WEIGHT = (1 << 5), UNIFIED_PAINT_COLOR = (1 << 6), + UNIFIED_PAINT_INPUT_SAMPLES = (1 << 7), /** Only used if unified size is enabled, mirrors the brush flag #BRUSH_LOCK_SIZE. */ UNIFIED_PAINT_BRUSH_LOCK_SIZE = (1 << 2), diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index a41c85cf040..7d916c61c32 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -2769,6 +2769,16 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Unprojected Radius", "Radius of brush in Blender units"); RNA_def_property_update(prop, 0, "rna_Brush_size_update"); + prop = RNA_def_property(srna, "input_samples", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "input_samples"); + RNA_def_property_range(prop, 1, PAINT_MAX_INPUT_SAMPLES); + RNA_def_property_ui_range(prop, 1, PAINT_MAX_INPUT_SAMPLES, 1, -1); + RNA_def_property_ui_text( + prop, + "Input Samples", + "Number of input samples to average together to smooth the brush stroke"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "jitter", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "jitter"); RNA_def_property_range(prop, 0.0f, 1000.0f); diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 926c156d29b..c59a52b93d9 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -4182,6 +4182,13 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Use Unified Color", "Instead of per-brush color, the color is shared across brushes"); + prop = RNA_def_property(srna, "use_unified_input_samples", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", UNIFIED_PAINT_INPUT_SAMPLES); + RNA_def_property_ui_text( + prop, + "Use Unified Input Samples", + "Instead of per-brush input samples, the value is shared across brushes"); + /* unified paint settings that override the equivalent settings * from the active brush */ prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); @@ -4232,6 +4239,17 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Secondary Color", ""); RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + prop = RNA_def_property(srna, "input_samples", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "input_samples"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_range(prop, 1, PAINT_MAX_INPUT_SAMPLES); + RNA_def_property_ui_range(prop, 1, PAINT_MAX_INPUT_SAMPLES, 1, -1); + RNA_def_property_ui_text( + prop, + "Input Samples", + "Number of input samples to average together to smooth the brush stroke"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + prop = RNA_def_property(srna, "use_locked_size", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, nullptr, "flag"); RNA_def_property_enum_items(prop, brush_size_unit_items); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.cc b/source/blender/makesrna/intern/rna_sculpt_paint.cc index e2eeb24431b..e34ecf236bc 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.cc +++ b/source/blender/makesrna/intern/rna_sculpt_paint.cc @@ -674,13 +674,6 @@ static void rna_def_paint(BlenderRNA *brna) "Update the geometry when it enters the view, providing faster view navigation"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); - prop = RNA_def_property(srna, "input_samples", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, nullptr, "num_input_samples"); - RNA_def_property_ui_range(prop, 1, PAINT_MAX_INPUT_SAMPLES, 1, -1); - RNA_def_property_ui_text( - prop, "Input Samples", "Average multiple input samples together to smooth the brush stroke"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); - prop = RNA_def_property(srna, "use_symmetry_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "symmetry_flags", PAINT_SYMM_X); RNA_def_property_ui_text(prop, "Symmetry X", "Mirror brush across the X axis");