diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index c8f4b222ed2..d95f78a7c41 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -635,6 +635,9 @@ def brush_settings(layout, context, brush, popover=False): row = layout.row() row.prop(brush, "tip_roundness") + row = layout.row() + row.prop(brush, "tip_scale_x") + elif sculpt_tool == 'ELASTIC_DEFORM': layout.separator() layout.prop(brush, "elastic_deform_type") diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 51e53ad541c..66bf8bb3ac7 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -247,7 +247,8 @@ void BKE_paint_face_set_overlay_color_get(int face_set, int seed, uchar r_color[ bool paint_calculate_rake_rotation(struct UnifiedPaintSettings *ups, struct Brush *brush, const float mouse_pos[2], - ePaintMode paint_mode); + ePaintMode paint_mode, + bool stroke_has_started); void paint_update_brush_rake_rotation(struct UnifiedPaintSettings *ups, struct Brush *brush, float rotation); diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index c4e2456652a..f513371c212 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -527,6 +527,8 @@ static void brush_defaults(Brush *brush) FROM_DEFAULT(mtex); FROM_DEFAULT(mask_mtex); FROM_DEFAULT(falloff_shape); + FROM_DEFAULT(tip_scale_x); + FROM_DEFAULT(tip_roundness); #undef FROM_DEFAULT #undef FROM_DEFAULT_PTR @@ -1939,8 +1941,6 @@ void BKE_brush_sculpt_reset(Brush *br) br->spacing = 10; br->alpha = 1.0f; br->flow = 1.0f; - br->tip_scale_x = 1.0f; - br->tip_roundness = 1.0f; br->density = 1.0f; br->flag &= ~BRUSH_SPACE_ATTEN; zero_v3(br->rgb); @@ -2660,7 +2660,7 @@ bool BKE_brush_has_cube_tip(const Brush *brush, ePaintMode paint_mode) } if (ELEM(brush->sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_PAINT) && - brush->tip_roundness < 1.0f) + (brush->tip_roundness < 1.0f || brush->tip_scale_x != 1.0f)) { return true; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 59cf478057d..495d0ddbefa 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1332,8 +1332,11 @@ float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y return gpm->data[(y * factor) * gridsize + (x * factor)]; } -/* Threshold to move before updating the brush rotation. */ -#define RAKE_THRESHHOLD 20 +/* Threshold to move before updating the brush rotation, reduces jitter. */ +static float paint_rake_rotation_spacing(UnifiedPaintSettings * /*ups*/, Brush *brush) +{ + return brush->sculpt_tool == SCULPT_TOOL_CLAY_STRIPS ? 1.0f : 20.0f; +} void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation) { @@ -1361,16 +1364,25 @@ static const bool paint_rake_rotation_active(const Brush &brush, ePaintMode pain bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, const float mouse_pos[2], - ePaintMode paint_mode) + ePaintMode paint_mode, + bool stroke_has_started) { bool ok = false; if (paint_rake_rotation_active(*brush, paint_mode)) { - const float r = RAKE_THRESHHOLD; + float r = paint_rake_rotation_spacing(ups, brush); float rotation; + /* Use a smaller limit if the stroke hasn't started + * to prevent excessive preroll. + */ + if (!stroke_has_started) { + r = min_ff(r, 4.0f); + } + float dpos[2]; sub_v2_v2v2(dpos, ups->last_rake, mouse_pos); + /* Limit how often we update the angle to prevent jitter. */ if (len_squared_v2(dpos) >= r * r) { rotation = atan2f(dpos[0], dpos[1]); diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index cb748b08d26..92a89587ec2 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -10,6 +10,7 @@ #include "CLG_log.h" +#include "DNA_brush_types.h" #include "DNA_light_types.h" #include "DNA_lightprobe_types.h" #include "DNA_modifier_types.h" @@ -316,6 +317,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + /* Fix brush->tip_scale_x which should never be zero. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->tip_scale_x == 0.0f) { + brush->tip_scale_x = 1.0f; + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index e6037b31043..38e8c7eb5fc 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -1870,7 +1870,7 @@ static void paint_cursor_update_rake_rotation(PaintCursorContext *pcontext) * For line strokes, such interference is visible. */ if (!pcontext->ups->stroke_active) { paint_calculate_rake_rotation( - pcontext->ups, pcontext->brush, pcontext->translation, pcontext->mode); + pcontext->ups, pcontext->brush, pcontext->translation, pcontext->mode, true); } } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.cc b/source/blender/editors/sculpt_paint/paint_stroke.cc index f24ebc5e7eb..66b1ee3abdc 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.cc +++ b/source/blender/editors/sculpt_paint/paint_stroke.cc @@ -444,7 +444,7 @@ static bool paint_brush_update(bContext *C, } /* curve strokes do their own rake calculation */ else if (!(brush->flag & BRUSH_CURVE)) { - if (!paint_calculate_rake_rotation(ups, brush, mouse_init, mode)) { + if (!paint_calculate_rake_rotation(ups, brush, mouse_init, mode, stroke->rake_started)) { /* Not enough motion to define an angle. */ if (!stroke->rake_started) { is_dry_run = true; @@ -1575,7 +1575,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS { copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); } - paint_calculate_rake_rotation(stroke->ups, br, mouse, mode); + paint_calculate_rake_rotation(stroke->ups, br, mouse, mode, true); } } else if (first_modal || diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 5d36582c200..bf635e06694 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1809,7 +1809,8 @@ bool SCULPT_brush_test_circle_sq(SculptBrushTest *test, const float co[3]) bool SCULPT_brush_test_cube(SculptBrushTest *test, const float co[3], const float local[4][4], - const float roundness) + const float roundness, + const float /*tip_scale_x*/) { float side = 1.0f; float local_co[3]; @@ -2984,7 +2985,9 @@ static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) static void calc_brush_local_mat(const float rotation, Object *ob, float local_mat[4][4], - float local_mat_inv[4][4]) + float local_mat_inv[4][4], + const float *co, + const float *no) { const StrokeCache *cache = ob->sculpt->cache; float tmat[4][4]; @@ -2993,6 +2996,13 @@ static void calc_brush_local_mat(const float rotation, float angle, v[3]; float up[3]; + if (!co) { + co = cache->location; + } + if (!no) { + no = cache->sculpt_normal; + } + /* Ensure `ob->world_to_object` is up to date. */ invert_m4_m4(ob->world_to_object, ob->object_to_world); @@ -3003,20 +3013,20 @@ static void calc_brush_local_mat(const float rotation, mat[3][3] = 1.0f; /* Get view's up vector in object-space. */ - calc_local_y(cache->vc, cache->location, up); + calc_local_y(cache->vc, co, up); /* Calculate the X axis of the local matrix. */ - cross_v3_v3v3(v, up, cache->sculpt_normal); + cross_v3_v3v3(v, up, no); /* Apply rotation (user angle, rake, etc.) to X axis. */ angle = rotation - cache->special_rotation; - rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle); + rotate_v3_v3v3fl(mat[0], v, no, angle); /* Get other axes. */ - cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]); - copy_v3_v3(mat[2], cache->sculpt_normal); + cross_v3_v3v3(mat[1], no, mat[0]); + copy_v3_v3(mat[2], no); /* Set location. */ - copy_v3_v3(mat[3], cache->location); + copy_v3_v3(mat[3], co); /* Scale by brush radius. */ float radius = cache->radius; @@ -3069,7 +3079,8 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0) { const Brush *brush = BKE_paint_brush(&sd->paint); const MTex *mask_tex = BKE_brush_mask_texture_get(brush, OB_MODE_SCULPT); - calc_brush_local_mat(mask_tex->rot, ob, cache->brush_local_mat, cache->brush_local_mat_inv); + calc_brush_local_mat( + mask_tex->rot, ob, cache->brush_local_mat, cache->brush_local_mat_inv, nullptr, nullptr); } } @@ -6411,7 +6422,8 @@ void SCULPT_topology_islands_ensure(Object *ob) ss->islands_valid = true; } -void SCULPT_cube_tip_init(Sculpt * /*sd*/, Object *ob, Brush *brush, float mat[4][4]) +void SCULPT_cube_tip_init( + Sculpt * /*sd*/, Object *ob, Brush *brush, float mat[4][4], const float *co, const float *no) { SculptSession *ss = ob->sculpt; float scale[4][4]; @@ -6419,7 +6431,7 @@ void SCULPT_cube_tip_init(Sculpt * /*sd*/, Object *ob, Brush *brush, float mat[4 float unused[4][4]; zero_m4(mat); - calc_brush_local_mat(0.0, ob, unused, mat); + calc_brush_local_mat(0.0, ob, unused, mat, co, no); /* Note: we ignore the radius scaling done inside of calc_brush_local_mat to * duplicate prior behavior. diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 721980f22fe..43e0dc7276b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1058,7 +1058,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { + if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x)) { continue; } @@ -1130,15 +1130,6 @@ void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span nodes) copy_v3_v3(area_no, area_no_sp); } - /* Delay the first daub because grab delta is not setup. */ - if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - return; - } - - if (is_zero_v3(ss->cache->grab_delta_symmetry)) { - return; - } - mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); mul_v3_fl(temp, displace); add_v3_v3(area_co, temp); @@ -1155,6 +1146,20 @@ void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span nodes) madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f); /* Initialize brush local-space matrix. */ + SCULPT_cube_tip_init(sd, ob, brush, mat, area_co, area_no); + + /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in + * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices + * during big deformation while keeping the surface as uniform as possible. */ + invert_m4(mat); + mul_v3_fl(mat[2], 1.25f); + invert_m4(mat); + +#if 0 /* The original matrix construction code, preserved here for reference. */ + if (is_zero_v3(ss->cache->grab_delta_symmetry)) { + return; + } + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); mat[0][3] = 0.0f; cross_v3_v3v3(mat[1], area_no, mat[0]); @@ -1169,12 +1174,8 @@ void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, Span nodes) scale_m4_fl(scale, ss->cache->radius); mul_m4_m4m4(tmat, mat, scale); - /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in - * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices - * during big deformation while keeping the surface as uniform as possible. */ - mul_v3_fl(tmat[2], 1.25f); - invert_m4_m4(mat, tmat); +#endif SculptThreadedTaskData data{}; data.sd = sd; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index cc441c27b19..d1bc497f8a2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1232,7 +1232,8 @@ bool SCULPT_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3 bool SCULPT_brush_test_cube(SculptBrushTest *test, const float co[3], const float local[4][4], - float roundness); + const float roundness, + const float tip_scale_x); bool SCULPT_brush_test_circle_sq(SculptBrushTest *test, const float co[3]); /** * Test AABB against sphere. @@ -1257,7 +1258,12 @@ SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, char falloff_shape); const float *SCULPT_brush_frontface_normal_from_falloff_shape(SculptSession *ss, char falloff_shape); -void SCULPT_cube_tip_init(Sculpt *sd, Object *ob, Brush *brush, float mat[4][4]); +void SCULPT_cube_tip_init(Sculpt *sd, + Object *ob, + Brush *brush, + float mat[4][4], + const float *co = nullptr, /* Custom brush center. */ + const float *no = nullptr); /* Custom brush normal. */ /** * Return a multiplier for brush strength on a particular vertex. diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 05c8f7894f4..fe4a55093e5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -143,7 +143,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, bool affect_vertex = false; float distance_to_stroke_location = 0.0f; if (brush->tip_roundness < 1.0f) { - affect_vertex = SCULPT_brush_test_cube(&test, vd.co, data->mat, brush->tip_roundness); + affect_vertex = SCULPT_brush_test_cube( + &test, vd.co, data->mat, brush->tip_roundness, brush->tip_scale_x); distance_to_stroke_location = ss->cache->radius * test.dist; } else { diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 26ad33750dc..48f43765b7d 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -102,6 +102,8 @@ .mtex = _DNA_DEFAULT_MTex, \ .mask_mtex = _DNA_DEFAULT_MTex, \ .falloff_shape = 0,\ + .tip_scale_x = 1.0f,\ + .tip_roundness = 1.0f,\ } /** \} */ diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 651f12086c2..60016b0f42f 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -2864,7 +2864,7 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "tip_scale_x", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, nullptr, "tip_scale_x"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_range(prop, 0.0001f, 1.0f, 0.001, 3); RNA_def_property_ui_text(prop, "Tip Scale X", "Scale of the brush tip in the X axis"); RNA_def_property_update(prop, 0, "rna_Brush_update");