From f2f8c5b2bd984f1034f4ba9999bdc5d3fe72d45a Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Fri, 6 Mar 2020 16:00:33 +0100 Subject: Cleanup: Move Multiplane Scrape brush to its own file --- source/blender/editors/sculpt_paint/CMakeLists.txt | 1 + source/blender/editors/sculpt_paint/paint_cursor.c | 66 +-- source/blender/editors/sculpt_paint/sculpt.c | 383 +---------------- .../blender/editors/sculpt_paint/sculpt_intern.h | 18 + .../sculpt_paint/sculpt_multiplane_scrape.c | 464 +++++++++++++++++++++ 5 files changed, 504 insertions(+), 428 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c (limited to 'source/blender') diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 1f568250f3f..f497b80e0f4 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC paint_vertex_weight_ops.c paint_vertex_weight_utils.c sculpt.c + sculpt_multiplane_scrape.c sculpt_cloth.c sculpt_pose.c sculpt_undo.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 963fc556827..07dfa970404 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1223,70 +1223,6 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession } } -static void sculpt_multiplane_scrape_preview_draw(const uint gpuattr, - SculptSession *ss, - const float outline_col[3], - const float outline_alpha) -{ - float local_mat_inv[4][4]; - invert_m4_m4(local_mat_inv, ss->cache->stroke_local_mat); - GPU_matrix_mul(local_mat_inv); - float angle = ss->cache->multiplane_scrape_angle; - if (ss->cache->pen_flip || ss->cache->invert) { - angle = -angle; - } - - float offset = ss->cache->radius * 0.25f; - - float p[3] = {0.0f, 0.0f, ss->cache->radius}; - float y_axis[3] = {0.0f, 1.0f, 0.0f}; - float p_l[3]; - float p_r[3]; - float area_center[3] = {0.0f, 0.0f, 0.0f}; - rotate_v3_v3v3fl(p_r, p, y_axis, DEG2RADF((angle + 180) * 0.5f)); - rotate_v3_v3v3fl(p_l, p, y_axis, DEG2RADF(-(angle + 180) * 0.5f)); - - immBegin(GPU_PRIM_LINES, 14); - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]); - - immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); - immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); - immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); - - immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - - immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); - immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]); - - immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); - immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]); - - immEnd(); - - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.1f); - immBegin(GPU_PRIM_TRIS, 12); - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]); - immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); - immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); - - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]); - immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); - immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); - immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); - - immEnd(); -} - static bool paint_use_2d_cursor(ePaintMode mode) { if (mode >= PAINT_MODE_TEXTURE_3D) { @@ -1629,7 +1565,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) NULL); GPU_matrix_push(); GPU_matrix_mul(vc.obact->obmat); - sculpt_multiplane_scrape_preview_draw(pos, ss, outline_col, outline_alpha); + SCULPT_multiplane_scrape_preview_draw(pos, ss, outline_col, outline_alpha); GPU_matrix_pop(); GPU_matrix_pop_projection(); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index dcbcdc98ddf..3f60d96b7ce 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1897,7 +1897,7 @@ static void calc_area_center( } } -static void calc_area_normal( +void SCULPT_calc_area_normal( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); @@ -2413,7 +2413,7 @@ static void calc_sculpt_normal( break; case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ob, nodes, totnode, r_area_no); + SCULPT_calc_area_normal(sd, ob, nodes, totnode, r_area_no); break; default: @@ -4690,7 +4690,7 @@ 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 int plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) +int SCULPT_plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { return (!(brush->flag & BRUSH_PLANE_TRIM) || ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); @@ -4705,13 +4705,13 @@ static bool plane_point_side_flip(const float co[3], const float plane[4], const return d <= 0.0f; } -static int plane_point_side(const float co[3], const float plane[4]) +int SCULPT_plane_point_side(const float co[3], const float plane[4]) { float d = plane_point_side_v3(plane, co); return d <= 0.0f; } -static float get_offset(Sculpt *sd, SculptSession *ss) +float SCULPT_brush_plane_offset_get(Sculpt *sd, SculptSession *ss) { Brush *brush = BKE_paint_brush(&sd->paint); @@ -4756,7 +4756,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { + if (SCULPT_plane_trim(ss->cache, brush, val)) { const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -4788,7 +4788,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float area_no[3]; float area_co[3]; - float offset = get_offset(sd, ss); + float offset = SCULPT_brush_plane_offset_get(sd, ss); float displace; float temp[3]; @@ -4944,7 +4944,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) const float initial_radius = fabsf(ss->cache->initial_radius); bool flip = ss->cache->bstrength < 0.0f; - float offset = get_offset(sd, ss); + float offset = SCULPT_brush_plane_offset_get(sd, ss); float displace; float area_no[3]; @@ -5001,349 +5001,6 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BKE_pbvh_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); } -/* -------------------------------------------------------------------- */ - -/** \name Sculpt Multiplane Scrape Brush - * \{ */ - -typedef struct MultiplaneScrapeSampleData { - float area_cos[2][3]; - float area_nos[2][3]; - int area_count[2]; -} MultiplaneScrapeSampleData; - -static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - MultiplaneScrapeSampleData *mssd = tls->userdata_chunk; - float(*mat)[4] = data->mat; - - PBVHVertexIter vd; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); - - /* Apply the brush normal radius to the test before sampling. */ - float test_radius = sqrtf(test.radius_squared); - test_radius *= brush->normal_radius_factor; - test.radius_squared = test_radius * test_radius; - - 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]; - float normal[3]; - if (vd.no) { - normal_short_to_float_v3(normal, vd.no); - } - else { - copy_v3_v3(normal, vd.fno); - } - mul_v3_m4v3(local_co, mat, vd.co); - /* Use the brush falloff to weight the sampled normals. */ - const float fade = SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - tls->thread_id); - - /* Sample the normal and area of the +X and -X axis individually. */ - if (local_co[0] > 0.0f) { - madd_v3_v3fl(mssd->area_nos[0], normal, fade); - add_v3_v3(mssd->area_cos[0], vd.co); - mssd->area_count[0]++; - } - else { - madd_v3_v3fl(mssd->area_nos[1], normal, fade); - add_v3_v3(mssd->area_cos[1], vd.co); - mssd->area_count[1]++; - } - } - BKE_pbvh_vertex_iter_end; - } -} - -static void calc_multiplane_scrape_surface_reduce(const void *__restrict UNUSED(userdata), - void *__restrict chunk_join, - void *__restrict chunk) -{ - MultiplaneScrapeSampleData *join = chunk_join; - MultiplaneScrapeSampleData *mssd = chunk; - - add_v3_v3(join->area_cos[0], mssd->area_cos[0]); - add_v3_v3(join->area_cos[1], mssd->area_cos[1]); - - add_v3_v3(join->area_nos[0], mssd->area_nos[0]); - add_v3_v3(join->area_nos[1], mssd->area_nos[1]); - - join->area_count[0] += mssd->area_count[0]; - join->area_count[1] += mssd->area_count[1]; -} - -static void do_multiplane_scrape_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; - float(*scrape_planes)[4] = data->multiplane_scrape_planes; - - float angle = data->multiplane_scrape_angle; - - PBVHVertexIter vd; - float(*proxy)[3]; - const float bstrength = fabsf(ss->cache->bstrength); - - 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); - - 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]; - bool deform = false; - - mul_v3_m4v3(local_co, mat, vd.co); - - if (local_co[0] > 0.0f) { - deform = !plane_point_side(vd.co, scrape_planes[0]); - } - else { - deform = !plane_point_side(vd.co, scrape_planes[1]); - } - - if (angle < 0.0f) { - deform = true; - } - - if (deform) { - float intr[3]; - float val[3]; - - if (local_co[0] > 0.0f) { - closest_to_plane_normalized_v3(intr, scrape_planes[0], vd.co); - } - else { - closest_to_plane_normalized_v3(intr, scrape_planes[1], vd.co); - } - - sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - /* Deform the local space along the Y axis to avoid artifacts on curved strokes. */ - /* This produces a not round brush tip. */ - local_co[1] *= 2.0f; - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - len_v3(local_co), - 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 void do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const bool flip = (ss->cache->bstrength < 0.0f); - const float radius = flip ? -ss->cache->radius : ss->cache->radius; - const float offset = get_offset(sd, ss); - const float displace = -radius * offset; - - /* The sculpt-plane normal (whatever its set to) */ - float area_no_sp[3]; - - /* Geometry normal. */ - float area_no[3]; - float area_co[3]; - - float temp[3]; - float mat[4][4]; - - SCULPT_calc_brush_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->multiplane_scrape_angle = 0.0f; - return; - } - - if (is_zero_v3(ss->cache->grab_delta_symmetry)) { - return; - } - - 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); - invert_m4(mat); - - /* Update matrix for the cursor preview. */ - if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0) { - copy_m4_m4(ss->cache->stroke_local_mat, mat); - } - - /* Dynamic mode. */ - - if (brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_DYNAMIC) { - /* Sample the individual normal and area center of the two areas at both sides of the cursor. - */ - SculptThreadedTaskData sample_data = { - .sd = NULL, - .ob = ob, - .brush = brush, - .nodes = nodes, - .totnode = totnode, - .mat = mat, - }; - - MultiplaneScrapeSampleData mssd = {{{0}}}; - - PBVHParallelSettings sample_settings; - BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce; - sample_settings.userdata_chunk = &mssd; - sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData); - - BKE_pbvh_parallel_range( - 0, totnode, &sample_data, calc_multiplane_scrape_surface_task_cb, &sample_settings); - - float sampled_plane_normals[2][3]; - float sampled_plane_co[2][3]; - float sampled_cv[2][3]; - float mid_co[3]; - - /* Use the area center of both planes to detect if we are sculpting along a concave or convex - * edge. */ - mul_v3_v3fl(sampled_plane_co[0], mssd.area_cos[0], 1.0f / (float)mssd.area_count[0]); - mul_v3_v3fl(sampled_plane_co[1], mssd.area_cos[1], 1.0f / (float)mssd.area_count[1]); - mid_v3_v3v3(mid_co, sampled_plane_co[0], sampled_plane_co[1]); - - /* Calculate the scrape planes angle based on the sampled normals. */ - mul_v3_v3fl(sampled_plane_normals[0], mssd.area_nos[0], 1.0f / (float)mssd.area_count[0]); - mul_v3_v3fl(sampled_plane_normals[1], mssd.area_nos[1], 1.0f / (float)mssd.area_count[1]); - normalize_v3(sampled_plane_normals[0]); - normalize_v3(sampled_plane_normals[1]); - - float sampled_angle = angle_v3v3(sampled_plane_normals[0], sampled_plane_normals[1]); - copy_v3_v3(sampled_cv[0], area_no); - sub_v3_v3v3(sampled_cv[1], ss->cache->location, mid_co); - - sampled_angle += DEG2RADF(brush->multiplane_scrape_angle) * ss->cache->pressure; - - /* Invert the angle if we are sculpting along a concave edge. */ - if (dot_v3v3(sampled_cv[0], sampled_cv[1]) < 0.0f) { - sampled_angle = -sampled_angle; - } - - /* In dynamic mode, set the angle to 0 when inverting the brush, so you can trim plane - * surfaces without changing the brush. */ - if (flip) { - sampled_angle = 0.0f; - } - else { - copy_v3_v3(area_co, ss->cache->location); - } - - /* Interpolate between the previous and new sampled angles to avoid artifacts when if angle - * difference between two samples is too big. */ - ss->cache->multiplane_scrape_angle = interpf( - RAD2DEGF(sampled_angle), ss->cache->multiplane_scrape_angle, 0.2f); - } - else { - - /* Standard mode: Scrape with the brush property fixed angle. */ - copy_v3_v3(area_co, ss->cache->location); - ss->cache->multiplane_scrape_angle = brush->multiplane_scrape_angle; - if (flip) { - ss->cache->multiplane_scrape_angle *= -1.0f; - } - } - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .mat = mat, - .multiplane_scrape_angle = ss->cache->multiplane_scrape_angle, - }; - - /* Calculate the final left and right scrape planes. */ - float plane_no[3]; - float plane_no_rot[3]; - float y_axis[3] = {0.0f, 1.0f, 0.0f}; - float mat_inv[4][4]; - invert_m4_m4(mat_inv, mat); - - mul_v3_mat3_m4v3(plane_no, mat, area_no); - rotate_v3_v3v3fl( - plane_no_rot, plane_no, y_axis, DEG2RADF(-ss->cache->multiplane_scrape_angle * 0.5f)); - mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot); - normalize_v3(plane_no); - plane_from_point_normal_v3(data.multiplane_scrape_planes[1], area_co, plane_no); - - mul_v3_mat3_m4v3(plane_no, mat, area_no); - rotate_v3_v3v3fl( - plane_no_rot, plane_no, y_axis, DEG2RADF(ss->cache->multiplane_scrape_angle * 0.5f)); - mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot); - normalize_v3(plane_no); - plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no); - - PBVHParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings); -} - -/** \} */ - static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -5377,7 +5034,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { + if (SCULPT_plane_trim(ss->cache, brush, val)) { /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */ const float fade = bstrength * SCULPT_brush_strength_factor(ss, @@ -5409,7 +5066,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t const bool flip = (ss->cache->bstrength < 0.0f); const float radius = flip ? -ss->cache->radius : ss->cache->radius; - const float offset = get_offset(sd, ss); + const float offset = SCULPT_brush_plane_offset_get(sd, ss); const float displace = radius * (0.25f + offset); /* The sculpt-plane normal (whatever its set to). */ @@ -5427,7 +5084,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t SCULPT_calc_brush_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); + SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no); } else { copy_v3_v3(area_no, area_no_sp); @@ -5502,7 +5159,7 @@ static void do_fill_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_sq_fn(&test, vd.co)) { - if (plane_point_side(vd.co, test.plane_tool)) { + if (SCULPT_plane_point_side(vd.co, test.plane_tool)) { float intr[3]; float val[3]; @@ -5510,7 +5167,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { + if (SCULPT_plane_trim(ss->cache, brush, val)) { const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -5542,7 +5199,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float area_no[3]; float area_co[3]; - float offset = get_offset(sd, ss); + float offset = SCULPT_brush_plane_offset_get(sd, ss); float displace; @@ -5594,7 +5251,7 @@ static void do_scrape_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_sq_fn(&test, vd.co)) { - if (!plane_point_side(vd.co, test.plane_tool)) { + if (!SCULPT_plane_point_side(vd.co, test.plane_tool)) { float intr[3]; float val[3]; @@ -5602,7 +5259,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { + if (SCULPT_plane_trim(ss->cache, brush, val)) { const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -5634,7 +5291,7 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float area_no[3]; float area_co[3]; - float offset = get_offset(sd, ss); + float offset = SCULPT_brush_plane_offset_get(sd, ss); float displace; @@ -5754,7 +5411,7 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to Brush *brush = BKE_paint_brush(&sd->paint); const float radius = ss->cache->radius; - const float offset = get_offset(sd, ss); + const float offset = SCULPT_brush_plane_offset_get(sd, ss); const float displace = radius * (0.25f + offset); /* Sampled geometry normal and area center. */ @@ -5770,7 +5427,7 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to SCULPT_calc_brush_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); + SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no); } else { copy_v3_v3(area_no, area_no_sp); @@ -6161,7 +5818,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe do_clay_strips_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_MULTIPLANE_SCRAPE: - do_multiplane_scrape_brush(sd, ob, nodes, totnode); + SCULPT_do_multiplane_scrape_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_CLAY_THUMB: do_clay_thumb_brush(sd, ob, nodes, totnode); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e65b7404558..612908edfa1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -155,12 +155,23 @@ void SCULPT_calc_brush_plane(struct Sculpt *sd, int totnode, float r_area_no[3], float r_area_co[3]); + +void SCULPT_calc_area_normal( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]); + int SCULPT_nearest_vertex_get(struct Sculpt *sd, struct Object *ob, const float co[3], float max_distance, bool use_original); +int SCULPT_plane_point_side(const float co[3], const float plane[4]); +int SCULPT_plane_trim(const struct StrokeCache *cache, + const struct Brush *brush, + const float val[3]); + +float SCULPT_brush_plane_offset_get(Sculpt *sd, SculptSession *ss); + ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3]); bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3], const char symm); bool SCULPT_is_symmetry_iteration_valid(char i, char symm); @@ -239,6 +250,13 @@ struct SculptPoseIKChain *SCULPT_pose_ik_chain_init(struct Sculpt *sd, const float radius); void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); +/* Multiplane Scrape Brush. */ +void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); +void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr, + SculptSession *ss, + const float outline_col[3], + const float outline_alpha); + /* Sculpt Visibility API */ void SCULPT_visibility_sync_all_face_sets_to_vertices(struct SculptSession *ss); void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c new file mode 100644 index 00000000000..038d0e5a08c --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -0,0 +1,464 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_task.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_brush_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "GPU_draw.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "bmesh.h" + +#include +#include + + +typedef struct MultiplaneScrapeSampleData { + float area_cos[2][3]; + float area_nos[2][3]; + int area_count[2]; +} MultiplaneScrapeSampleData; + +static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + MultiplaneScrapeSampleData *mssd = tls->userdata_chunk; + float(*mat)[4] = data->mat; + + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + + /* Apply the brush normal radius to the test before sampling. */ + float test_radius = sqrtf(test.radius_squared); + test_radius *= brush->normal_radius_factor; + test.radius_squared = test_radius * test_radius; + + 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]; + float normal[3]; + if (vd.no) { + normal_short_to_float_v3(normal, vd.no); + } + else { + copy_v3_v3(normal, vd.fno); + } + mul_v3_m4v3(local_co, mat, vd.co); + /* Use the brush falloff to weight the sampled normals. */ + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + tls->thread_id); + + /* Sample the normal and area of the +X and -X axis individually. */ + if (local_co[0] > 0.0f) { + madd_v3_v3fl(mssd->area_nos[0], normal, fade); + add_v3_v3(mssd->area_cos[0], vd.co); + mssd->area_count[0]++; + } + else { + madd_v3_v3fl(mssd->area_nos[1], normal, fade); + add_v3_v3(mssd->area_cos[1], vd.co); + mssd->area_count[1]++; + } + } + BKE_pbvh_vertex_iter_end; + } +} + +static void calc_multiplane_scrape_surface_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + MultiplaneScrapeSampleData *join = chunk_join; + MultiplaneScrapeSampleData *mssd = chunk; + + add_v3_v3(join->area_cos[0], mssd->area_cos[0]); + add_v3_v3(join->area_cos[1], mssd->area_cos[1]); + + add_v3_v3(join->area_nos[0], mssd->area_nos[0]); + add_v3_v3(join->area_nos[1], mssd->area_nos[1]); + + join->area_count[0] += mssd->area_count[0]; + join->area_count[1] += mssd->area_count[1]; +} + +static void do_multiplane_scrape_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; + float(*scrape_planes)[4] = data->multiplane_scrape_planes; + + float angle = data->multiplane_scrape_angle; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = fabsf(ss->cache->bstrength); + + 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); + + 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]; + bool deform = false; + + mul_v3_m4v3(local_co, mat, vd.co); + + if (local_co[0] > 0.0f) { + deform = !SCULPT_plane_point_side(vd.co, scrape_planes[0]); + } + else { + deform = !SCULPT_plane_point_side(vd.co, scrape_planes[1]); + } + + if (angle < 0.0f) { + deform = true; + } + + if (deform) { + float intr[3]; + float val[3]; + + if (local_co[0] > 0.0f) { + closest_to_plane_normalized_v3(intr, scrape_planes[0], vd.co); + } + else { + closest_to_plane_normalized_v3(intr, scrape_planes[1], vd.co); + } + + sub_v3_v3v3(val, intr, vd.co); + if (SCULPT_plane_trim(ss->cache, brush, val)) { + /* Deform the local space along the Y axis to avoid artifacts on curved strokes. */ + /* This produces a not round brush tip. */ + local_co[1] *= 2.0f; + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + len_v3(local_co), + 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; +} + + +/* Public functions. */ + +/* Main Brush Function. */ +void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const bool flip = (ss->cache->bstrength < 0.0f); + const float radius = flip ? -ss->cache->radius : ss->cache->radius; + const float offset = SCULPT_brush_plane_offset_get(sd, ss); + const float displace = -radius * offset; + + /* The sculpt-plane normal (whatever its set to) */ + float area_no_sp[3]; + + /* Geometry normal. */ + float area_no[3]; + float area_co[3]; + + float temp[3]; + float mat[4][4]; + + SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) { + SCULPT_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->multiplane_scrape_angle = 0.0f; + return; + } + + if (is_zero_v3(ss->cache->grab_delta_symmetry)) { + return; + } + + 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); + invert_m4(mat); + + /* Update matrix for the cursor preview. */ + if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0) { + copy_m4_m4(ss->cache->stroke_local_mat, mat); + } + + /* Dynamic mode. */ + + if (brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_DYNAMIC) { + /* Sample the individual normal and area center of the two areas at both sides of the cursor. + */ + SculptThreadedTaskData sample_data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .mat = mat, + }; + + MultiplaneScrapeSampleData mssd = {{{0}}}; + + PBVHParallelSettings sample_settings; + BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce; + sample_settings.userdata_chunk = &mssd; + sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData); + + BKE_pbvh_parallel_range( + 0, totnode, &sample_data, calc_multiplane_scrape_surface_task_cb, &sample_settings); + + float sampled_plane_normals[2][3]; + float sampled_plane_co[2][3]; + float sampled_cv[2][3]; + float mid_co[3]; + + /* Use the area center of both planes to detect if we are sculpting along a concave or convex + * edge. */ + mul_v3_v3fl(sampled_plane_co[0], mssd.area_cos[0], 1.0f / (float)mssd.area_count[0]); + mul_v3_v3fl(sampled_plane_co[1], mssd.area_cos[1], 1.0f / (float)mssd.area_count[1]); + mid_v3_v3v3(mid_co, sampled_plane_co[0], sampled_plane_co[1]); + + /* Calculate the scrape planes angle based on the sampled normals. */ + mul_v3_v3fl(sampled_plane_normals[0], mssd.area_nos[0], 1.0f / (float)mssd.area_count[0]); + mul_v3_v3fl(sampled_plane_normals[1], mssd.area_nos[1], 1.0f / (float)mssd.area_count[1]); + normalize_v3(sampled_plane_normals[0]); + normalize_v3(sampled_plane_normals[1]); + + float sampled_angle = angle_v3v3(sampled_plane_normals[0], sampled_plane_normals[1]); + copy_v3_v3(sampled_cv[0], area_no); + sub_v3_v3v3(sampled_cv[1], ss->cache->location, mid_co); + + sampled_angle += DEG2RADF(brush->multiplane_scrape_angle) * ss->cache->pressure; + + /* Invert the angle if we are sculpting along a concave edge. */ + if (dot_v3v3(sampled_cv[0], sampled_cv[1]) < 0.0f) { + sampled_angle = -sampled_angle; + } + + /* In dynamic mode, set the angle to 0 when inverting the brush, so you can trim plane + * surfaces without changing the brush. */ + if (flip) { + sampled_angle = 0.0f; + } + else { + copy_v3_v3(area_co, ss->cache->location); + } + + /* Interpolate between the previous and new sampled angles to avoid artifacts when if angle + * difference between two samples is too big. */ + ss->cache->multiplane_scrape_angle = interpf( + RAD2DEGF(sampled_angle), ss->cache->multiplane_scrape_angle, 0.2f); + } + else { + + /* Standard mode: Scrape with the brush property fixed angle. */ + copy_v3_v3(area_co, ss->cache->location); + ss->cache->multiplane_scrape_angle = brush->multiplane_scrape_angle; + if (flip) { + ss->cache->multiplane_scrape_angle *= -1.0f; + } + } + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .mat = mat, + .multiplane_scrape_angle = ss->cache->multiplane_scrape_angle, + }; + + /* Calculate the final left and right scrape planes. */ + float plane_no[3]; + float plane_no_rot[3]; + float y_axis[3] = {0.0f, 1.0f, 0.0f}; + float mat_inv[4][4]; + invert_m4_m4(mat_inv, mat); + + mul_v3_mat3_m4v3(plane_no, mat, area_no); + rotate_v3_v3v3fl( + plane_no_rot, plane_no, y_axis, DEG2RADF(-ss->cache->multiplane_scrape_angle * 0.5f)); + mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot); + normalize_v3(plane_no); + plane_from_point_normal_v3(data.multiplane_scrape_planes[1], area_co, plane_no); + + mul_v3_mat3_m4v3(plane_no, mat, area_no); + rotate_v3_v3v3fl( + plane_no_rot, plane_no, y_axis, DEG2RADF(ss->cache->multiplane_scrape_angle * 0.5f)); + mul_v3_mat3_m4v3(plane_no, mat_inv, plane_no_rot); + normalize_v3(plane_no); + plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no); + + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings); +} + +void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr, + SculptSession *ss, + const float outline_col[3], + const float outline_alpha) +{ + float local_mat_inv[4][4]; + invert_m4_m4(local_mat_inv, ss->cache->stroke_local_mat); + GPU_matrix_mul(local_mat_inv); + float angle = ss->cache->multiplane_scrape_angle; + if (ss->cache->pen_flip || ss->cache->invert) { + angle = -angle; + } + + float offset = ss->cache->radius * 0.25f; + + float p[3] = {0.0f, 0.0f, ss->cache->radius}; + float y_axis[3] = {0.0f, 1.0f, 0.0f}; + float p_l[3]; + float p_r[3]; + float area_center[3] = {0.0f, 0.0f, 0.0f}; + rotate_v3_v3v3fl(p_r, p, y_axis, DEG2RADF((angle + 180) * 0.5f)); + rotate_v3_v3v3fl(p_l, p, y_axis, DEG2RADF(-(angle + 180) * 0.5f)); + + immBegin(GPU_PRIM_LINES, 14); + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]); + + immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); + immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); + immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); + + immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + + immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); + immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]); + + immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); + immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]); + + immEnd(); + + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.1f); + immBegin(GPU_PRIM_TRIS, 12); + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + immVertex3f(gpuattr, p_r[0], p_r[1] + offset, p_r[2]); + immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); + immVertex3f(gpuattr, p_r[0], p_r[1] - offset, p_r[2]); + + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + immVertex3f(gpuattr, p_l[0], p_l[1] + offset, p_l[2]); + immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] + offset, area_center[2]); + immVertex3f(gpuattr, area_center[0], area_center[1] - offset, area_center[2]); + immVertex3f(gpuattr, p_l[0], p_l[1] - offset, p_l[2]); + + immEnd(); +} -- cgit v1.2.3