From 351034891e433e83248203fada4c4a3d55bfae0c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 10 Jul 2023 04:56:02 -0700 Subject: [PATCH] Sculpt: Cleanup duplicate code in clay strips brush Clay strips was using it's own brush local matrix, which wasn't quite compatible with texture matrices. This could lead to brush textures not lining up with the stroke dabs. There was also a bug where the stroke was starting 20 pixels into the stroke, which is much higher than necassary to derive the initial rake angle. Notes: * The clay strips brush now uses SCULPT_cube_tip_init to calculate the local brush matrix. * SCULPT_cube_tip_init now accepts custom brush location and radius arguments. * The mouse sample preroll used to calculate initial brush rotation angle is now smaller than the update interval. * Clay strips now supports tip_scale_x, which has also been added to DNA defaults. --- .../startup/bl_ui/properties_paint_common.py | 3 ++ source/blender/blenkernel/BKE_paint.h | 3 +- source/blender/blenkernel/intern/brush.cc | 6 ++-- source/blender/blenkernel/intern/paint.cc | 20 ++++++++--- .../blenloader/intern/versioning_400.cc | 8 +++++ .../editors/sculpt_paint/paint_cursor.cc | 2 +- .../editors/sculpt_paint/paint_stroke.cc | 4 +-- source/blender/editors/sculpt_paint/sculpt.cc | 34 +++++++++++++------ .../sculpt_paint/sculpt_brush_types.cc | 31 +++++++++-------- .../editors/sculpt_paint/sculpt_intern.hh | 10 ++++-- .../sculpt_paint/sculpt_paint_color.cc | 3 +- source/blender/makesdna/DNA_brush_defaults.h | 2 ++ source/blender/makesrna/intern/rna_brush.cc | 2 +- 13 files changed, 87 insertions(+), 41 deletions(-) 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");