Sculpt: New "auto-fset" setting

Added a new "auto face set" setting for brushes.
It basically invokes the draw face set tool,
with a few differences:

* The painted face set is fixed by user setting.
* More then one face set can be created,
  these are assigned based on distance to
  the stroke line.
* TODO: write a proper API for wrangling stroke
        curves (should interpolate at least G2!).

The point of this is to enable better hard surface
brushes.  Since the settings are extremely finicky
I've added an example of one (based on clay strips)
to startup.blend.

The necassary steps to make a hard brush out of this
are as follows:

1. Autosmooth: ~0.4;
2. Autosmooth radius scale: ~1.5.
3. Auto fset: on.
4. Hard edge mode: on (but turn off inherit).
This commit is contained in:
Joseph Eagar 2021-10-26 01:55:13 -07:00
parent 3bcfe9b5ac
commit a7947a233d
10 changed files with 454 additions and 46 deletions

Binary file not shown.

View File

@ -1183,12 +1183,15 @@ def brush_settings(layout, context, brush, popover=False):
# auto_smooth_factor and use_inverse_smooth_pressure
if capabilities.has_auto_smooth:
box = layout.column() # .column() is a bit more compact
box = layout.box().column() # .column() is a bit more compact
box.label(text="Auto-Smooth")
UnifiedPaintPanel.prop_unified(box,
context,
brush,
"auto_smooth_factor",
text="Factor",
pressure_name="use_inverse_smooth_pressure",
slider=True,)
@ -1220,6 +1223,7 @@ def brush_settings(layout, context, brush, popover=False):
context,
brush,
"auto_smooth_projection",
text="Projection",
slider=True)
if advanced:
@ -1239,11 +1243,59 @@ def brush_settings(layout, context, brush, popover=False):
"projection",
slider=True)
UnifiedPaintPanel.prop_unified(layout,
context,
brush,
"use_smoothed_rake")
box = layout.box().column() # .column() is a bit more compact
box.label(text="Auto Face Set")
UnifiedPaintPanel.prop_unified(box,
context,
brush,
"use_autofset")
if UnifiedPaintPanel.get_channel_value(context, brush, "use_autofset"):
UnifiedPaintPanel.channel_unified(box,
context,
brush,
"autofset_radius_scale",
slider=True)
UnifiedPaintPanel.channel_unified(box,
context,
brush,
"autofset_use_spacing")
if UnifiedPaintPanel.get_channel_value(context, brush, "autofset_use_spacing"):
UnifiedPaintPanel.channel_unified(box,
context,
brush,
"autofset_spacing")
UnifiedPaintPanel.channel_unified(box,
context,
brush,
"autofset_start")
UnifiedPaintPanel.channel_unified(box,
context,
brush,
"autofset_count")
UnifiedPaintPanel.channel_unified(box,
context,
brush,
"autofset_curve")
if capabilities.has_vcol_boundary_smooth:
layout.prop(brush, "vcol_boundary_factor", slider=True)
UnifiedPaintPanel.prop_unified(layout,
context,
brush,
"vcol_boundary_factor",
slider=True)
if (capabilities.has_topology_rake and context.sculpt_object.use_dynamic_topology_sculpting):
box = layout.column() # .column() is a bit more compact
box = layout.box().column() # .column() is a bit more compact
box.label(text="Topology Rake")
#box.prop(brush, "topology_rake_factor", slider=True)
UnifiedPaintPanel.prop_unified(box,
@ -1251,7 +1303,7 @@ def brush_settings(layout, context, brush, popover=False):
brush,
"topology_rake_factor",
slider=True,
text="Topology Rake")
text="Factor")
if advanced:
box.prop(brush, "use_custom_topology_rake_spacing", text="Custom Spacing")

View File

@ -207,8 +207,8 @@ places in rna_engine_codebase are relevent:
"used for DynTopo", 1.0f, 0.001f, 5.0f, 0.01f, 2.0f, false, BRUSH_CHANNEL_INHERIT)
MAKE_BOOL(dyntopo_disable_smooth, "Disable Dyntopo Smooth", "Disable the small amount of smoothing dyntopo applies to improve numerical stability", false)
MAKE_FLOAT_EX(projection, "Projection", "Amount of volume preserving projection", 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, false)
MAKE_FLOAT_EX(autosmooth_projection, "Auto-Smooth Projection", "Amount of volume preserving projection for autosmooth", 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, false)
MAKE_FLOAT_EX(topology_rake_projection, "Rake Projection", "Amount of volume preserving projection", 0.975f, 0.0f, 1.0f, 0.0f, 1.0f, false)
MAKE_FLOAT_EX(autosmooth_projection, "Projection", "Amount of volume preserving projection for autosmooth", 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, false)
MAKE_FLOAT_EX(topology_rake_projection, "Projection", "Amount of volume preserving projection", 0.975f, 0.0f, 1.0f, 0.0f, 1.0f, false)
MAKE_FLOAT(fset_slide, "Face Set Projection", "Stick face set boundaries to surface of mesh", 1.0f, 0.0f, 1.0f)
MAKE_FLOAT(boundary_smooth, "Boundary Smooth", "Smooth hard boundaries", 0.0f, 0.0f, 1.0f)
MAKE_BOOL(topology_rake_use_spacing, "Use Rake Spacing", "Use custom spacing for topology rake", false)
@ -599,6 +599,14 @@ MAKE_ENUM_EX(sharp_mode, "Sharp Mode", "", 0, 0, {
{-1}
})
MAKE_BOOL_EX(use_autofset, "Auto Face Set", "Automatically make a face set", false, 0)
MAKE_FLOAT_EX_FLAG(autofset_radius_scale, "Radius Scale", "Scale brush radius for auto face set", 1.0f, 0.001f, 10.0f, 0.01f, 3.0f, false, false)
MAKE_CURVE(autofset_curve, "Curve", "", CURVE_PRESET_LINE)
MAKE_INT_EX(autofset_count, "Face Set Count", "", 2, 1, 10, 1, 5)
MAKE_INT_EX(autofset_start, "Face Set Start", "", 2, 1, 1024, 1, 1024)
MAKE_FLOAT_EX_FLAG(autofset_spacing, "Spacing", "Spacing for auto face set", 4, 1, 1000, 1, 300, false, 0)
MAKE_BOOL_EX(autofset_use_spacing, "Use Spacing", "Use spacing for auto face set", false, 0)
//MAKE_FLOAT3_EX
/* clang-format on */
#if defined(BRUSH_CHANNEL_DEFINE_TYPES) || defined(BRUSH_CHANNEL_DEFINE_EXTERNAL)

View File

@ -1533,6 +1533,42 @@ void BKE_brush_commandset_inherit_all_mappings(BrushChannelSet *chset)
}
}
static void commandlist_add_auto_fset(BrushChannelSet *chset,
BrushCommandList *cl,
Brush *brush,
int tool,
BrushMappingData *mapdata)
{
if (!BRUSHSET_GET_INT(chset, use_autofset, NULL)) {
return;
}
BrushCommand *cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true),
SCULPT_TOOL_AUTO_FSET);
float radius = BRUSHSET_GET_FLOAT(chset, radius, NULL) *
BRUSHSET_GET_FLOAT(chset, autofset_radius_scale, NULL);
float spacing = BRUSHSET_GET_FLOAT(chset, spacing, NULL);
if (BRUSHSET_GET_INT(chset, autofset_use_spacing, NULL)) {
spacing = BRUSHSET_GET_FLOAT(chset, autofset_spacing, NULL);
float_set_uninherit(cmd->params, spacing, spacing);
}
float_set_uninherit(cmd->params, radius, radius);
BrushChannel *ch = BRUSHSET_ENSURE_BUILTIN(cmd->params, falloff_curve);
BrushChannel *ch2 = BRUSHSET_LOOKUP(chset, autofset_curve);
if (ch2) {
BKE_brush_channel_curve_assign(ch, &ch2->curve);
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
}
else {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
}
static void commandlist_add_dyntopo(BrushChannelSet *chset,
BrushCommandList *cl,
Brush *brush,
@ -1588,6 +1624,9 @@ static void bke_builtin_commandlist_create_paint(Brush *brush,
bool hard_edge_mode = BRUSHSET_GET_INT(chset, hard_edge_mode, NULL);
commandlist_add_dyntopo(chset, cl, brush, tool, hard_edge_mode, radius);
/*build auto fset command*/
commandlist_add_auto_fset(chset, cl, brush, tool, mapdata);
float autosmooth = BRUSHSET_GET_FLOAT(chset, autosmooth, NULL);
if (autosmooth > 0.0f) {
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true), SCULPT_TOOL_SMOOTH);
@ -1627,8 +1666,6 @@ static void bke_builtin_commandlist_create_paint(Brush *brush,
}
#undef GETF
// float
}
void BKE_builtin_apply_hard_edge_mode(BrushChannelSet *chset, bool do_apply)
@ -1719,6 +1756,9 @@ void BKE_builtin_commandlist_create(Brush *brush,
autosmooth_spacing = BKE_brush_channelset_get_float(chset, "spacing", NULL);
}
/*build auto fset command*/
commandlist_add_auto_fset(chset, cl, brush, tool, mapdata);
float autosmooth = BKE_brush_channelset_get_float(chset, "autosmooth", NULL);
if (!no_autosmooth && autosmooth > 0.0f) {
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true), SCULPT_TOOL_SMOOTH);

View File

@ -257,8 +257,12 @@ static bool check_builtin_init()
SUBTYPE_SET(radius, BRUSH_CHANNEL_PIXEL);
SUBTYPE_SET(spacing, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(autofset_spacing, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(autosmooth_spacing, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(topology_rake_spacing, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(autofset_radius_scale, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(autosmooth_radius_scale, BRUSH_CHANNEL_PERCENT);
SUBTYPE_SET(topology_rake_radius_scale, BRUSH_CHANNEL_PERCENT);
@ -1137,6 +1141,14 @@ void BKE_brush_builtin_patch(Brush *brush, int tool)
ADDCH(smooth_stroke_radius);
ADDCH(smooth_deform_type);
ADDCH(use_autofset);
ADDCH(autofset_radius_scale);
ADDCH(autofset_curve);
ADDCH(autofset_count);
ADDCH(autofset_start);
ADDCH(autofset_spacing);
ADDCH(autofset_use_spacing);
switch (tool) {
case SCULPT_TOOL_CLAY:
if (set_mappings) {

View File

@ -215,6 +215,34 @@ int SCULPT_get_vector_intern(
}
}
BrushChannel *SCULPT_get_final_channel_intern(const SculptSession *ss,
const char *idname,
const Sculpt *sd,
const Brush *br)
{
BrushChannel *ch = NULL;
if (ss->cache && ss->cache->channels_final) {
ch = BKE_brush_channelset_lookup(ss->cache->channels_final, idname);
}
else if (br && br->channels && sd && sd->channels) {
ch = BKE_brush_channelset_lookup(br->channels, idname);
BrushChannel *ch2 = BKE_brush_channelset_lookup(sd->channels, idname);
if (ch2 && (!ch || (ch->flag & BRUSH_CHANNEL_INHERIT))) {
ch = ch2;
}
}
else if (br && br->channels) {
ch = BKE_brush_channelset_lookup(br->channels, idname);
}
else if (sd && sd->channels) {
ch = BKE_brush_channelset_lookup(sd->channels, idname);
}
return ch;
}
/* Sculpt PBVH abstraction API
*
* This is read-only, for writing use PBVH vertex iterators. There vd.index matches
@ -5100,7 +5128,7 @@ static void do_brush_action_task_cb(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
/* Face Sets modifications do a single undo push */
if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_DRAW_FACE_SETS, SCULPT_TOOL_AUTO_FSET)) {
BKE_pbvh_node_mark_redraw(data->nodes[n]);
/* Draw face sets in smooth mode moves the vertices. */
if (ss->cache->alt_smooth) {
@ -5708,7 +5736,7 @@ static void get_nodes_undo(Sculpt *sd,
BKE_pbvh_node_mark_update_color(nodes[i]);
}
}
else if (tool == SCULPT_TOOL_DRAW_FACE_SETS) {
else if (ELEM(tool, SCULPT_TOOL_DRAW_FACE_SETS, SCULPT_TOOL_AUTO_FSET)) {
for (int i = 0; i < totnode; i++) {
if (ss->cache->alt_smooth) {
SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_COORDS);
@ -6010,6 +6038,9 @@ static void SCULPT_run_command(
sculpt_topology_update(sd, ob, brush, ups, NULL);
// do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
break;
case SCULPT_TOOL_AUTO_FSET:
SCULPT_do_auto_face_set(sd, ob, nodes, totnode);
break;
}
if (ss->needs_pbvh_rebuild) {
@ -6474,6 +6505,9 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,
flip_v3_v3(cache->view_normal, cache->true_view_normal, symm);
flip_v3_v3(cache->view_origin, cache->true_view_origin, symm);
flip_v3_v3(cache->prev_grab_delta_symmetry, cache->prev_grab_delta, symm);
flip_v3_v3(cache->next_grab_delta_symmetry, cache->next_grab_delta, symm);
flip_v3_v3(cache->initial_location, cache->true_initial_location, symm);
flip_v3_v3(cache->initial_normal, cache->true_initial_normal, symm);
@ -6798,6 +6832,8 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Topology Rake";
case SCULPT_TOOL_DYNTOPO:
return "DynTopo";
case SCULPT_TOOL_AUTO_FSET:
return "Auto Face Set";
}
return "Sculpting";
@ -7201,25 +7237,30 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
};
int tool = brush->sculpt_tool;
if (!ELEM(tool,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_CLOTH,
SCULPT_TOOL_NUDGE,
SCULPT_TOOL_CLAY_STRIPS,
SCULPT_TOOL_TWIST,
SCULPT_TOOL_PINCH,
SCULPT_TOOL_MULTIPLANE_SCRAPE,
SCULPT_TOOL_CLAY_THUMB,
SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_POSE,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_ARRAY,
SCULPT_TOOL_THUMB) &&
!sculpt_brush_use_topology_rake(ss, brush)) {
bool bad = !ELEM(tool,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_CLOTH,
SCULPT_TOOL_NUDGE,
SCULPT_TOOL_CLAY_STRIPS,
SCULPT_TOOL_TWIST,
SCULPT_TOOL_PINCH,
SCULPT_TOOL_MULTIPLANE_SCRAPE,
SCULPT_TOOL_CLAY_THUMB,
SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_POSE,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_ARRAY,
SCULPT_TOOL_THUMB);
bad = bad && !sculpt_brush_use_topology_rake(ss, brush);
bad = bad && !SCULPT_get_bool(ss, use_autofset, NULL, brush);
if (bad) {
return;
}
float grab_location[3], imat[4][4], delta[3], loc[3];
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
@ -7237,6 +7278,8 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
add_v3_v3(cache->true_location, cache->grab_delta);
}
copy_v3_v3(cache->prev_grab_delta, cache->grab_delta);
/* Compute 3d coordinate at same z from original location + mouse. */
mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location);
ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->region, loc, mouse, grab_location);
@ -7331,6 +7374,41 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
copy_v3_v3(cache->rake_data.follow_co, grab_location);
}
if (SCULPT_stroke_is_first_brush_step(cache)) {
copy_v3_v3(cache->prev_grab_delta, cache->grab_delta);
for (int i = 0; i < GRAB_DELTA_MA_SIZE; i++) {
copy_v3_v3(cache->grab_delta_avg[i], cache->grab_delta);
}
}
// XXX implement me
if (SCULPT_get_int(ss, use_smoothed_rake, NULL, brush)) {
// delay by one so we can have a useful value for next_grab_delta
float grab_delta[3] = {0.0f, 0.0f, 0.0f};
for (int i = 0; i < GRAB_DELTA_MA_SIZE; i++) {
add_v3_v3(grab_delta, cache->grab_delta_avg[i]);
}
mul_v3_fl(grab_delta, 1.0f / (float)GRAB_DELTA_MA_SIZE);
copy_v3_v3(cache->grab_delta_avg[cache->grab_delta_avg_cur], cache->grab_delta);
cache->grab_delta_avg_cur = (cache->grab_delta_avg_cur + 1) % GRAB_DELTA_MA_SIZE;
copy_v3_v3(cache->grab_delta, grab_delta);
zero_v3(cache->next_grab_delta);
for (int i = 0; i < GRAB_DELTA_MA_SIZE; i++) {
add_v3_v3(cache->next_grab_delta, cache->grab_delta_avg[i]);
}
mul_v3_fl(cache->next_grab_delta, 1.0f / (float)GRAB_DELTA_MA_SIZE);
}
else {
copy_v3_v3(cache->next_grab_delta, cache->grab_delta);
}
if (!sculpt_brush_needs_rake_rotation(brush)) {
return;
}
@ -7368,6 +7446,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
cache->is_rake_rotation_valid = true;
}
}
sculpt_rake_data_update(&cache->rake_data, grab_location);
}

View File

@ -4105,4 +4105,83 @@ void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **node
BLI_task_parallel_range(0, totnode, &data, do_displacement_eraser_brush_task_cb_ex, &settings);
}
void SCULPT_do_auto_face_set(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
#if 0
if (BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH) {
void cxx_do_draw_brush(Sculpt * sd, Object * ob, PBVHNode * *nodes, int totnode);
cxx_do_draw_brush(sd, ob, nodes, totnode);
return;
}
#endif
SculptSession *ss = ob->sculpt;
Brush *brush = ss->cache ? ss->cache->brush : BKE_paint_brush(&sd->paint);
float directions[3][3];
for (int i = 0; i < 3; i++) {
float direction[3];
switch (i) {
case 0:
copy_v3_v3(direction, ss->cache->prev_grab_delta_symmetry);
break;
case 1:
copy_v3_v3(direction, ss->cache->grab_delta_symmetry);
break;
case 2:
copy_v3_v3(direction, ss->cache->next_grab_delta_symmetry);
break;
}
float tmp[3];
mul_v3_v3fl(
tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction));
sub_v3_v3(direction, tmp);
normalize_v3(direction);
copy_v3_v3(directions[i], direction);
}
BrushChannel *curve_ch = SCULPT_get_final_channel(ss, autofset_curve, sd, brush);
CurveMapping *cuma = BKE_brush_channel_curvemapping_get(&curve_ch->curve, false);
if (cuma) { // ensure cuma is ready for evaluation
BKE_curvemapping_init(cuma);
}
/* Cancel if there's no grab data. */
if (is_zero_v3(directions[1])) {
return;
}
/* XXX: this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise
* initialize before threads so they can do curve mapping. */
BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */
SculptFaceSetDrawData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
.totnode = totnode,
.use_fset_curve = true,
.use_fset_strength = false,
.bstrength = 1.0f,
.faceset = SCULPT_get_int(ss, autofset_start, sd, brush),
.count = SCULPT_get_int(ss, autofset_count, sd, brush),
.prev_stroke_direction = directions[0],
.stroke_direction = directions[1],
.next_stroke_direction = directions[2],
.curve_ch = curve_ch,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings);
}
/** \} */

View File

@ -301,22 +301,89 @@ static void sculpt_faceset_bm_end(SculptSession *ss, BMesh *bm)
/* Draw Face Sets Brush. */
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
ATTR_NO_OPT static int new_fset_apply_curve(SculptSession *ss,
SculptFaceSetDrawData *data,
int new_fset,
float poly_center[3],
float no[3],
SculptBrushTest *test,
BrushChannel *curve_ch,
int count)
{
SculptThreadedTaskData *data = userdata;
float fade2;
float tmp[3];
float n[3];
sub_v3_v3v3(tmp, poly_center, test->location);
cross_v3_v3v3(n, no, data->stroke_direction);
normalize_v3(n);
// find t along brush line
float t = dot_v3v3(data->stroke_direction, tmp) / ss->cache->radius;
CLAMP(t, -1.0f, 1.0f);
t = t * 0.5 + 0.5;
// find start and end points;
float start[3], end[3];
copy_v3_v3(start, ss->cache->last_location);
copy_v3_v3(end, ss->cache->location);
madd_v3_v3fl(start, data->prev_stroke_direction, 0.5f * ss->cache->radius);
madd_v3_v3fl(end, data->next_stroke_direction, 0.5f * ss->cache->radius);
float co[3];
// interpolate direction and pos across stroke line
float dir[3];
if (t < 0.5) {
interp_v3_v3v3(co, start, test->location, t * 2.0f);
interp_v3_v3v3(dir, data->prev_stroke_direction, data->stroke_direction, t * 2.0f);
}
else {
interp_v3_v3v3(co, test->location, end, (t - 0.5f) * 2.0f);
interp_v3_v3v3(dir, data->stroke_direction, data->next_stroke_direction, (t - 0.5f) * 2.0f);
}
sub_v3_v3v3(tmp, poly_center, co);
normalize_v3(dir);
// get final distance from stroke curve
cross_v3_v3v3(n, no, dir);
normalize_v3(n);
fade2 = fabsf(dot_v3v3(n, tmp)) / ss->cache->radius;
CLAMP(fade2, 0.0f, 1.0f);
fade2 = BKE_brush_channel_curve_evaluate(curve_ch, fade2, 1.0f);
new_fset += (int)((1.0f - fade2) * (float)count);
return new_fset;
}
void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptFaceSetDrawData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength;
const float bstrength = data->bstrength;
const bool use_fset_strength = data->use_fset_strength;
const bool use_fset_curve = data->use_fset_curve;
const int count = data->count;
const int active_fset = data->faceset;
BrushChannel *curve_ch = data->curve_ch;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
const int active_fset = abs(ss->cache->paint_face_set);
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
const float test_limit = 0.05f;
@ -366,6 +433,16 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.vertex,
thread_id);
int new_fset = active_fset;
if (use_fset_curve) {
float no[3];
SCULPT_vertex_normal_get(ss, vd.vertex, no);
new_fset = new_fset_apply_curve(
ss, data, new_fset, poly_center, no, &test, curve_ch, count);
}
if (fade > test_limit && ss->face_sets[vert_map->indices[j]] > 0) {
bool ok = true;
@ -422,7 +499,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
}
if (ok) {
ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
ss->face_sets[vert_map->indices[j]] = new_fset;
}
}
}
@ -447,9 +524,19 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.vertex,
thread_id);
int new_fset = active_fset;
if (use_fset_curve) {
float no[3];
SCULPT_vertex_normal_get(ss, vd.vertex, no);
new_fset = new_fset_apply_curve(
ss, data, new_fset, poly_center, no, &test, curve_ch, count);
}
int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
if (fade > test_limit && fset > 0) {
if ((!use_fset_strength || fade > test_limit) && fset > 0) {
BMLoop *l = f->l_first;
bool ok = true;
@ -501,7 +588,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
} while ((l = l->next) != f->l_first);
if (ok) {
BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, active_fset);
BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, new_fset);
}
}
}
@ -521,9 +608,18 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
int new_fset = active_fset;
if (fade > 0.05f) {
SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
if (use_fset_curve) {
float no[3];
SCULPT_vertex_normal_get(ss, vd.vertex, no);
new_fset = new_fset_apply_curve(
ss, data, new_fset, ss->cache->location, no, &test, curve_ch, count);
}
if (!use_fset_strength || fade > test_limit) {
SCULPT_vertex_face_set_set(ss, vd.vertex, new_fset);
}
}
}
@ -540,7 +636,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptFaceSetDrawData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float bstrength = ss->cache->bstrength;
@ -606,12 +702,15 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
};
SculptFaceSetDrawData data = {.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
.faceset = abs(ss->cache->paint_face_set),
.use_fset_curve = false,
.use_fset_strength = true,
.bstrength = ss->cache->bstrength,
.count = 1};
bool threaded = true;

View File

@ -306,6 +306,13 @@ int SCULPT_get_vector_intern(
#define SCULPT_get_vector(ss, idname, out, sd, br) \
SCULPT_get_vector_intern(ss, BRUSH_BUILTIN_##idname, out, sd, br)
BrushChannel *SCULPT_get_final_channel_intern(const SculptSession *ss,
const char *idname,
const Sculpt *sd,
const Brush *br);
#define SCULPT_get_final_channel(ss, idname, sd, br) \
SCULPT_get_final_channel_intern(ss, BRUSH_BUILTIN_##idname, sd, br)
SculptCornerType SCULPT_vertex_is_corner(const SculptSession *ss,
const SculptVertRef index,
SculptCornerType cornertype);
@ -1188,6 +1195,7 @@ bool SCULPT_pbvh_calc_area_normal(const struct Brush *brush,
#define SCULPT_CLAY_STABILIZER_LEN 10
#define SCULPT_SPEED_MA_SIZE 4
#define GRAB_DELTA_MA_SIZE 3
typedef struct AutomaskingSettings {
/* Flags from eAutomasking_flag. */
@ -1269,6 +1277,12 @@ typedef struct StrokeCache {
float grab_delta[3], grab_delta_symmetry[3];
float old_grab_location[3], orig_grab_location[3];
// next_grab_delta is same as grab_delta except in smooth rake mode
float prev_grab_delta[3], next_grab_delta[3];
float prev_grab_delta_symmetry[3], next_grab_delta_symmetry[3];
float grab_delta_avg[GRAB_DELTA_MA_SIZE][3];
int grab_delta_avg_cur;
/* screen-space rotation defined by mouse motion */
float rake_rotation[4], rake_rotation_symmetry[4];
bool is_rake_rotation_valid;
@ -2163,7 +2177,31 @@ void SCULPT_bmesh_topology_rake(struct Sculpt *sd,
void SCULPT_stroke_cache_snap_context_init(struct bContext *C, struct Object *ob);
void SCULPT_fairing_brush_exec_fairing_for_cache(struct Sculpt *sd, struct Object *ob);
void SCULPT_do_auto_face_set(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* end sculpt_brushes.c stuff */
void SCULPT_OT_brush_stroke(struct wmOperatorType *ot);
typedef struct SculptFaceSetDrawData {
struct Sculpt *sd;
struct Object *ob;
PBVHNode **nodes;
int totnode;
struct Brush *brush;
float bstrength;
int faceset;
int count;
bool use_fset_curve;
bool use_fset_strength;
float *prev_stroke_direction;
float *stroke_direction;
float *next_stroke_direction;
struct BrushChannel *curve_ch;
} SculptFaceSetDrawData;
void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const struct TaskParallelTLS *__restrict tls);

View File

@ -507,7 +507,8 @@ typedef enum eBrushSculptTool {
SCULPT_TOOL_UV_SMOOTH = 39,
SCULPT_TOOL_TOPOLOGY_RAKE = 40,
SCULPT_TOOL_DYNTOPO = 41
SCULPT_TOOL_DYNTOPO = 41,
SCULPT_TOOL_AUTO_FSET = 42
} eBrushSculptTool;
/* Brush.uv_sculpt_tool */