Sculpt: Normal-based automasking modes

Two new normal-based automasking modes.

The first mode, "brush", compares vertex normals with the initial
normal at the beginning of the brush stroke.

The second, "view", compares vertex normals with the view normal.
If "occlusion" is on then rays will be shot from each vertex to test
if it is occluded by other geometry (note: this can be very slow).\
Only geometry inside the sculpt mesh is considered.

Each mode has an associated angular limit and a falloff.

Reviewed by: Julien Kaspar and Jeroen Bakker
Differential Revision: https://developer.blender.org/D15297
Ref D15297
This commit is contained in:
Joseph Eagar 2022-09-28 23:21:56 -07:00
parent a4ff106c58
commit bbc69563d0
Notes: blender-bot 2023-02-14 04:40:22 +01:00
Referenced by issue #102253, Crash when using Rotate Brush and Normal/View Automasking
Referenced by issue #99983, Store PBVH GPU Batches In Intermediate Nodes
Referenced by issue #98593, Paint Mode: Backface Occlusion
27 changed files with 1050 additions and 130 deletions

View File

@ -945,10 +945,27 @@ def brush_settings_advanced(layout, context, brush, popover=False):
col.prop(brush, "use_automasking_boundary_face_sets", text="Face Sets Boundary")
col.prop(brush, "use_automasking_cavity", text="Cavity")
col.prop(brush, "use_automasking_cavity_inverted", text="Cavity (Inverted)")
col.prop(brush, "use_automasking_start_normal", text="Area Normal")
col.prop(brush, "use_automasking_view_normal", text="View Normal")
col.separator()
col.prop(brush, "automasking_boundary_edges_propagation_steps")
sculpt = context.tool_settings.sculpt
if brush.use_automasking_start_normal:
col.separator()
col.prop(sculpt, "automasking_start_normal_limit")
col.prop(sculpt, "automasking_start_normal_falloff")
if brush.use_automasking_view_normal:
col.separator()
col.prop(brush, "use_automasking_view_occlusion", text="Occlusion")
col.prop(sculpt, "automasking_view_normal_limit")
col.prop(sculpt, "automasking_view_normal_falloff")
if brush.use_automasking_cavity or brush.use_automasking_cavity_inverted:
col.separator()

View File

@ -5497,6 +5497,8 @@ class VIEW3D_MT_sculpt_automasking_pie(Menu):
pie.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary")
pie.prop(sculpt, "use_automasking_cavity", text="Cavity")
pie.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (Inverted)")
pie.prop(sculpt, "use_automasking_start_normal", text="Area Normal")
pie.prop(sculpt, "use_automasking_view_normal", text="View Normal")
class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu):

View File

@ -974,6 +974,21 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary")
col.prop(sculpt, "use_automasking_cavity", text="Cavity")
col.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (Inverted)")
col.prop(sculpt, "use_automasking_start_normal", text="Area Normal")
col.prop(sculpt, "use_automasking_view_normal", text="View Normal")
if sculpt.use_automasking_start_normal:
col.separator()
col.prop(sculpt, "automasking_start_normal_limit")
col.prop(sculpt, "automasking_start_normal_falloff")
if sculpt.use_automasking_view_normal:
col.separator()
col.prop(sculpt, "use_automasking_view_occlusion", text="Occlusion")
col.prop(sculpt, "automasking_view_normal_limit")
col.prop(sculpt, "automasking_view_normal_falloff")
col.separator()
col.prop(sculpt.brush, "automasking_boundary_edges_propagation_steps")

View File

@ -474,11 +474,6 @@ typedef struct SculptBoundary {
} twist;
} SculptBoundary;
typedef struct CavityMaskData {
float factor;
int stroke_id;
} CavityMaskData;
typedef struct SculptFakeNeighbors {
bool use_fake_neighbors;
@ -554,13 +549,13 @@ typedef struct SculptAttributePointers {
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
* initialized in #SCULPT_automasking_cache_init when needed. */
SculptAttribute *automasking_factor;
SculptAttribute *automasking_occlusion; /* CD_PROP_INT8. */
SculptAttribute *automasking_stroke_id;
SculptAttribute *automasking_cavity;
/* BMesh */
SculptAttribute *dyntopo_node_id_vertex;
SculptAttribute *dyntopo_node_id_face;
SculptAttribute *stroke_id;
SculptAttribute *cavity;
} SculptAttributePointers;
typedef struct SculptSession {
@ -747,14 +742,16 @@ typedef struct SculptSession {
*/
bool sticky_shading_color;
uchar stroke_id;
/**
* Last used painting canvas key.
*/
char *last_paint_canvas_key;
float last_normal[3];
uchar stroke_id;
int last_automasking_settings_hash;
uchar last_cavity_stroke_id;
uchar last_automask_stroke_id;
} SculptSession;
void BKE_sculptsession_free(struct Object *ob);

View File

@ -2079,6 +2079,14 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene)
sd->constant_detail = 3.0f;
}
if (!sd->automasking_start_normal_limit) {
sd->automasking_start_normal_limit = 20.0f / 180.0f * M_PI;
sd->automasking_start_normal_falloff = 0.25f;
sd->automasking_view_normal_limit = 90.0f / 180.0f * M_PI;
sd->automasking_view_normal_falloff = 0.25f;
}
/* Set sane default tiling offsets. */
if (!sd->paint.tile_offset[0]) {
sd->paint.tile_offset[0] = 1.0f;

View File

@ -34,7 +34,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C,
/* sculpt_transform.c */
void ED_sculpt_update_modal_transform(struct bContext *C, struct Object *ob);
void ED_sculpt_init_transform(struct bContext *C, struct Object *ob, const char *undo_name);
void ED_sculpt_init_transform(struct bContext *C, struct Object *ob, const int mval[2], const char *undo_name);
void ED_sculpt_end_transform(struct bContext *C, struct Object *ob);
/* sculpt_undo.c */

View File

@ -1268,11 +1268,11 @@ static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush
/**
* Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action
*/
static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush)
static int sculpt_brush_needs_normal(const SculptSession *ss, Sculpt *sd, const Brush *brush)
{
return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) &&
(ss->cache->normal_weight > 0.0f)) ||
SCULPT_automasking_needs_normal(ss, sd, brush) ||
ELEM(brush->sculpt_tool,
SCULPT_TOOL_BLOB,
SCULPT_TOOL_CREASE,
@ -2413,7 +2413,8 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
const float fno[3],
float mask,
const PBVHVertRef vertex,
int thread_id)
const int thread_id,
AutomaskingNodeData *automask_data)
{
StrokeCache *cache = ss->cache;
const Scene *scene = cache->vc->scene;
@ -2497,7 +2498,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
avg *= 1.0f - mask;
/* Auto-masking. */
avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex);
avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex, automask_data);
return avg;
}
@ -3087,7 +3088,8 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
NULL);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@ -3396,7 +3398,7 @@ static void do_brush_action(Sculpt *sd,
BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
}
if (sculpt_brush_needs_normal(ss, brush)) {
if (sculpt_brush_needs_normal(ss, sd, brush)) {
update_sculpt_normal(sd, ob, nodes, totnode);
}
@ -3548,7 +3550,7 @@ static void do_brush_action(Sculpt *sd,
SCULPT_bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor);
}
if (!SCULPT_tool_can_reuse_cavity_mask(brush->sculpt_tool) ||
if (!SCULPT_tool_can_reuse_automask(brush->sculpt_tool) ||
(ss->cache->supports_gravity && sd->gravity_factor > 0.0f)) {
/* Clear cavity mask cache. */
ss->last_automasking_settings_hash = 0;
@ -4242,8 +4244,6 @@ static void sculpt_update_cache_invariants(
ss->cache = cache;
cache->stroke_id = ss->stroke_id;
/* Set scaling adjustment. */
max_scale = 0.0f;
for (int i = 0; i < 3; i++) {
@ -5413,6 +5413,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
}
SCULPT_stroke_id_next(ob);
ss->cache->stroke_id = ss->stroke_id;
return true;
}
@ -6012,6 +6013,70 @@ void SCULPT_fake_neighbors_free(Object *ob)
sculpt_pose_fake_neighbors_free(ss);
}
void SCULPT_automasking_node_begin(Object *ob,
const SculptSession *UNUSED(ss),
AutomaskingCache *automasking,
AutomaskingNodeData *node_data,
PBVHNode *node)
{
if (!automasking) {
memset(node_data, 0, sizeof(*node_data));
return;
}
node_data->node = node;
node_data->have_orig_data = automasking->settings.flags &
(BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL);
if (node_data->have_orig_data) {
SCULPT_orig_vert_data_init(&node_data->orig_data, ob, node, SCULPT_UNDO_COORDS);
}
else {
memset(&node_data->orig_data, 0, sizeof(node_data->orig_data));
}
}
void SCULPT_automasking_node_update(SculptSession *UNUSED(ss),
AutomaskingNodeData *automask_data,
PBVHVertexIter *vd)
{
if (automask_data->have_orig_data) {
SCULPT_orig_vert_data_update(&automask_data->orig_data, vd);
}
}
bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original)
{
float ray_start[3], ray_end[3], ray_normal[3], face_normal[3];
float co[3];
copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex));
float mouse[2];
ED_view3d_project_float_v2_m4(ss->cache->vc->region, co, mouse, ss->cache->projection_mat);
int depth = SCULPT_raycast_init(ss->cache->vc, mouse, ray_end, ray_start, ray_normal, original);
negate_v3(ray_normal);
copy_v3_v3(ray_start, SCULPT_vertex_co_get(ss, vertex));
madd_v3_v3fl(ray_start, ray_normal, 0.002);
SculptRaycastData srd = {0};
srd.original = original;
srd.ss = ss;
srd.hit = false;
srd.ray_start = ray_start;
srd.ray_normal = ray_normal;
srd.depth = depth;
srd.face_normal = face_normal;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
return srd.hit;
}
void SCULPT_stroke_id_next(Object *ob)
{
/* Manually wrap in int32 space to avoid tripping up undefined behavior
@ -6024,11 +6089,14 @@ void SCULPT_stroke_id_ensure(Object *ob)
{
SculptSession *ss = ob->sculpt;
if (!ss->attrs.stroke_id) {
if (!ss->attrs.automasking_stroke_id) {
SculptAttributeParams params = {0};
ss->attrs.stroke_id = BKE_sculpt_attribute_ensure(
ob, ATTR_DOMAIN_POINT, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(stroke_id), &params);
ss->attrs.automasking_stroke_id = BKE_sculpt_attribute_ensure(
ob,
ATTR_DOMAIN_POINT,
CD_PROP_INT8,
SCULPT_ATTRIBUTE_NAME(automasking_stroke_id),
&params);
}
}

View File

@ -98,6 +98,12 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
return true;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BRUSH_NORMAL)) {
return true;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_VIEW_NORMAL)) {
return true;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CAVITY_ALL)) {
return true;
}
@ -127,6 +133,50 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br
return sculpt->automasking_flags;
}
bool SCULPT_automasking_needs_normal(const SculptSession *UNUSED(ss),
const Sculpt *sculpt,
const Brush *brush)
{
int flags = sculpt_automasking_mode_effective_bits(sculpt, brush);
return flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL);
}
static float sculpt_automasking_normal_calc(SculptSession *ss,
PBVHVertRef vertex,
float3 &normal,
float limit_lower,
float limit_upper,
AutomaskingNodeData *automask_data)
{
float3 normal_v;
if (automask_data->have_orig_data) {
normal_v = automask_data->orig_data.no;
}
else {
SCULPT_vertex_normal_get(ss, vertex, normal_v);
}
float angle = saacos(dot_v3v3(normal, normal_v));
/* note that limit is pre-divided by M_PI */
if (angle > limit_lower && angle < limit_upper) {
float t = 1.0f - (angle - limit_lower) / (limit_upper - limit_lower);
/* smoothstep */
t = t * t * (3.0 - 2.0 * t);
return t;
}
else if (angle > limit_upper) {
return 0.0f;
}
return 1.0f;
}
static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
{
@ -134,13 +184,93 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush
if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
return true;
}
if (automasking_flags &
(BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS | BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
(BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS |
BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL)) {
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
}
return false;
}
static float automasking_brush_normal_factor(AutomaskingCache *automasking,
SculptSession *ss,
PBVHVertRef vertex,
AutomaskingNodeData *automask_data)
{
float falloff = automasking->settings.start_normal_falloff * M_PI;
float3 initial_normal;
if (ss->cache) {
initial_normal = ss->cache->initial_normal;
}
else {
initial_normal = ss->filter_cache->initial_normal;
}
return sculpt_automasking_normal_calc(ss,
vertex,
initial_normal,
automasking->settings.start_normal_limit - falloff * 0.5f,
automasking->settings.start_normal_limit + falloff * 0.5f,
automask_data);
}
static float automasking_view_normal_factor(AutomaskingCache *automasking,
SculptSession *ss,
PBVHVertRef vertex,
AutomaskingNodeData *automask_data)
{
float falloff = automasking->settings.view_normal_falloff * M_PI;
float3 view_normal;
if (ss->cache) {
view_normal = ss->cache->view_normal;
}
else {
view_normal = ss->filter_cache->view_normal;
}
return sculpt_automasking_normal_calc(ss,
vertex,
view_normal,
automasking->settings.view_normal_limit,
automasking->settings.view_normal_limit + falloff,
automask_data);
}
static float automasking_view_occlusion_factor(AutomaskingCache *automasking,
SculptSession *ss,
PBVHVertRef vertex,
uchar stroke_id,
AutomaskingNodeData *UNUSED(automask_data))
{
char f = *(char *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion);
if (stroke_id != automasking->current_stroke_id) {
f = *(char *)SCULPT_vertex_attr_get(
vertex,
ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1;
}
return f == 2;
}
/* Updates vertex stroke id. */
static float automasking_factor_end(SculptSession *ss,
AutomaskingCache *automasking,
PBVHVertRef vertex,
float value)
{
if (ss->attrs.automasking_stroke_id) {
*(uchar *)SCULPT_vertex_attr_get(
vertex, ss->attrs.automasking_stroke_id) = automasking->current_stroke_id;
}
return value;
}
static float sculpt_cavity_calc_factor(AutomaskingCache *automasking, float factor)
{
float sign = signf(factor);
@ -297,8 +427,7 @@ static void sculpt_calc_blurred_cavity(SculptSession *ss,
factor_sum = sculpt_cavity_calc_factor(automasking, factor_sum);
*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.cavity) = factor_sum;
*(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id) = automasking->cavity_stroke_id;
*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum;
}
int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking)
@ -331,6 +460,20 @@ int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking)
hash = BLI_hash_int_2d(hash, automasking->settings.initial_face_set);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL) {
hash = BLI_hash_int_2d(hash,
*reinterpret_cast<uint *>(&automasking->settings.view_normal_falloff));
hash = BLI_hash_int_2d(hash,
*reinterpret_cast<uint *>(&automasking->settings.view_normal_limit));
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL) {
hash = BLI_hash_int_2d(hash,
*reinterpret_cast<uint *>(&automasking->settings.start_normal_falloff));
hash = BLI_hash_int_2d(hash,
*reinterpret_cast<uint *>(&automasking->settings.start_normal_limit));
}
return hash;
}
@ -338,13 +481,13 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking,
SculptSession *ss,
PBVHVertRef vertex)
{
uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id);
uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id);
if (stroke_id != automasking->cavity_stroke_id) {
if (stroke_id != automasking->current_stroke_id) {
sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex);
}
float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.cavity);
float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity);
bool inverted = automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED;
if ((automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) &&
@ -359,7 +502,8 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking,
float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
SculptSession *ss,
PBVHVertRef vert)
PBVHVertRef vert,
AutomaskingNodeData *automask_data)
{
if (!automasking) {
return 1.0f;
@ -378,6 +522,18 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
return factor;
}
uchar stroke_id = ss->attrs.automasking_stroke_id ?
*(uchar *)(SCULPT_vertex_attr_get(vert, ss->attrs.automasking_stroke_id)) :
-1;
bool do_occlusion = (automasking->settings.flags &
(BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL)) ==
(BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL);
if (do_occlusion &&
automasking_view_occlusion_factor(automasking, ss, vert, stroke_id, automask_data)) {
return automasking_factor_end(ss, automasking, vert, 0.0f);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) {
return 0.0f;
@ -396,11 +552,23 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
}
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) {
return sculpt_automasking_cavity_factor(automasking, ss, vert);
float mask = 1.0f;
if ((ss->cache || ss->filter_cache) &&
(automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL)) {
mask *= automasking_brush_normal_factor(automasking, ss, vert, automask_data);
}
return 1.0f;
if ((ss->cache || ss->filter_cache) &&
(automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) {
mask *= automasking_view_normal_factor(automasking, ss, vert, automask_data);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) {
mask *= sculpt_automasking_cavity_factor(automasking, ss, vert);
}
return automasking_factor_end(ss, automasking, vert, mask);
}
void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
@ -582,6 +750,11 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski
automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
automasking->settings.view_normal_limit = sd->automasking_view_normal_limit;
automasking->settings.view_normal_falloff = sd->automasking_view_normal_falloff;
automasking->settings.start_normal_limit = sd->automasking_start_normal_limit;
automasking->settings.start_normal_falloff = sd->automasking_start_normal_falloff;
if (brush && (brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
automasking->settings.cavity_curve = brush->automasking_cavity_curve;
automasking->settings.cavity_factor = brush->automasking_cavity_factor;
@ -594,7 +767,42 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski
}
}
bool SCULPT_tool_can_reuse_cavity_mask(int sculpt_tool)
static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automasking,
Object *ob,
eAutomasking_flag mode)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
/* No need to build original data since this is only called at the beginning of strokes.*/
AutomaskingNodeData nodedata;
nodedata.have_orig_data = false;
for (int i = 0; i < totvert; i++) {
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
float f = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor);
if ((int)mode & BRUSH_AUTOMASKING_BRUSH_NORMAL) {
f *= automasking_brush_normal_factor(automasking, ss, vertex, &nodedata);
}
if ((int)mode & BRUSH_AUTOMASKING_VIEW_NORMAL) {
if ((int)mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) {
f *= automasking_view_occlusion_factor(automasking, ss, vertex, -1, &nodedata);
}
f *= automasking_view_normal_factor(automasking, ss, vertex, &nodedata);
}
if (ss->attrs.automasking_stroke_id) {
*(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id) = ss->stroke_id;
}
*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f;
}
}
bool SCULPT_tool_can_reuse_automask(int sculpt_tool)
{
return ELEM(sculpt_tool,
SCULPT_TOOL_PAINT,
@ -617,32 +825,61 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush);
SCULPT_boundary_info_ensure(ob);
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CAVITY_ALL)) {
automasking->current_stroke_id = ss->stroke_id;
bool use_stroke_id = false;
int mode = sculpt_automasking_mode_effective_bits(sd, brush);
if ((mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && (mode & BRUSH_AUTOMASKING_VIEW_NORMAL)) {
use_stroke_id = true;
if (!ss->attrs.automasking_occlusion) {
SculptAttributeParams params = {0};
ss->attrs.automasking_occlusion = BKE_sculpt_attribute_ensure(
ob,
ATTR_DOMAIN_POINT,
CD_PROP_INT8,
SCULPT_ATTRIBUTE_NAME(automasking_occlusion),
&params);
}
}
if (mode & BRUSH_AUTOMASKING_CAVITY_ALL) {
use_stroke_id = true;
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CAVITY_USE_CURVE)) {
BKE_curvemapping_init(brush->automasking_cavity_curve);
BKE_curvemapping_init(sd->automasking_cavity_curve);
}
SCULPT_stroke_id_ensure(ob);
automasking->cavity_stroke_id = ss->stroke_id;
if (!ss->attrs.cavity) {
if (!ss->attrs.automasking_cavity) {
SculptAttributeParams params = {0};
ss->attrs.cavity = BKE_sculpt_attribute_ensure(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(cavity), &params);
ss->attrs.automasking_cavity = BKE_sculpt_attribute_ensure(
ob,
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT,
SCULPT_ATTRIBUTE_NAME(automasking_cavity),
&params);
}
/* Can we reuse the previous stroke's cavity mask? */
else if (brush && SCULPT_tool_can_reuse_cavity_mask(brush->sculpt_tool)) {
}
if (use_stroke_id) {
SCULPT_stroke_id_ensure(ob);
bool have_occlusion = (mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) &&
(mode & BRUSH_AUTOMASKING_VIEW_NORMAL);
if (brush && SCULPT_tool_can_reuse_automask(brush->sculpt_tool) && !have_occlusion) {
int hash = SCULPT_automasking_settings_hash(ob, automasking);
if (hash == ss->last_automasking_settings_hash) {
automasking->cavity_stroke_id = ss->last_automasking_settings_hash;
automasking->can_reuse_cavity = true;
automasking->current_stroke_id = ss->last_automask_stroke_id;
automasking->can_reuse_mask = true;
}
}
if (!automasking->can_reuse_cavity) {
ss->last_cavity_stroke_id = ss->stroke_id;
if (!automasking->can_reuse_mask) {
ss->last_automask_stroke_id = ss->stroke_id;
}
}
@ -675,6 +912,14 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
sculpt_face_sets_automasking_init(sd, ob);
}
int normal_bits = sculpt_automasking_mode_effective_bits(sd, brush) &
(BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL |
BRUSH_AUTOMASKING_VIEW_OCCLUSION);
if (normal_bits) {
sculpt_normal_occlusion_automasking_fill(automasking, ob, (eAutomasking_flag)normal_bits);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps);
@ -690,5 +935,8 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
bool SCULPT_automasking_needs_original(const Sculpt *sd, const Brush *brush)
{
return sculpt_automasking_mode_effective_bits(sd, brush) & BRUSH_AUTOMASKING_CAVITY_ALL;
return sculpt_automasking_mode_effective_bits(sd, brush) &
(BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_BRUSH_NORMAL |
BRUSH_AUTOMASKING_VIEW_NORMAL);
}

View File

@ -681,12 +681,16 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
angle_factor = floorf(angle_factor * 10) / 10.0f;
}
const float angle = angle_factor * M_PI;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!SCULPT_check_vertex_pivot_symmetry(
orig_data.co, boundary->initial_vertex_position, symm)) {
@ -694,7 +698,8 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
const float automask = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]);
@ -729,12 +734,16 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!SCULPT_check_vertex_pivot_symmetry(
orig_data.co, boundary->initial_vertex_position, symm)) {
@ -742,7 +751,8 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
const float automask = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@ -773,6 +783,9 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
@ -781,6 +794,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!SCULPT_check_vertex_pivot_symmetry(
orig_data.co, boundary->initial_vertex_position, symm)) {
@ -788,7 +802,8 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
const float automask = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@ -819,12 +834,16 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!SCULPT_check_vertex_pivot_symmetry(
orig_data.co, boundary->initial_vertex_position, symm)) {
@ -832,7 +851,8 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
const float automask = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@ -862,6 +882,9 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@ -876,6 +899,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!SCULPT_check_vertex_pivot_symmetry(
orig_data.co, boundary->initial_vertex_position, symm)) {
@ -883,7 +907,8 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
const float automask = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position);

View File

@ -302,10 +302,17 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
/* Offset vertex. */
const float fade = SCULPT_brush_strength_factor(ss,
brush,
@ -315,7 +322,8 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@ -387,6 +395,10 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -405,6 +417,8 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -413,7 +427,8 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);
@ -485,6 +500,10 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -503,6 +522,8 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -511,7 +532,8 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);
@ -601,6 +623,10 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
/* Tilted plane (front part of the brush). */
plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -621,6 +647,8 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix);
sub_v3_v3v3(val, intr_tilt, vd.co);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -629,7 +657,8 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);
@ -764,6 +793,10 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -776,6 +809,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
sub_v3_v3v3(val, intr, vd.co);
if (SCULPT_plane_trim(ss->cache, brush, val)) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -784,7 +819,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);
@ -922,6 +958,10 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
plane_from_point_normal_v3(test.plane_tool, area_co, area_no);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -933,6 +973,8 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
sub_v3_v3v3(val, intr, vd.co);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -941,7 +983,8 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);
@ -1041,6 +1084,10 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) {
continue;
@ -1058,6 +1105,9 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
if (!SCULPT_plane_trim(ss->cache, brush, val)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
/* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
@ -1067,7 +1117,8 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);
@ -1202,6 +1253,10 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
KelvinletParams params;
BKE_kelvinlet_init_params(&params, ss->cache->radius, bstrength, 1.0f, 0.4f);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@ -1212,6 +1267,8 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
fade = 1.0f;
}
else {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -1220,7 +1277,8 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
}
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
@ -1267,7 +1325,9 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
if (vd.mask) {
mul_v3_fl(disp, 1.0f - *vd.mask);
}
mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
mul_v3_fl(
disp,
SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data));
copy_v3_v3(proxy[vd.i], disp);
}
@ -1339,12 +1399,18 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
@ -1353,7 +1419,8 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@ -1412,6 +1479,10 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
@ -1427,7 +1498,8 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
sub_v3_v3v3(vec, orig_data.co, ss->cache->location);
axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade);
@ -1485,12 +1557,18 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -1499,7 +1577,8 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
const int vi = vd.index;
float *disp_factor;
@ -1600,10 +1679,16 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -1612,7 +1697,8 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float val[3];
if (vd.fno) {
@ -1668,10 +1754,16 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -1680,7 +1772,8 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@ -1746,11 +1839,17 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
/* Offset vertex. */
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -1759,7 +1858,8 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float val1[3];
float val2[3];
@ -1863,10 +1963,16 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
copy_v3_v3(x_object_space, stroke_xz[0]);
copy_v3_v3(z_object_space, stroke_xz[1]);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -1875,7 +1981,8 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float disp_center[3];
float x_disp[3];
float z_disp[3];
@ -1977,12 +2084,18 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
@ -1991,7 +2104,8 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
if (grab_silhouette) {
float silhouette_test_dir[3];
@ -2055,6 +2169,9 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
const float bstrength = ss->cache->bstrength;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@ -2110,7 +2227,9 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(final_disp, 1.0f - *vd.mask);
}
mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
mul_v3_fl(
final_disp,
SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex, &automask_data));
copy_v3_v3(proxy[vd.i], final_disp);
@ -2174,12 +2293,18 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
/* Offset vertex. */
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
@ -2188,7 +2313,8 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@ -2258,11 +2384,17 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
@ -2271,7 +2403,8 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float current_disp[3];
float current_disp_norm[3];
float final_disp[3] = {0.0f, 0.0f, 0.0f};
@ -2415,11 +2548,17 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = SCULPT_brush_strength_factor(ss,
brush,
orig_data.co,
@ -2428,7 +2567,8 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
NULL,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
if (vd.mvert) {
@ -2491,11 +2631,17 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -2504,7 +2650,8 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float limit_co[3];
float disp[3];
@ -2557,11 +2704,17 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -2570,7 +2723,8 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float current_disp[3];
float current_disp_norm[3];
@ -2718,16 +2872,29 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
const float fade =
bstrength *
SCULPT_brush_strength_factor(
ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) *
ss->cache->pressure;
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength *
SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
*vd.mask,
vd.vertex,
thread_id,
&automask_data) *
ss->cache->pressure;
float avg[3], val[3];
@ -2797,13 +2964,26 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
const float fade = SCULPT_brush_strength_factor(
ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
0.0f,
vd.vertex,
thread_id,
&automask_data);
if (bstrength > 0.0f) {
(*vd.mask) += fade * bstrength * (1.0f - *vd.mask);

View File

@ -504,6 +504,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor);
}
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
float force[3];
float sim_location[3];
@ -544,7 +548,8 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float brush_disp[3];
@ -765,6 +770,9 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
}
AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
float sim_location[3];
@ -788,7 +796,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor);
const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) *
SCULPT_automasking_factor_get(automasking, ss, vd.vertex);
SCULPT_automasking_factor_get(automasking, ss, vd.vertex, &automask_data);
madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v);
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
@ -821,6 +829,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
{
AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss);
AutomaskingNodeData automask_data = {0};
automask_data.have_orig_data = true;
for (int constraint_it = 0; constraint_it < CLOTH_SIMULATION_ITERATIONS; constraint_it++) {
for (int i = 0; i < cloth_sim->tot_length_constraints; i++) {
@ -860,9 +871,11 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2);
const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, vertex1)) *
SCULPT_automasking_factor_get(automasking, ss, vertex1);
SCULPT_automasking_factor_get(
automasking, ss, vertex1, &automask_data);
const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, vertex2)) *
SCULPT_automasking_factor_get(automasking, ss, vertex2);
SCULPT_automasking_factor_get(
automasking, ss, vertex2, &automask_data);
float sim_location[3];
cloth_brush_simulation_location_get(ss, brush, sim_location);
@ -1434,11 +1447,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
sculpt_gravity[2] = -1.0f;
}
mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, SCULPT_automasking_active_cache_get(ss), &automask_data, node);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float fade = vd.mask ? *vd.mask : 0.0f;
fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
fade = 1.0f - fade;
float force[3] = {0.0f, 0.0f, 0.0f};
float disp[3], temp[3], transform[3][3];
@ -1581,7 +1598,8 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_undo_push_begin(ob, op);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
SCULPT_filter_cache_init(
C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
@ -1644,14 +1662,14 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* RNA. */
SCULPT_mesh_filter_properties(ot);
RNA_def_enum(ot->srna,
"type",
prop_cloth_filter_type,
CLOTH_FILTER_GRAVITY,
"Filter Type",
"Operation that is going to be applied to the mesh");
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
RNA_def_enum_flag(ot->srna,
"force_axis",
prop_cloth_filter_force_axis_items,

View File

@ -129,6 +129,9 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
@ -154,7 +157,8 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
if (fade > 0.05f) {
ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set;
@ -173,7 +177,8 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
if (fade > 0.05f) {
SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
@ -205,6 +210,9 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
}
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@ -222,7 +230,8 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
if (vd.mvert) {

View File

@ -95,6 +95,10 @@ static void color_filter_task_cb(void *__restrict userdata,
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[n]);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
@ -104,7 +108,8 @@ static void color_filter_task_cb(void *__restrict userdata,
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f) {
continue;
}
@ -290,6 +295,8 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_RUNNING_MODAL;
}
SCULPT_stroke_id_next(ob);
const float len = event->prev_press_xy[0] - event->xy[0];
filter_strength = filter_strength * -len * 0.001f;
@ -361,7 +368,8 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR);
SCULPT_filter_cache_init(
C, ob, sd, SCULPT_UNDO_COLOR, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
FilterCache *filter_cache = ss->filter_cache;
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
@ -386,9 +394,9 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* rna */
SCULPT_mesh_filter_properties(ot);
RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter Type", "");
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
PropertyRNA *prop = RNA_def_float_color(
ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f);

View File

@ -101,7 +101,12 @@ static void filter_cache_init_task_cb(void *__restrict userdata,
SCULPT_undo_push_node(data->ob, node, data->filter_undo_type);
}
void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int undo_type)
void SCULPT_filter_cache_init(bContext *C,
Object *ob,
Sculpt *sd,
const int undo_type,
const int mval[2],
float area_normal_radius)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
@ -159,6 +164,79 @@ void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int und
ED_view3d_viewcontext_init(C, &vc, depsgraph);
copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat);
copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv);
Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
float co[3];
float mval_fl[2] = {(float)mval[0], (float)mval[1]};
if (SCULPT_stroke_get_location(C, co, mval_fl, false)) {
PBVHNode **nodes;
int totnode;
/* Get radius from brush. */
Brush *brush = BKE_paint_brush(&sd->paint);
float radius;
if (brush) {
if (BKE_brush_use_locked_size(scene, brush)) {
radius = paint_calc_object_space_radius(
&vc, co, (float)BKE_brush_size_get(scene, brush) * area_normal_radius);
}
else {
radius = BKE_brush_unprojected_radius_get(scene, brush) * area_normal_radius;
}
}
else {
radius = paint_calc_object_space_radius(&vc, co, (float)ups->size * area_normal_radius);
}
SculptSearchSphereData search_data = {
.original = true,
.center = co,
.radius_squared = radius * radius,
.ignore_fully_ineffective = true,
};
BKE_pbvh_search_gather(pbvh, SCULPT_search_sphere_cb, &search_data, &nodes, &totnode);
if (SCULPT_pbvh_calc_area_normal(
brush, ob, nodes, totnode, true, ss->filter_cache->initial_normal)) {
copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal);
}
else {
copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal);
}
MEM_SAFE_FREE(nodes);
/* Update last stroke location */
mul_m4_v3(ob->obmat, co);
add_v3_v3(ups->average_stroke_accum, co);
ups->average_stroke_counter++;
ups->last_stroke_valid = true;
}
else {
/* Use last normal. */
copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal);
}
/* Update view normal */
float projection_mat[4][4];
float mat[3][3];
float viewDir[3] = {0.0f, 0.0f, 1.0f};
ED_view3d_ob_project_mat_get(vc.rv3d, ob, projection_mat);
invert_m4_m4(ob->imat, ob->obmat);
copy_m3_m4(mat, vc.rv3d->viewinv);
mul_m3_v3(mat, viewDir);
copy_m3_m4(mat, ob->imat);
mul_m3_v3(mat, viewDir);
normalize_v3_v3(ss->filter_cache->view_normal, viewDir);
}
void SCULPT_filter_cache_free(SculptSession *ss)
@ -288,15 +366,20 @@ static void mesh_filter_task_cb(void *__restrict userdata,
/* This produces better results as the relax operation is no completely focused on the
* boundaries. */
const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3];
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) {
/* Surface Smooth can't skip the loop for this vertex as it needs to calculate its
@ -580,11 +663,18 @@ static void mesh_filter_surface_smooth_displace_task_cb(
PBVHNode *node = data->nodes[i];
PBVHVertexIter vd;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[i]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade *= SCULPT_automasking_factor_get(
ss->filter_cache->automasking, ss, vd.vertex, &automask_data);
if (fade == 0.0f) {
continue;
}
@ -622,6 +712,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
const float len = event->prev_press_xy[0] - event->xy[0];
filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC;
SCULPT_stroke_id_next(ob);
SCULPT_vertex_random_access_ensure(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type);
@ -699,7 +790,8 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_undo_push_begin(ob, op);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
SCULPT_filter_cache_init(
C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius"));
FilterCache *filter_cache = ss->filter_cache;
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
@ -746,6 +838,22 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_RUNNING_MODAL;
}
void SCULPT_mesh_filter_properties(struct wmOperatorType *ot)
{
RNA_def_float(
ot->srna,
"area_normal_radius",
0.25,
0.001,
5.0,
"Normal Radius",
"Radius used for calculating area normal on initial click,\nin percentage of brush radius.",
0.01,
1.0);
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
}
void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
{
/* Identifiers. */
@ -761,14 +869,14 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* RNA. */
SCULPT_mesh_filter_properties(ot);
RNA_def_enum(ot->srna,
"type",
prop_mesh_filter_types,
MESH_FILTER_INFLATE,
"Filter Type",
"Operation that is going to be applied to the mesh");
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
RNA_def_enum_flag(ot->srna,
"deform_axis",
prop_mesh_filter_deform_axis_items,

View File

@ -27,6 +27,7 @@ extern "C" {
#endif
struct AutomaskingCache;
struct AutomaskingNodeData;
struct Image;
struct ImageUser;
struct KeyBlock;
@ -394,16 +395,20 @@ typedef struct AutomaskingSettings {
/* Flags from eAutomasking_flag. */
int flags;
int initial_face_set;
float cavity_factor;
int cavity_blur_steps;
struct CurveMapping *cavity_curve;
float start_normal_limit, start_normal_falloff;
float view_normal_limit, view_normal_falloff;
} AutomaskingSettings;
typedef struct AutomaskingCache {
AutomaskingSettings settings;
bool can_reuse_cavity;
uchar cavity_stroke_id;
bool can_reuse_mask;
uchar current_stroke_id;
} AutomaskingCache;
typedef struct FilterCache {
@ -463,6 +468,8 @@ typedef struct FilterCache {
/* Auto-masking. */
AutomaskingCache *automasking;
float initial_normal[3];
float view_normal[3];
/* Pre-smoothed colors used by sharpening. Colors are HSL. */
float (*pre_smoothed_color)[4];
@ -913,6 +920,8 @@ float SCULPT_vertex_mask_get(struct SculptSession *ss, PBVHVertRef vertex);
void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]);
void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]);
bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool original);
/** Returns true if a color attribute exists in the current sculpt session. */
bool SCULPT_has_colors(const SculptSession *ss);
@ -1194,7 +1203,8 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss,
const float fno[3],
float mask,
const PBVHVertRef vertex,
int thread_id);
int thread_id,
struct AutomaskingNodeData *automask_data);
/**
* Tilts a normal by the x and y tilt values using the view axis.
@ -1282,9 +1292,30 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
/** \name Auto-masking.
* \{ */
typedef struct AutomaskingNodeData {
PBVHNode *node;
SculptOrigVertData orig_data;
bool have_orig_data;
} AutomaskingNodeData;
/** Call before PBVH vertex iteration.
* \param automask_data: pointer to an uninitialized AutomaskingNodeData struct.
*/
void SCULPT_automasking_node_begin(struct Object *ob,
const SculptSession *ss,
struct AutomaskingCache *automasking,
AutomaskingNodeData *automask_data,
PBVHNode *node);
/* Call before SCULPT_automasking_factor_get and SCULPT_brush_strength_factor. */
void SCULPT_automasking_node_update(SculptSession *ss,
AutomaskingNodeData *automask_data,
PBVHVertexIter *vd);
float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking,
SculptSession *ss,
PBVHVertRef vertex);
PBVHVertRef vertex,
AutomaskingNodeData *automask_data);
/* Returns the automasking cache depending on the active tool. Used for code that can run both for
* brushes and filter. */
@ -1300,6 +1331,9 @@ float *SCULPT_boundary_automasking_init(Object *ob,
eBoundaryAutomaskMode mode,
int propagation_steps,
float *automask_factor);
bool SCULPT_automasking_needs_normal(const SculptSession *ss,
const Sculpt *sculpt,
const Brush *brush);
bool SCULPT_automasking_needs_original(const struct Sculpt *sd, const struct Brush *brush);
int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking);
@ -1329,8 +1363,14 @@ float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_r
/** \name Filter API
* \{ */
void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, int undo_type);
void SCULPT_filter_cache_init(struct bContext *C,
Object *ob,
Sculpt *sd,
int undo_type,
const int mval[2],
float area_normal_radius);
void SCULPT_filter_cache_free(SculptSession *ss);
void SCULPT_mesh_filter_properties(struct wmOperatorType *ot);
void SCULPT_mask_filter_smooth_apply(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, int smooth_iterations);
@ -1849,7 +1889,7 @@ BLI_INLINE bool SCULPT_tool_is_face_sets(int tool)
void SCULPT_stroke_id_ensure(struct Object *ob);
void SCULPT_stroke_id_next(struct Object *ob);
bool SCULPT_tool_can_reuse_cavity_mask(int sculpt_tool);
bool SCULPT_tool_can_reuse_automask(int sculpt_tool);
#ifdef __cplusplus
}

View File

@ -69,6 +69,10 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
test_radius *= brush->normal_radius_factor;
test.radius_squared = test_radius * test_radius;
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@ -78,6 +82,9 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
float normal[3];
copy_v3_v3(normal, vd.no ? vd.no : vd.fno);
mul_v3_m4v3(local_co, mat, vd.co);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
/* Use the brush falloff to weight the sampled normals. */
const float fade = SCULPT_brush_strength_factor(ss,
brush,
@ -87,7 +94,8 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
/* Sample the normal and area of the +X and -X axis individually. */
if (local_co[0] > 0.0f) {
@ -144,6 +152,10 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@ -184,6 +196,9 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
if (!SCULPT_plane_trim(ss->cache, brush, val)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
/* 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;
@ -195,7 +210,8 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
mul_v3_v3fl(proxy[vd.i], val, fade);

View File

@ -1033,8 +1033,15 @@ static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata,
SCULPT_undo_push_node(tdata->ob, node, SCULPT_UNDO_MASK);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
tdata->ob, ss, ss->cache->automasking, &automask_data, node);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float automask = SCULPT_automasking_factor_get(tdata->automasking, ss, vd.vertex);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float automask = SCULPT_automasking_factor_get(
tdata->automasking, ss, vd.vertex, &automask_data);
float mask;
switch (mode) {

View File

@ -70,10 +70,17 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -82,7 +89,8 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float smooth_color[4];
SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
@ -125,6 +133,10 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
if (brush->flag & BRUSH_USE_GRADIENT) {
switch (brush->gradient_stroke_mode) {
case BRUSH_GRADIENT_PRESSURE:
@ -161,6 +173,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -169,7 +183,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
/* Density. */
float noise = 1.0f;
@ -197,7 +212,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
/* Final mix over the original color using brush alpha. We apply auto-making again
* at this point to avoid washing out non-binary masking modes like cavity masking. */
float automasking = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float automasking = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha * automasking);
float col[4];
@ -404,10 +420,17 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
sub_v3_v3v3(brush_delta, ss->cache->location, ss->cache->last_location);
}
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -416,7 +439,8 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float current_disp[3];
float current_disp_norm[3];

View File

@ -153,7 +153,10 @@ template<typename ImageBuffer> class PaintingKernel {
init_brush_test();
}
bool paint(const Triangles &triangles, const PackedPixelRow &pixel_row, ImBuf *image_buffer)
bool paint(const Triangles &triangles,
const PackedPixelRow &pixel_row,
ImBuf *image_buffer,
AutomaskingNodeData *automask_data)
{
image_accessor.set_image_position(image_buffer, pixel_row.start_image_coordinate);
const TrianglePaintInput triangle = triangles.get_paint_input(pixel_row.triangle_index);
@ -171,6 +174,7 @@ template<typename ImageBuffer> class PaintingKernel {
const float3 normal(0.0f, 0.0f, 0.0f);
const float3 face_normal(0.0f, 0.0f, 0.0f);
const float mask = 0.0f;
const float falloff_strength = SCULPT_brush_strength_factor(
ss,
brush,
@ -180,7 +184,8 @@ template<typename ImageBuffer> class PaintingKernel {
face_normal,
mask,
BKE_pbvh_make_vref(PBVH_REF_NONE),
thread_id);
thread_id,
automask_data);
float4 paint_color = brush_color * falloff_strength * brush_strength;
float4 buffer_color;
blend_color_mix_float(buffer_color, color, paint_color);
@ -321,6 +326,9 @@ static void do_paint_pixels(void *__restrict userdata,
PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, mvert);
PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, mvert);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
ImageUser image_user = *data->image_data.image_user;
bool pixels_updated = false;
for (UDIMTilePixels &tile_data : node_data.tiles) {
@ -347,10 +355,12 @@ static void do_paint_pixels(void *__restrict userdata,
}
bool pixels_painted = false;
if (image_buffer->rect_float != nullptr) {
pixels_painted = kernel_float4.paint(node_data.triangles, pixel_row, image_buffer);
pixels_painted = kernel_float4.paint(
node_data.triangles, pixel_row, image_buffer, &automask_data);
}
else {
pixels_painted = kernel_byte4.paint(node_data.triangles, pixel_row, image_buffer);
pixels_painted = kernel_byte4.paint(
node_data.triangles, pixel_row, image_buffer, &automask_data);
}
if (pixels_painted) {

View File

@ -157,9 +157,13 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
SCULPT_automasking_node_update(ss, &automask_data, &vd);
float total_disp[3];
zero_v3(total_disp);
@ -182,7 +186,8 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
/* Apply the vertex mask to the displacement. */
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
const float automask = SCULPT_automasking_factor_get(
ss->cache->automasking, ss, vd.vertex, &automask_data);
mul_v3_fl(disp, mask * automask);
/* Accumulate the displacement. */

View File

@ -217,11 +217,17 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -230,7 +236,8 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float disp[3];
madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
@ -300,11 +307,17 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(
ss,
brush,
@ -314,7 +327,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
vd.vertex,
thread_id);
thread_id,
&automask_data);
if (smooth_mask) {
float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask;
val *= fade * bstrength;
@ -470,12 +484,18 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
const int thread_id = BLI_task_parallel_thread_id(tls);
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -484,7 +504,8 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
float disp[3];
SCULPT_surface_smooth_laplacian_step(
@ -512,11 +533,17 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
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);
AutomaskingNodeData automask_data;
SCULPT_automasking_node_begin(
data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
SCULPT_automasking_node_update(ss, &automask_data, &vd);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@ -525,7 +552,8 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.vertex,
thread_id);
thread_id,
&automask_data);
SCULPT_surface_smooth_displace_step(
ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade);
}

View File

@ -46,7 +46,7 @@
#include <math.h>
#include <stdlib.h>
void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_name)
void ED_sculpt_init_transform(struct bContext *C, Object *ob, const int mval[2], const char *undo_name)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
@ -66,7 +66,8 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_n
ss->pivot_rot[3] = 1.0f;
SCULPT_vertex_random_access_ensure(ss);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0);
if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) {
ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL;

View File

@ -87,7 +87,7 @@ static void createTransSculpt(bContext *C, TransInfo *t)
copy_m3_m4(td->axismtx, ob->obmat);
BLI_assert(!(t->options & CTX_PAINT_CURVE));
ED_sculpt_init_transform(C, ob, t->undo_name);
ED_sculpt_init_transform(C, ob, t->mval, t->undo_name);
}
/** \} */

View File

@ -338,6 +338,10 @@ typedef enum eAutomasking_flag {
BRUSH_AUTOMASKING_CAVITY_INVERTED = (1 << 5),
BRUSH_AUTOMASKING_CAVITY_ALL = (1 << 4) | (1 << 5),
BRUSH_AUTOMASKING_CAVITY_USE_CURVE = (1 << 6),
/* (1 << 7) - unused. */
BRUSH_AUTOMASKING_BRUSH_NORMAL = (1 << 8),
BRUSH_AUTOMASKING_VIEW_NORMAL = (1 << 9),
BRUSH_AUTOMASKING_VIEW_OCCLUSION = (1 << 10),
} eAutomasking_flag;
typedef enum ePaintBrush_flag {

View File

@ -1025,6 +1025,9 @@ typedef struct Sculpt {
float automasking_cavity_factor;
char _pad[4];
float automasking_start_normal_limit, automasking_start_normal_falloff;
float automasking_view_normal_limit, automasking_view_normal_falloff;
struct CurveMapping *automasking_cavity_curve;
struct CurveMapping *automasking_cavity_curve_op; /* For use by operators */
struct Object *gravity_object;

View File

@ -3270,6 +3270,28 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_automasking_start_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_BRUSH_NORMAL);
RNA_def_property_ui_text(prop,
"Area Normal",
"Affect only vertices with a similar normal to where the stroke starts");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_automasking_view_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_NORMAL);
RNA_def_property_ui_text(prop,
"View Normal",
"Affect only vertices with a normal that faces the viewer");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_automasking_view_occlusion", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_OCCLUSION);
RNA_def_property_ui_text(prop, "Occlusion", "Only affect vertices that are not occluded by other faces. (Slower performance)");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "use_scene_spacing", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, brush_spacing_unit_items);

View File

@ -953,7 +953,64 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Cavity Curve", "Curve used for the sensitivity");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_start_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_BRUSH_NORMAL);
RNA_def_property_ui_text(prop,
"Area Normal",
"Affect only vertices with a similar normal to where the stroke starts");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_view_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_NORMAL);
RNA_def_property_ui_text(prop,
"View Normal",
"Affect only vertices with a normal that faces the viewer");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_view_occlusion", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_OCCLUSION);
RNA_def_property_ui_text(prop, "Occlusion", "Only affect vertices that are not occluded by other faces. (Slower performance)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_start_normal_limit", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(
prop, NULL, "automasking_start_normal_limit");
RNA_def_property_range(prop, 0.0001f, M_PI);
RNA_def_property_ui_text(prop,
"Area Normal Limit",
"The range of angles that will be affected");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_start_normal_falloff", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(
prop, NULL, "automasking_start_normal_falloff");
RNA_def_property_range(prop, 0.0001f, 1.0f);
RNA_def_property_ui_text(prop,
"Area Normal Falloff",
"Extend the angular range with a falloff gradient");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_view_normal_limit", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(
prop, NULL, "automasking_view_normal_limit");
RNA_def_property_range(prop, 0.0001f, M_PI);
RNA_def_property_ui_text(prop,
"View Normal Limit",
"The range of angles that will be affected");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_view_normal_falloff", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(
prop, NULL, "automasking_view_normal_falloff");
RNA_def_property_range(prop, 0.0001f, 1.0f);
RNA_def_property_ui_text(prop,
"View Normal Falloff",
"Extend the angular range with a falloff gradient");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "symmetrize_direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_symmetrize_direction_items);
RNA_def_property_ui_text(prop, "Direction", "Source and destination for symmetrize operator");