Sculpt-dev: New displacement heal brush

This brush goes through all
the grids inside each PBVH node under
the brush, converts coordinates to
tangent space, filters out extreme displacements
and then converts back.

Simple, but very effective.

TODO: make this into a mesh filter too.
This commit is contained in:
Joseph Eagar 2021-12-21 11:31:59 -05:00
parent 084a967ab4
commit 8f18ee27e7
10 changed files with 241 additions and 19 deletions

View File

@ -315,6 +315,17 @@ void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
float r_point[3]);
void BKE_subdiv_ccg_eval_limit_point_and_derivatives(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
float r_point[3],
float r_dPdu[3],
float r_dPdv[3]);
void BKE_subdiv_ccg_get_tangent_matrix(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
float mat[3][3],
float r_point[3]);
typedef enum SubdivCCGAdjacencyType {
SUBDIV_CCG_ADJACENT_NONE,
SUBDIV_CCG_ADJACENT_VERTEX,

View File

@ -33,6 +33,8 @@
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "multires_inline.h"
#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
#include "BKE_global.h"
@ -2101,4 +2103,38 @@ void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG *subdiv_ccg,
BKE_subdiv_eval_limit_point(subdiv, ptex_face_index, u, v, r_point);
}
void BKE_subdiv_ccg_eval_limit_point_and_derivatives(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
float r_point[3],
float r_dPdu[3],
float r_dPdv[3])
{
Subdiv *subdiv = subdiv_ccg->subdiv;
int ptex_face_index;
float u, v;
subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v);
BKE_subdiv_eval_limit_point_and_derivatives(
subdiv, ptex_face_index, u, v, r_point, r_dPdu, r_dPdv);
}
void BKE_subdiv_ccg_get_tangent_matrix(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
float mat[3][3],
float r_point[3])
{
int ptex_face_index;
float u, v;
float du[3], dv[3];
const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord->grid_index);
const SubdivCCGFace *faces = subdiv_ccg->faces;
const SubdivCCGFace *face = &faces[face_index];
const float corner = coord->grid_index - face->start_grid_index;
subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v);
BKE_subdiv_ccg_eval_limit_point_and_derivatives(subdiv_ccg, coord, r_point, du, dv);
BKE_multires_construct_tangent_matrix(mat, du, dv, corner);
}
/** \} */

View File

@ -202,13 +202,13 @@ extern "C" {
BLI_lfmempool *BLI_lfmempool_create(int esize, int psize)
{
LockFreePool *pool = OBJECT_GUARDED_NEW(LockFreePool, esize, psize);
LockFreePool *pool = MEM_new<LockFreePool>(__func__, esize, psize);
return reinterpret_cast<BLI_lfmempool *>(pool);
}
void BLI_lfmempool_destroy(BLI_lfmempool *pool)
{
OBJECT_GUARDED_DELETE(cast_pool(pool), LockFreePool);
MEM_delete<LockFreePool>(cast_pool(pool));
}
void *BLI_lfmempool_alloc(BLI_lfmempool *pool)

View File

@ -752,6 +752,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
brush.sculpt.cloth
brush.sculpt.crease
brush.sculpt.displacement_eraser
brush.sculpt.displacement_heal
brush.sculpt.displacement_smear
brush.sculpt.draw
brush.sculpt.draw_face_sets

View File

@ -3986,6 +3986,7 @@ static float brush_strength(const Sculpt *sd,
case SCULPT_TOOL_LAYER:
case SCULPT_TOOL_SYMMETRIZE:
return alpha * flip * pressure * overlap * feather;
case SCULPT_TOOL_DISPLACEMENT_HEAL:
case SCULPT_TOOL_DISPLACEMENT_ERASER:
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_FAIRING:
@ -4122,15 +4123,15 @@ static float brush_strength(const Sculpt *sd,
}
}
float SCULPT_brush_strength_factor(SculptSession *ss,
const Brush *br,
const float brush_point[3],
const float len,
const short vno[3],
const float fno[3],
const float mask,
const SculptVertRef vertex_index,
const int thread_id)
ATTR_NO_OPT float SCULPT_brush_strength_factor(SculptSession *ss,
const Brush *br,
const float brush_point[3],
const float len,
const short vno[3],
const float fno[3],
const float mask,
const SculptVertRef vertex_index,
const int thread_id)
{
StrokeCache *cache = ss->cache;
const Scene *scene = cache->vc->scene;
@ -5486,6 +5487,9 @@ void do_brush_action(
SCULPT_enhance_details_brush(
sd, ob, nodes, totnode, SCULPT_get_int(ss, enhance_detail_presteps, sd, brush));
break;
case SCULPT_TOOL_DISPLACEMENT_HEAL:
SCULPT_do_displacement_heal_brush(sd, ob, nodes, totnode);
break;
}
bool apply_autosmooth = !ELEM(SCULPT_get_tool(ss, brush),
@ -6052,6 +6056,8 @@ static void SCULPT_run_command(
case SCULPT_TOOL_ENHANCE_DETAILS:
SCULPT_enhance_details_brush(
sd, ob, nodes, totnode, SCULPT_get_int(ss, enhance_detail_presteps, sd, brush));
case SCULPT_TOOL_DISPLACEMENT_HEAL:
SCULPT_do_displacement_heal_brush(sd, ob, nodes, totnode);
break;
}

View File

@ -82,6 +82,7 @@
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_subdiv_ccg.h"
#include "BKE_subdiv_eval.h"
#include "BKE_subsurf.h"
#include "DEG_depsgraph.h"
@ -318,7 +319,6 @@ static void sculpt_project_v3_normal_align(SculptSession *ss,
/************************************** Brushes ******************************/
/* -------------------------------------------------------------------- */
/** \name Sculpt Draw Brush
* \{ */
@ -622,7 +622,7 @@ void SCULPT_do_twist_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
int totbit = 0;
for (int i = 0; i < 3; i++) {
if (ss->cache->mirror_symmetry_pass & (1<<i)) {
if (ss->cache->mirror_symmetry_pass & (1 << i)) {
totbit++;
}
}
@ -1205,9 +1205,9 @@ static void calc_clay_surface_reduce(const void *__restrict UNUSED(userdata),
join->plane_dist[1] = MIN2(csd->plane_dist[1], join->plane_dist[1]);
}
static void do_clay_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
ATTR_NO_OPT static void do_clay_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@ -1262,7 +1262,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_node_mark_update(data->nodes[n]);
}
void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
ATTR_NO_OPT void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@ -3620,7 +3620,6 @@ void SCULPT_fairing_brush_exec_fairing_for_cache(Sculpt *sd, Object *ob)
/** \} */
/* -------------------------------------------------------------------- */
void SCULPT_do_auto_face_set(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
@ -4235,3 +4234,166 @@ void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
break;
}
}
BLI_INLINE SculptVertRef grid_xy_to_vertex(int x, int y, int grid_i, int gridsize)
{
return (SculptVertRef){.i = 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++) {
SculptVertRef 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);
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;
SculptVertRef 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);
interp_v3_v3v3(co, co, tmp, bstrength);
//copy_v3_v3(co, tmp);
}
}
}
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);
}

View File

@ -2333,3 +2333,7 @@ void SCULPT_enhance_details_brush(struct Sculpt *sd,
struct PBVHNode **nodes,
const int totnode,
int presteps);
void SCULPT_do_displacement_heal_brush(struct Sculpt *sd,
struct Object *ob,
struct PBVHNode **nodes,
int totnode);

View File

@ -510,7 +510,8 @@ typedef enum eBrushSculptTool {
SCULPT_TOOL_DYNTOPO = 41,
SCULPT_TOOL_AUTO_FSET = 42,
SCULPT_TOOL_RELAX = 43,
SCULPT_TOOL_ENHANCE_DETAILS = 44
SCULPT_TOOL_ENHANCE_DETAILS = 44,
SCULPT_TOOL_DISPLACEMENT_HEAL = 45
} eBrushSculptTool;
/* Brush.uv_sculpt_tool */

View File

@ -147,6 +147,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{SCULPT_TOOL_ARRAY, "ARRAY", ICON_BRUSH_SCULPT_DRAW, "Array", ""},
{SCULPT_TOOL_VCOL_BOUNDARY, "VCOL_BOUNDARY", ICON_BRUSH_VCOL_BOUNDARY, "Sharpen Color Boundary", ""},
{SCULPT_TOOL_UV_SMOOTH, "UV_SMOOTH", ICON_BRUSH_GRAB, "UV Smooth", ""},
{SCULPT_TOOL_DISPLACEMENT_HEAL, "DISPLACEMENT_HEAL", ICON_BRUSH_GRAB, "Displacement Heal", ""},
{0, NULL, 0, NULL, NULL},
};
/* clang-format on */