From 0b1221c813ef3ae83bb8279aca15d018c68305da Mon Sep 17 00:00:00 2001 From: Marcelo Mutzbauer <1xundoredo@gmail.com> Date: Mon, 25 Dec 2023 18:33:03 +0100 Subject: [PATCH 1/6] Fix #116418 --- source/blender/editors/sculpt_paint/sculpt.cc | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index e2a0d3ad9e1..fc7e139ef7f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2739,20 +2739,22 @@ static void update_sculpt_normal(Sculpt *sd, Object *ob, Span nodes) } } -static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) +static void calc_local_from_screen(ViewContext *vc, + const float center[3], + const float screen_dir[2], + float r_local_dir[3]) { Object *ob = vc->obact; float loc[3]; - const float xy_delta[2] = {0.0f, 1.0f}; - mul_v3_m4v3(loc, ob->world_to_object, center); + mul_v3_m4v3(loc, ob->object_to_world, center); const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); - ED_view3d_win_to_delta(vc->region, xy_delta, zfac, y); - normalize_v3(y); + ED_view3d_win_to_delta(vc->region, screen_dir, zfac, r_local_dir); + normalize_v3(r_local_dir); - add_v3_v3(y, ob->loc); - mul_m4_v3(ob->world_to_object, y); + add_v3_v3(r_local_dir, ob->loc); + mul_m4_v3(ob->world_to_object, r_local_dir); } static void calc_brush_local_mat(const float rotation, @@ -2765,7 +2767,6 @@ static void calc_brush_local_mat(const float rotation, float mat[4][4]; float scale[4][4]; float angle, v[3]; - float up[3]; /* Ensure `ob->world_to_object` is up to date. */ invert_m4_m4(ob->world_to_object, ob->object_to_world); @@ -2776,17 +2777,26 @@ static void calc_brush_local_mat(const float rotation, mat[2][3] = 0.0f; mat[3][3] = 1.0f; - /* Get view's up vector in object-space. */ - calc_local_y(cache->vc, cache->location, up); - - /* Calculate the X axis of the local matrix. */ - cross_v3_v3v3(v, up, cache->sculpt_normal); - /* Apply rotation (user angle, rake, etc.) to X axis. */ + /* Read rotation (user angle, rake, etc.) to find the view's transverse direction. */ angle = rotation - cache->special_rotation; - rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle); + float transverse_view[2]; + float transverse_local[3]; + transverse_view[0] = cosf(angle); + transverse_view[1] = sinf(angle); + /* Convert view's brush transverse drection to object-space */ + calc_local_from_screen(cache->vc, cache->location, transverse_view, transverse_local); + + /* Calculate the movement direction for the local matrix. + * Note that there is a deliberate prioritization here: Our calculations are + * designed such that the _motion vector_ gets projected into the tangent space; + * in most cases this will be more intuitive than projecting the transverse + * direction (which is orthogonal to the motion direction and therefore less + * apparent to the user). */ + cross_v3_v3v3(v, transverse_local, cache->sculpt_normal); + normalize_v3_v3(mat[1], v); /* Get other axes. */ - cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]); + cross_v3_v3v3(mat[0], mat[1], cache->sculpt_normal); copy_v3_v3(mat[2], cache->sculpt_normal); /* Set location. */ -- 2.30.2 From 58287ec640f581d79b6e1a6cfdf922783ab9bb0f Mon Sep 17 00:00:00 2001 From: Marcelo Mutzbauer <1xundoredo@gmail.com> Date: Mon, 25 Dec 2023 18:56:48 +0100 Subject: [PATCH 2/6] Minor rearrangements --- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index fc7e139ef7f..7660b998502 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2780,10 +2780,10 @@ static void calc_brush_local_mat(const float rotation, /* Read rotation (user angle, rake, etc.) to find the view's transverse direction. */ angle = rotation - cache->special_rotation; float transverse_view[2]; - float transverse_local[3]; transverse_view[0] = cosf(angle); transverse_view[1] = sinf(angle); /* Convert view's brush transverse drection to object-space */ + float transverse_local[3]; calc_local_from_screen(cache->vc, cache->location, transverse_view, transverse_local); /* Calculate the movement direction for the local matrix. -- 2.30.2 From 86c6ee962425435826b2eaacd9597876ac85ae98 Mon Sep 17 00:00:00 2001 From: Marcelo Mutzbauer <1xundoredo@gmail.com> Date: Mon, 25 Dec 2023 23:10:39 +0100 Subject: [PATCH 3/6] More intuitive(?) measurement of the rotation angle --- source/blender/blenkernel/intern/paint.cc | 4 ++-- source/blender/editors/sculpt_paint/sculpt.cc | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index aea34a2000b..9edc615aa27 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1352,11 +1352,11 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, } float dpos[2]; - sub_v2_v2v2(dpos, ups->last_rake, mouse_pos); + sub_v2_v2v2(dpos, mouse_pos, ups->last_rake); /* Limit how often we update the angle to prevent jitter. */ if (len_squared_v2(dpos) >= r * r) { - rotation = atan2f(dpos[0], dpos[1]); + rotation = atan2f(dpos[1], dpos[0]); copy_v2_v2(ups->last_rake, mouse_pos); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7660b998502..7c1379ccaaa 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2777,22 +2777,26 @@ static void calc_brush_local_mat(const float rotation, mat[2][3] = 0.0f; mat[3][3] = 1.0f; - /* Read rotation (user angle, rake, etc.) to find the view's transverse direction. */ - angle = rotation - cache->special_rotation; - float transverse_view[2]; - transverse_view[0] = cosf(angle); - transverse_view[1] = sinf(angle); - /* Convert view's brush transverse drection to object-space */ - float transverse_local[3]; - calc_local_from_screen(cache->vc, cache->location, transverse_view, transverse_local); + /* Read rotation (user angle, rake, etc.) to find the view's movement direction. */ + angle = rotation + cache->special_rotation; + float normal_screen[2]; + normal_screen[0] = -sinf(angle); + normal_screen[1] = cosf(angle); + /* Convert view's brush transverse direction to object-space, + * i.e. the normal of the plane described by the motion */ + float normal_local[3]; + calc_local_from_screen(cache->vc, cache->location, normal_screen, normal_local); /* Calculate the movement direction for the local matrix. * Note that there is a deliberate prioritization here: Our calculations are * designed such that the _motion vector_ gets projected into the tangent space; * in most cases this will be more intuitive than projecting the transverse * direction (which is orthogonal to the motion direction and therefore less - * apparent to the user). */ - cross_v3_v3v3(v, transverse_local, cache->sculpt_normal); + * apparent to the user). + * The Y-axis of the brush-local frame has to lie in the intersection of the tangent plane + * and the motion plane. */ + + cross_v3_v3v3(v, normal_local, cache->sculpt_normal); normalize_v3_v3(mat[1], v); /* Get other axes. */ -- 2.30.2 From 04906729be38d62ea478e36dc3491c2ba495475e Mon Sep 17 00:00:00 2001 From: Marcelo Mutzbauer <1xundoredo@gmail.com> Date: Mon, 25 Dec 2023 23:19:56 +0100 Subject: [PATCH 4/6] Better naming --- source/blender/editors/sculpt_paint/sculpt.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7c1379ccaaa..128c54b5092 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2779,13 +2779,13 @@ static void calc_brush_local_mat(const float rotation, /* Read rotation (user angle, rake, etc.) to find the view's movement direction. */ angle = rotation + cache->special_rotation; - float normal_screen[2]; - normal_screen[0] = -sinf(angle); - normal_screen[1] = cosf(angle); + float motion_normal_screen[2]; + motion_normal_screen[0] = -sinf(angle); + motion_normal_screen[1] = cosf(angle); /* Convert view's brush transverse direction to object-space, * i.e. the normal of the plane described by the motion */ - float normal_local[3]; - calc_local_from_screen(cache->vc, cache->location, normal_screen, normal_local); + float motion_normal_local[3]; + calc_local_from_screen(cache->vc, cache->location, motion_normal_screen, motion_normal_local); /* Calculate the movement direction for the local matrix. * Note that there is a deliberate prioritization here: Our calculations are @@ -2796,7 +2796,7 @@ static void calc_brush_local_mat(const float rotation, * The Y-axis of the brush-local frame has to lie in the intersection of the tangent plane * and the motion plane. */ - cross_v3_v3v3(v, normal_local, cache->sculpt_normal); + cross_v3_v3v3(v, motion_normal_local, cache->sculpt_normal); normalize_v3_v3(mat[1], v); /* Get other axes. */ -- 2.30.2 From 1ae70f82ee3edbb08de5425bd860868a4bd647c3 Mon Sep 17 00:00:00 2001 From: Marcelo Mutzbauer <1xundoredo@gmail.com> Date: Tue, 2 Jan 2024 18:23:43 +0100 Subject: [PATCH 5/6] Fix some rotation problems --- source/blender/blenkernel/intern/brush.cc | 3 ++- source/blender/blenkernel/intern/paint.cc | 2 +- source/blender/editors/sculpt_paint/paint_cursor.cc | 2 +- source/blender/editors/sculpt_paint/paint_stroke.cc | 6 +++--- source/blender/editors/sculpt_paint/sculpt.cc | 11 +++++++---- source/blender/makesdna/DNA_scene_types.h | 3 +++ 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index a070a6731c9..685b8f05625 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -2149,7 +2149,8 @@ float BKE_brush_sample_tex_3d(const Scene *scene, * the texture is not rotated by skipping the calls to * atan2, sqrtf, sin, and cos. */ if (rotation > 0.001f || rotation < -0.001f) { - const float angle = atan2f(y, x) + rotation; + /* Since we are sampling, rotation must be negated */ + const float angle = atan2f(y, x) - rotation; const float flen = sqrtf(x * x + y * y); x = flen * cosf(angle); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 9edc615aa27..1038bae53de 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1356,7 +1356,7 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, /* Limit how often we update the angle to prevent jitter. */ if (len_squared_v2(dpos) >= r * r) { - rotation = atan2f(dpos[1], dpos[0]); + rotation = atan2f(dpos[1], dpos[0]) + float(0.5f * M_PI); copy_v2_v2(ups->last_rake, mouse_pos); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index dbd516654ff..7dd85358623 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -584,7 +584,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, /* Brush rotation. */ GPU_matrix_translate_2fv(center); - GPU_matrix_rotate_2d(-RAD2DEGF(primary ? ups->brush_rotation : ups->brush_rotation_sec)); + GPU_matrix_rotate_2d(RAD2DEGF(primary ? ups->brush_rotation : ups->brush_rotation_sec)); GPU_matrix_translate_2f(-center[0], -center[1]); /* Scale based on tablet pressure. */ diff --git a/source/blender/editors/sculpt_paint/paint_stroke.cc b/source/blender/editors/sculpt_paint/paint_stroke.cc index 1fd84646b6f..0c6c5010d83 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.cc +++ b/source/blender/editors/sculpt_paint/paint_stroke.cc @@ -46,7 +46,7 @@ #include "paint_intern.hh" #include "sculpt_intern.hh" -//#define DEBUG_TIME +// #define DEBUG_TIME #ifdef DEBUG_TIME # include "PIL_time_utildefines.h" @@ -399,7 +399,7 @@ static bool paint_brush_update(bContext *C, ups->anchored_size = ups->pixel_radius = sqrtf(dx * dx + dy * dy); - ups->brush_rotation = ups->brush_rotation_sec = atan2f(dx, dy) + float(M_PI); + ups->brush_rotation = ups->brush_rotation_sec = atan2f(dy, dx) + float(0.5f * M_PI); if (brush->flag & BRUSH_EDGE_TO_EDGE) { halfway[0] = dx * 0.5f + stroke->initial_mouse[0]; @@ -1372,7 +1372,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { if (do_rake) { - float rotation = atan2f(tangents[2 * j], tangents[2 * j + 1]); + float rotation = atan2f(tangents[2 * j + 1], tangents[2 * j]) + float(0.5f * M_PI); paint_update_brush_rake_rotation(ups, br, rotation); } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 128c54b5092..c209c3a0a92 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2777,11 +2777,14 @@ static void calc_brush_local_mat(const float rotation, mat[2][3] = 0.0f; mat[3][3] = 1.0f; - /* Read rotation (user angle, rake, etc.) to find the view's movement direction. */ + /* Read rotation (user angle, rake, etc.) to find the view's movement direction (negative X of + * the brush). */ angle = rotation + cache->special_rotation; + /* By convention, motion direction points down the brush's Y axis, the angle represents the X + * axis, normal is a 90 deg ccw rotation of the motion direction. */ float motion_normal_screen[2]; - motion_normal_screen[0] = -sinf(angle); - motion_normal_screen[1] = cosf(angle); + motion_normal_screen[0] = cosf(angle); + motion_normal_screen[1] = sinf(angle); /* Convert view's brush transverse direction to object-space, * i.e. the normal of the plane described by the motion */ float motion_normal_local[3]; @@ -2796,7 +2799,7 @@ static void calc_brush_local_mat(const float rotation, * The Y-axis of the brush-local frame has to lie in the intersection of the tangent plane * and the motion plane. */ - cross_v3_v3v3(v, motion_normal_local, cache->sculpt_normal); + cross_v3_v3v3(v, cache->sculpt_normal, motion_normal_local); normalize_v3_v3(mat[1], v); /* Get other axes. */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index effad7d3116..6f99a40d51a 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1351,6 +1351,9 @@ typedef struct UnifiedPaintSettings { float average_stroke_accum[3]; int average_stroke_counter; + /* How much brush should be rotated in the view plane, 0 means x points right, y points up. + * The convention is that the brush's _negative_ Y axis points in the tangent direction (of the + * mouse curve, Bezier curve, etc.) */ float brush_rotation; float brush_rotation_sec; -- 2.30.2 From caf44e3c624fe300c78221b4ac95e2a4be706a8c Mon Sep 17 00:00:00 2001 From: Marcelo Mutzbauer <1xundoredo@gmail.com> Date: Wed, 3 Jan 2024 15:34:14 +0100 Subject: [PATCH 6/6] Forgot to add a file in my previous commit --- source/blender/blenkernel/intern/brush.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 685b8f05625..5c146316f27 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -2116,7 +2116,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { /* keep coordinates relative to mouse */ - rotation += ups->brush_rotation; + rotation -= ups->brush_rotation; x = point_2d[0] - ups->tex_mouse[0]; y = point_2d[1] - ups->tex_mouse[1]; @@ -2134,7 +2134,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, y = point_2d[1]; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_RANDOM) { - rotation += ups->brush_rotation; + rotation -= ups->brush_rotation; /* these contain a random coordinate */ x = point_2d[0] - ups->tex_mouse[0]; y = point_2d[1] - ups->tex_mouse[1]; @@ -2149,8 +2149,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, * the texture is not rotated by skipping the calls to * atan2, sqrtf, sin, and cos. */ if (rotation > 0.001f || rotation < -0.001f) { - /* Since we are sampling, rotation must be negated */ - const float angle = atan2f(y, x) - rotation; + const float angle = atan2f(y, x) + rotation; const float flen = sqrtf(x * x + y * y); x = flen * cosf(angle); @@ -2230,7 +2229,7 @@ float BKE_brush_sample_masktex( if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { /* keep coordinates relative to mouse */ - rotation += ups->brush_rotation_sec; + rotation -= ups->brush_rotation_sec; x = point_2d[0] - ups->mask_tex_mouse[0]; y = point_2d[1] - ups->mask_tex_mouse[1]; @@ -2248,7 +2247,7 @@ float BKE_brush_sample_masktex( y = point_2d[1]; } else if (mtex->brush_map_mode == MTEX_MAP_MODE_RANDOM) { - rotation += ups->brush_rotation_sec; + rotation -= ups->brush_rotation_sec; /* these contain a random coordinate */ x = point_2d[0] - ups->mask_tex_mouse[0]; y = point_2d[1] - ups->mask_tex_mouse[1]; -- 2.30.2