diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index d35ef05a7a4..45ec044ff92 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -97,6 +97,7 @@ void print_qt(const char *str, const float q[4]); /* conversion */ void axis_angle_to_quat(float r[4], const float axis[3], const float angle); void axis_angle_to_mat3(float R[3][3], const float axis[3], const float angle); +void axis_angle_to_mat3_no_norm(float R[3][3], const float axis[3], const float angle); void axis_angle_to_mat4(float R[4][4], const float axis[3], const float angle); void quat_to_axis_angle(float axis[3], float *angle, const float q[4]); diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index cf9280d418f..2d044a13841 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -749,10 +749,33 @@ void eulO_to_axis_angle(float axis[3], float *angle, const float eul[3], const s quat_to_axis_angle(axis, angle, q); } -/* axis angle to 3x3 matrix - safer version (normalization of axis performed) - * - * note: we may want a normalized and non normalized version of this function. - */ +/* axis angle to 3x3 matrix - note: requires that axis is normalized */ +void axis_angle_to_mat3_no_norm(float mat[3][3], const float nor[3], const float angle) +{ + float nsi[3], co, si, ico; + + /* now convert this to a 3x3 matrix */ + co = cosf(angle); + si = sinf(angle); + + ico = (1.0f - co); + nsi[0] = nor[0] * si; + nsi[1] = nor[1] * si; + nsi[2] = nor[2] * si; + + mat[0][0] = ((nor[0] * nor[0]) * ico) + co; + mat[0][1] = ((nor[0] * nor[1]) * ico) + nsi[2]; + mat[0][2] = ((nor[0] * nor[2]) * ico) - nsi[1]; + mat[1][0] = ((nor[0] * nor[1]) * ico) - nsi[2]; + mat[1][1] = ((nor[1] * nor[1]) * ico) + co; + mat[1][2] = ((nor[1] * nor[2]) * ico) + nsi[0]; + mat[2][0] = ((nor[0] * nor[2]) * ico) + nsi[1]; + mat[2][1] = ((nor[1] * nor[2]) * ico) - nsi[0]; + mat[2][2] = ((nor[2] * nor[2]) * ico) + co; +} + + +/* axis angle to 3x3 matrix - safer version (normalization of axis performed) */ void axis_angle_to_mat3(float mat[3][3], const float axis[3], const float angle) { float nor[3], nsi[3], co, si, ico; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 3dd6decfcff..ed9f77837be 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -286,7 +286,12 @@ typedef struct StrokeCache { int original; float anchored_location[3]; - float vertex_rotation; + float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ + float previous_vertex_rotation; /* previous rotation, used to detect if we rotate more than + * PI radians */ + short num_vertex_turns; /* records number of full 2*PI turns */ + float initial_mouse_dir[2]; /* used to calculate initial angle */ + bool init_dir_set; /* detect if we have initialized the initial mouse direction */ char saved_active_brush_name[MAX_ID_NAME]; char saved_mask_brush_tool; @@ -2085,19 +2090,9 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod Brush *brush = BKE_paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; int n; - float m[4][4], rot[4][4], lmat[4][4], ilmat[4][4]; static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; - unit_m4(m); - unit_m4(lmat); - - copy_v3_v3(lmat[3], ss->cache->location); - invert_m4_m4(ilmat, lmat); - axis_angle_to_mat4(rot, ss->cache->sculpt_normal_symm, angle); - - mul_serie_m4(m, lmat, rot, ilmat, NULL, NULL, NULL, NULL, NULL); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; @@ -2116,6 +2111,7 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod sculpt_orig_vert_data_update(&orig_data, &vd); if (sculpt_brush_test(&test, orig_data.co)) { + float vec[3], rot[3][3]; const float fade = bstrength * tex_strength(ss, brush, orig_data.co, test.dist, @@ -2123,9 +2119,11 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f); - mul_v3_m4v3(proxy[vd.i], m, orig_data.co); + sub_v3_v3v3(vec, orig_data.co, ss->cache->location); + axis_angle_to_mat3_no_norm(rot, ss->cache->sculpt_normal_symm, angle*fade); + mul_v3_m3v3(proxy[vd.i], rot, vec); + add_v3_v3(proxy[vd.i], ss->cache->location); sub_v3_v3(proxy[vd.i], orig_data.co); - mul_v3_fl(proxy[vd.i], fade); if (vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; @@ -3785,6 +3783,9 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio cache->first_time = 1; cache->vertex_rotation = 0; + cache->num_vertex_turns = 0; + cache->previous_vertex_rotation = 0; + cache->init_dir_set = false; sculpt_omp_start(sd, ss); } @@ -3947,10 +3948,46 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, sculpt_update_brush_delta(ups, ob, brush); if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + #define PIXEL_INPUT_THRESHHOLD 5 + const float dx = cache->mouse[0] - cache->initial_mouse[0]; const float dy = cache->mouse[1] - cache->initial_mouse[1]; - cache->vertex_rotation = -atan2f(dx, dy) * cache->bstrength; + /* only update when we have enough precision, by having the mouse adequately away from center + * may be better to convert to radial representation but square works for small values too*/ + if(fabs(dx) > PIXEL_INPUT_THRESHHOLD && fabs(dy) > PIXEL_INPUT_THRESHHOLD) { + float mouse_angle; + float dir[2] = {dx, dy}; + float cosval, sinval; + normalize_v2(dir); + + if (!cache->init_dir_set) { + copy_v2_v2(cache->initial_mouse_dir, dir); + cache->init_dir_set = true; + } + + /* calculate mouse angle between initial and final mouse position */ + cosval = dot_v2v2(dir, cache->initial_mouse_dir); + sinval = cross_v2v2(dir, cache->initial_mouse_dir); + + /* clamp to avoid nans in acos */ + CLAMP(cosval, -1.0, 1.0); + mouse_angle = (sinval > 0)? acos(cosval) : -acos(cosval); + + /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. + * to distinguish between transition from 0 to -1 and -PI to +PI, use comparison with PI/2 */ + if (mouse_angle * cache->previous_vertex_rotation < 0 && fabs(cache->previous_vertex_rotation) > M_PI_2) { + if (cache->previous_vertex_rotation < 0) + cache->num_vertex_turns--; + else + cache->num_vertex_turns++; + } + cache->previous_vertex_rotation = mouse_angle; + + cache->vertex_rotation = -(mouse_angle + 2*M_PI*cache->num_vertex_turns)* cache->bstrength; + + #undef PIXEL_INPUT_THRESHHOLD + } ups->draw_anchored = 1; copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse);