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.
This commit is contained in:
Campbell Barton
2016-01-21 21:05:49 +11:00
parent adcef2bd36
commit f758ee50e6
6 changed files with 165 additions and 1 deletions

View File

@@ -951,6 +951,12 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
row = col.row(align=True) row = col.row(align=True)
row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch") row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch")
# rake_factor
if capabilities.has_rake_factor:
col.separator()
row = col.row(align=True)
row.prop(brush, "rake_factor", slider=True)
# use_original_normal and sculpt_plane # use_original_normal and sculpt_plane
if capabilities.has_sculpt_plane: if capabilities.has_sculpt_plane:
col.separator() col.separator()

View File

@@ -234,6 +234,7 @@ int paint_curve_poll(struct bContext *C);
int facemask_paint_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_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 */ /* stroke operator */
typedef enum BrushStrokeMode { typedef enum BrushStrokeMode {

View File

@@ -397,6 +397,29 @@ void flip_v3_v3(float out[3], const float in[3], const char symm)
out[2] = in[2]; 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 */ /* 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) void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette)
{ {

View File

@@ -158,9 +158,21 @@ static int sculpt_brush_needs_normal(const Brush *brush)
(brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)); (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 { typedef enum StrokeFlags {
CLIP_X = 1, CLIP_X = 1,
@@ -208,6 +220,11 @@ typedef struct StrokeCache {
float grab_delta[3], grab_delta_symmetry[3]; float grab_delta[3], grab_delta_symmetry[3];
float old_grab_location[3], orig_grab_location[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; 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 */ * 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*/ 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 /** \name SculptProjectVector
* *
* Fast-path for #project_plane_v3_v3v3 * Fast-path for #project_plane_v3_v3v3
@@ -2230,6 +2284,7 @@ static void do_snake_hook_brush_task_cb_ex(
SculptBrushTest test; SculptBrushTest test;
float (*proxy)[3]; float (*proxy)[3];
const float bstrength = ss->cache->bstrength; 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; 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); 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) if (vd.mvert)
vd.mvert->flag |= ME_VERT_PBVH_UPDATE; 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); flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm);
mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); 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); 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); copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse);
ups->anchored_size = ups->pixel_radius; 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);
}
} }
} }

View File

@@ -69,6 +69,8 @@ typedef struct Brush {
char icon_filepath[1024]; /* 1024 = FILE_MAX */ char icon_filepath[1024]; /* 1024 = FILE_MAX */
float normal_weight; float normal_weight;
float rake_factor; /* rake actual data (not texture), used for sculpt */
int pad;
short blend; /* blend mode */ short blend; /* blend mode */
short ob_mode; /* & with ob->mode to see if the brush is compatible, use for display only. */ short ob_mode; /* & with ob->mode to see if the brush is compatible, use for display only. */
@@ -273,6 +275,10 @@ typedef enum BrushSculptTool {
SCULPT_TOOL_SNAKE_HOOK \ SCULPT_TOOL_SNAKE_HOOK \
) )
#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, \
SCULPT_TOOL_SNAKE_HOOK \
)
#define SCULPT_TOOL_HAS_DYNTOPO(t) (ELEM(t, \ #define SCULPT_TOOL_HAS_DYNTOPO(t) (ELEM(t, \
/* These brushes, as currently coded, cannot support dynamic topology */ \ /* These brushes, as currently coded, cannot support dynamic topology */ \
SCULPT_TOOL_GRAB, \ SCULPT_TOOL_GRAB, \

View File

@@ -154,6 +154,12 @@ static int rna_SculptToolCapabilities_has_normal_weight_get(PointerRNA *ptr)
return SCULPT_TOOL_HAS_NORMAL_WEIGHT(br->sculpt_tool); return SCULPT_TOOL_HAS_NORMAL_WEIGHT(br->sculpt_tool);
} }
static int rna_SculptToolCapabilities_has_rake_factor_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
return SCULPT_TOOL_HAS_RAKE(br->sculpt_tool);
}
static int rna_BrushCapabilities_has_overlay_get(PointerRNA *ptr) static int rna_BrushCapabilities_has_overlay_get(PointerRNA *ptr)
{ {
Brush *br = (Brush *)ptr->data; Brush *br = (Brush *)ptr->data;
@@ -706,6 +712,7 @@ static void rna_def_sculpt_capabilities(BlenderRNA *brna)
SCULPT_TOOL_CAPABILITY(has_height, "Has Height"); SCULPT_TOOL_CAPABILITY(has_height, "Has Height");
SCULPT_TOOL_CAPABILITY(has_jitter, "Has Jitter"); SCULPT_TOOL_CAPABILITY(has_jitter, "Has Jitter");
SCULPT_TOOL_CAPABILITY(has_normal_weight, "Has Crease/Pinch Factor"); SCULPT_TOOL_CAPABILITY(has_normal_weight, "Has Crease/Pinch Factor");
SCULPT_TOOL_CAPABILITY(has_rake_factor, "Has Rake Factor");
SCULPT_TOOL_CAPABILITY(has_persistence, "Has Persistence"); SCULPT_TOOL_CAPABILITY(has_persistence, "Has Persistence");
SCULPT_TOOL_CAPABILITY(has_pinch_factor, "Has Pinch Factor"); SCULPT_TOOL_CAPABILITY(has_pinch_factor, "Has Pinch Factor");
SCULPT_TOOL_CAPABILITY(has_plane_offset, "Has Plane Offset"); SCULPT_TOOL_CAPABILITY(has_plane_offset, "Has Plane Offset");
@@ -1025,6 +1032,14 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Normal Weight", "How much grab will pull vertexes out of surface during a grab"); RNA_def_property_ui_text(prop, "Normal Weight", "How much grab will pull vertexes out of surface during a grab");
RNA_def_property_update(prop, 0, "rna_Brush_update"); RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "rake_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "rake_factor");
RNA_def_property_float_default(prop, 0);
RNA_def_property_range(prop, 0.0f, 10.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
RNA_def_property_ui_text(prop, "Rake", "How much grab will follow cursor rotation");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "crease_pinch_factor", PROP_FLOAT, PROP_FACTOR); prop = RNA_def_property(srna, "crease_pinch_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "crease_pinch_factor"); RNA_def_property_float_sdna(prop, NULL, "crease_pinch_factor");
RNA_def_property_float_default(prop, 2.0f / 3.0f); RNA_def_property_float_default(prop, 2.0f / 3.0f);