Vertex Paint: projection options

This makes vertex paint match image painting more closely.

- Add falloff shape option sphere/circle
  where sphere uses a 3D radius around the cursor and
  circle uses a 2D radius (projected), like previous releases.
- Add normal angle option so you can control the falloff.
- Add Cull option, to paint onto faces pointing away.

Disabling normals, culling and using circle falloff
allows you to paint through the mesh.
This commit is contained in:
2017-10-02 21:07:25 +11:00
parent 9d34ae6048
commit b2a10fa615
10 changed files with 400 additions and 172 deletions

View File

@@ -1761,9 +1761,16 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel):
wpaint = tool_settings.weight_paint wpaint = tool_settings.weight_paint
col = layout.column() col = layout.column()
col.label("Falloff:")
row = col.row() row = col.row()
row.prop(wpaint, "falloff_shape", expand=True)
row.prop(wpaint, "use_normal") row = col.row()
row.prop(wpaint, "use_backface_culling")
row = col.row()
row.prop(wpaint, "use_normal_falloff")
sub = row.row()
sub.active = (wpaint.use_normal_falloff)
sub.prop(wpaint, "normal_angle", text="")
col = layout.column() col = layout.column()
row = col.row() row = col.row()
row.prop(wpaint, "use_spray") row.prop(wpaint, "use_spray")
@@ -1798,19 +1805,21 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel):
vpaint = toolsettings.vertex_paint vpaint = toolsettings.vertex_paint
col = layout.column() col = layout.column()
col.label("Falloff:")
row = col.row() row = col.row()
# col.prop(vpaint, "mode", text="") row.prop(vpaint, "falloff_shape", expand=True)
row.prop(vpaint, "use_normal") row = col.row()
row.prop(vpaint, "use_backface_culling")
row = col.row()
row.prop(vpaint, "use_normal_falloff")
sub = row.row()
sub.active = (vpaint.use_normal_falloff)
sub.prop(vpaint, "normal_angle", text="")
col.prop(vpaint, "use_spray") col.prop(vpaint, "use_spray")
self.unified_paint_settings(col, context) self.unified_paint_settings(col, context)
# Commented out because the Apply button isn't an operator yet, making these settings useless
#~ col.label(text="Gamma:")
#~ col.prop(vpaint, "gamma", text="")
#~ col.label(text="Multiply:")
#~ col.prop(vpaint, "mul", text="")
class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel): class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel):
bl_category = "Tools" bl_category = "Tools"

View File

@@ -70,7 +70,7 @@ void BKE_brush_randomize_texture_coords(struct UnifiedPaintSettings *ups, bool m
/* brush curve */ /* brush curve */
void BKE_brush_curve_preset(struct Brush *b, int preset); void BKE_brush_curve_preset(struct Brush *b, int preset);
float BKE_brush_curve_strength_clamped(struct Brush *br, float p, const float len); float BKE_brush_curve_strength_clamped(struct Brush *br, float p, const float len);
float BKE_brush_curve_strength(struct Brush *br, float p, const float len); float BKE_brush_curve_strength(const struct Brush *br, float p, const float len);
/* sampling */ /* sampling */
float BKE_brush_sample_tex_3D( float BKE_brush_sample_tex_3D(

View File

@@ -231,7 +231,7 @@ typedef struct SculptSession {
/* Keep track of how much each vertex has been painted (non-airbrush only). */ /* Keep track of how much each vertex has been painted (non-airbrush only). */
float *alpha_weight; float *alpha_weight;
/* Needed to continuously re-apply over the same weights (VP_SPRAY disabled). /* Needed to continuously re-apply over the same weights (VP_FLAG_SPRAY disabled).
* Lazy initialize as needed (flag is set to 1 to tag it as uninitialized). */ * Lazy initialize as needed (flag is set to 1 to tag it as uninitialized). */
struct MDeformVert *dvert_prev; struct MDeformVert *dvert_prev;
} wpaint; } wpaint;

View File

@@ -966,7 +966,7 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
} }
/* Uses the brush curve control to find a strength value */ /* Uses the brush curve control to find a strength value */
float BKE_brush_curve_strength(Brush *br, float p, const float len) float BKE_brush_curve_strength(const Brush *br, float p, const float len)
{ {
float strength; float strength;

View File

@@ -1699,6 +1699,20 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
} }
} }
} }
if (!DNA_struct_elem_find(fd->filesdna, "VPaint", "char", "falloff_shape")) {
for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
ToolSettings *ts = scene->toolsettings;
for (int i = 0; i < 2; i++) {
VPaint *vp = i ? ts->vpaint : ts->wpaint;
if (vp != NULL) {
/* remove all other flags */
vp->flag &= (VP_FLAG_SPRAY | VP_FLAG_VGROUP_RESTRICT);
vp->normal_angle = 80;
}
}
}
}
} }
} }

View File

@@ -88,6 +88,66 @@ struct WPaintAverageAccum {
double value; double value;
}; };
struct NormalAnglePrecalc {
bool do_mask_normal;
/* what angle to mask at */
float angle;
/* cos(angle), faster to compare */
float angle__cos;
float angle_inner;
float angle_inner__cos;
/* difference between angle and angle_inner, for easy access */
float angle_range;
};
static void view_angle_limits_init(
struct NormalAnglePrecalc *a, float angle, bool do_mask_normal)
{
a->do_mask_normal = do_mask_normal;
if (do_mask_normal) {
a->angle_inner = angle;
a->angle = (a->angle_inner + 90.0f) * 0.5f;
}
else {
a->angle_inner = a->angle = angle;
}
a->angle_inner *= (float)(M_PI_2 / 90);
a->angle *= (float)(M_PI_2 / 90);
a->angle_range = a->angle - a->angle_inner;
if (a->angle_range <= 0.0f) {
a->do_mask_normal = false; /* no need to do blending */
}
a->angle__cos = cosf(a->angle);
a->angle_inner__cos = cosf(a->angle_inner);
}
static float view_angle_limits_apply_falloff(
const struct NormalAnglePrecalc *a, float angle_cos, float *mask_p)
{
if (angle_cos <= a->angle__cos) {
/* outsize the normal limit */
return false;
}
else if (angle_cos < a->angle_inner__cos) {
*mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range;
return true;
}
else {
return true;
}
}
static bool vwpaint_use_normal(const VPaint *vp)
{
return ((vp->flag & VP_FLAG_PROJECT_BACKFACE) == 0) ||
((vp->flag & VP_FLAG_PROJECT_FLAT) == 0);
}
static void defweight_prev_restore_or_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index) static void defweight_prev_restore_or_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index)
{ {
MDeformVert *dv_curr = &dvert_curr[index]; MDeformVert *dv_curr = &dvert_curr[index];
@@ -185,7 +245,7 @@ static VPaint *new_vpaint(int wpaint)
{ {
VPaint *vp = MEM_callocN(sizeof(VPaint), "VPaint"); VPaint *vp = MEM_callocN(sizeof(VPaint), "VPaint");
vp->flag = (wpaint) ? 0 : VP_SPRAY; vp->flag = (wpaint) ? 0 : VP_FLAG_SPRAY;
vp->paint.flags |= PAINT_SHOW_BRUSH; vp->paint.flags |= PAINT_SHOW_BRUSH;
return vp; return vp;
@@ -213,7 +273,7 @@ static uint vpaint_blend(
uint color_blend = ED_vpaint_blend_tool(tool, color_curr, color_paint, alpha_i); uint color_blend = ED_vpaint_blend_tool(tool, color_curr, color_paint, alpha_i);
/* if no spray, clip color adding with colorig & orig alpha */ /* if no spray, clip color adding with colorig & orig alpha */
if ((vp->flag & VP_SPRAY) == 0) { if ((vp->flag & VP_FLAG_SPRAY) == 0) {
uint color_test, a; uint color_test, a;
char *cp, *ct, *co; char *cp, *ct, *co;
@@ -299,7 +359,7 @@ static float calc_vp_alpha_col_dl(
if (strength > 0.0f) { if (strength > 0.0f) {
float alpha = brush_alpha_pressure * strength; float alpha = brush_alpha_pressure * strength;
if (vp->flag & VP_NORMALS) { if ((vp->flag & VP_FLAG_PROJECT_FLAT) == 0) {
float dvec[3]; float dvec[3];
/* transpose ! */ /* transpose ! */
@@ -670,7 +730,7 @@ static void do_weight_paint_vertex_single(
index_mirr = vgroup_mirr = -1; index_mirr = vgroup_mirr = -1;
} }
if ((wp->flag & VP_SPRAY) == 0) { if ((wp->flag & VP_FLAG_SPRAY) == 0) {
struct MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; struct MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
defweight_prev_restore_or_init(dvert_prev, me->dvert, index); defweight_prev_restore_or_init(dvert_prev, me->dvert, index);
if (index_mirr != -1) { if (index_mirr != -1) {
@@ -678,7 +738,7 @@ static void do_weight_paint_vertex_single(
} }
} }
if (wp->flag & VP_ONLYVGROUP) { if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
dw = defvert_find_index(dv, wpi->active.index); dw = defvert_find_index(dv, wpi->active.index);
} }
else { else {
@@ -692,7 +752,7 @@ static void do_weight_paint_vertex_single(
/* get the mirror def vars */ /* get the mirror def vars */
if (index_mirr != -1) { if (index_mirr != -1) {
dv_mirr = &me->dvert[index_mirr]; dv_mirr = &me->dvert[index_mirr];
if (wp->flag & VP_ONLYVGROUP) { if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr); dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr);
if (dw_mirr == NULL) { if (dw_mirr == NULL) {
@@ -814,7 +874,7 @@ static void do_weight_paint_vertex_multi(
} }
} }
if ((wp->flag & VP_SPRAY) == 0) { if ((wp->flag & VP_FLAG_SPRAY) == 0) {
struct MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; struct MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
defweight_prev_restore_or_init(dvert_prev, me->dvert, index); defweight_prev_restore_or_init(dvert_prev, me->dvert, index);
if (index_mirr != -1) { if (index_mirr != -1) {
@@ -947,7 +1007,7 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
/* Create average brush arrays */ /* Create average brush arrays */
if (ob->mode == OB_MODE_VERTEX_PAINT) { if (ob->mode == OB_MODE_VERTEX_PAINT) {
if ((ts->vpaint->flag & VP_SPRAY) == 0) { if ((ts->vpaint->flag & VP_FLAG_SPRAY) == 0) {
if (ob->sculpt->mode.vpaint.previous_color == NULL) { if (ob->sculpt->mode.vpaint.previous_color == NULL) {
ob->sculpt->mode.vpaint.previous_color = ob->sculpt->mode.vpaint.previous_color =
MEM_callocN(me->totloop * sizeof(uint), __func__); MEM_callocN(me->totloop * sizeof(uint), __func__);
@@ -968,7 +1028,7 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
} }
} }
else if (ob->mode == OB_MODE_WEIGHT_PAINT) { else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
if ((ts->wpaint->flag & VP_SPRAY) == 0) { if ((ts->wpaint->flag & VP_FLAG_SPRAY) == 0) {
if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { if (ob->sculpt->mode.wpaint.alpha_weight == NULL) {
ob->sculpt->mode.wpaint.alpha_weight = ob->sculpt->mode.wpaint.alpha_weight =
MEM_callocN(me->totvert * sizeof(float), __func__); MEM_callocN(me->totvert * sizeof(float), __func__);
@@ -1116,6 +1176,7 @@ void PAINT_OT_weight_paint_toggle(wmOperatorType *ot)
struct WPaintData { struct WPaintData {
ViewContext vc; ViewContext vc;
struct NormalAnglePrecalc normal_angle_precalc;
struct WeightPaintGroupData active, mirror; struct WeightPaintGroupData active, mirror;
@@ -1138,12 +1199,12 @@ struct WPaintData {
/* Initialize the stroke cache invariants from operator properties */ /* Initialize the stroke cache invariants from operator properties */
static void vwpaint_update_cache_invariants( static void vwpaint_update_cache_invariants(
bContext *C, VPaint *vd, SculptSession *ss, wmOperator *op, const float mouse[2]) bContext *C, VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2])
{ {
StrokeCache *cache; StrokeCache *cache;
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
Brush *brush = BKE_paint_brush(&vd->paint); Brush *brush = BKE_paint_brush(&vp->paint);
ViewContext *vc = paint_stroke_view_context(op->customdata); ViewContext *vc = paint_stroke_view_context(op->customdata);
Object *ob = CTX_data_active_object(C); Object *ob = CTX_data_active_object(C);
float mat[3][3]; float mat[3][3];
@@ -1195,12 +1256,12 @@ static void vwpaint_update_cache_invariants(
} }
/* Initialize the stroke cache variants from operator properties */ /* Initialize the stroke cache variants from operator properties */
static void vwpaint_update_cache_variants(bContext *C, VPaint *vd, Object *ob, PointerRNA *ptr) static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, PointerRNA *ptr)
{ {
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache; StrokeCache *cache = ss->cache;
Brush *brush = BKE_paint_brush(&vd->paint); Brush *brush = BKE_paint_brush(&vp->paint);
/* This effects the actual brush radius, so things farther away /* This effects the actual brush radius, so things farther away
* are compared with a larger radius and vise versa. */ * are compared with a larger radius and vise versa. */
@@ -1258,7 +1319,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
int defbase_tot, defbase_tot_sel; int defbase_tot, defbase_tot_sel;
bool *defbase_sel; bool *defbase_sel;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
VPaint *vd = CTX_data_tool_settings(C)->wpaint; VPaint *vp = CTX_data_tool_settings(C)->wpaint;
float mat[4][4], imat[4][4]; float mat[4][4], imat[4][4];
@@ -1313,6 +1374,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData");
paint_stroke_set_mode_data(stroke, wpd); paint_stroke_set_mode_data(stroke, wpd);
view3d_set_viewcontext(C, &wpd->vc); view3d_set_viewcontext(C, &wpd->vc);
view_angle_limits_init(&wpd->normal_angle_precalc, vp->normal_angle, (vp->flag & VP_FLAG_PROJECT_FLAT) == 0);
wpd->active.index = vgroup_index.active; wpd->active.index = vgroup_index.active;
wpd->mirror.index = vgroup_index.mirror; wpd->mirror.index = vgroup_index.mirror;
@@ -1369,7 +1431,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
/* If not previously created, create vertex/weight paint mode session data */ /* If not previously created, create vertex/weight paint mode session data */
vertex_paint_init_session(scene, ob); vertex_paint_init_session(scene, ob);
vwpaint_update_cache_invariants(C, vd, ss, op, mouse); vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
vertex_paint_init_session_data(ts, ob); vertex_paint_init_session_data(ts, ob);
if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { if (ob->sculpt->mode.wpaint.dvert_prev != NULL) {
@@ -1391,7 +1453,7 @@ static float dot_vf3vs3(const float brushNormal[3], const short vertexNormal[3])
} }
static void get_brush_alpha_data( static void get_brush_alpha_data(
Scene *scene, SculptSession *ss, Brush *brush, const Scene *scene, const SculptSession *ss, const Brush *brush,
float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure) float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure)
{ {
*r_brush_size_pressure = *r_brush_size_pressure =
@@ -1417,6 +1479,22 @@ static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintIn
} }
} }
static SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(
SculptSession *ss, SculptBrushTest *test, char falloff_shape)
{
sculpt_brush_test_init(ss, test);
SculptBrushTestFn sculpt_brush_test_sq_fn;
if (falloff_shape == VP_FALLOFF_SHAPE_SPHERE) {
sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq;
}
else {
/* VP_FALLOFF_SHAPE_TUBE */
plane_from_point_normal_v3(test->plane, test->location, ss->cache->view_normal);
sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq;
}
return sculpt_brush_test_sq_fn;
}
static void do_wpaint_brush_blur_task_cb_ex( static void do_wpaint_brush_blur_task_cb_ex(
void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id)) void *userdata, void *UNUSED(userdata_chunk), const int n, const int UNUSED(thread_id))
{ {
@@ -1425,25 +1503,26 @@ static void do_wpaint_brush_blur_task_cb_ex(
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh); CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
Brush *brush = data->brush; const Brush *brush = data->brush;
StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
Scene *scene = CTX_data_scene(data->C); Scene *scene = CTX_data_scene(data->C);
const float brush_strength = cache->bstrength;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_sq(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop coopresponds to the current grid. /* For grid based pbvh, take the vert whose loop coopresponds to the current grid.
* Otherwise, take the current vert. */ * Otherwise, take the current vert. */
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
@@ -1469,16 +1548,19 @@ static void do_wpaint_brush_blur_task_cb_ex(
/* Apply the weight to the vertex. */ /* Apply the weight to the vertex. */
if (total_hit_loops != 0) { if (total_hit_loops != 0) {
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; float brush_strength = cache->bstrength;
if (view_dot > 0.0f) { const float angle_cos = (use_normal && vd.no) ?
dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
(angle_cos > 0.0f)) &&
((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength)))
{
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
float final_alpha = float final_alpha =
brush_fade * brush_strength * brush_fade * brush_strength *
grid_alpha * brush_alpha_pressure; grid_alpha * brush_alpha_pressure;
if (data->vp->flag & VP_NORMALS) {
final_alpha *= view_dot;
}
if (brush->flag & BRUSH_ACCUMULATE) { if (brush->flag & BRUSH_ACCUMULATE) {
float mask_accum = ss->mode.wpaint.previous_accum[v_index]; float mask_accum = ss->mode.wpaint.previous_accum[v_index];
final_alpha = min_ff(final_alpha + mask_accum, brush_strength); final_alpha = min_ff(final_alpha + mask_accum, brush_strength);
@@ -1506,12 +1588,12 @@ static void do_wpaint_brush_smear_task_cb_ex(
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh); CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap;
Brush *brush = data->brush; const Brush *brush = data->brush;
Scene *scene = CTX_data_scene(data->C); const Scene *scene = CTX_data_scene(data->C);
StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
const float brush_strength = cache->bstrength;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
float brush_dir[3]; float brush_dir[3];
@@ -1522,26 +1604,32 @@ static void do_wpaint_brush_smear_task_cb_ex(
if (normalize_v3(brush_dir) != 0.0f) { if (normalize_v3(brush_dir) != 0.0f) {
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_fast(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
if (view_dot > 0.0f) { * Otherwise, take the current vert. */
bool do_color = false; const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
const MVert *mv_curr = &data->me->mvert[v_index];
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid. /* If the vertex is selected */
* Otherwise, take the current vert. */ if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) {
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; float brush_strength = cache->bstrength;
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; const float angle_cos = (use_normal && vd.no) ?
const MVert *mv_curr = &data->me->mvert[v_index]; dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
/* If the vertex is selected */ (angle_cos > 0.0f)) &&
if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { ((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength)))
{
bool do_color = false;
/* Minimum dot product between brush direction and current /* Minimum dot product between brush direction and current
* to neighbor direction is 0.0, meaning orthogonal. */ * to neighbor direction is 0.0, meaning orthogonal. */
float stroke_dot_max = 0.0f; float stroke_dot_max = 0.0f;
@@ -1578,14 +1666,10 @@ static void do_wpaint_brush_smear_task_cb_ex(
} }
/* Apply weight to vertex */ /* Apply weight to vertex */
if (do_color) { if (do_color) {
const float brush_fade = BKE_brush_curve_strength(brush, test.dist, cache->radius); const float brush_fade = BKE_brush_curve_strength(brush, 0.0f, cache->radius);
float final_alpha = float final_alpha =
brush_fade * brush_strength * brush_fade * brush_strength *
grid_alpha * brush_alpha_pressure; grid_alpha * brush_alpha_pressure;
if (data->vp->flag & VP_NORMALS) {
final_alpha *= view_dot;
}
do_weight_paint_vertex( do_weight_paint_vertex(
data->vp, data->ob, data->wpi, data->vp, data->ob, data->wpi,
v_index, final_alpha, (float)weight_final); v_index, final_alpha, (float)weight_final);
@@ -1604,26 +1688,27 @@ static void do_wpaint_brush_draw_task_cb_ex(
SculptThreadedTaskData *data = userdata; SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt; SculptSession *ss = data->ob->sculpt;
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh); CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
Scene *scene = CTX_data_scene(data->C); const Scene *scene = CTX_data_scene(data->C);
Brush *brush = data->brush; const Brush *brush = data->brush;
StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
const float brush_strength = cache->bstrength;
const float paintweight = BKE_brush_weight_get(scene, brush); const float paintweight = BKE_brush_weight_get(scene, brush);
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_sq(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* Note: grids are 1:1 with corners (aka loops). /* Note: grids are 1:1 with corners (aka loops).
* For multires, take the vert whose loop cooresponds to the current grid. * For multires, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */ * Otherwise, take the current vert. */
@@ -1633,14 +1718,16 @@ static void do_wpaint_brush_draw_task_cb_ex(
const char v_flag = data->me->mvert[v_index].flag; const char v_flag = data->me->mvert[v_index].flag;
/* If the vertex is selected */ /* If the vertex is selected */
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; float brush_strength = cache->bstrength;
if (view_dot > 0.0f) { const float angle_cos = (use_normal && vd.no) ?
dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
(angle_cos > 0.0f)) &&
((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength)))
{
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
float final_alpha = brush_fade * brush_strength * grid_alpha * brush_alpha_pressure; float final_alpha = brush_fade * brush_strength * grid_alpha * brush_alpha_pressure;
if (data->vp->flag & VP_NORMALS) {
final_alpha *= view_dot;
}
if (brush->flag & BRUSH_ACCUMULATE) { if (brush->flag & BRUSH_ACCUMULATE) {
float mask_accum = ss->mode.wpaint.previous_accum[v_index]; float mask_accum = ss->mode.wpaint.previous_accum[v_index];
final_alpha = min_ff(final_alpha + mask_accum, brush_strength); final_alpha = min_ff(final_alpha + mask_accum, brush_strength);
@@ -1648,7 +1735,7 @@ static void do_wpaint_brush_draw_task_cb_ex(
} }
/* Non-spray logic. */ /* Non-spray logic. */
if ((data->vp->flag & VP_SPRAY) == 0) { if ((data->vp->flag & VP_FLAG_SPRAY) == 0) {
/* Only paint if we have greater alpha. */ /* Only paint if we have greater alpha. */
if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) {
ss->mode.wpaint.alpha_weight[v_index] = final_alpha; ss->mode.wpaint.alpha_weight[v_index] = final_alpha;
@@ -1676,6 +1763,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
StrokeCache *cache = ss->cache; StrokeCache *cache = ss->cache;
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh); CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
@@ -1684,16 +1772,18 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
accum->value = 0.0; accum->value = 0.0;
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_sq(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; const float angle_cos = (use_normal && vd.no) ?
if (view_dot > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) {
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
// const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; // const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
const char v_flag = data->me->mvert[v_index].flag; const char v_flag = data->me->mvert[v_index].flag;
@@ -1776,6 +1866,50 @@ static void wpaint_paint_leaves(
} }
} }
static PBVHNode **vwpaint_pbvh_gather_generic(
Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode)
{
SculptSession *ss = ob->sculpt;
const bool use_normal = vwpaint_use_normal(wp);
PBVHNode **nodes = NULL;
/* Build a list of all nodes that are potentially within the brush's area of influence */
if (wp->falloff_shape == VP_FALLOFF_SHAPE_SPHERE) {
SculptSearchSphereData data = {
.ss = ss,
.sd = sd,
.radius_squared = ss->cache->radius_squared,
.original = true,
};
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode);
if (use_normal) {
sculpt_pbvh_calc_area_normal(brush, ob, nodes, *r_totnode, true, ss->cache->sculpt_normal_symm);
}
else {
zero_v3(ss->cache->sculpt_normal_symm);
}
}
else {
struct DistRayAABB_Precalc dist_ray_to_aabb_precalc;
dist_squared_ray_to_aabb_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal);
SculptSearchCircleData data = {
.ss = ss,
.sd = sd,
.radius_squared = ss->cache->radius_squared,
.original = true,
.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
};
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode);
if (use_normal) {
copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal);
}
else {
zero_v3(ss->cache->sculpt_normal_symm);
}
}
return nodes;
}
static void wpaint_do_paint( static void wpaint_do_paint(
bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi, bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi,
Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle) Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle)
@@ -1784,19 +1918,9 @@ static void wpaint_do_paint(
ss->cache->radial_symmetry_pass = i; ss->cache->radial_symmetry_pass = i;
sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle);
SculptSearchSphereData data;
PBVHNode **nodes = NULL;
int totnode; int totnode;
PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, wp, sd, brush, &totnode);
/* Build a list of all nodes that are potentially within the brush's area of influence */
data.ss = ss;
data.sd = sd;
data.radius_squared = ss->cache->radius_squared;
data.original = true;
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, ss->cache->sculpt_normal_symm);
wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode); wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode);
if (nodes) if (nodes)
@@ -2179,6 +2303,8 @@ typedef struct PolyFaceMap {
struct VPaintData { struct VPaintData {
ViewContext vc; ViewContext vc;
struct NormalAnglePrecalc normal_angle_precalc;
uint paintcol; uint paintcol;
struct VertProjHandle *vp_handle; struct VertProjHandle *vp_handle;
@@ -2223,6 +2349,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); vpd = MEM_callocN(sizeof(*vpd), "VPaintData");
paint_stroke_set_mode_data(stroke, vpd); paint_stroke_set_mode_data(stroke, vpd);
view3d_set_viewcontext(C, &vpd->vc); view3d_set_viewcontext(C, &vpd->vc);
view_angle_limits_init(&vpd->normal_angle_precalc, vp->normal_angle, (vp->flag & VP_FLAG_PROJECT_FLAT) == 0);
vpd->paintcol = vpaint_get_current_col(scene, vp); vpd->paintcol = vpaint_get_current_col(scene, vp);
@@ -2287,16 +2414,17 @@ static void do_vpaint_brush_calc_average_color_cb_ex(
memset(accum->value, 0, sizeof(accum->value)); memset(accum->value, 0, sizeof(accum->value));
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_fast(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
if (BKE_brush_curve_strength(data->brush, test.dist, cache->radius) > 0.0) { if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) {
/* If the vertex is selected for painting. */ /* If the vertex is selected for painting. */
const MVert *mv = &data->me->mvert[v_index]; const MVert *mv = &data->me->mvert[v_index];
if (!use_vert_sel || mv->flag & SELECT) { if (!use_vert_sel || mv->flag & SELECT) {
@@ -2344,25 +2472,26 @@ static void do_vpaint_brush_draw_task_cb_ex(
CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh); CCGDerivedMesh *ccgdm = BKE_pbvh_get_ccgdm(ss->pbvh);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
Brush *brush = data->brush; const Brush *brush = data->brush;
StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
const float brush_strength = cache->bstrength;
uint *lcol = data->lcol; uint *lcol = data->lcol;
Scene *scene = CTX_data_scene(data->C); const Scene *scene = CTX_data_scene(data->C);
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_sq(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* Note: Grids are 1:1 with corners (aka loops). /* Note: Grids are 1:1 with corners (aka loops).
* For grid based pbvh, take the vert whose loop cooresponds to the current grid. * For grid based pbvh, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */ * Otherwise, take the current vert. */
@@ -2374,8 +2503,14 @@ static void do_vpaint_brush_draw_task_cb_ex(
if (!use_vert_sel || mv->flag & SELECT) { if (!use_vert_sel || mv->flag & SELECT) {
/* Calc the dot prod. between ray norm on surf and current vert /* Calc the dot prod. between ray norm on surf and current vert
* (ie splash prevention factor), and only paint front facing verts. */ * (ie splash prevention factor), and only paint front facing verts. */
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; float brush_strength = cache->bstrength;
if (view_dot > 0.0f) { const float angle_cos = (use_normal && vd.no) ?
dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
(angle_cos > 0.0f)) &&
((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength)))
{
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
uint color_final = data->vpd->paintcol; uint color_final = data->vpd->paintcol;
@@ -2404,10 +2539,6 @@ static void do_vpaint_brush_draw_task_cb_ex(
float final_alpha = float final_alpha =
255 * brush_fade * brush_strength * 255 * brush_fade * brush_strength *
tex_alpha * brush_alpha_pressure * grid_alpha; tex_alpha * brush_alpha_pressure * grid_alpha;
if (data->vp->flag & VP_NORMALS) {
final_alpha *= view_dot;
}
if (brush->flag & BRUSH_ACCUMULATE) { if (brush->flag & BRUSH_ACCUMULATE) {
float mask_accum = ss->mode.vpaint.previous_accum[l_index]; float mask_accum = ss->mode.vpaint.previous_accum[l_index];
final_alpha = min_ff(final_alpha + mask_accum, 255.0f * brush_strength); final_alpha = min_ff(final_alpha + mask_accum, 255.0f * brush_strength);
@@ -2436,36 +2567,43 @@ static void do_vpaint_brush_blur_task_cb_ex(
Scene *scene = CTX_data_scene(data->C); Scene *scene = CTX_data_scene(data->C);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
Brush *brush = data->brush; const Brush *brush = data->brush;
StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
const float brush_strength = cache->bstrength;
uint *lcol = data->lcol; uint *lcol = data->lcol;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_sq(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid. /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */ * Otherwise, take the current vert. */
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; const float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f;
const MVert *mv = &data->me->mvert[v_index]; const MVert *mv = &data->me->mvert[v_index];
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; /* If the vertex is selected for painting. */
if (view_dot > 0.0f) { if (!use_vert_sel || mv->flag & SELECT) {
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); float brush_strength = cache->bstrength;
const float angle_cos = (use_normal && vd.no) ?
dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
(angle_cos > 0.0f)) &&
((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength)))
{
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
/* If the vertex is selected for painting. */
if (!use_vert_sel || mv->flag & SELECT) {
/* Get the average poly color */ /* Get the average poly color */
uint color_final = 0; uint color_final = 0;
int total_hit_loops = 0; int total_hit_loops = 0;
@@ -2512,10 +2650,6 @@ static void do_vpaint_brush_blur_task_cb_ex(
float final_alpha = float final_alpha =
255 * brush_fade * brush_strength * 255 * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha; brush_alpha_pressure * grid_alpha;
if (data->vp->flag & VP_NORMALS) {
final_alpha *= view_dot;
}
/* Mix the new color with the original /* Mix the new color with the original
* based on the brush strength and the curve. */ * based on the brush strength and the curve. */
lcol[l_index] = vpaint_blend( lcol[l_index] = vpaint_blend(
@@ -2540,13 +2674,13 @@ static void do_vpaint_brush_smear_task_cb_ex(
Scene *scene = CTX_data_scene(data->C); Scene *scene = CTX_data_scene(data->C);
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
Brush *brush = data->brush; const Brush *brush = data->brush;
StrokeCache *cache = ss->cache; const StrokeCache *cache = ss->cache;
const float brush_strength = cache->bstrength;
uint *lcol = data->lcol; uint *lcol = data->lcol;
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
float brush_dir[3]; float brush_dir[3];
const bool use_normal = vwpaint_use_normal(data->vp);
const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
@@ -2556,14 +2690,15 @@ static void do_vpaint_brush_smear_task_cb_ex(
if (normalize_v3(brush_dir) != 0.0f) { if (normalize_v3(brush_dir) != 0.0f) {
SculptBrushTest test; SculptBrushTest test;
sculpt_brush_test_init(ss, &test); SculptBrushTestFn sculpt_brush_test_sq_fn =
sculpt_brush_test_init_with_falloff_shape(ss, &test, data->vp->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{ {
/* Test to see if the vertex coordinates are within the spherical brush region. */ /* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sphere_sq(&test, vd.co)) { if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop cooresponds to the current grid. /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
* Otherwise, take the current vert. */ * Otherwise, take the current vert. */
const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const int v_index = ccgdm ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
@@ -2574,8 +2709,14 @@ static void do_vpaint_brush_smear_task_cb_ex(
if (!use_vert_sel || mv_curr->flag & SELECT) { if (!use_vert_sel || mv_curr->flag & SELECT) {
/* Calc the dot prod. between ray norm on surf and current vert /* Calc the dot prod. between ray norm on surf and current vert
* (ie splash prevention factor), and only paint front facing verts. */ * (ie splash prevention factor), and only paint front facing verts. */
const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; float brush_strength = cache->bstrength;
if (view_dot > 0.0f) { const float angle_cos = (use_normal && vd.no) ?
dot_vf3vs3(ss->cache->sculpt_normal_symm, vd.no) : 1.0f;
if (((data->vp->flag & VP_FLAG_PROJECT_BACKFACE) ||
(angle_cos > 0.0f)) &&
((data->vp->flag & VP_FLAG_PROJECT_FLAT) ||
view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength)))
{
const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius);
bool do_color = false; bool do_color = false;
@@ -2636,10 +2777,6 @@ static void do_vpaint_brush_smear_task_cb_ex(
float final_alpha = float final_alpha =
255 * brush_fade * brush_strength * 255 * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha; brush_alpha_pressure * grid_alpha;
if (data->vp->flag & VP_NORMALS) {
final_alpha *= view_dot;
}
/* Mix the new color with the original /* Mix the new color with the original
* based on the brush strength and the curve. */ * based on the brush strength and the curve. */
lcol[l_index] = vpaint_blend( lcol[l_index] = vpaint_blend(
@@ -2721,27 +2858,18 @@ static void vpaint_paint_leaves(
} }
static void vpaint_do_paint( static void vpaint_do_paint(
bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd,
Object *ob, Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle) Object *ob, Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle)
{ {
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
ss->cache->radial_symmetry_pass = i; ss->cache->radial_symmetry_pass = i;
sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle);
SculptSearchSphereData data;
PBVHNode **nodes = NULL;
int totnode; int totnode;
PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode);
/* Build a list of all nodes that are potentially within the brush's area of influence */
data.ss = ss;
data.sd = sd;
data.radius_squared = ss->cache->radius_squared;
data.original = true;
BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, ss->cache->sculpt_normal_symm);
/* Paint those leaves. */ /* Paint those leaves. */
vpaint_paint_leaves(C, sd, vd, vpd, ob, me, nodes, totnode); vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode);
if (nodes) { if (nodes) {
MEM_freeN(nodes); MEM_freeN(nodes);
@@ -2749,31 +2877,31 @@ static void vpaint_do_paint(
} }
static void vpaint_do_radial_symmetry( static void vpaint_do_radial_symmetry(
bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob, Mesh *me, bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob, Mesh *me,
Brush *brush, const char symm, const int axis) Brush *brush, const char symm, const int axis)
{ {
for (int i = 1; i < vd->radial_symm[axis - 'X']; i++) { for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) {
const float angle = (2.0 * M_PI) * i / vd->radial_symm[axis - 'X']; const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X'];
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, symm, axis, i, angle); vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle);
} }
} }
/* near duplicate of: sculpt.c's, 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */ /* near duplicate of: sculpt.c's, 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
static void vpaint_do_symmetrical_brush_actions( static void vpaint_do_symmetrical_brush_actions(
bContext *C, Sculpt *sd, VPaint *vd, struct VPaintData *vpd, Object *ob) bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob)
{ {
Brush *brush = BKE_paint_brush(&vd->paint); Brush *brush = BKE_paint_brush(&vp->paint);
Mesh *me = ob->data; Mesh *me = ob->data;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache; StrokeCache *cache = ss->cache;
const char symm = vd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; const char symm = vp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
int i = 0; int i = 0;
/* initial stroke */ /* initial stroke */
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0); vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X');
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y');
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z');
cache->symmetry = symm; cache->symmetry = symm;
@@ -2785,16 +2913,16 @@ static void vpaint_do_symmetrical_brush_actions(
sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); sculpt_cache_calc_brushdata_symm(cache, i, 0, 0);
if (i & (1 << 0)) { if (i & (1 << 0)) {
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0); vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X');
} }
if (i & (1 << 1)) { if (i & (1 << 1)) {
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Y', 0, 0); vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0);
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y');
} }
if (i & (1 << 2)) { if (i & (1 << 2)) {
vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Z', 0, 0); vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0);
vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z');
} }
} }
} }

View File

@@ -587,6 +587,24 @@ bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3
return len_squared_v3v3(co, test->location) <= test->radius_squared; return len_squared_v3v3(co, test->location) <= test->radius_squared;
} }
bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3])
{
float co_proj[3];
closest_to_plane_normalized_v3(co_proj, test->plane, co);
float distsq = len_squared_v3v3(co_proj, test->location);
if (distsq <= test->radius_squared) {
if (sculpt_brush_test_clipping(test, co)) {
return 0;
}
test->dist = distsq;
return 1;
}
else {
return 0;
}
}
bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]) bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4])
{ {
float side = M_SQRT1_2; float side = M_SQRT1_2;
@@ -1216,6 +1234,24 @@ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
return len_squared_v3(t) < data->radius_squared; return len_squared_v3(t) < data->radius_squared;
} }
/* 2D projection (distance to line). */
bool sculpt_search_circle_cb(PBVHNode *node, void *data_v)
{
SculptSearchCircleData *data = data_v;
float bb_min[3], bb_max[3];
if (data->original)
BKE_pbvh_node_get_original_BB(node, bb_min, bb_max);
else
BKE_pbvh_node_get_BB(node, bb_min, bb_min);
float dummy_co[3], dummy_depth;
const float dist_sq = dist_squared_ray_to_aabb(
data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth);
return dist_sq < data->radius_squared;
}
/* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3]) static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3])
{ {

View File

@@ -188,6 +188,8 @@ typedef struct SculptBrushTest {
struct RegionView3D *clip_rv3d; struct RegionView3D *clip_rv3d;
} SculptBrushTest; } SculptBrushTest;
typedef bool (*SculptBrushTestFn)(SculptBrushTest *test, const float co[3]);
typedef struct { typedef struct {
struct Sculpt *sd; struct Sculpt *sd;
struct SculptSession *ss; struct SculptSession *ss;
@@ -195,12 +197,22 @@ typedef struct {
bool original; bool original;
} SculptSearchSphereData; } SculptSearchSphereData;
typedef struct {
struct Sculpt *sd;
struct SculptSession *ss;
float radius_squared;
bool original;
struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc;
} SculptSearchCircleData;
void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test); void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test);
bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]); bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]); bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]); bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]);
bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]); bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]);
bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]);
bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v); bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v);
bool sculpt_search_circle_cb(PBVHNode *node, void *data_v);
float tex_strength( float tex_strength(
struct SculptSession *ss, struct Brush *br, struct SculptSession *ss, struct Brush *br,
const float point[3], const float point[3],

View File

@@ -1117,18 +1117,26 @@ typedef struct UvSculpt {
/* Vertex Paint */ /* Vertex Paint */
typedef struct VPaint { typedef struct VPaint {
Paint paint; Paint paint;
short flag, pad; short flag;
char falloff_shape, normal_angle;
int radial_symm[3]; /* For mirrored painting */ int radial_symm[3]; /* For mirrored painting */
} VPaint; } VPaint;
/* VPaint.flag */ /* VPaint.flag */
enum { enum {
// VP_COLINDEX = (1 << 0), /* only paint onto active material*/ /* deprecated since before 2.49 */ VP_FLAG_PROJECT_BACKFACE = (1 << 0),
// VP_AREA = (1 << 1), /* deprecated since 2.70 */ /* TODO */
VP_NORMALS = (1 << 3), // VP_FLAG_PROJECT_XRAY = (1 << 1),
VP_SPRAY = (1 << 4), VP_FLAG_PROJECT_FLAT = (1 << 3),
// VP_MIRROR_X = (1 << 5), /* deprecated in 2.5x use (me->editflag & ME_EDIT_MIRROR_X) */ VP_FLAG_SPRAY = (1 << 4),
VP_ONLYVGROUP = (1 << 7) /* weight paint only */ /* weight paint only */
VP_FLAG_VGROUP_RESTRICT = (1 << 7)
};
/* VPaint.falloff_shape */
enum {
VP_FALLOFF_SHAPE_SPHERE = 0,
VP_FALLOFF_SHAPE_TUBE = 1,
}; };
/* ------------------------------------------- */ /* ------------------------------------------- */

View File

@@ -671,23 +671,44 @@ static void rna_def_vertex_paint(BlenderRNA *brna)
RNA_def_struct_path_func(srna, "rna_VertexPaint_path"); RNA_def_struct_path_func(srna, "rna_VertexPaint_path");
RNA_def_struct_ui_text(srna, "Vertex Paint", "Properties of vertex and weight paint mode"); RNA_def_struct_ui_text(srna, "Vertex Paint", "Properties of vertex and weight paint mode");
/* vertex paint only */ static EnumPropertyItem prop_falloff_items[] = {
prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE); {VP_FALLOFF_SHAPE_SPHERE, "SPHERE", 0, "Sphere", "Spherical falloff from the brush"},
RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_NORMALS); {VP_FALLOFF_SHAPE_TUBE, "TUBE", 0, "Circle", "Circular falloff from the brush along the view"},
RNA_def_property_ui_text(prop, "Normals", "Apply the vertex normal before painting"); {0, NULL, 0, NULL, NULL}
};
prop = RNA_def_property(srna, "falloff_shape", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "falloff_shape");
RNA_def_property_enum_items(prop, prop_falloff_items);
RNA_def_property_ui_text(prop, "Falloff", "");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_backface_culling", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", VP_FLAG_PROJECT_BACKFACE);
RNA_def_property_ui_text(prop, "Cull", "Ignore vertices pointing away from the view (faster)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_normal_falloff", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", VP_FLAG_PROJECT_FLAT);
RNA_def_property_ui_text(prop, "Normals", "Paint most on faces pointing towards the view");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_spray", PROP_BOOLEAN, PROP_NONE); prop = RNA_def_property(srna, "use_spray", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_SPRAY); RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_FLAG_SPRAY);
RNA_def_property_ui_text(prop, "Spray", "Keep applying paint effect while holding mouse"); RNA_def_property_ui_text(prop, "Spray", "Keep applying paint effect while holding mouse");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
/* weight paint only */ /* weight paint only */
prop = RNA_def_property(srna, "use_group_restrict", PROP_BOOLEAN, PROP_NONE); prop = RNA_def_property(srna, "use_group_restrict", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_ONLYVGROUP); RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_FLAG_VGROUP_RESTRICT);
RNA_def_property_ui_text(prop, "Restrict", "Restrict painting to vertices in the group"); RNA_def_property_ui_text(prop, "Restrict", "Restrict painting to vertices in the group");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "normal_angle", PROP_INT, PROP_UNSIGNED);
RNA_def_property_range(prop, 0, 90);
RNA_def_property_ui_text(prop, "Angle", "Paint most on faces pointing towards the view according to this angle");
/* Mirroring */ /* Mirroring */
prop = RNA_def_property(srna, "radial_symmetry", PROP_INT, PROP_XYZ); prop = RNA_def_property(srna, "radial_symmetry", PROP_INT, PROP_XYZ);
RNA_def_property_int_sdna(prop, NULL, "radial_symm"); RNA_def_property_int_sdna(prop, NULL, "radial_symm");