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:
@@ -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()
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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, \
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user