Sculpt: Split normal radius and area radius

This enables an extra layer of control in the sculpt brushes.
For now it is enabled only in Scrape, but it should work in all brushes (like normal radius). In the future it may also be enabled in other brushes.
You can tweak in this property in the scrape brush to achieve a much better behavior when working on curve surfaces and control how much volume you want to trim. In most cases, it also fixes the bug where the brush keeps trimming in the same area without disabling accumulate.
It should be possible to fix some other artifacts in other brushes by tweaking this default property.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D5993
This commit is contained in:
Pablo Dobarro 2019-12-16 23:16:21 +01:00
parent 6ee6a42d10
commit df45257ec5
Notes: blender-bot 2023-02-14 06:00:51 +01:00
Referenced by issue #73741, Grid Floor clips unselected mesh in Wireframe mode.
10 changed files with 105 additions and 24 deletions

View File

@ -628,10 +628,14 @@ def brush_settings(layout, context, brush, popover=False):
layout.separator()
if brush.sculpt_tool == 'SCRAPE':
row = layout.row()
row.prop(brush, "area_radius_factor", slider=True)
row = layout.row()
row.prop(brush, "invert_to_scrape_fill", text="Invert to Fill")
if brush.sculpt_tool == 'FILL':
row = layout.row()
row.prop(brush, "area_radius_factor", slider=True)
row = layout.row()
row.prop(brush, "invert_to_scrape_fill", text="Invert to Scrape")

View File

@ -82,6 +82,7 @@ static void brush_defaults(Brush *brush)
FROM_DEFAULT(topology_rake_factor);
FROM_DEFAULT(crease_pinch_factor);
FROM_DEFAULT(normal_radius_factor);
FROM_DEFAULT(area_radius_factor);
FROM_DEFAULT(sculpt_plane);
FROM_DEFAULT(plane_offset);
FROM_DEFAULT(clone.alpha);

View File

@ -105,6 +105,7 @@ MINLINE void add_v4_fl(float r[4], float f);
MINLINE void add_v2_v2(float r[2], const float a[2]);
MINLINE void add_v2_v2_db(double r[2], const double a[2]);
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2]);
MINLINE void add_v2_v2_int(int r[2], const int a[2]);
MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2]);
MINLINE void add_v3_v3(float r[3], const float a[3]);
MINLINE void add_v3_v3_db(double r[3], const double a[3]);

View File

@ -373,6 +373,12 @@ MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
r[1] = a[1] + b[1];
}
MINLINE void add_v2_v2_int(int r[2], const int a[2])
{
r[0] = r[0] + a[0];
r[1] = r[1] + a[1];
}
MINLINE void add_v2_v2v2_int(int r[2], const int a[2], const int b[2])
{
r[0] = a[0] + b[0];

View File

@ -4254,6 +4254,12 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
if (br->ob_mode & OB_MODE_SCULPT && br->area_radius_factor == 0.0f) {
br->area_radius_factor = 0.5f;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 282, 2)) {

View File

@ -1439,7 +1439,8 @@ typedef struct AreaNormalCenterTLSData {
/* 0 = towards view, 1 = flipped */
float area_cos[2][3];
float area_nos[2][3];
int area_count[2];
int count_no[2];
int count_co[2];
} AreaNormalCenterTLSData;
static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
@ -1456,24 +1457,45 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
SculptUndoNode *unode = NULL;
bool use_original = false;
bool normal_test_r, area_test_r;
if (ss->cache && ss->cache->original) {
unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
use_original = (unode->co || unode->bm_entry);
}
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
SculptBrushTest normal_test;
SculptBrushTestFn sculpt_brush_normal_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
ss, &normal_test, data->brush->falloff_shape);
/* Update the test radius to sample the normal using the normal radius of the brush. */
if (data->brush->ob_mode == OB_MODE_SCULPT) {
float test_radius = sqrtf(test.radius_squared);
/* Layer brush produces artifacts with normal radius. */
float test_radius = sqrtf(normal_test.radius_squared);
/* Layer brush produces artifacts with normal and area radius. */
if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) {
test_radius *= data->brush->normal_radius_factor;
}
test.radius_squared = test_radius * test_radius;
normal_test.radius_squared = test_radius * test_radius;
}
SculptBrushTest area_test;
SculptBrushTestFn sculpt_brush_area_test_sq_fn = sculpt_brush_test_init_with_falloff_shape(
ss, &area_test, data->brush->falloff_shape);
if (data->brush->ob_mode == OB_MODE_SCULPT) {
float test_radius = sqrtf(area_test.radius_squared);
/* Layer brush produces artifacts with normal and area radius */
if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) {
/* Enable area radius control only on Scrape for now */
if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) &&
data->brush->area_radius_factor > 0.0f) {
test_radius *= data->brush->area_radius_factor;
}
else {
test_radius *= data->brush->normal_radius_factor;
}
}
area_test.radius_squared = test_radius * test_radius;
}
/* When the mesh is edited we can't rely on original coords
@ -1493,22 +1515,26 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
};
float co[3];
closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri));
closest_on_tri_to_point_v3(co, normal_test.location, UNPACK3(co_tri));
if (sculpt_brush_test_sq_fn(&test, co)) {
normal_test_r = sculpt_brush_normal_test_sq_fn(&normal_test, co);
area_test_r = sculpt_brush_area_test_sq_fn(&area_test, co);
if (normal_test_r || area_test_r) {
float no[3];
int flip_index;
normal_tri_v3(no, UNPACK3(co_tri));
flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f);
if (use_area_cos) {
if (use_area_cos && area_test_r) {
add_v3_v3(anctd->area_cos[flip_index], co);
anctd->count_co[flip_index] += 1;
}
if (use_area_nos) {
if (use_area_nos && normal_test_r) {
add_v3_v3(anctd->area_nos[flip_index], no);
anctd->count_no[flip_index] += 1;
}
anctd->area_count[flip_index] += 1;
}
}
}
@ -1532,7 +1558,10 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
co = vd.co;
}
if (sculpt_brush_test_sq_fn(&test, co)) {
normal_test_r = sculpt_brush_normal_test_sq_fn(&normal_test, co);
area_test_r = sculpt_brush_area_test_sq_fn(&area_test, co);
if (normal_test_r || area_test_r) {
float no_buf[3];
const float *no;
int flip_index;
@ -1555,13 +1584,14 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
flip_index = (dot_v3v3(ss->cache ? ss->cache->view_normal : ss->cursor_view_normal, no) <=
0.0f);
if (use_area_cos) {
if (use_area_cos && area_test_r) {
add_v3_v3(anctd->area_cos[flip_index], co);
anctd->count_co[flip_index] += 1;
}
if (use_area_nos) {
if (use_area_nos && normal_test_r) {
add_v3_v3(anctd->area_nos[flip_index], no);
anctd->count_no[flip_index] += 1;
}
anctd->area_count[flip_index] += 1;
}
}
BKE_pbvh_vertex_iter_end;
@ -1584,8 +1614,8 @@ static void calc_area_normal_and_center_reduce(const void *__restrict UNUSED(use
add_v3_v3(join->area_nos[1], anctd->area_nos[1]);
/* Weights. */
join->area_count[0] += anctd->area_count[0];
join->area_count[1] += anctd->area_count[1];
add_v2_v2_int(join->count_no, anctd->count_no);
add_v2_v2_int(join->count_co, anctd->count_co);
}
static void calc_area_center(
@ -1607,7 +1637,7 @@ static void calc_area_center(
.use_area_cos = true,
};
AreaNormalCenterTLSData anctd = {{{0}}};
AreaNormalCenterTLSData anctd = {0};
PBVHParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
@ -1618,14 +1648,21 @@ static void calc_area_center(
/* For flatten center. */
for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
if (anctd.area_count[n] != 0) {
mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.area_count[n]);
if (anctd.count_co[n] != 0) {
mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.count_co[n]);
break;
}
}
if (n == 2) {
zero_v3(r_area_co);
}
if (anctd.count_co[0] == 0 && anctd.count_co[1] == 0) {
if (ss->cache) {
copy_v3_v3(r_area_co, ss->cache->location);
}
}
}
static void calc_area_normal(
@ -1711,15 +1748,22 @@ static void calc_area_normal_and_center(
/* For flatten center. */
for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) {
if (anctd.area_count[n] != 0) {
mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.area_count[n]);
if (anctd.count_co[n] != 0) {
mul_v3_v3fl(r_area_co, anctd.area_cos[n], 1.0f / anctd.count_co[n]);
break;
}
}
if (n == 2) {
zero_v3(r_area_co);
}
if (anctd.count_co[0] == 0 && anctd.count_co[1] == 0) {
if (ss->cache) {
copy_v3_v3(r_area_co, ss->cache->location);
}
}
/* For area normal. */
for (n = 0; n < ARRAY_SIZE(anctd.area_nos); n++) {
if (normalize_v3_v3(r_area_no, anctd.area_nos[n]) != 0.0f) {

View File

@ -207,6 +207,13 @@ typedef struct SculptThreadedTaskData {
bool use_area_cos;
bool use_area_nos;
/* 0=towards view, 1=flipped */
float (*area_cos)[3];
float (*area_nos)[3];
int *count_no;
int *count_co;
bool any_vertex_sampled;
float *prev_mask;

View File

@ -46,6 +46,7 @@
.topology_rake_factor = 0.0f, \
.crease_pinch_factor = 0.5f, \
.normal_radius_factor = 0.5f, \
.area_radius_factor = 0.5f, \
.sculpt_plane = SCULPT_DISP_DIR_AREA, \
/* How far above or below the plane that is found by averaging the faces. */ \
.plane_offset = 0.0f, \

View File

@ -311,7 +311,7 @@ typedef struct Brush {
char mask_tool;
/** Active grease pencil tool. */
char gpencil_tool;
char _pad1[1];
char _pad1[5];
float autosmooth_factor;
@ -320,6 +320,7 @@ typedef struct Brush {
float crease_pinch_factor;
float normal_radius_factor;
float area_radius_factor;
float plane_trim;
/** Affectable height of brush (layer height for layer tool, i.e.). */

View File

@ -1980,6 +1980,16 @@ static void rna_def_brush(BlenderRNA *brna)
"used to sample the normal");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "area_radius_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "area_radius_factor");
RNA_def_property_range(prop, 0.0f, 2.0f);
RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.001, 3);
RNA_def_property_ui_text(prop,
"Area Radius",
"Ratio between the brush radius and the radius that is going to be "
"used to sample the area center");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "stencil_pos", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "stencil_pos");
RNA_def_property_array(prop, 2);