From f758ee50e6d96e5342a9971bd76d9ff0352aafa3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 21 Jan 2016 21:05:49 +1100 Subject: Sculpt: Add rake option to snake-hook This allows for dragging out shapes that rotate to follow the cursor motion. Values over 1 can be set for 'interesting' artistic effects. --- source/blender/editors/sculpt_paint/paint_intern.h | 1 + source/blender/editors/sculpt_paint/paint_utils.c | 23 +++++ source/blender/editors/sculpt_paint/sculpt.c | 115 ++++++++++++++++++++- 3 files changed, 138 insertions(+), 1 deletion(-) (limited to 'source/blender/editors/sculpt_paint') diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f1e35f84576..a3d74956d90 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -234,6 +234,7 @@ int paint_curve_poll(struct bContext *C); int facemask_paint_poll(struct bContext *C); void flip_v3_v3(float out[3], const float in[3], const char symm); +void flip_qt_qt(float out[3], const float in[3], const char symm); /* stroke operator */ typedef enum BrushStrokeMode { diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 7b66632fa42..dc0852aaee0 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -397,6 +397,29 @@ void flip_v3_v3(float out[3], const float in[3], const char symm) out[2] = in[2]; } +void flip_qt_qt(float out[4], const float in[4], const char symm) +{ + float axis[3], angle; + + quat_to_axis_angle(axis, &angle, in); + normalize_v3(axis); + + if (symm & PAINT_SYMM_X) { + axis[0] *= -1.0f; + angle *= -1.0f; + } + if (symm & PAINT_SYMM_Y) { + axis[1] *= -1.0f; + angle *= -1.0f; + } + if (symm & PAINT_SYMM_Z) { + axis[2] *= -1.0f; + angle *= -1.0f; + } + + axis_angle_normalized_to_quat(out, axis, angle); +} + /* used for both 3d view and image window */ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette) { diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index f97d877255b..587f33468a6 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -158,9 +158,21 @@ static int sculpt_brush_needs_normal(const Brush *brush) (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)); } - /** \} */ +static bool sculpt_brush_needs_rake_rotation(const Brush *brush) +{ + return SCULPT_TOOL_HAS_RAKE(brush->sculpt_tool) && (brush->rake_factor != 0.0f); +} + +/* Factor of brush to have rake point following behind + * (could be configurable but this is reasonable default). */ +#define SCULPT_RAKE_BRUSH_FACTOR 0.25f + +struct SculptRakeData { + float follow_dist; + float follow_co[3]; +}; typedef enum StrokeFlags { CLIP_X = 1, @@ -208,6 +220,11 @@ typedef struct StrokeCache { float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; + /* screen-space rotation defined by mouse motion */ + float rake_rotation[4], rake_rotation_symmetry[4]; + bool is_rake_rotation_valid; + struct SculptRakeData rake_data; + int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only; * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/ @@ -328,6 +345,43 @@ static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, } } +static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[3]) +{ + float rake_dist = len_v3v3(srd->follow_co, co); + if (rake_dist > srd->follow_dist) { + interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); + } +} + + +static void sculpt_rake_rotate( + const SculptSession *ss, const float sculpt_co[3], const float v_co[3], float factor, float r_delta[3]) +{ + float vec_rot[3]; + +#if 0 + /* lerp */ + sub_v3_v3v3(vec_rot, v_co, sculpt_co); + mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); + mul_v3_fl(r_delta, factor); +#else + /* slerp */ + float q_interp[4]; + sub_v3_v3v3(vec_rot, v_co, sculpt_co); + + copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); + mul_fac_qt_fl(q_interp, factor); + mul_qt_v3(q_interp, vec_rot); + + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); +#endif + +} + + /** \name SculptProjectVector * * Fast-path for #project_plane_v3_v3v3 @@ -2230,6 +2284,7 @@ static void do_snake_hook_brush_task_cb_ex( SculptBrushTest test; float (*proxy)[3]; const float bstrength = ss->cache->bstrength; + const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -2243,6 +2298,12 @@ static void do_snake_hook_brush_task_cb_ex( mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + if (do_rake_rotation) { + float delta_rotate[3]; + sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); + add_v3_v3(proxy[vd.i], delta_rotate); + } + if (vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } @@ -3605,6 +3666,10 @@ static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm); mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); } + + if (cache->is_rake_rotation_valid) { + flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); + } } typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups); @@ -4098,6 +4163,54 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); ups->anchored_size = ups->pixel_radius; } + + + /* handle 'rake' */ + cache->is_rake_rotation_valid = false; + + if (cache->first_time) { + copy_v3_v3(cache->rake_data.follow_co, grab_location); + } + + if (sculpt_brush_needs_rake_rotation(brush)) { + cache->rake_data.follow_dist = cache->radius * SCULPT_RAKE_BRUSH_FACTOR; + + if (!is_zero_v3(cache->grab_delta)) { + const float eps = 0.00001f; + + float v1[3], v2[3]; + + copy_v3_v3(v1, cache->rake_data.follow_co); + copy_v3_v3(v2, cache->rake_data.follow_co); + sub_v3_v3(v2, cache->grab_delta); + + sub_v3_v3(v1, grab_location); + sub_v3_v3(v2, grab_location); + + if ((normalize_v3(v2) > eps) && + (normalize_v3(v1) > eps) && + (len_squared_v3v3(v1, v2) > eps)) + { + const float rake_dist_sq = len_squared_v3v3(cache->rake_data.follow_co, grab_location); + const float rake_fade = (rake_dist_sq > SQUARE(cache->rake_data.follow_dist)) ? + 1.0f : sqrtf(rake_dist_sq) / cache->rake_data.follow_dist; + + float axis[3], angle; + float tquat[4]; + + rotation_between_vecs_to_quat(tquat, v1, v2); + + /* use axis-angle to scale rotation since the factor may be above 1 */ + quat_to_axis_angle(axis, &angle, tquat); + normalize_v3(axis); + + angle *= brush->rake_factor * rake_fade; + axis_angle_normalized_to_quat(cache->rake_rotation, axis, angle); + cache->is_rake_rotation_valid = true; + } + } + sculpt_rake_data_update(&cache->rake_data, grab_location); + } } } -- cgit v1.2.3