Sculpt: Add rake option to snake-hook
[blender-staging.git] / source / blender / editors / sculpt_paint / sculpt.c
index f97d877255b4984c571c29c6e142e8d72dedb8b0..587f33468a611df065173501768c7add0a09014e 100644 (file)
@@ -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);
+               }
        }
 }