Sculpt: Clay Strips Tip Roundness property

This patch allow to change the brush tip shape between a square and a
circle using a brush property.

After this change we are no longer testing the distance against a cube
(the Z axis is not used). I did not test this in depth, but if it does
not produce any artifacts I think we can keep it this way instead of
adding more complexity to the code.

In this new distance test the brush falloff is only applied on the
rounded parts of the square to avoid sharp artifacts in the
diagonals. Because of this, the round version is much softer than
the square one. The planned hardness property will fix this, but
this can also be avoided by setting the fallof to a custom curve.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D6165
This commit is contained in:
Pablo Dobarro 2020-02-11 20:04:41 +01:00
parent 0ab7e32158
commit 6ee6a42d10
8 changed files with 60 additions and 14 deletions

View File

@ -609,6 +609,10 @@ def brush_settings(layout, context, brush, popover=False):
layout.operator("sculpt.set_persistent_base")
layout.separator()
if brush.sculpt_tool == 'CLAY_STRIPS':
row = layout.row()
row.prop(brush, "tip_roundness")
if brush.sculpt_tool == 'ELASTIC_DEFORM':
layout.separator()
layout.prop(brush, "elastic_deform_type")

View File

@ -949,9 +949,10 @@ void BKE_brush_sculpt_reset(Brush *br)
br->flag |= BRUSH_ACCUMULATE | BRUSH_SIZE_PRESSURE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->alpha = 0.6f;
br->spacing = 5;
br->normal_radius_factor = 1.55f;
br->curve_preset = BRUSH_CURVE_SPHERE;
br->spacing = 6;
br->tip_roundness = 0.18f;
br->curve_preset = BRUSH_CURVE_SMOOTHER;
break;
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
br->flag2 |= BRUSH_MULTIPLANE_SCRAPE_DYNAMIC | BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW;

View File

@ -391,7 +391,7 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope
cuma->curve[6].y = 0.025f;
break;
case CURVE_PRESET_BELL:
cuma->curve[0].x = 0;
cuma->curve[0].x = 0.0f;
cuma->curve[0].y = 0.025f;
cuma->curve[1].x = 0.50f;

View File

@ -4428,7 +4428,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
/* Brush cursor alpha */
for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
br->add_col[3] = 0.9f;
br->sub_col[3] = 0.9f;
@ -4447,5 +4446,14 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
br->flag2 |= BRUSH_POSE_IK_ANCHORED;
}
}
/* Tip Roundness. */
if (!DNA_struct_elem_find(fd->filesdna, "Brush", "float", "tip_roundness")) {
for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
if (br->ob_mode & OB_MODE_SCULPT && br->sculpt_tool == SCULPT_TOOL_CLAY_STRIPS) {
br->tip_roundness = 0.18f;
}
}
}
}
}

View File

@ -1109,7 +1109,10 @@ bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3])
}
}
bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4])
bool sculpt_brush_test_cube(SculptBrushTest *test,
const float co[3],
float local[4][4],
const float roundness)
{
float side = M_SQRT1_2;
float local_co[3];
@ -1124,14 +1127,32 @@ bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float loca
local_co[1] = fabsf(local_co[1]);
local_co[2] = fabsf(local_co[2]);
const float p = 8.0f;
if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
test->dist = ((powf(local_co[0], p) + powf(local_co[1], p) + powf(local_co[2], p)) /
powf(side, p));
/* Keep the square and circular brush tips the same size. */
side += (1.0f - side) * roundness;
const float hardness = 1.0f - roundness;
const float constant_side = hardness * side;
const float falloff_side = roundness * side;
if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
/* Corner, distance to the center of the corner circle. */
if (min_ff(local_co[0], local_co[1]) > constant_side) {
float r_point[3];
copy_v3_fl(r_point, constant_side);
test->dist = len_v2v2(r_point, local_co) / falloff_side;
return true;
}
/* Side, distance to the square XY axis. */
if (max_ff(local_co[0], local_co[1]) > constant_side) {
test->dist = (max_ff(local_co[0], local_co[1]) - constant_side) / falloff_side;
return true;
}
/* Inside the square, constant distance. */
test->dist = 0.0f;
return true;
}
else {
/* Outside the square. */
return false;
}
}
@ -5467,7 +5488,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_cube(&test, vd.co, mat)) {
if (sculpt_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) {
if (plane_point_side_flip(vd.co, test.plane_tool, flip)) {
float intr[3];
float val[3];
@ -7047,12 +7068,11 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
case SCULPT_TOOL_CLAY:
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));
return max_ff(initial_size * 0.30f, 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;
}

View File

@ -280,7 +280,10 @@ void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test);
bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]);
bool sculpt_brush_test_cube(SculptBrushTest *test,
const float co[3],
float local[4][4],
const float roundness);
bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]);
bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v);
bool sculpt_search_circle_cb(PBVHNode *node, void *data_v);

View File

@ -311,7 +311,7 @@ typedef struct Brush {
char mask_tool;
/** Active grease pencil tool. */
char gpencil_tool;
char _pad1[5];
char _pad1[1];
float autosmooth_factor;
@ -330,6 +330,10 @@ typedef struct Brush {
int curve_preset;
int automasking_flags;
/* Factor that controls the shape of the brush tip by rounding the corners of a square. */
/* 0.0 value produces a square, 1.0 produces a circle. */
float tip_roundness;
int elastic_deform_type;
float elastic_deform_volume_preservation;

View File

@ -1943,6 +1943,12 @@ static void rna_def_brush(BlenderRNA *brna)
"Number of segments of the inverse kinematics chain that will deform the mesh");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "tip_roundness", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "tip_roundness");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Tip Roundness", "Roundness of the brush tip");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "auto_smooth_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "autosmooth_factor");
RNA_def_property_float_default(prop, 0);