Cleanup: Reduce use and scope of templates in vertex paint

Many high-level functions with unrelated code were templated based on
the color attribute domain and type. In the end, those were just a few
branches though, similar to other branches. So to reduce binary bloat
and clarify code, move tempates to the point where separate types are
actually needed. Also move constant code out of loops, and use generic
arrays and spans to store some caches. The binary ended up 53 KB
smaller for me, which isn't much but it's in the right direction.

Performance wise nothing really changes. The vast majority of time in
vertex paint is spent on unrelated things like calculating normals or
uploading GPU buffers anyway.

The one ugly part I didn't account for when I started is the casting
between the "ColorGeometry" and "ColorPaint" types. I'm not sure that
this is correct, but it's just a more explicit version of what was
there already.

Pull Request: https://projects.blender.org/blender/blender/pulls/109941
This commit is contained in:
Hans Goudey
2023-07-11 15:52:28 +02:00
committed by Hans Goudey
parent efbca8d660
commit 087612c042
5 changed files with 547 additions and 642 deletions

View File

@@ -87,6 +87,17 @@ struct FloatTraits {
} }
}; };
template<typename T> struct TraitsType {
using type = void;
};
template<> struct TraitsType<ColorPaint4f> {
using type = FloatTraits;
};
template<> struct TraitsType<ColorPaint4b> {
using type = ByteTraits;
};
template<typename T> using Traits = typename TraitsType<T>::type;
static float get_luminance(ColorPaint4f c) static float get_luminance(ColorPaint4f c)
{ {
return IMB_colormanagement_get_luminance(&c.r); return IMB_colormanagement_get_luminance(&c.r);

View File

@@ -136,8 +136,6 @@ void PAINT_OT_weight_gradient(wmOperatorType *ot);
void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot); void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot);
void PAINT_OT_vertex_paint(wmOperatorType *ot); void PAINT_OT_vertex_paint(wmOperatorType *ot);
unsigned int vpaint_get_current_color(Scene *scene, VPaint *vp, bool secondary);
/** /**
* \note weight-paint has an equivalent function: #ED_wpaint_blend_tool * \note weight-paint has an equivalent function: #ED_wpaint_blend_tool
*/ */

View File

@@ -77,11 +77,6 @@ using namespace blender::color;
/** \name Internal Utilities /** \name Internal Utilities
* \{ */ * \{ */
static uint color2uint(ColorPaint4b c)
{
return *(reinterpret_cast<uint *>(&c));
}
static bool isZero(ColorPaint4f c) static bool isZero(ColorPaint4f c)
{ {
return c.r == 0.0f && c.g == 0.0f && c.b == 0.0f && c.a == 0.0f; return c.r == 0.0f && c.g == 0.0f && c.b == 0.0f && c.a == 0.0f;
@@ -92,7 +87,7 @@ static bool isZero(ColorPaint4b c)
return c.r == 0 && c.g == 0 && c.b == 0 && c.a == 0; return c.r == 0 && c.g == 0 && c.b == 0 && c.a == 0;
} }
template<typename Color = ColorPaint4f> static ColorPaint4f toFloat(const Color &c) template<typename Color> static ColorPaint4f toFloat(const Color &c)
{ {
if constexpr (std::is_same_v<Color, ColorPaint4b>) { if constexpr (std::is_same_v<Color, ColorPaint4b>) {
return c.decode(); return c.decode();
@@ -102,7 +97,7 @@ template<typename Color = ColorPaint4f> static ColorPaint4f toFloat(const Color
} }
} }
template<typename Color = ColorPaint4f> static Color fromFloat(const ColorPaint4f &c) template<typename Color> static Color fromFloat(const ColorPaint4f &c)
{ {
if constexpr (std::is_same_v<Color, ColorPaint4b>) { if constexpr (std::is_same_v<Color, ColorPaint4b>) {
return c.encode(); return c.encode();
@@ -135,26 +130,6 @@ struct NormalAnglePrecalc {
float angle_range; float angle_range;
}; };
/* Returns number of elements. */
static int get_vcol_elements(Mesh *me, size_t *r_elem_size)
{
const std::optional<bke::AttributeMetaData> meta_data = *me->attributes().lookup_meta_data(
me->active_color_attribute);
if (r_elem_size) {
*r_elem_size = meta_data->data_type == CD_PROP_COLOR ? sizeof(float[4]) : 4ULL;
}
switch (meta_data->domain) {
case ATTR_DOMAIN_POINT:
return me->totvert;
case ATTR_DOMAIN_CORNER:
return me->totloop;
default:
return 0;
}
}
static void view_angle_limits_init(NormalAnglePrecalc *a, float angle, bool do_mask_normal) static void view_angle_limits_init(NormalAnglePrecalc *a, float angle, bool do_mask_normal)
{ {
angle = RAD2DEGF(angle); angle = RAD2DEGF(angle);
@@ -328,8 +303,7 @@ bool weight_paint_poll_ignore_tool(bContext *C)
return weight_paint_poll_ex(C, false); return weight_paint_poll_ex(C, false);
} }
template<typename Color, typename Traits, eAttrDomain domain> static ColorPaint4f vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
{ {
const Brush *brush = BKE_paint_brush_for_read(&vp->paint); const Brush *brush = BKE_paint_brush_for_read(&vp->paint);
float color[4]; float color[4];
@@ -339,15 +313,7 @@ static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */ color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */
return fromFloat<Color>(ColorPaint4f(color)); return ColorPaint4f(color);
}
uint vpaint_get_current_color(Scene *scene, VPaint *vp, bool secondary)
{
ColorPaint4b col = vpaint_get_current_col<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
scene, vp, secondary);
return color2uint(col);
} }
/* wpaint has 'wpaint_blend' */ /* wpaint has 'wpaint_blend' */
@@ -1213,7 +1179,7 @@ static void vwpaint_init_stroke(Depsgraph *depsgraph, Object *ob)
* vwpaint_update_cache_invariants and vwpaint_update_cache_variants. * vwpaint_update_cache_invariants and vwpaint_update_cache_variants.
*/ */
if (!ss->cache) { if (!ss->cache) {
ss->cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache"); ss->cache = MEM_new<StrokeCache>(__func__);
} }
} }
@@ -1226,18 +1192,16 @@ static void vertex_paint_init_stroke(Scene *scene, Depsgraph *depsgraph, Object
/* Allocate scratch array for previous colors if needed. */ /* Allocate scratch array for previous colors if needed. */
if (!brush_use_accumulate(ts->vpaint)) { if (!brush_use_accumulate(ts->vpaint)) {
if (!ss->cache->prev_colors_vpaint) { if (ss->cache->prev_colors_vpaint.is_empty()) {
Mesh *me = BKE_object_get_original_mesh(ob); const Mesh *mesh = BKE_object_get_original_mesh(ob);
size_t elem_size; const GVArray attribute = *mesh->attributes().lookup(mesh->active_color_attribute);
int elem_num; ss->cache->prev_colors_vpaint = GArray(attribute.type(), attribute.size());
attribute.type().value_initialize_n(ss->cache->prev_colors_vpaint.data(),
elem_num = get_vcol_elements(me, &elem_size); ss->cache->prev_colors_vpaint.size());
ss->cache->prev_colors_vpaint = (uint *)MEM_callocN(elem_num * elem_size, __func__);
} }
} }
else { else {
MEM_SAFE_FREE(ss->cache->prev_colors_vpaint); ss->cache->prev_colors_vpaint = {};
} }
} }
@@ -1618,7 +1582,7 @@ static void vwpaint_update_cache_invariants(
/* VW paint needs to allocate stroke cache before update is called. */ /* VW paint needs to allocate stroke cache before update is called. */
if (!ss->cache) { if (!ss->cache) {
cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache"); cache = MEM_new<StrokeCache>(__func__);
ss->cache = cache; ss->cache = cache;
} }
else { else {
@@ -2826,17 +2790,30 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
* - revise whether op->customdata should be added in object, in set_vpaint. * - revise whether op->customdata should be added in object, in set_vpaint.
*/ */
struct VPaintDataBase { template<typename Func>
static void to_static_color_type(const eCustomDataType type, const Func &func)
{
switch (type) {
case CD_PROP_COLOR:
func(ColorGeometry4f());
break;
case CD_PROP_BYTE_COLOR:
func(ColorGeometry4b());
break;
default:
BLI_assert_unreachable();
break;
}
}
struct VPaintData {
ViewContext vc; ViewContext vc;
eAttrDomain domain; eAttrDomain domain;
eCustomDataType type; eCustomDataType type;
};
template<typename Color, typename Traits, eAttrDomain domain>
struct VPaintData : public VPaintDataBase {
NormalAnglePrecalc normal_angle_precalc; NormalAnglePrecalc normal_angle_precalc;
Color paintcol; ColorPaint4f paintcol;
VertProjHandle *vp_handle; VertProjHandle *vp_handle;
CoNo *vertexcosnos; CoNo *vertexcosnos;
@@ -2845,33 +2822,24 @@ struct VPaintData : public VPaintDataBase {
/* Special storage for smear brush, avoid feedback loop - update each step. */ /* Special storage for smear brush, avoid feedback loop - update each step. */
struct { struct {
void *color_prev; GArray<> color_prev;
void *color_curr; GArray<> color_curr;
} smear; } smear;
}; };
template<typename Color, typename Traits, eAttrDomain domain> static VPaintData *vpaint_init_vpaint(bContext *C,
static void *vpaint_init_vpaint(bContext *C, wmOperator *op,
wmOperator *op, Scene *scene,
Scene *scene, Depsgraph *depsgraph,
Depsgraph *depsgraph, VPaint *vp,
VPaint *vp, Object *ob,
Object *ob, Mesh *me,
Mesh *me, const eAttrDomain domain,
const Brush *brush) const eCustomDataType type,
const Brush *brush)
{ {
VPaintData<Color, Traits, domain> *vpd; VPaintData *vpd = MEM_new<VPaintData>(__func__);
vpd->type = type;
/* make mode data storage */
vpd = MEM_new<VPaintData<Color, Traits, domain>>("VPaintData");
if constexpr (std::is_same_v<Color, ColorPaint4f>) {
vpd->type = CD_PROP_COLOR;
}
else {
vpd->type = CD_PROP_BYTE_COLOR;
}
vpd->domain = domain; vpd->domain = domain;
ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph); ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph);
@@ -2879,17 +2847,17 @@ static void *vpaint_init_vpaint(bContext *C,
vp->paint.brush->falloff_angle, vp->paint.brush->falloff_angle,
(vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
vpd->paintcol = vpaint_get_current_col<Color, Traits, domain>( vpd->paintcol = vpaint_get_current_col(
scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT));
vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex;
if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
const GVArray attribute = *me->attributes().lookup(me->active_color_attribute, domain); const GVArray attribute = *me->attributes().lookup(me->active_color_attribute, domain);
vpd->smear.color_prev = MEM_malloc_arrayN(attribute.size(), attribute.type().size(), __func__); vpd->smear.color_prev = GArray(attribute.type(), attribute.size());
attribute.materialize(vpd->smear.color_prev); attribute.materialize(vpd->smear.color_prev.data());
vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev); vpd->smear.color_curr = vpd->smear.color_prev;
} }
/* Create projection handle */ /* Create projection handle */
@@ -2899,7 +2867,7 @@ static void *vpaint_init_vpaint(bContext *C,
ob->sculpt->building_vp_handle = false; ob->sculpt->building_vp_handle = false;
} }
return static_cast<void *>(vpd); return vpd;
} }
static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
@@ -2928,30 +2896,8 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
return false; return false;
} }
void *vpd = nullptr; VPaintData *vpd = vpaint_init_vpaint(
C, op, scene, depsgraph, vp, ob, me, meta_data->domain, meta_data->data_type, brush);
if (meta_data->domain == ATTR_DOMAIN_POINT) {
if (meta_data->data_type == CD_PROP_COLOR) {
vpd = vpaint_init_vpaint<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
C, op, scene, depsgraph, vp, ob, me, brush);
}
else if (meta_data->data_type == CD_PROP_BYTE_COLOR) {
vpd = vpaint_init_vpaint<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(
C, op, scene, depsgraph, vp, ob, me, brush);
}
}
else if (meta_data->domain == ATTR_DOMAIN_CORNER) {
if (meta_data->data_type == CD_PROP_COLOR) {
vpd = vpaint_init_vpaint<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(
C, op, scene, depsgraph, vp, ob, me, brush);
}
else if (meta_data->data_type == CD_PROP_BYTE_COLOR) {
vpd = vpaint_init_vpaint<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
C, op, scene, depsgraph, vp, ob, me, brush);
}
}
BLI_assert(vpd != nullptr);
paint_stroke_set_mode_data(stroke, vpd); paint_stroke_set_mode_data(stroke, vpd);
@@ -2963,24 +2909,40 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
return true; return true;
} }
template<class Color = ColorPaint4b, typename Traits = ByteTraits>
static void do_vpaint_brush_blur_loops(bContext *C, static void do_vpaint_brush_blur_loops(bContext *C,
Sculpt * /*sd*/, Sculpt * /*sd*/,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, ATTR_DOMAIN_CORNER> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
Color *lcol) GMutableSpan attribute)
{ {
using Blend = typename Traits::BlendType;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const Brush *brush = ob->sculpt->cache->brush; const Brush *brush = ob->sculpt->cache->brush;
const Scene *scene = CTX_data_scene(C); const Scene *scene = CTX_data_scene(C);
Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
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(vp);
const bool use_vert_sel = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) !=
0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test_init;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test_init, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
GMutableSpan g_previous_color = ss->cache->prev_colors_vpaint;
const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>( const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
@@ -2988,26 +2950,8 @@ static void do_vpaint_brush_blur_loops(bContext *C,
".select_poly", ATTR_DOMAIN_FACE, false); ".select_poly", ATTR_DOMAIN_FACE, false);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
SculptBrushTest test = test_init;
for (int n : range) { for (int n : range) {
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
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(vp);
const bool use_vert_sel = (me->editflag &
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -3036,90 +2980,119 @@ static void do_vpaint_brush_blur_loops(bContext *C,
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);
/* Get the average poly color */ to_static_color_type(vpd->type, [&](auto dummy) {
Color color_final(0, 0, 0, 0); using T = decltype(dummy);
using Color =
std::conditional_t<std::is_same_v<T, ColorGeometry4f>, ColorPaint4f, ColorPaint4b>;
using Traits = color::Traits<Color>;
using Blend = typename Traits::BlendType;
MutableSpan<Color> previous_color = g_previous_color.typed<T>().template cast<Color>();
MutableSpan<Color> colors = attribute.typed<T>().template cast<Color>();
/* Get the average poly color */
Color color_final(0, 0, 0, 0);
int total_hit_loops = 0; int total_hit_loops = 0;
Blend blend[4] = {0}; Blend blend[4] = {0};
for (const int p_index : gmap->vert_to_poly[v_index]) { for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) { if (use_face_sel && !select_poly[p_index]) {
continue; return;
} }
const blender::IndexRange poly = ss->polys[p_index]; const blender::IndexRange poly = ss->polys[p_index];
total_hit_loops += poly.size(); total_hit_loops += poly.size();
for (const int corner : poly) { for (const int corner : poly) {
Color *col = lcol + corner; const Color &col = colors[corner];
/* Color is squared to compensate the `sqrt` color encoding. */ /* Color is squared to compensate the `sqrt` color encoding. */
blend[0] += (Blend)col->r * (Blend)col->r; blend[0] += (Blend)col.r * (Blend)col.r;
blend[1] += (Blend)col->g * (Blend)col->g; blend[1] += (Blend)col.g * (Blend)col.g;
blend[2] += (Blend)col->b * (Blend)col->b; blend[2] += (Blend)col.b * (Blend)col.b;
blend[3] += (Blend)col->a * (Blend)col->a; blend[3] += (Blend)col.a * (Blend)col.a;
}
}
if (total_hit_loops == 0) {
continue;
}
/* Use rgb^2 color averaging. */
Color *col = &color_final;
color_final.r = Traits::round(sqrtf(Traits::divide_round(blend[0], total_hit_loops)));
color_final.g = Traits::round(sqrtf(Traits::divide_round(blend[1], total_hit_loops)));
color_final.b = Traits::round(sqrtf(Traits::divide_round(blend[2], total_hit_loops)));
color_final.a = Traits::round(sqrtf(Traits::divide_round(blend[3], total_hit_loops)));
/* For each poly owning this vert,
* paint each loop belonging to this vert. */
for (const int j : gmap->vert_to_poly[v_index].index_range()) {
const int p_index = gmap->vert_to_poly[v_index][j];
const int l_index = gmap->vert_to_loop[v_index][j];
BLI_assert(ss->corner_verts[l_index] == v_index);
if (use_face_sel && !select_poly[p_index]) {
continue;
}
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
if (previous_color != nullptr) {
/* Get the previous loop color */
if (isZero(previous_color[l_index])) {
previous_color[l_index] = lcol[l_index];
} }
color_orig = previous_color[l_index];
} }
const float final_alpha = Traits::range * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha; if (total_hit_loops == 0) {
/* Mix the new color with the original return;
* based on the brush strength and the curve. */ }
lcol[l_index] = vpaint_blend<Color, Traits>(
vp, lcol[l_index], color_orig, *col, final_alpha, Traits::range * brush_strength); /* Use rgb^2 color averaging. */
} Color *col = &color_final;
color_final.r = Traits::round(sqrtf(Traits::divide_round(blend[0], total_hit_loops)));
color_final.g = Traits::round(sqrtf(Traits::divide_round(blend[1], total_hit_loops)));
color_final.b = Traits::round(sqrtf(Traits::divide_round(blend[2], total_hit_loops)));
color_final.a = Traits::round(sqrtf(Traits::divide_round(blend[3], total_hit_loops)));
/* For each poly owning this vert,
* paint each loop belonging to this vert. */
for (const int j : gmap->vert_to_poly[v_index].index_range()) {
const int p_index = gmap->vert_to_poly[v_index][j];
const int l_index = gmap->vert_to_loop[v_index][j];
BLI_assert(ss->corner_verts[l_index] == v_index);
if (use_face_sel && !select_poly[p_index]) {
continue;
}
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
if (!previous_color.is_empty()) {
/* Get the previous loop color */
if (isZero(previous_color[l_index])) {
previous_color[l_index] = colors[l_index];
}
color_orig = previous_color[l_index];
}
const float final_alpha = Traits::range * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original
* based on the brush strength and the curve. */
colors[l_index] = vpaint_blend<Color, Traits>(vp,
colors[l_index],
color_orig,
*col,
final_alpha,
Traits::range * brush_strength);
}
});
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
}; };
}); });
} }
template<class Color = ColorPaint4b, typename Traits = ByteTraits>
static void do_vpaint_brush_blur_verts(bContext *C, static void do_vpaint_brush_blur_verts(bContext *C,
Sculpt * /*sd*/, Sculpt * /*sd*/,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, ATTR_DOMAIN_POINT> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
Color *lcol) GMutableSpan attribute)
{ {
using Blend = typename Traits::BlendType;
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const Brush *brush = ob->sculpt->cache->brush; const Brush *brush = ob->sculpt->cache->brush;
const Scene *scene = CTX_data_scene(C); const Scene *scene = CTX_data_scene(C);
Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
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(vp);
const bool use_vert_sel = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) !=
0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test_init;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test_init, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
GMutableSpan g_previous_color = ss->cache->prev_colors_vpaint;
const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>( const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
@@ -3127,26 +3100,8 @@ static void do_vpaint_brush_blur_verts(bContext *C,
".select_poly", ATTR_DOMAIN_FACE, false); ".select_poly", ATTR_DOMAIN_FACE, false);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
SculptBrushTest test = test_init;
for (int n : range) { for (int n : range) {
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
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(vp);
const bool use_vert_sel = (me->editflag &
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -3175,51 +3130,51 @@ static void do_vpaint_brush_blur_verts(bContext *C,
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);
/* Get the average poly color */ /* Get the average poly color */
Color color_final(0, 0, 0, 0); to_static_color_type(vpd->type, [&](auto dummy) {
using T = decltype(dummy);
using Color =
std::conditional_t<std::is_same_v<T, ColorGeometry4f>, ColorPaint4f, ColorPaint4b>;
using Traits = color::Traits<Color>;
using Blend = typename Traits::BlendType;
MutableSpan<Color> previous_color = g_previous_color.typed<T>().template cast<Color>();
MutableSpan<Color> colors = attribute.typed<T>().template cast<Color>();
Color color_final(0, 0, 0, 0);
int total_hit_loops = 0; int total_hit_loops = 0;
Blend blend[4] = {0}; Blend blend[4] = {0};
for (const int p_index : gmap->vert_to_poly[v_index]) { for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) { if (use_face_sel && !select_poly[p_index]) {
continue; continue;
}
const blender::IndexRange poly = ss->polys[p_index];
total_hit_loops += poly.size();
for (const int vert : ss->corner_verts.slice(poly)) {
const Color &col = colors[vert];
/* Color is squared to compensate the `sqrt` color encoding. */
blend[0] += (Blend)col.r * (Blend)col.r;
blend[1] += (Blend)col.g * (Blend)col.g;
blend[2] += (Blend)col.b * (Blend)col.b;
blend[3] += (Blend)col.a * (Blend)col.a;
}
} }
const blender::IndexRange poly = ss->polys[p_index];
total_hit_loops += poly.size();
for (const int vert : ss->corner_verts.slice(poly)) {
Color *col = lcol + vert;
/* Color is squared to compensate the `sqrt` color encoding. */ if (total_hit_loops == 0) {
blend[0] += (Blend)col->r * (Blend)col->r; return;
blend[1] += (Blend)col->g * (Blend)col->g;
blend[2] += (Blend)col->b * (Blend)col->b;
blend[3] += (Blend)col->a * (Blend)col->a;
} }
} /* Use rgb^2 color averaging. */
color_final.r = Traits::round(sqrtf(Traits::divide_round(blend[0], total_hit_loops)));
color_final.g = Traits::round(sqrtf(Traits::divide_round(blend[1], total_hit_loops)));
color_final.b = Traits::round(sqrtf(Traits::divide_round(blend[2], total_hit_loops)));
color_final.a = Traits::round(sqrtf(Traits::divide_round(blend[3], total_hit_loops)));
if (total_hit_loops == 0) {
continue;
}
/* Use rgb^2 color averaging. */
Color *col = &color_final;
color_final.r = Traits::round(sqrtf(Traits::divide_round(blend[0], total_hit_loops)));
color_final.g = Traits::round(sqrtf(Traits::divide_round(blend[1], total_hit_loops)));
color_final.b = Traits::round(sqrtf(Traits::divide_round(blend[2], total_hit_loops)));
color_final.a = Traits::round(sqrtf(Traits::divide_round(blend[3], total_hit_loops)));
/* For each poly owning this vert,
* paint each loop belonging to this vert. */
for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) {
continue;
}
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
if (previous_color != nullptr) { if (!previous_color.is_empty()) {
/* Get the previous loop color */ /* Get the previous loop color */
if (isZero(previous_color[v_index])) { if (isZero(previous_color[v_index])) {
previous_color[v_index] = lcol[v_index]; previous_color[v_index] = colors[v_index];
} }
color_orig = previous_color[v_index]; color_orig = previous_color[v_index];
} }
@@ -3227,24 +3182,27 @@ static void do_vpaint_brush_blur_verts(bContext *C,
brush_alpha_pressure * grid_alpha; brush_alpha_pressure * grid_alpha;
/* 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[v_index] = vpaint_blend<Color, Traits>( colors[v_index] = vpaint_blend<Color, Traits>(vp,
vp, lcol[v_index], color_orig, *col, final_alpha, Traits::range * brush_strength); colors[v_index],
} color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
});
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
}; };
}); });
} }
template<typename Color = ColorPaint4b, typename Traits, eAttrDomain domain>
static void do_vpaint_brush_smear(bContext *C, static void do_vpaint_brush_smear(bContext *C,
Sculpt * /*sd*/, Sculpt * /*sd*/,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
Color *lcol) GMutableSpan attribute)
{ {
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
@@ -3258,9 +3216,31 @@ static void do_vpaint_brush_smear(bContext *C,
const Brush *brush = ob->sculpt->cache->brush; const Brush *brush = ob->sculpt->cache->brush;
const Scene *scene = CTX_data_scene(C); const Scene *scene = CTX_data_scene(C);
Color *color_curr = static_cast<Color *>(vpd->smear.color_curr); GMutableSpan g_color_curr = vpd->smear.color_curr;
Color *color_prev_smear = static_cast<Color *>(vpd->smear.color_prev); GMutableSpan g_color_prev_smear = vpd->smear.color_prev;
Color *color_prev = reinterpret_cast<Color *>(ss->cache->prev_colors_vpaint); GMutableSpan g_color_prev = ss->cache->prev_colors_vpaint;
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(vp);
const bool use_vert_sel = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) !=
0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
float brush_dir[3];
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal);
if (normalize_v3(brush_dir) == 0.0f) {
return;
}
SculptBrushTest test_init;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test_init, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>( const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
@@ -3268,29 +3248,8 @@ static void do_vpaint_brush_smear(bContext *C,
".select_poly", ATTR_DOMAIN_FACE, false); ".select_poly", ATTR_DOMAIN_FACE, false);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
SculptBrushTest test = test_init;
for (int n : range) { for (int n : range) {
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(vp);
const bool use_vert_sel = (me->editflag &
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
float brush_dir[3];
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal);
if (normalize_v3(brush_dir) == 0.0f) {
continue;
}
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -3328,198 +3287,215 @@ static void do_vpaint_brush_smear(bContext *C,
/* Get the color of the loop in the opposite /* Get the color of the loop in the opposite
* direction of the brush movement */ * direction of the brush movement */
Color color_final(0, 0, 0, 0); to_static_color_type(vpd->type, [&](auto dummy) {
using T = decltype(dummy);
using Color =
std::conditional_t<std::is_same_v<T, ColorGeometry4f>, ColorPaint4f, ColorPaint4b>;
using Traits = color::Traits<Color>;
MutableSpan<Color> color_curr = g_color_curr.typed<T>().template cast<Color>();
MutableSpan<Color> color_prev_smear =
g_color_prev_smear.typed<T>().template cast<Color>();
MutableSpan<Color> color_prev = g_color_prev.typed<T>().template cast<Color>();
MutableSpan<Color> colors = attribute.typed<T>().template cast<Color>();
for (const int j : gmap->vert_to_poly[v_index].index_range()) { Color color_final(0, 0, 0, 0);
const int p_index = gmap->vert_to_poly[v_index][j];
const int l_index = gmap->vert_to_loop[v_index][j]; for (const int j : gmap->vert_to_poly[v_index].index_range()) {
BLI_assert(ss->corner_verts[l_index] == v_index); const int p_index = gmap->vert_to_poly[v_index][j];
UNUSED_VARS_NDEBUG(l_index); const int l_index = gmap->vert_to_loop[v_index][j];
if (use_face_sel && !select_poly[p_index]) { BLI_assert(ss->corner_verts[l_index] == v_index);
continue; UNUSED_VARS_NDEBUG(l_index);
if (use_face_sel && !select_poly[p_index]) {
continue;
}
for (const int corner : ss->polys[p_index]) {
const int v_other_index = ss->corner_verts[corner];
if (v_other_index == v_index) {
continue;
}
/* Get the direction from the
* selected vert to the neighbor. */
float other_dir[3];
sub_v3_v3v3(other_dir, mv_curr, ss->vert_positions[v_other_index]);
project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal);
normalize_v3(other_dir);
const float stroke_dot = dot_v3v3(other_dir, brush_dir);
int elem_index;
if (vpd->domain == ATTR_DOMAIN_POINT) {
elem_index = v_other_index;
}
else {
elem_index = corner;
}
if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot;
color_final = color_prev_smear[elem_index];
do_color = true;
}
}
} }
for (const int corner : ss->polys[p_index]) {
const int v_other_index = ss->corner_verts[corner]; if (!do_color) {
if (v_other_index == v_index) { return;
}
const float final_alpha = Traits::range * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha;
/* For each poly owning this vert,
* paint each loop belonging to this vert. */
for (const int j : gmap->vert_to_poly[v_index].index_range()) {
const int p_index = gmap->vert_to_poly[v_index][j];
int elem_index;
if (vpd->domain == ATTR_DOMAIN_POINT) {
elem_index = v_index;
}
else {
const int l_index = gmap->vert_to_loop[v_index][j];
elem_index = l_index;
BLI_assert(ss->corner_verts[l_index] == v_index);
}
if (use_face_sel && !select_poly[p_index]) {
continue; continue;
} }
/* Get the direction from the
* selected vert to the neighbor. */
float other_dir[3];
sub_v3_v3v3(other_dir, mv_curr, ss->vert_positions[v_other_index]);
project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal);
normalize_v3(other_dir);
const float stroke_dot = dot_v3v3(other_dir, brush_dir);
int elem_index;
if constexpr (domain == ATTR_DOMAIN_POINT) {
elem_index = v_other_index;
}
else {
elem_index = corner;
}
if (stroke_dot > stroke_dot_max) {
stroke_dot_max = stroke_dot;
color_final = color_prev_smear[elem_index];
do_color = true;
}
}
}
if (!do_color) {
continue;
}
const float final_alpha = Traits::range * brush_fade * brush_strength *
brush_alpha_pressure * grid_alpha;
/* For each poly owning this vert,
* paint each loop belonging to this vert. */
for (const int j : gmap->vert_to_poly[v_index].index_range()) {
const int p_index = gmap->vert_to_poly[v_index][j];
int elem_index;
if constexpr (domain == ATTR_DOMAIN_POINT) {
elem_index = v_index;
}
else {
const int l_index = gmap->vert_to_loop[v_index][j];
elem_index = l_index;
BLI_assert(ss->corner_verts[l_index] == v_index);
}
if (use_face_sel && !select_poly[p_index]) {
continue;
}
/* Get the previous element color */
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
if (color_prev != nullptr) {
/* Get the previous element color */ /* Get the previous element color */
if (isZero(color_prev[elem_index])) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
color_prev[elem_index] = lcol[elem_index];
}
color_orig = color_prev[elem_index];
}
/* Mix the new color with the original
* based on the brush strength and the curve. */
lcol[elem_index] = vpaint_blend<Color, Traits>(vp,
lcol[elem_index],
color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
color_curr[elem_index] = lcol[elem_index]; if (!color_prev.is_empty()) {
} /* Get the previous element color */
if (isZero(color_prev[elem_index])) {
color_prev[elem_index] = colors[elem_index];
}
color_orig = color_prev[elem_index];
}
/* Mix the new color with the original
* based on the brush strength and the curve. */
colors[elem_index] = vpaint_blend<Color, Traits>(vp,
colors[elem_index],
color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
color_curr[elem_index] = colors[elem_index];
}
});
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
}); });
} }
template<typename Color, typename Traits, eAttrDomain domain> static void calculate_average_color(VPaintData *vpd,
static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
const Brush *brush, const Brush *brush,
Color *lcol, const GSpan attribute,
Span<PBVHNode *> nodes) Span<PBVHNode *> nodes)
{ {
using Blend = typename Traits::BlendType; SculptSession *ss = ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
StrokeCache *cache = ss->cache;
const bool use_vert_sel = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) !=
0;
SculptBrushTest test_init;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test_init, brush->falloff_shape);
const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>( const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
VPaintAverageAccum<Blend> *accum = (VPaintAverageAccum<Blend> *)MEM_mallocN( to_static_color_type(vpd->type, [&](auto dummy) {
sizeof(*accum) * nodes.size(), __func__); using T = decltype(dummy);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { using Color =
for (int n : range) { std::conditional_t<std::is_same_v<T, ColorGeometry4f>, ColorPaint4f, ColorPaint4b>;
SculptSession *ss = ob->sculpt; using Traits = color::Traits<Color>;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); using Blend = typename Traits::BlendType;
const bool has_grids = (pbvh_type == PBVH_GRIDS); const Span<Color> colors = attribute.typed<T>().template cast<Color>();
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
StrokeCache *cache = ss->cache; Array<VPaintAverageAccum<Blend>> accum(nodes.size());
const bool use_vert_sel = (me->editflag & blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; SculptBrushTest test = test_init;
for (int n : range) {
VPaintAverageAccum<Blend> &accum2 = accum[n];
accum2.len = 0;
memset(accum2.value, 0, sizeof(accum2.value));
VPaintAverageAccum<Blend> *accum2 = accum + n; /* For each vertex */
accum2->len = 0; PBVHVertexIter vd;
memset(accum2->value, 0, sizeof(accum2->value)); BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
SculptBrushTest test; if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( continue;
ss, &test, brush->falloff_shape);
/* For each vertex */
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
if (BKE_brush_curve_strength(brush, 0.0, cache->radius) <= 0.0f) {
continue;
}
const int v_index = has_grids ? ss->corner_verts[vd.grid_indices[vd.g]] :
vd.vert_indices[vd.i];
/* If the vertex is selected for painting. */
if (use_vert_sel && !select_vert[v_index]) {
continue;
}
accum2->len += gmap->vert_to_poly[v_index].size();
/* if a vertex is within the brush region, then add its color to the blend. */
for (int j = 0; j < gmap->vert_to_poly[v_index].size(); j++) {
int elem_index;
if constexpr (domain == ATTR_DOMAIN_CORNER) {
elem_index = gmap->vert_to_loop[v_index][j];
} }
else { if (BKE_brush_curve_strength(brush, 0.0, cache->radius) <= 0.0f) {
elem_index = v_index; continue;
}
const int v_index = has_grids ? ss->corner_verts[vd.grid_indices[vd.g]] :
vd.vert_indices[vd.i];
/* If the vertex is selected for painting. */
if (use_vert_sel && !select_vert[v_index]) {
continue;
} }
Color *col = lcol + elem_index; accum2.len += gmap->vert_to_poly[v_index].size();
/* if a vertex is within the brush region, then add its color to the blend. */
for (int j = 0; j < gmap->vert_to_poly[v_index].size(); j++) {
int elem_index;
/* Color is squared to compensate the `sqrt` color encoding. */ if (vpd->domain == ATTR_DOMAIN_CORNER) {
accum2->value[0] += col->r * col->r; elem_index = gmap->vert_to_loop[v_index][j];
accum2->value[1] += col->g * col->g; }
accum2->value[2] += col->b * col->b; else {
elem_index = v_index;
}
/* Color is squared to compensate the `sqrt` color encoding. */
const Color &col = colors[elem_index];
accum2.value[0] += col.r * col.r;
accum2.value[1] += col.g * col.g;
accum2.value[2] += col.b * col.b;
}
} }
BKE_pbvh_vertex_iter_end;
} }
BKE_pbvh_vertex_iter_end; });
Blend accum_len = 0;
Blend accum_value[3] = {0};
Color blend(0, 0, 0, 0);
for (int i = 0; i < nodes.size(); i++) {
accum_len += accum[i].len;
accum_value[0] += accum[i].value[0];
accum_value[1] += accum[i].value[1];
accum_value[2] += accum[i].value[2];
}
if (accum_len != 0) {
blend.r = Traits::round(sqrtf(Traits::divide_round(accum_value[0], accum_len)));
blend.g = Traits::round(sqrtf(Traits::divide_round(accum_value[1], accum_len)));
blend.b = Traits::round(sqrtf(Traits::divide_round(accum_value[2], accum_len)));
blend.a = Traits::range;
vpd->paintcol = toFloat(blend);
} }
}); });
Blend accum_len = 0;
Blend accum_value[3] = {0};
Color blend(0, 0, 0, 0);
for (int i = 0; i < nodes.size(); i++) {
accum_len += accum[i].len;
accum_value[0] += accum[i].value[0];
accum_value[1] += accum[i].value[1];
accum_value[2] += accum[i].value[2];
}
if (accum_len != 0) {
blend.r = Traits::round(sqrtf(Traits::divide_round(accum_value[0], accum_len)));
blend.g = Traits::round(sqrtf(Traits::divide_round(accum_value[1], accum_len)));
blend.b = Traits::round(sqrtf(Traits::divide_round(accum_value[2], accum_len)));
blend.a = Traits::range;
vpd->paintcol = blend;
}
} }
template<typename Color, typename Traits, eAttrDomain domain> template<typename Color>
static float paint_and_tex_color_alpha(VPaint *vp, static float paint_and_tex_color_alpha(VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
const float v_co[3], const float v_co[3],
Color *r_color) Color *r_color)
{ {
@@ -3535,15 +3511,14 @@ static float paint_and_tex_color_alpha(VPaint *vp,
return rgba[3]; return rgba[3];
} }
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_draw(bContext *C, static void vpaint_do_draw(bContext *C,
Sculpt * /*sd*/, Sculpt * /*sd*/,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
Color *lcol) GMutableSpan attribute)
{ {
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
@@ -3551,7 +3526,25 @@ static void vpaint_do_draw(bContext *C,
const Brush *brush = ob->sculpt->cache->brush; const Brush *brush = ob->sculpt->cache->brush;
const Scene *scene = CTX_data_scene(C); const Scene *scene = CTX_data_scene(C);
Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); const bool has_grids = (pbvh_type == PBVH_GRIDS);
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
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(vp);
const bool use_vert_sel = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) !=
0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test_init;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test_init, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
GMutableSpan g_previous_color = ss->cache->prev_colors_vpaint;
const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>( const blender::VArray<bool> select_vert = *me->attributes().lookup_or_default<bool>(
".select_vert", ATTR_DOMAIN_POINT, false); ".select_vert", ATTR_DOMAIN_POINT, false);
@@ -3560,26 +3553,7 @@ static void vpaint_do_draw(bContext *C,
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) { blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
for (int n : range) { for (int n : range) {
const bool has_grids = (pbvh_type == PBVH_GRIDS); SculptBrushTest test = test_init;
const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
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(vp);
const bool use_vert_sel = (me->editflag &
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, brush->falloff_shape);
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
ss, brush->falloff_shape);
Color paintcol = vpd->paintcol;
/* For each vertex */ /* For each vertex */
PBVHVertexIter vd; PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -3610,100 +3584,106 @@ static void vpaint_do_draw(bContext *C,
} }
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);
Color color_final = paintcol; to_static_color_type(vpd->type, [&](auto dummy) {
using T = decltype(dummy);
using Color =
std::conditional_t<std::is_same_v<T, ColorGeometry4f>, ColorPaint4f, ColorPaint4b>;
using Traits = color::Traits<Color>;
MutableSpan<Color> colors = attribute.typed<T>().template cast<Color>();
MutableSpan<Color> previous_color = g_previous_color.typed<T>().template cast<Color>();
Color color_final = fromFloat<Color>(vpd->paintcol);
/* If we're painting with a texture, sample the texture color and alpha. */ /* If we're painting with a texture, sample the texture color and alpha. */
float tex_alpha = 1.0; float tex_alpha = 1.0;
if (vpd->is_texbrush) { if (vpd->is_texbrush) {
/* NOTE: we may want to paint alpha as vertex color alpha. */ /* NOTE: we may want to paint alpha as vertex color alpha. */
tex_alpha = paint_and_tex_color_alpha<Color, Traits, domain>( tex_alpha = paint_and_tex_color_alpha<Color>(
vp, vpd, vpd->vertexcosnos[v_index].co, &color_final); vp, vpd, vpd->vertexcosnos[v_index].co, &color_final);
}
Color color_orig(0, 0, 0, 0);
if constexpr (domain == ATTR_DOMAIN_POINT) {
int v_index = vd.index;
if (previous_color != nullptr) {
/* Get the previous loop color */
if (isZero(previous_color[v_index])) {
previous_color[v_index] = lcol[v_index];
}
color_orig = previous_color[v_index];
} }
const float final_alpha = Traits::frange * brush_fade * brush_strength * tex_alpha *
brush_alpha_pressure * grid_alpha;
lcol[v_index] = vpaint_blend<Color, Traits>(vp, Color color_orig(0, 0, 0, 0);
lcol[v_index],
color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
}
else {
/* For each poly owning this vert, paint each loop belonging to this vert. */
for (const int j : gmap->vert_to_poly[v_index].index_range()) {
const int p_index = gmap->vert_to_poly[v_index][j];
const int l_index = gmap->vert_to_loop[v_index][j];
BLI_assert(ss->corner_verts[l_index] == v_index);
if (use_face_sel && !select_poly[p_index]) {
continue;
}
Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */
if (previous_color != nullptr) { if (vpd->domain == ATTR_DOMAIN_POINT) {
int v_index = vd.index;
if (!previous_color.is_empty()) {
/* Get the previous loop color */ /* Get the previous loop color */
if (isZero(previous_color[l_index])) { if (isZero(previous_color[v_index])) {
previous_color[l_index] = lcol[l_index]; previous_color[v_index] = colors[v_index];
} }
color_orig = previous_color[l_index]; color_orig = previous_color[v_index];
} }
const float final_alpha = Traits::frange * brush_fade * brush_strength * tex_alpha * const float final_alpha = Traits::frange * brush_fade * brush_strength * tex_alpha *
brush_alpha_pressure * grid_alpha; brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original based on final_alpha. */ colors[v_index] = vpaint_blend<Color, Traits>(vp,
lcol[l_index] = vpaint_blend<Color, Traits>(vp, colors[v_index],
lcol[l_index], color_orig,
color_orig, color_final,
color_final, final_alpha,
final_alpha, Traits::range * brush_strength);
Traits::range * brush_strength);
} }
} else {
/* For each poly owning this vert, paint each loop belonging to this vert. */
for (const int j : gmap->vert_to_poly[v_index].index_range()) {
const int p_index = gmap->vert_to_poly[v_index][j];
const int l_index = gmap->vert_to_loop[v_index][j];
BLI_assert(ss->corner_verts[l_index] == v_index);
if (use_face_sel && !select_poly[p_index]) {
continue;
}
Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */
if (!previous_color.is_empty()) {
/* Get the previous loop color */
if (isZero(previous_color[l_index])) {
previous_color[l_index] = colors[l_index];
}
color_orig = previous_color[l_index];
}
const float final_alpha = Traits::frange * brush_fade * brush_strength * tex_alpha *
brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original based on final_alpha. */
colors[l_index] = vpaint_blend<Color, Traits>(vp,
colors[l_index],
color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
}
}
});
} }
BKE_pbvh_vertex_iter_end; BKE_pbvh_vertex_iter_end;
} }
}); });
} }
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_blur(bContext *C, static void vpaint_do_blur(bContext *C,
Sculpt *sd, Sculpt *sd,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Span<PBVHNode *> nodes, Span<PBVHNode *> nodes,
Color *lcol) GMutableSpan attribute)
{ {
if constexpr (domain == ATTR_DOMAIN_POINT) { if (vpd->domain == ATTR_DOMAIN_POINT) {
do_vpaint_brush_blur_verts<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, lcol); do_vpaint_brush_blur_verts(C, sd, vp, vpd, ob, me, nodes, attribute);
} }
else { else {
do_vpaint_brush_blur_loops<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, lcol); do_vpaint_brush_blur_loops(C, sd, vp, vpd, ob, me, nodes, attribute);
} }
} }
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_paint_leaves(bContext *C, static void vpaint_paint_leaves(bContext *C,
Sculpt *sd, Sculpt *sd,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Color *lcol, GMutableSpan attribute,
Span<PBVHNode *> nodes) Span<PBVHNode *> nodes)
{ {
for (PBVHNode *node : nodes) { for (PBVHNode *node : nodes) {
@@ -3714,27 +3694,26 @@ static void vpaint_paint_leaves(bContext *C,
switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) {
case VPAINT_TOOL_AVERAGE: case VPAINT_TOOL_AVERAGE:
calculate_average_color<Color, Traits, domain>(vpd, ob, me, brush, lcol, nodes); calculate_average_color(vpd, ob, me, brush, attribute, nodes);
break; break;
case VPAINT_TOOL_DRAW: case VPAINT_TOOL_DRAW:
vpaint_do_draw<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, lcol); vpaint_do_draw(C, sd, vp, vpd, ob, me, nodes, attribute);
break; break;
case VPAINT_TOOL_BLUR: case VPAINT_TOOL_BLUR:
vpaint_do_blur<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, lcol); vpaint_do_blur(C, sd, vp, vpd, ob, me, nodes, attribute);
break; break;
case VPAINT_TOOL_SMEAR: case VPAINT_TOOL_SMEAR:
do_vpaint_brush_smear<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, lcol); do_vpaint_brush_smear(C, sd, vp, vpd, ob, me, nodes, attribute);
break; break;
default: default:
break; break;
} }
} }
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_paint(bContext *C, static void vpaint_do_paint(bContext *C,
Sculpt *sd, Sculpt *sd,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Brush *brush, Brush *brush,
@@ -3751,21 +3730,18 @@ static void vpaint_do_paint(bContext *C,
bke::GSpanAttributeWriter attribute = me->attributes_for_write().lookup_for_write_span( bke::GSpanAttributeWriter attribute = me->attributes_for_write().lookup_for_write_span(
me->active_color_attribute); me->active_color_attribute);
BLI_assert(attribute.domain == domain); BLI_assert(attribute.domain == vpd->domain);
Color *color_data = static_cast<Color *>(attribute.span.data());
/* Paint those leaves. */ /* Paint those leaves. */
vpaint_paint_leaves<Color, Traits, domain>(C, sd, vp, vpd, ob, me, color_data, nodes); vpaint_paint_leaves(C, sd, vp, vpd, ob, me, attribute.span, nodes);
attribute.finish(); attribute.finish();
} }
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_radial_symmetry(bContext *C, static void vpaint_do_radial_symmetry(bContext *C,
Sculpt *sd, Sculpt *sd,
VPaint *vp, VPaint *vp,
VPaintData<Color, Traits, domain> *vpd, VPaintData *vpd,
Object *ob, Object *ob,
Mesh *me, Mesh *me,
Brush *brush, Brush *brush,
@@ -3774,15 +3750,14 @@ static void vpaint_do_radial_symmetry(bContext *C,
{ {
for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) {
const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X'];
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, 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.cc's, /* near duplicate of: sculpt.cc's,
* 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */ * 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_symmetrical_brush_actions( static void vpaint_do_symmetrical_brush_actions(
bContext *C, Sculpt *sd, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob) bContext *C, Sculpt *sd, VPaint *vp, VPaintData *vpd, Object *ob)
{ {
Brush *brush = BKE_paint_brush(&vp->paint); Brush *brush = BKE_paint_brush(&vp->paint);
Mesh *me = (Mesh *)ob->data; Mesh *me = (Mesh *)ob->data;
@@ -3794,13 +3769,10 @@ static void vpaint_do_symmetrical_brush_actions(
/* initial stroke */ /* initial stroke */
const ePaintSymmetryFlags initial_symm = ePaintSymmetryFlags(0); const ePaintSymmetryFlags initial_symm = ePaintSymmetryFlags(0);
cache->mirror_symmetry_pass = ePaintSymmetryFlags(0); cache->mirror_symmetry_pass = ePaintSymmetryFlags(0);
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, initial_symm, 'X', 0, 0); vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, initial_symm, 'X', 0, 0);
vpaint_do_radial_symmetry<Color, Traits, domain>( vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, initial_symm, 'X');
C, sd, vp, vpd, ob, me, brush, initial_symm, 'X'); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, initial_symm, 'Y');
vpaint_do_radial_symmetry<Color, Traits, domain>( vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, initial_symm, 'Z');
C, sd, vp, vpd, ob, me, brush, initial_symm, 'Y');
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, initial_symm, 'Z');
cache->symmetry = symm; cache->symmetry = symm;
@@ -3814,22 +3786,16 @@ static void vpaint_do_symmetrical_brush_actions(
SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0); SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0);
if (i & (1 << 0)) { if (i & (1 << 0)) {
vpaint_do_paint<Color, Traits, domain>( vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm_pass, 'X', 0, 0);
C, sd, vp, vpd, ob, me, brush, symm_pass, 'X', 0, 0); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, symm_pass, 'X');
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'X');
} }
if (i & (1 << 1)) { if (i & (1 << 1)) {
vpaint_do_paint<Color, Traits, domain>( vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm_pass, 'Y', 0, 0);
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Y', 0, 0); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, symm_pass, 'Y');
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Y');
} }
if (i & (1 << 2)) { if (i & (1 << 2)) {
vpaint_do_paint<Color, Traits, domain>( vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm_pass, 'Z', 0, 0);
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Z', 0, 0); vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, symm_pass, 'Z');
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Z');
} }
} }
} }
@@ -3838,13 +3804,14 @@ static void vpaint_do_symmetrical_brush_actions(
cache->is_last_valid = true; cache->is_last_valid = true;
} }
template<typename Color, typename Traits, eAttrDomain domain> static void vpaint_stroke_update_step(bContext *C,
static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, PointerRNA *itemptr) wmOperator * /*op*/,
PaintStroke *stroke,
PointerRNA *itemptr)
{ {
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C); ToolSettings *ts = CTX_data_tool_settings(C);
VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>( VPaintData *vpd = static_cast<VPaintData *>(paint_stroke_mode_data(stroke));
paint_stroke_mode_data(stroke));
VPaint *vp = ts->vpaint; VPaint *vp = ts->vpaint;
ViewContext *vc = &vpd->vc; ViewContext *vc = &vpd->vc;
Object *ob = vc->obact; Object *ob = vc->obact;
@@ -3862,20 +3829,14 @@ static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, P
swap_m4m4(vc->rv3d->persmat, mat); swap_m4m4(vc->rv3d->persmat, mat);
vpaint_do_symmetrical_brush_actions<Color, Traits, domain>(C, sd, vp, vpd, ob); vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob);
swap_m4m4(vc->rv3d->persmat, mat); swap_m4m4(vc->rv3d->persmat, mat);
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
Mesh *me = BKE_object_get_original_mesh(ob); vpd->smear.color_prev = vpd->smear.color_curr;
size_t elem_size;
int elem_num;
elem_num = get_vcol_elements(me, &elem_size);
memcpy(vpd->smear.color_prev, vpd->smear.color_curr, elem_size * elem_num);
} }
/* Calculate pivot for rotation around selection if needed. /* Calculate pivot for rotation around selection if needed.
@@ -3889,82 +3850,20 @@ static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, P
DEG_id_tag_update((ID *)ob->data, ID_RECALC_GEOMETRY); DEG_id_tag_update((ID *)ob->data, ID_RECALC_GEOMETRY);
} }
static void vpaint_stroke_update_step(bContext *C, static void vpaint_stroke_done(const bContext *C, PaintStroke *stroke)
wmOperator * /*op*/,
PaintStroke *stroke,
PointerRNA *itemptr)
{ {
VPaintDataBase *vpd = static_cast<VPaintDataBase *>(paint_stroke_mode_data(stroke)); VPaintData *vpd = static_cast<VPaintData *>(paint_stroke_mode_data(stroke));
Object *ob = vpd->vc.obact;
if (vpd->domain == ATTR_DOMAIN_POINT) {
if (vpd->type == CD_PROP_COLOR) {
vpaint_stroke_update_step_intern<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
C, stroke, itemptr);
}
else if (vpd->type == CD_PROP_BYTE_COLOR) {
vpaint_stroke_update_step_intern<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(
C, stroke, itemptr);
}
}
else if (vpd->domain == ATTR_DOMAIN_CORNER) {
if (vpd->type == CD_PROP_COLOR) {
vpaint_stroke_update_step_intern<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(
C, stroke, itemptr);
}
else if (vpd->type == CD_PROP_BYTE_COLOR) {
vpaint_stroke_update_step_intern<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
C, stroke, itemptr);
}
}
}
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_free_vpaintdata(Object * /*ob*/, void *_vpd)
{
VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(_vpd);
if (vpd->is_texbrush) { if (vpd->is_texbrush) {
ED_vpaint_proj_handle_free(vpd->vp_handle); ED_vpaint_proj_handle_free(vpd->vp_handle);
} }
if (vpd->smear.color_prev) { MEM_delete(vpd);
MEM_freeN(vpd->smear.color_prev);
}
if (vpd->smear.color_curr) {
MEM_freeN(vpd->smear.color_curr);
}
MEM_delete<VPaintData<Color, Traits, domain>>(vpd);
}
static void vpaint_stroke_done(const bContext *C, PaintStroke *stroke)
{
void *vpd_ptr = paint_stroke_mode_data(stroke);
VPaintDataBase *vpd = static_cast<VPaintDataBase *>(vpd_ptr);
ViewContext *vc = &vpd->vc;
Object *ob = vc->obact;
if (vpd->domain == ATTR_DOMAIN_POINT) {
if (vpd->type == CD_PROP_COLOR) {
vpaint_free_vpaintdata<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(ob, vpd);
}
else if (vpd->type == CD_PROP_BYTE_COLOR) {
vpaint_free_vpaintdata<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(ob, vpd);
}
}
else if (vpd->domain == ATTR_DOMAIN_CORNER) {
if (vpd->type == CD_PROP_COLOR) {
vpaint_free_vpaintdata<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(ob, vpd);
}
else if (vpd->type == CD_PROP_BYTE_COLOR) {
vpaint_free_vpaintdata<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(ob, vpd);
}
}
SculptSession *ss = ob->sculpt; SculptSession *ss = ob->sculpt;
if (ss->cache->alt_smooth) { if (ss->cache && ss->cache->alt_smooth) {
ToolSettings *ts = CTX_data_tool_settings(C); ToolSettings *ts = CTX_data_tool_settings(C);
VPaint *vp = ts->vpaint; VPaint *vp = ts->vpaint;
smooth_brush_toggle_off(C, &vp->paint, ss->cache); smooth_brush_toggle_off(C, &vp->paint, ss->cache);
@@ -4214,9 +4113,7 @@ static int vertex_color_set_exec(bContext *C, wmOperator * /*op*/)
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C); Object *obact = CTX_data_active_object(C);
// uint paintcol = vpaint_get_current_color(scene, scene->toolsettings->vpaint, false); ColorPaint4f paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint, false);
ColorPaint4f paintcol = vpaint_get_current_col<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
scene, scene->toolsettings->vpaint, false);
if (paint_object_attributes_active_color_fill_ex(obact, paintcol)) { if (paint_object_attributes_active_color_fill_ex(obact, paintcol)) {
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);

View File

@@ -4334,7 +4334,6 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->detail_directions); MEM_SAFE_FREE(cache->detail_directions);
MEM_SAFE_FREE(cache->prev_displacement); MEM_SAFE_FREE(cache->prev_displacement);
MEM_SAFE_FREE(cache->limit_surface_co); MEM_SAFE_FREE(cache->limit_surface_co);
MEM_SAFE_FREE(cache->prev_colors_vpaint);
if (cache->pose_ik_chain) { if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain); SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@@ -4350,7 +4349,7 @@ void SCULPT_cache_free(StrokeCache *cache)
SCULPT_cloth_simulation_free(cache->cloth_sim); SCULPT_cloth_simulation_free(cache->cloth_sim);
} }
MEM_freeN(cache); MEM_delete(cache);
} }
/* Initialize mirror modifier clipping. */ /* Initialize mirror modifier clipping. */
@@ -4454,8 +4453,7 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache
static void sculpt_update_cache_invariants( static void sculpt_update_cache_invariants(
bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2]) bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2])
{ {
StrokeCache *cache = static_cast<StrokeCache *>( StrokeCache *cache = MEM_new<StrokeCache>(__func__);
MEM_callocN(sizeof(StrokeCache), "stroke cache"));
ToolSettings *tool_settings = CTX_data_tool_settings(C); ToolSettings *tool_settings = CTX_data_tool_settings(C);
UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings; UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings;
Brush *brush = BKE_paint_brush(&sd->paint); Brush *brush = BKE_paint_brush(&sd->paint);

View File

@@ -21,6 +21,7 @@
#include "BLI_bitmap.h" #include "BLI_bitmap.h"
#include "BLI_compiler_attrs.h" #include "BLI_compiler_attrs.h"
#include "BLI_compiler_compat.h" #include "BLI_compiler_compat.h"
#include "BLI_generic_array.hh"
#include "BLI_gsqueue.h" #include "BLI_gsqueue.h"
#include "BLI_implicit_sharing.hh" #include "BLI_implicit_sharing.hh"
#include "BLI_span.hh" #include "BLI_span.hh"
@@ -555,7 +556,7 @@ struct StrokeCache {
float mouse_event[2]; float mouse_event[2];
float (*prev_colors)[4]; float (*prev_colors)[4];
void *prev_colors_vpaint; blender::GArray<> prev_colors_vpaint;
/* Multires Displacement Smear. */ /* Multires Displacement Smear. */
float (*prev_displacement)[3]; float (*prev_displacement)[3];