From ae349eb2d50524b030f702b8ed3fd75531d4db7e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 19 Jan 2022 17:23:36 -0800 Subject: Sculpt: Multires Heal Brush This brush fixes the random spikes that occasionally happen in multires models. These spikes can be nearly impossible to fix manually and can make working with multires a nightmare. --- source/blender/editors/sculpt_paint/sculpt.c | 6 + .../editors/sculpt_paint/sculpt_brush_types.c | 178 +++++++++++++++++++++ .../blender/editors/sculpt_paint/sculpt_intern.h | 4 + 3 files changed, 188 insertions(+) (limited to 'source/blender/editors/sculpt_paint') diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 5ac13ebdd93..9d07471c7bd 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2229,6 +2229,7 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_DRAW_SHARP: case SCULPT_TOOL_LAYER: return alpha * flip * pressure * overlap * feather; + case SCULPT_TOOL_DISPLACEMENT_HEAL: case SCULPT_TOOL_DISPLACEMENT_ERASER: return alpha * pressure * overlap * feather; case SCULPT_TOOL_CLOTH: @@ -3400,6 +3401,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_DISPLACEMENT_SMEAR: SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_DISPLACEMENT_HEAL: + SCULPT_do_displacement_heal_brush(sd, ob, nodes, totnode); + break; case SCULPT_TOOL_PAINT: SCULPT_do_paint_brush(sd, ob, nodes, totnode); break; @@ -3950,6 +3954,8 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Multires Displacement Eraser"; case SCULPT_TOOL_DISPLACEMENT_SMEAR: return "Multires Displacement Smear"; + case SCULPT_TOOL_DISPLACEMENT_HEAL: + return "Multires Heal"; case SCULPT_TOOL_PAINT: return "Paint Brush"; case SCULPT_TOOL_SMEAR: diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c index c2acc361a79..5d0f4f2e30e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c @@ -2845,3 +2845,181 @@ void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Multires Heal Brush + * \{ */ + +BLI_INLINE int grid_xy_to_vertex(int x, int y, int grid_i, int gridsize) +{ + return grid_i * gridsize * gridsize + y * gridsize + x; +} + +typedef struct DisplacementHealTaskData { + Object *ob; + Brush *brush; + Sculpt *sd; + PBVHNode **nodes; + BLI_bitmap *bitmap; + float plane_view[3]; + float bstrength; +} DisplacementHealTaskData; + +static void do_displacement_heal_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + DisplacementHealTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + PBVHNode *node = data->nodes[n]; + + CCGElem **grids; + + int *grid_indices, totgrid, maxgrid, gridsize; + const float bstrength = data->bstrength; + + BKE_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid, &maxgrid, &gridsize, &grids); + + float(*disps)[3] = MEM_calloc_arrayN(gridsize * gridsize, sizeof(float) * 3, __func__); + float(*mats)[16] = MEM_calloc_arrayN(gridsize * gridsize, sizeof(float) * 16, __func__); + float(*limits)[3] = MEM_calloc_arrayN(gridsize * gridsize, sizeof(float) * 3, __func__); + + bool modified = false; + + for (int i = 0; i < totgrid; i++) { + const int grid_i = grid_indices[i]; + + for (int x = 0; x < gridsize; x++) { + for (int y = 0; y < gridsize; y++) { + int vertex = grid_xy_to_vertex(x, y, grid_i, gridsize); + + SubdivCCGCoord coord = {.grid_index = grid_i, .x = x, .y = y}; + int locali = y * gridsize + x; + float mat[3][3], p[3]; + + BKE_subdiv_ccg_get_tangent_matrix(ss->subdiv_ccg, &coord, mat, p); + copy_m3_m3(mats[locali], mat); + + invert_m3(mat); + + float disp[3]; + copy_v3_v3(disp, SCULPT_vertex_co_get(ss, vertex)); + sub_v3_v3(disp, p); + mul_v3_m3v3(disp, mat, disp); + + float test = dot_v3v3(disp, disp); + if (isnan(test) || isinf(test)) { + zero_v3(disp); + } + + copy_v3_v3(disps[locali], disp); + copy_v3_v3(limits[locali], p); + } + } + + for (int x = 0; x < gridsize; x++) { + for (int y = 0; y < gridsize; y++) { + int locali = y * gridsize + x; + + int vertex = grid_xy_to_vertex(x, y, grid_i, gridsize); + float *disp = disps[locali]; + float avg[3] = {0.0f, 0.0f, 0.0f}; + float tot = 0.0f; + + for (int x2 = x - 1; x2 <= x + 1; x2++) { + for (int y2 = y - 1; y2 <= y + 1; y2++) { + if (x2 < 0 || y2 < 0 || x2 >= gridsize || y2 >= gridsize) { + continue; + } + + int local2 = y2 * gridsize + x2; + + add_v3_v3(avg, disps[local2]); + tot += 1.0f; + } + } + + if (tot == 0.0f) { + continue; + } + + mul_v3_fl(avg, 1.0 / tot); + + if (dot_v3v3(avg, avg) == 0.0f || dot_v3v3(disp, disp) == 0.0f) { + continue; + } + + float ratio = len_v3(disp) / len_v3(avg); + + if (ratio < 1.0f) { + continue; + } + + modified = true; + + ratio = pow(ratio, 0.1f); + float tmp[3]; + + copy_v3_v3(tmp, disp); + mul_v3_fl(tmp, 1.0f / ratio); + mul_v3_m3v3(tmp, mats[locali], tmp); + add_v3_v3(tmp, limits[locali]); + + float *co = (float *)SCULPT_vertex_co_get(ss, vertex); + float test = dot_v3v3(co, co); + + if (isnan(test) || isinf(test)) { + copy_v3_v3(co, tmp); + } + else { + interp_v3_v3v3(co, co, tmp, bstrength); + } + } + } + } + + MEM_SAFE_FREE(disps); + MEM_SAFE_FREE(mats); + MEM_SAFE_FREE(limits); + + if (modified) { + BKE_pbvh_node_mark_update(node); + } +} + +void SCULPT_do_displacement_heal_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (!ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { + return; + } + + SCULPT_boundary_info_ensure(ob); + + const int totvert = SCULPT_vertex_count_get(ss); + BLI_bitmap *bitmap = BLI_BITMAP_NEW(totvert, __func__); + const float bstrength = fabsf(ss->cache->bstrength); + + /* paranoia check */ + ss->cache->radius_squared = ss->cache->radius * ss->cache->radius; + + /* Threaded loop over nodes. */ + DisplacementHealTaskData data = {.sd = sd, + .ob = ob, + .brush = brush, + .bstrength = bstrength, + .nodes = nodes, + .bitmap = bitmap}; + + copy_v3_v3(data.plane_view, ss->cache->view_normal); + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_displacement_heal_cb, &settings); + + MEM_SAFE_FREE(bitmap); +} +/** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index f84380b4f64..99cc011ef1d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1719,6 +1719,10 @@ void SCULPT_do_slide_relax_brush(struct Sculpt *sd, struct PBVHNode **nodes, int totnode); +void SCULPT_do_displacement_heal_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); void SCULPT_do_displacement_smear_brush(struct Sculpt *sd, struct Object *ob, struct PBVHNode **nodes, -- cgit v1.2.3