Sculpt: Pinch only in the direction perpendicular to the stroke

By pinching this way, we can fix some artifacts when sculpting following
the topology direction. It does not make much difference with dyntopo/
remesher, but I think this should improve the quality of the brush when
working with Multires.

Reviewed By: JulienKaspar, jbakker

Differential Revision: https://developer.blender.org/D6587
This commit is contained in:
Pablo Dobarro 2020-02-02 20:11:51 +01:00
parent e77e6ba308
commit ee5c13c45c
2 changed files with 146 additions and 89 deletions

View File

@ -3230,6 +3230,91 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
}
}
static void calc_sculpt_plane(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3])
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 &&
ss->cache->tile_pass == 0 &&
(ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_PLANE) ||
!(brush->flag & BRUSH_ORIGINAL_NORMAL))) {
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
copy_v3_v3(r_area_no, ss->cache->true_view_normal);
break;
case SCULPT_DISP_DIR_X:
ARRAY_SET_ITEMS(r_area_no, 1.0f, 0.0f, 0.0f);
break;
case SCULPT_DISP_DIR_Y:
ARRAY_SET_ITEMS(r_area_no, 0.0f, 1.0f, 0.0f);
break;
case SCULPT_DISP_DIR_Z:
ARRAY_SET_ITEMS(r_area_no, 0.0f, 0.0f, 1.0f);
break;
case SCULPT_DISP_DIR_AREA:
calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co);
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal);
normalize_v3(r_area_no);
}
break;
default:
break;
}
/* For flatten center. */
/* Flatten center has not been calculated yet if we are not using the area normal. */
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) {
calc_area_center(sd, ob, nodes, totnode, r_area_co);
}
/* For area normal. */
if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
}
else {
copy_v3_v3(ss->cache->sculpt_normal, r_area_no);
}
/* For flatten center. */
if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_PLANE)) {
copy_v3_v3(r_area_co, ss->cache->last_center);
}
else {
copy_v3_v3(ss->cache->last_center, r_area_co);
}
}
else {
/* For area normal. */
copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
/* For flatten center. */
copy_v3_v3(r_area_co, ss->cache->last_center);
/* For area normal. */
flip_v3(r_area_no, ss->cache->mirror_symmetry_pass);
/* For flatten center. */
flip_v3(r_area_co, ss->cache->mirror_symmetry_pass);
/* For area normal. */
mul_m4_v3(ss->cache->symm_rot_mat, r_area_no);
/* For flatten center. */
mul_m4_v3(ss->cache->symm_rot_mat, r_area_co);
/* Shift the plane for the current tile. */
add_v3_v3(r_area_co, ss->cache->plane_offset);
}
}
/** \} */
/**
@ -3354,6 +3439,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float(*stroke_xz)[3] = data->stroke_xz;
PBVHVertexIter vd;
float(*proxy)[3];
@ -3365,6 +3451,11 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
float x_object_space[3];
float z_object_space[3];
copy_v3_v3(x_object_space, stroke_xz[0]);
copy_v3_v3(z_object_space, stroke_xz[1]);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
@ -3377,13 +3468,26 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
vd.mask ? *vd.mask : 0.0f,
vd.index,
tls->thread_id);
float val[3];
float disp_center[3];
float x_disp[3];
float z_disp[3];
/* Calcualte displacement from the vertex to the brush center. */
sub_v3_v3v3(disp_center, test.location, vd.co);
/* Project the displacement into the X vector (aligned to the stroke). */
mul_v3_v3fl(x_disp, x_object_space, dot_v3v3(disp_center, x_object_space));
/* Project the displacement into the Z vector (aligned to the surface normal). */
mul_v3_v3fl(z_disp, z_object_space, dot_v3v3(disp_center, z_object_space));
/* Add the two projected vectors to calculate the final displacement. The Y component is
* removed */
add_v3_v3v3(disp_center, x_disp, z_disp);
sub_v3_v3v3(val, test.location, vd.co);
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
project_plane_v3_v3v3(val, val, ss->cache->view_normal);
project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal);
}
mul_v3_v3fl(proxy[vd.i], val, fade);
mul_v3_v3fl(proxy[vd.i], disp_center, fade);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@ -3395,13 +3499,45 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
float area_no[3];
float area_co[3];
float mat[4][4];
calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co);
/* delay the first daub because grab delta is not setup */
if (ss->cache->first_time) {
return;
}
if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
return;
}
/* Init mat */
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);
float stroke_xz[2][3];
normalize_v3_v3(stroke_xz[0], mat[0]);
normalize_v3_v3(stroke_xz[1], mat[2]);
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
.stroke_xz = stroke_xz,
};
PBVHParallelSettings settings;
@ -4771,91 +4907,6 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
BKE_pbvh_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings);
}
static void calc_sculpt_plane(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3])
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 &&
ss->cache->tile_pass == 0 &&
(ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_PLANE) ||
!(brush->flag & BRUSH_ORIGINAL_NORMAL))) {
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
copy_v3_v3(r_area_no, ss->cache->true_view_normal);
break;
case SCULPT_DISP_DIR_X:
ARRAY_SET_ITEMS(r_area_no, 1.0f, 0.0f, 0.0f);
break;
case SCULPT_DISP_DIR_Y:
ARRAY_SET_ITEMS(r_area_no, 0.0f, 1.0f, 0.0f);
break;
case SCULPT_DISP_DIR_Z:
ARRAY_SET_ITEMS(r_area_no, 0.0f, 0.0f, 1.0f);
break;
case SCULPT_DISP_DIR_AREA:
calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co);
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal);
normalize_v3(r_area_no);
}
break;
default:
break;
}
/* For flatten center. */
/* Flatten center has not been calculated yet if we are not using the area normal. */
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) {
calc_area_center(sd, ob, nodes, totnode, r_area_co);
}
/* For area normal. */
if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
}
else {
copy_v3_v3(ss->cache->sculpt_normal, r_area_no);
}
/* For flatten center. */
if ((!ss->cache->first_time) && (brush->flag & BRUSH_ORIGINAL_PLANE)) {
copy_v3_v3(r_area_co, ss->cache->last_center);
}
else {
copy_v3_v3(ss->cache->last_center, r_area_co);
}
}
else {
/* For area normal. */
copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
/* For flatten center. */
copy_v3_v3(r_area_co, ss->cache->last_center);
/* For area normal. */
flip_v3(r_area_no, ss->cache->mirror_symmetry_pass);
/* For flatten center. */
flip_v3(r_area_co, ss->cache->mirror_symmetry_pass);
/* For area normal. */
mul_m4_v3(ss->cache->symm_rot_mat, r_area_no);
/* For flatten center. */
mul_m4_v3(ss->cache->symm_rot_mat, r_area_co);
/* Shift the plane for the current tile. */
add_v3_v3(r_area_co, ss->cache->plane_offset);
}
}
static int plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3])
{
return (!(brush->flag & BRUSH_PLANE_TRIM) ||
@ -7137,6 +7188,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_NUDGE,
SCULPT_TOOL_CLAY_STRIPS,
SCULPT_TOOL_PINCH,
SCULPT_TOOL_MULTIPLANE_SCRAPE,
SCULPT_TOOL_CLAY_THUMB,
SCULPT_TOOL_SNAKE_HOOK,
@ -7174,6 +7226,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
add_v3_v3(cache->grab_delta, delta);
break;
case SCULPT_TOOL_CLAY_STRIPS:
case SCULPT_TOOL_PINCH:
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
case SCULPT_TOOL_CLAY_THUMB:
case SCULPT_TOOL_NUDGE:

View File

@ -202,6 +202,10 @@ typedef struct SculptThreadedTaskData {
float (*mat)[4];
float (*vertCos)[3];
/* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to
* the stroke direction are needed. */
float (*stroke_xz)[3];
int filter_type;
float filter_strength;