diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 29 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_brush.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_paint.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_270.c | 14 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_vertex.c | 422 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 36 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 22 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_sculpt_paint.c | 33 |
10 files changed, 401 insertions, 173 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index d260bfe3460..798b5e28d9c 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1761,9 +1761,16 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): wpaint = tool_settings.weight_paint col = layout.column() + col.label("Falloff:") row = col.row() - - row.prop(wpaint, "use_normal") + row.prop(wpaint, "falloff_shape", expand=True) + 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() row = col.row() row.prop(wpaint, "use_spray") @@ -1798,19 +1805,21 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel): vpaint = toolsettings.vertex_paint col = layout.column() + col.label("Falloff:") + row = col.row() + row.prop(vpaint, "falloff_shape", expand=True) row = col.row() - # col.prop(vpaint, "mode", text="") - row.prop(vpaint, "use_normal") + 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") 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): bl_category = "Tools" diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index dedb75a080a..c1e107e101a 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -70,7 +70,7 @@ void BKE_brush_randomize_texture_coords(struct UnifiedPaintSettings *ups, bool m /* brush curve */ 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(struct Brush *br, float p, const float len); +float BKE_brush_curve_strength(const struct Brush *br, float p, const float len); /* sampling */ float BKE_brush_sample_tex_3D( diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index fe7461c810d..19f332f5f54 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -231,7 +231,7 @@ typedef struct SculptSession { /* Keep track of how much each vertex has been painted (non-airbrush only). */ 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). */ struct MDeformVert *dvert_prev; } wpaint; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 03b0710c8fc..0300dfe5891 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -966,7 +966,7 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask) } /* 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; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 8459e080f99..b374e49fc83 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -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; + } + } + } + } } } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 7b534e12f31..ea85ae70794 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -88,6 +88,66 @@ struct WPaintAverageAccum { 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) { MDeformVert *dv_curr = &dvert_curr[index]; @@ -185,7 +245,7 @@ static VPaint *new_vpaint(int wpaint) { 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; return vp; @@ -213,7 +273,7 @@ static uint vpaint_blend( 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 ((vp->flag & VP_SPRAY) == 0) { + if ((vp->flag & VP_FLAG_SPRAY) == 0) { uint color_test, a; char *cp, *ct, *co; @@ -299,7 +359,7 @@ static float calc_vp_alpha_col_dl( if (strength > 0.0f) { float alpha = brush_alpha_pressure * strength; - if (vp->flag & VP_NORMALS) { + if ((vp->flag & VP_FLAG_PROJECT_FLAT) == 0) { float dvec[3]; /* transpose ! */ @@ -670,7 +730,7 @@ static void do_weight_paint_vertex_single( 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; defweight_prev_restore_or_init(dvert_prev, me->dvert, index); 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); } else { @@ -692,7 +752,7 @@ static void do_weight_paint_vertex_single( /* get the mirror def vars */ if (index_mirr != -1) { 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); 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; defweight_prev_restore_or_init(dvert_prev, me->dvert, index); if (index_mirr != -1) { @@ -947,7 +1007,7 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) /* Create average brush arrays */ 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) { ob->sculpt->mode.vpaint.previous_color = 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) { - if ((ts->wpaint->flag & VP_SPRAY) == 0) { + if ((ts->wpaint->flag & VP_FLAG_SPRAY) == 0) { if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { ob->sculpt->mode.wpaint.alpha_weight = MEM_callocN(me->totvert * sizeof(float), __func__); @@ -1116,6 +1176,7 @@ void PAINT_OT_weight_paint_toggle(wmOperatorType *ot) struct WPaintData { ViewContext vc; + struct NormalAnglePrecalc normal_angle_precalc; struct WeightPaintGroupData active, mirror; @@ -1138,12 +1199,12 @@ struct WPaintData { /* Initialize the stroke cache invariants from operator properties */ 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; Scene *scene = CTX_data_scene(C); 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); Object *ob = CTX_data_active_object(C); float mat[3][3]; @@ -1195,12 +1256,12 @@ static void vwpaint_update_cache_invariants( } /* 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); SculptSession *ss = ob->sculpt; 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 * 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; bool *defbase_sel; 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]; @@ -1313,6 +1374,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); paint_stroke_set_mode_data(stroke, wpd); 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->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 */ 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); 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( - 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) { *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( 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); const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; - Brush *brush = data->brush; - StrokeCache *cache = ss->cache; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; Scene *scene = CTX_data_scene(data->C); - const float brush_strength = cache->bstrength; 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); + 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_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; 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 */ PBVHVertexIter vd; 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. */ - 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. * Otherwise, take the current vert. */ 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. */ if (total_hit_loops != 0) { - const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; - if (view_dot > 0.0f) { + 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->wpd->normal_angle_precalc, angle_cos, &brush_strength))) + { 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; - if (data->vp->flag & VP_NORMALS) { - final_alpha *= view_dot; - } if (brush->flag & BRUSH_ACCUMULATE) { float mask_accum = ss->mode.wpaint.previous_accum[v_index]; 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); const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; - Brush *brush = data->brush; - Scene *scene = CTX_data_scene(data->C); - StrokeCache *cache = ss->cache; - const float brush_strength = cache->bstrength; + const Brush *brush = data->brush; + const Scene *scene = CTX_data_scene(data->C); + const StrokeCache *cache = ss->cache; 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); + 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_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; float brush_dir[3]; @@ -1522,26 +1604,32 @@ static void do_wpaint_brush_smear_task_cb_ex( if (normalize_v3(brush_dir) != 0.0f) { 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 */ PBVHVertexIter vd; 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. */ - if (sculpt_brush_test_sphere_fast(&test, vd.co)) { - const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; - if (view_dot > 0.0f) { - bool do_color = false; - - /* For grid based pbvh, take the vert whose loop cooresponds to the current grid. - * 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 float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv_curr = &data->me->mvert[v_index]; - - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop cooresponds to the current grid. + * 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 float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv_curr = &data->me->mvert[v_index]; + + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { + 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->wpd->normal_angle_precalc, angle_cos, &brush_strength))) + { + bool do_color = false; /* Minimum dot product between brush direction and current * to neighbor direction is 0.0, meaning orthogonal. */ float stroke_dot_max = 0.0f; @@ -1578,14 +1666,10 @@ static void do_wpaint_brush_smear_task_cb_ex( } /* Apply weight to vertex */ 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 = brush_fade * brush_strength * grid_alpha * brush_alpha_pressure; - - if (data->vp->flag & VP_NORMALS) { - final_alpha *= view_dot; - } do_weight_paint_vertex( data->vp, data->ob, data->wpi, v_index, final_alpha, (float)weight_final); @@ -1604,26 +1688,27 @@ static void do_wpaint_brush_draw_task_cb_ex( SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; 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; - StrokeCache *cache = ss->cache; - const float brush_strength = cache->bstrength; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; const float paintweight = BKE_brush_weight_get(scene, brush); 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); + 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_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; 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 */ PBVHVertexIter vd; 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. */ - 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). * For multires, take the vert whose loop cooresponds to the current grid. * 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; /* If the vertex is selected */ 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; - if (view_dot > 0.0f) { + 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->wpd->normal_angle_precalc, angle_cos, &brush_strength))) + { 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; - - if (data->vp->flag & VP_NORMALS) { - final_alpha *= view_dot; - } if (brush->flag & BRUSH_ACCUMULATE) { float mask_accum = ss->mode.wpaint.previous_accum[v_index]; 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. */ - if ((data->vp->flag & VP_SPRAY) == 0) { + if ((data->vp->flag & VP_FLAG_SPRAY) == 0) { /* Only paint if we have greater alpha. */ if (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; 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_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; 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 */ PBVHVertexIter vd; 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. */ - if (sculpt_brush_test_sphere_sq(&test, vd.co)) { - const float view_dot = (vd.no) ? dot_vf3vs3(cache->sculpt_normal_symm, vd.no) : 1.0; - if (view_dot > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float angle_cos = (use_normal && vd.no) ? + 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 float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; 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( 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) @@ -1784,19 +1918,9 @@ static void wpaint_do_paint( ss->cache->radial_symmetry_pass = i; sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - SculptSearchSphereData data; - PBVHNode **nodes = NULL; 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); if (nodes) @@ -2179,6 +2303,8 @@ typedef struct PolyFaceMap { struct VPaintData { ViewContext vc; + struct NormalAnglePrecalc normal_angle_precalc; + uint paintcol; 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"); paint_stroke_set_mode_data(stroke, vpd); 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); @@ -2287,16 +2414,17 @@ static void do_vpaint_brush_calc_average_color_cb_ex( memset(accum->value, 0, sizeof(accum->value)); 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 */ PBVHVertexIter vd; 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. */ - 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]; - 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. */ const MVert *mv = &data->me->mvert[v_index]; 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); const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - Brush *brush = data->brush; - StrokeCache *cache = ss->cache; - const float brush_strength = cache->bstrength; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; 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; 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_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; 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 */ PBVHVertexIter vd; 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. */ - 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). * For grid based pbvh, take the vert whose loop cooresponds to the current grid. * 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) { /* Calc the dot prod. between ray norm on surf and current vert * (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; - if (view_dot > 0.0f) { + 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); uint color_final = data->vpd->paintcol; @@ -2404,10 +2539,6 @@ static void do_vpaint_brush_draw_task_cb_ex( float final_alpha = 255 * brush_fade * brush_strength * tex_alpha * brush_alpha_pressure * grid_alpha; - - if (data->vp->flag & VP_NORMALS) { - final_alpha *= view_dot; - } if (brush->flag & BRUSH_ACCUMULATE) { float mask_accum = ss->mode.vpaint.previous_accum[l_index]; 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); const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - Brush *brush = data->brush; - StrokeCache *cache = ss->cache; - const float brush_strength = cache->bstrength; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; uint *lcol = data->lcol; 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); + 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_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; 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 */ PBVHVertexIter vd; 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. */ - 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. * 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 float grid_alpha = ccgdm ? 1.0f / vd.gridsize : 1.0f; 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 (view_dot > 0.0f) { - 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) { + 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 */ uint color_final = 0; int total_hit_loops = 0; @@ -2512,10 +2650,6 @@ static void do_vpaint_brush_blur_task_cb_ex( float final_alpha = 255 * brush_fade * brush_strength * brush_alpha_pressure * grid_alpha; - - if (data->vp->flag & VP_NORMALS) { - final_alpha *= view_dot; - } /* Mix the new color with the original * based on the brush strength and the curve. */ 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); const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - Brush *brush = data->brush; - StrokeCache *cache = ss->cache; - const float brush_strength = cache->bstrength; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; uint *lcol = data->lcol; 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); 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_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) { 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 */ PBVHVertexIter vd; 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. */ - 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. * Otherwise, take the current vert. */ 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) { /* Calc the dot prod. between ray norm on surf and current vert * (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; - if (view_dot > 0.0f) { + 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); bool do_color = false; @@ -2636,10 +2777,6 @@ static void do_vpaint_brush_smear_task_cb_ex( float final_alpha = 255 * brush_fade * brush_strength * brush_alpha_pressure * grid_alpha; - - if (data->vp->flag & VP_NORMALS) { - final_alpha *= view_dot; - } /* Mix the new color with the original * based on the brush strength and the curve. */ lcol[l_index] = vpaint_blend( @@ -2721,27 +2858,18 @@ static void vpaint_paint_leaves( } 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) { SculptSession *ss = ob->sculpt; ss->cache->radial_symmetry_pass = i; sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - SculptSearchSphereData data; - PBVHNode **nodes = NULL; - int 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); + int totnode; + PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode); /* 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) { MEM_freeN(nodes); @@ -2749,31 +2877,31 @@ static void vpaint_do_paint( } 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) { - for (int i = 1; i < vd->radial_symm[axis - 'X']; i++) { - const float angle = (2.0 * M_PI) * i / vd->radial_symm[axis - 'X']; - vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, symm, axis, i, angle); + for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { + const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; + 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'. */ 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; SculptSession *ss = ob->sculpt; 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; /* initial stroke */ - vpaint_do_paint(C, sd, vd, 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, vd, vpd, ob, me, brush, i, 'Y'); - vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z'); + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); cache->symmetry = symm; @@ -2785,16 +2913,16 @@ static void vpaint_do_symmetrical_brush_actions( sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); if (i & (1 << 0)) { - vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'X'); + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); } if (i & (1 << 1)) { - vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Y', 0, 0); - vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Y'); + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); } if (i & (1 << 2)) { - vpaint_do_paint(C, sd, vd, vpd, ob, me, brush, i, 'Z', 0, 0); - vpaint_do_radial_symmetry(C, sd, vd, vpd, ob, me, brush, i, 'Z'); + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 69ae47790a0..1698ab9e5c7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -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; } +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]) { 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; } +/* 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 */ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3]) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 7d9d3ad783d..425cecb0010 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -188,6 +188,8 @@ typedef struct SculptBrushTest { struct RegionView3D *clip_rv3d; } SculptBrushTest; +typedef bool (*SculptBrushTestFn)(SculptBrushTest *test, const float co[3]); + typedef struct { struct Sculpt *sd; struct SculptSession *ss; @@ -195,12 +197,22 @@ typedef struct { bool original; } 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); 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_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_circle_sq(SculptBrushTest *test, const float co[3]); bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v); +bool sculpt_search_circle_cb(PBVHNode *node, void *data_v); float tex_strength( struct SculptSession *ss, struct Brush *br, const float point[3], diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3201b75ee1e..e9caf7ada1c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1117,18 +1117,26 @@ typedef struct UvSculpt { /* Vertex Paint */ typedef struct VPaint { Paint paint; - short flag, pad; + short flag; + char falloff_shape, normal_angle; int radial_symm[3]; /* For mirrored painting */ } VPaint; /* VPaint.flag */ enum { - // VP_COLINDEX = (1 << 0), /* only paint onto active material*/ /* deprecated since before 2.49 */ - // VP_AREA = (1 << 1), /* deprecated since 2.70 */ - VP_NORMALS = (1 << 3), - VP_SPRAY = (1 << 4), - // VP_MIRROR_X = (1 << 5), /* deprecated in 2.5x use (me->editflag & ME_EDIT_MIRROR_X) */ - VP_ONLYVGROUP = (1 << 7) /* weight paint only */ + VP_FLAG_PROJECT_BACKFACE = (1 << 0), + /* TODO */ + // VP_FLAG_PROJECT_XRAY = (1 << 1), + VP_FLAG_PROJECT_FLAT = (1 << 3), + VP_FLAG_SPRAY = (1 << 4), + /* weight paint only */ + VP_FLAG_VGROUP_RESTRICT = (1 << 7) +}; + +/* VPaint.falloff_shape */ +enum { + VP_FALLOFF_SHAPE_SPHERE = 0, + VP_FALLOFF_SHAPE_TUBE = 1, }; /* ------------------------------------------- */ diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 09da34d59d8..6c89767ad29 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -671,23 +671,44 @@ static void rna_def_vertex_paint(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_VertexPaint_path"); RNA_def_struct_ui_text(srna, "Vertex Paint", "Properties of vertex and weight paint mode"); - /* vertex paint only */ - prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_NORMALS); - RNA_def_property_ui_text(prop, "Normals", "Apply the vertex normal before painting"); + static EnumPropertyItem prop_falloff_items[] = { + {VP_FALLOFF_SHAPE_SPHERE, "SPHERE", 0, "Sphere", "Spherical falloff from the brush"}, + {VP_FALLOFF_SHAPE_TUBE, "TUBE", 0, "Circle", "Circular falloff from the brush along the view"}, + {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); + 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_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* weight paint only */ 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_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 */ prop = RNA_def_property(srna, "radial_symmetry", PROP_INT, PROP_XYZ); RNA_def_property_int_sdna(prop, NULL, "radial_symm"); |