Sculpt: Clay Thumb Brush
This brush simulates deforming clay with your fingers, accumulating material during the stroke. It has a plane that tilts during the stroke in the front part of the brush to achieve this effect. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D6238
This commit is contained in:
parent
f1f2d9fe74
commit
015d5eda88
|
@ -938,6 +938,13 @@ void BKE_brush_sculpt_reset(Brush *br)
|
|||
br->autosmooth_factor = 0.25f;
|
||||
br->normal_radius_factor = 0.75f;
|
||||
break;
|
||||
case SCULPT_TOOL_CLAY_THUMB:
|
||||
br->alpha = 0.5f;
|
||||
br->normal_radius_factor = 1.0f;
|
||||
br->spacing = 6;
|
||||
br->flag |= BRUSH_SIZE_PRESSURE;
|
||||
br->flag &= ~BRUSH_SPACE_ATTEN;
|
||||
break;
|
||||
case SCULPT_TOOL_CLAY_STRIPS:
|
||||
br->flag |= BRUSH_ACCUMULATE | BRUSH_SIZE_PRESSURE;
|
||||
br->flag &= ~BRUSH_SPACE_ATTEN;
|
||||
|
@ -1019,6 +1026,7 @@ void BKE_brush_sculpt_reset(Brush *br)
|
|||
case SCULPT_TOOL_DRAW_SHARP:
|
||||
case SCULPT_TOOL_CLAY:
|
||||
case SCULPT_TOOL_CLAY_STRIPS:
|
||||
case SCULPT_TOOL_CLAY_THUMB:
|
||||
case SCULPT_TOOL_LAYER:
|
||||
case SCULPT_TOOL_INFLATE:
|
||||
case SCULPT_TOOL_BLOB:
|
||||
|
@ -1428,6 +1436,7 @@ bool BKE_brush_sculpt_has_secondary_color(const Brush *brush)
|
|||
SCULPT_TOOL_INFLATE,
|
||||
SCULPT_TOOL_CLAY,
|
||||
SCULPT_TOOL_CLAY_STRIPS,
|
||||
SCULPT_TOOL_CLAY_THUMB,
|
||||
SCULPT_TOOL_PINCH,
|
||||
SCULPT_TOOL_CREASE,
|
||||
SCULPT_TOOL_LAYER,
|
||||
|
|
|
@ -1754,6 +1754,9 @@ static float brush_strength(const Sculpt *sd,
|
|||
/* Clay Strips needs less strength to compensate the curve. */
|
||||
final_pressure = pressure * pressure * pressure;
|
||||
return alpha * flip * final_pressure * overlap * feather * 0.3f;
|
||||
case SCULPT_TOOL_CLAY_THUMB:
|
||||
final_pressure = pressure * pressure;
|
||||
return alpha * flip * final_pressure * overlap * feather * 1.3f;
|
||||
|
||||
case SCULPT_TOOL_MASK:
|
||||
overlap = (1.0f + overlap) / 2.0f;
|
||||
|
@ -5753,6 +5756,188 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
|
|||
BKE_pbvh_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Sculpt Clay Thumb Brush
|
||||
* \{ */
|
||||
|
||||
static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const Brush *brush = data->brush;
|
||||
float(*mat)[4] = data->mat;
|
||||
const float *area_no_sp = data->area_no_sp;
|
||||
const float *area_co = data->area_co;
|
||||
const float hardness = 0.50f;
|
||||
|
||||
PBVHVertexIter vd;
|
||||
float(*proxy)[3];
|
||||
const float bstrength = data->clay_strength;
|
||||
|
||||
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
|
||||
ss, &test, data->brush->falloff_shape);
|
||||
|
||||
float plane_tilt[4];
|
||||
float normal_tilt[3];
|
||||
float imat[4][4];
|
||||
|
||||
invert_m4_m4(imat, mat);
|
||||
rotate_v3_v3v3fl(normal_tilt, area_no_sp, imat[0], DEG2RADF(-ss->cache->clay_thumb_front_angle));
|
||||
|
||||
/* Plane aligned to the geometry normal (back part of the brush). */
|
||||
plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp);
|
||||
/* Tilted plane (front part of the brush). */
|
||||
plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt);
|
||||
|
||||
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
|
||||
{
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
float local_co[3];
|
||||
mul_v3_m4v3(local_co, mat, vd.co);
|
||||
float intr[3], intr_tilt[3];
|
||||
float val[3];
|
||||
|
||||
closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
|
||||
closest_to_plane_normalized_v3(intr_tilt, plane_tilt, vd.co);
|
||||
|
||||
/* Mix the deformation of the aligned and the tilted plane based on the brush space vertex
|
||||
* coordinates. */
|
||||
/* We can also control the mix with a curve if it produces noticeable artifacts in the center
|
||||
* of the brush. */
|
||||
const float tilt_mix = local_co[1] > 0.0f ? 0.0f : 1.0f;
|
||||
interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix);
|
||||
sub_v3_v3v3(val, intr_tilt, vd.co);
|
||||
|
||||
/* Deform the real vertex test distance with a hardness factor. This moves the falloff
|
||||
* towards the edges of the brush, producing a more defined falloff and a flat center. */
|
||||
float dist = sqrtf(test.dist);
|
||||
float p = dist / ss->cache->radius;
|
||||
p = (p - hardness) / (1.0f - hardness);
|
||||
CLAMP(p, 0.0f, 1.0f);
|
||||
dist *= p;
|
||||
const float fade = bstrength * tex_strength(ss,
|
||||
brush,
|
||||
vd.co,
|
||||
dist,
|
||||
vd.no,
|
||||
vd.fno,
|
||||
vd.mask ? *vd.mask : 0.0f,
|
||||
vd.index,
|
||||
tls->thread_id);
|
||||
|
||||
mul_v3_v3fl(proxy[vd.i], val, fade);
|
||||
|
||||
if (vd.mvert) {
|
||||
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
static float sculpt_clay_thumb_get_stabilized_pressure(StrokeCache *cache)
|
||||
{
|
||||
float final_pressure = 0.0f;
|
||||
for (int i = 0; i < CLAY_STABILIZER_LEN; i++) {
|
||||
final_pressure += cache->clay_pressure_stabilizer[i];
|
||||
}
|
||||
return final_pressure / (float)CLAY_STABILIZER_LEN;
|
||||
}
|
||||
|
||||
static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
Brush *brush = BKE_paint_brush(&sd->paint);
|
||||
|
||||
const float radius = ss->cache->radius;
|
||||
const float offset = get_offset(sd, ss);
|
||||
const float displace = radius * (0.25f + offset);
|
||||
|
||||
/* Sampled geometry normal and area center. */
|
||||
float area_no_sp[3];
|
||||
float area_no[3];
|
||||
float area_co[3];
|
||||
|
||||
float temp[3];
|
||||
float mat[4][4];
|
||||
float scale[4][4];
|
||||
float tmat[4][4];
|
||||
|
||||
calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co);
|
||||
|
||||
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
|
||||
calc_area_normal(sd, ob, nodes, totnode, area_no);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(area_no, area_no_sp);
|
||||
}
|
||||
|
||||
/* Delay the first daub because grab delta is not setup. */
|
||||
if (ss->cache->first_time) {
|
||||
ss->cache->clay_thumb_front_angle = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Simulate the clay accumulation by increasing the plane angle as more samples are added to the
|
||||
* stroke. */
|
||||
if (ss->cache->mirror_symmetry_pass == 0) {
|
||||
ss->cache->clay_thumb_front_angle += 0.8f;
|
||||
CLAMP(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f);
|
||||
}
|
||||
|
||||
if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Displace the brush planes. */
|
||||
copy_v3_v3(area_co, ss->cache->location);
|
||||
mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
|
||||
mul_v3_fl(temp, displace);
|
||||
add_v3_v3(area_co, temp);
|
||||
|
||||
/* Init brush local space matrix. */
|
||||
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
|
||||
mat[0][3] = 0.0f;
|
||||
cross_v3_v3v3(mat[1], area_no, mat[0]);
|
||||
mat[1][3] = 0.0f;
|
||||
copy_v3_v3(mat[2], area_no);
|
||||
mat[2][3] = 0.0f;
|
||||
copy_v3_v3(mat[3], ss->cache->location);
|
||||
mat[3][3] = 1.0f;
|
||||
normalize_m4(mat);
|
||||
|
||||
/* Scale brush local space matrix. */
|
||||
scale_m4_fl(scale, ss->cache->radius);
|
||||
mul_m4_m4m4(tmat, mat, scale);
|
||||
invert_m4_m4(mat, tmat);
|
||||
|
||||
float clay_strength = ss->cache->bstrength *
|
||||
sculpt_clay_thumb_get_stabilized_pressure(ss->cache);
|
||||
|
||||
SculptThreadedTaskData data = {
|
||||
.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.area_no_sp = area_no_sp,
|
||||
.area_co = ss->cache->location,
|
||||
.mat = mat,
|
||||
.clay_strength = clay_strength,
|
||||
};
|
||||
|
||||
PBVHParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
|
||||
BKE_pbvh_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
static void do_gravity_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict tls)
|
||||
|
@ -6061,6 +6246,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
|
|||
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
|
||||
do_multiplane_scrape_brush(sd, ob, nodes, totnode);
|
||||
break;
|
||||
case SCULPT_TOOL_CLAY_THUMB:
|
||||
do_clay_thumb_brush(sd, ob, nodes, totnode);
|
||||
break;
|
||||
case SCULPT_TOOL_FILL:
|
||||
if (invert && brush->flag & BRUSH_INVERT_TO_SCRAPE_FILL) {
|
||||
do_scrape_brush(sd, ob, nodes, totnode);
|
||||
|
@ -6586,6 +6774,8 @@ static const char *sculpt_tool_name(Sculpt *sd)
|
|||
return "Clay Brush";
|
||||
case SCULPT_TOOL_CLAY_STRIPS:
|
||||
return "Clay Strips Brush";
|
||||
case SCULPT_TOOL_CLAY_THUMB:
|
||||
return "Clay Thumb Brush";
|
||||
case SCULPT_TOOL_FILL:
|
||||
return "Fill Brush";
|
||||
case SCULPT_TOOL_SCRAPE:
|
||||
|
@ -6854,6 +7044,11 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
|
|||
return max_ff(initial_size * 0.20f, initial_size * pow3f(cache->pressure));
|
||||
case SCULPT_TOOL_CLAY_STRIPS:
|
||||
return max_ff(initial_size * 0.35f, initial_size * pow2f(cache->pressure));
|
||||
case SCULPT_TOOL_CLAY_THUMB: {
|
||||
float clay_stabilized_pressure = sculpt_clay_thumb_get_stabilized_pressure(cache);
|
||||
return initial_size * clay_stabilized_pressure;
|
||||
}
|
||||
|
||||
default:
|
||||
return initial_size * cache->pressure;
|
||||
}
|
||||
|
@ -6875,6 +7070,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
|
|||
SCULPT_TOOL_NUDGE,
|
||||
SCULPT_TOOL_CLAY_STRIPS,
|
||||
SCULPT_TOOL_MULTIPLANE_SCRAPE,
|
||||
SCULPT_TOOL_CLAY_THUMB,
|
||||
SCULPT_TOOL_SNAKE_HOOK,
|
||||
SCULPT_TOOL_POSE,
|
||||
SCULPT_TOOL_THUMB) ||
|
||||
|
@ -6911,6 +7107,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
|
|||
break;
|
||||
case SCULPT_TOOL_CLAY_STRIPS:
|
||||
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
|
||||
case SCULPT_TOOL_CLAY_THUMB:
|
||||
case SCULPT_TOOL_NUDGE:
|
||||
case SCULPT_TOOL_SNAKE_HOOK:
|
||||
if (brush->flag & BRUSH_ANCHORED) {
|
||||
|
@ -7054,6 +7251,23 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
|
|||
}
|
||||
}
|
||||
|
||||
/* Clay stabilized pressure. */
|
||||
if (brush->sculpt_tool == SCULPT_TOOL_CLAY_THUMB) {
|
||||
if (ss->cache->first_time) {
|
||||
for (int i = 0; i < CLAY_STABILIZER_LEN; i++) {
|
||||
ss->cache->clay_pressure_stabilizer[i] = 0.0f;
|
||||
}
|
||||
ss->cache->clay_pressure_stabilizer_index = 0;
|
||||
}
|
||||
else {
|
||||
cache->clay_pressure_stabilizer[cache->clay_pressure_stabilizer_index] = cache->pressure;
|
||||
cache->clay_pressure_stabilizer_index += 1;
|
||||
if (cache->clay_pressure_stabilizer_index >= CLAY_STABILIZER_LEN) {
|
||||
cache->clay_pressure_stabilizer_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BKE_brush_use_size_pressure(brush) &&
|
||||
paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) {
|
||||
cache->radius = sculpt_brush_dynamic_size_get(brush, cache, cache->initial_radius);
|
||||
|
|
|
@ -221,6 +221,9 @@ typedef struct SculptThreadedTaskData {
|
|||
float max_distance_squared;
|
||||
float nearest_vertex_search_co[3];
|
||||
|
||||
/* Stabilized strength for the Clay Thumb brush. */
|
||||
float clay_strength;
|
||||
|
||||
int mask_expand_update_it;
|
||||
bool mask_expand_invert_mask;
|
||||
bool mask_expand_use_normals;
|
||||
|
@ -312,6 +315,8 @@ bool sculpt_pbvh_calc_area_normal(const struct Brush *brush,
|
|||
* For descriptions of these settings, check the operator properties.
|
||||
*/
|
||||
|
||||
#define CLAY_STABILIZER_LEN 10
|
||||
|
||||
typedef struct StrokeCache {
|
||||
/* Invariants */
|
||||
float initial_radius;
|
||||
|
@ -390,6 +395,13 @@ typedef struct StrokeCache {
|
|||
/* Pose brush */
|
||||
struct SculptPoseIKChain *pose_ik_chain;
|
||||
|
||||
/* Clay Thumb brush */
|
||||
/* Angle of the front tilting plane of the brush to simulate clay accumulation. */
|
||||
float clay_thumb_front_angle;
|
||||
/* Stores pressure samples to get an stabilized strength and radius variation. */
|
||||
float clay_pressure_stabilizer[CLAY_STABILIZER_LEN];
|
||||
int clay_pressure_stabilizer_index;
|
||||
|
||||
float vertex_rotation; /* amount to rotate the vertices when using rotate brush */
|
||||
struct Dial *dial;
|
||||
|
||||
|
|
|
@ -505,6 +505,7 @@ typedef enum eBrushSculptTool {
|
|||
SCULPT_TOOL_POSE = 22,
|
||||
SCULPT_TOOL_MULTIPLANE_SCRAPE = 23,
|
||||
SCULPT_TOOL_SLIDE_RELAX = 24,
|
||||
SCULPT_TOOL_CLAY_THUMB = 25,
|
||||
} eBrushSculptTool;
|
||||
|
||||
/* Brush.uv_sculpt_tool */
|
||||
|
@ -526,6 +527,7 @@ typedef enum eBrushUVSculptTool {
|
|||
SCULPT_TOOL_INFLATE, \
|
||||
SCULPT_TOOL_CLAY, \
|
||||
SCULPT_TOOL_CLAY_STRIPS, \
|
||||
SCULPT_TOOL_CLAY_THUMB, \
|
||||
SCULPT_TOOL_ROTATE, \
|
||||
SCULPT_TOOL_SCRAPE, \
|
||||
SCULPT_TOOL_FLATTEN)
|
||||
|
|
|
@ -74,6 +74,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
|
|||
{SCULPT_TOOL_DRAW_SHARP, "DRAW_SHARP", ICON_BRUSH_SCULPT_DRAW, "Draw Sharp", ""},
|
||||
{SCULPT_TOOL_CLAY, "CLAY", ICON_BRUSH_CLAY, "Clay", ""},
|
||||
{SCULPT_TOOL_CLAY_STRIPS, "CLAY_STRIPS", ICON_BRUSH_CLAY_STRIPS, "Clay Strips", ""},
|
||||
{SCULPT_TOOL_CLAY_THUMB, "CLAY_THUMB", ICON_BRUSH_CLAY_STRIPS, "Clay Thumb", ""},
|
||||
{SCULPT_TOOL_LAYER, "LAYER", ICON_BRUSH_LAYER, "Layer", ""},
|
||||
{SCULPT_TOOL_INFLATE, "INFLATE", ICON_BRUSH_INFLATE, "Inflate", ""},
|
||||
{SCULPT_TOOL_BLOB, "BLOB", ICON_BRUSH_BLOB, "Blob", ""},
|
||||
|
@ -275,6 +276,7 @@ static bool rna_BrushCapabilitiesSculpt_has_plane_offset_get(PointerRNA *ptr)
|
|||
return ELEM(br->sculpt_tool,
|
||||
SCULPT_TOOL_CLAY,
|
||||
SCULPT_TOOL_CLAY_STRIPS,
|
||||
SCULPT_TOOL_CLAY_THUMB,
|
||||
SCULPT_TOOL_FILL,
|
||||
SCULPT_TOOL_FLATTEN,
|
||||
SCULPT_TOOL_SCRAPE);
|
||||
|
|
Loading…
Reference in New Issue