diff options
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 9 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 214 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 2 |
5 files changed, 239 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 230fe831184..a2e3a997408 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -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, diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index d1509e10886..f4c94b6e5de 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -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); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index b29bbfd1fb0..0b5bb942032 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -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; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index e711fd13822..278256f39c8 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -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) diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 3c9942f32c7..42f475dda94 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -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); |