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)
{
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(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
*/

View File

@@ -77,11 +77,6 @@ using namespace blender::color;
/** \name Internal Utilities
* \{ */
static uint color2uint(ColorPaint4b c)
{
return *(reinterpret_cast<uint *>(&c));
}
static bool isZero(ColorPaint4f c)
{
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;
}
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>) {
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>) {
return c.encode();
@@ -135,26 +130,6 @@ struct NormalAnglePrecalc {
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)
{
angle = RAD2DEGF(angle);
@@ -328,8 +303,7 @@ bool weight_paint_poll_ignore_tool(bContext *C)
return weight_paint_poll_ex(C, false);
}
template<typename Color, typename Traits, eAttrDomain domain>
static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
static ColorPaint4f vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
{
const Brush *brush = BKE_paint_brush_for_read(&vp->paint);
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 */
return fromFloat<Color>(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);
return ColorPaint4f(color);
}
/* 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.
*/
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. */
if (!brush_use_accumulate(ts->vpaint)) {
if (!ss->cache->prev_colors_vpaint) {
Mesh *me = BKE_object_get_original_mesh(ob);
size_t elem_size;
int elem_num;
elem_num = get_vcol_elements(me, &elem_size);
ss->cache->prev_colors_vpaint = (uint *)MEM_callocN(elem_num * elem_size, __func__);
if (ss->cache->prev_colors_vpaint.is_empty()) {
const Mesh *mesh = BKE_object_get_original_mesh(ob);
const GVArray attribute = *mesh->attributes().lookup(mesh->active_color_attribute);
ss->cache->prev_colors_vpaint = GArray(attribute.type(), attribute.size());
attribute.type().value_initialize_n(ss->cache->prev_colors_vpaint.data(),
ss->cache->prev_colors_vpaint.size());
}
}
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. */
if (!ss->cache) {
cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache");
cache = MEM_new<StrokeCache>(__func__);
ss->cache = cache;
}
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.
*/
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;
eAttrDomain domain;
eCustomDataType type;
};
template<typename Color, typename Traits, eAttrDomain domain>
struct VPaintData : public VPaintDataBase {
NormalAnglePrecalc normal_angle_precalc;
Color paintcol;
ColorPaint4f paintcol;
VertProjHandle *vp_handle;
CoNo *vertexcosnos;
@@ -2845,33 +2822,24 @@ struct VPaintData : public VPaintDataBase {
/* Special storage for smear brush, avoid feedback loop - update each step. */
struct {
void *color_prev;
void *color_curr;
GArray<> color_prev;
GArray<> color_curr;
} smear;
};
template<typename Color, typename Traits, eAttrDomain domain>
static void *vpaint_init_vpaint(bContext *C,
wmOperator *op,
Scene *scene,
Depsgraph *depsgraph,
VPaint *vp,
Object *ob,
Mesh *me,
const Brush *brush)
static VPaintData *vpaint_init_vpaint(bContext *C,
wmOperator *op,
Scene *scene,
Depsgraph *depsgraph,
VPaint *vp,
Object *ob,
Mesh *me,
const eAttrDomain domain,
const eCustomDataType type,
const Brush *brush)
{
VPaintData<Color, Traits, domain> *vpd;
/* 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;
}
VPaintData *vpd = MEM_new<VPaintData>(__func__);
vpd->type = type;
vpd->domain = domain;
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->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));
vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex;
if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
const GVArray attribute = *me->attributes().lookup(me->active_color_attribute, domain);
vpd->smear.color_prev = MEM_malloc_arrayN(attribute.size(), attribute.type().size(), __func__);
attribute.materialize(vpd->smear.color_prev);
vpd->smear.color_prev = GArray(attribute.type(), attribute.size());
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 */
@@ -2899,7 +2867,7 @@ static void *vpaint_init_vpaint(bContext *C,
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])
@@ -2928,30 +2896,8 @@ static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
return false;
}
void *vpd = nullptr;
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);
VPaintData *vpd = vpaint_init_vpaint(
C, op, scene, depsgraph, vp, ob, me, meta_data->domain, meta_data->data_type, brush);
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;
}
template<class Color = ColorPaint4b, typename Traits = ByteTraits>
static void do_vpaint_brush_blur_loops(bContext *C,
Sculpt * /*sd*/,
VPaint *vp,
VPaintData<Color, Traits, ATTR_DOMAIN_CORNER> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Span<PBVHNode *> nodes,
Color *lcol)
GMutableSpan attribute)
{
using Blend = typename Traits::BlendType;
SculptSession *ss = ob->sculpt;
const Brush *brush = ob->sculpt->cache->brush;
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>(
".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);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
SculptBrushTest test = test_init;
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 */
PBVHVertexIter vd;
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);
/* 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>();
/* Get the average poly color */
Color color_final(0, 0, 0, 0);
int total_hit_loops = 0;
Blend blend[4] = {0};
int total_hit_loops = 0;
Blend blend[4] = {0};
for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) {
continue;
}
const blender::IndexRange poly = ss->polys[p_index];
total_hit_loops += poly.size();
for (const int corner : poly) {
Color *col = lcol + corner;
/* 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;
}
}
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];
for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) {
return;
}
const blender::IndexRange poly = ss->polys[p_index];
total_hit_loops += poly.size();
for (const int corner : poly) {
const Color &col = colors[corner];
/* 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;
}
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. */
lcol[l_index] = vpaint_blend<Color, Traits>(
vp, lcol[l_index], color_orig, *col, final_alpha, Traits::range * brush_strength);
}
if (total_hit_loops == 0) {
return;
}
/* 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;
};
});
}
template<class Color = ColorPaint4b, typename Traits = ByteTraits>
static void do_vpaint_brush_blur_verts(bContext *C,
Sculpt * /*sd*/,
VPaint *vp,
VPaintData<Color, Traits, ATTR_DOMAIN_POINT> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Span<PBVHNode *> nodes,
Color *lcol)
GMutableSpan attribute)
{
using Blend = typename Traits::BlendType;
SculptSession *ss = ob->sculpt;
const Brush *brush = ob->sculpt->cache->brush;
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>(
".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);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
SculptBrushTest test = test_init;
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 */
PBVHVertexIter vd;
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);
/* 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;
Blend blend[4] = {0};
int total_hit_loops = 0;
Blend blend[4] = {0};
for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) {
continue;
for (const int p_index : gmap->vert_to_poly[v_index]) {
if (use_face_sel && !select_poly[p_index]) {
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. */
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;
if (total_hit_loops == 0) {
return;
}
}
/* 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 */
if (previous_color != nullptr) {
if (!previous_color.is_empty()) {
/* Get the previous loop color */
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];
}
@@ -3227,24 +3182,27 @@ static void do_vpaint_brush_blur_verts(bContext *C,
brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original
* based on the brush strength and the curve. */
lcol[v_index] = vpaint_blend<Color, Traits>(
vp, lcol[v_index], color_orig, *col, final_alpha, Traits::range * brush_strength);
}
colors[v_index] = vpaint_blend<Color, Traits>(vp,
colors[v_index],
color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
});
}
BKE_pbvh_vertex_iter_end;
};
});
}
template<typename Color = ColorPaint4b, typename Traits, eAttrDomain domain>
static void do_vpaint_brush_smear(bContext *C,
Sculpt * /*sd*/,
VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Span<PBVHNode *> nodes,
Color *lcol)
GMutableSpan attribute)
{
SculptSession *ss = ob->sculpt;
@@ -3258,9 +3216,31 @@ static void do_vpaint_brush_smear(bContext *C,
const Brush *brush = ob->sculpt->cache->brush;
const Scene *scene = CTX_data_scene(C);
Color *color_curr = static_cast<Color *>(vpd->smear.color_curr);
Color *color_prev_smear = static_cast<Color *>(vpd->smear.color_prev);
Color *color_prev = reinterpret_cast<Color *>(ss->cache->prev_colors_vpaint);
GMutableSpan g_color_curr = vpd->smear.color_curr;
GMutableSpan g_color_prev_smear = vpd->smear.color_prev;
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>(
".select_vert", ATTR_DOMAIN_POINT, false);
@@ -3268,29 +3248,8 @@ static void do_vpaint_brush_smear(bContext *C,
".select_poly", ATTR_DOMAIN_FACE, false);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
SculptBrushTest test = test_init;
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 */
PBVHVertexIter vd;
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
* 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()) {
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);
UNUSED_VARS_NDEBUG(l_index);
if (use_face_sel && !select_poly[p_index]) {
continue;
Color color_final(0, 0, 0, 0);
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);
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 (v_other_index == v_index) {
if (!do_color) {
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;
}
/* 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 */
if (isZero(color_prev[elem_index])) {
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 color_orig(0, 0, 0, 0); /* unused when array is nullptr */
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;
}
});
}
template<typename Color, typename Traits, eAttrDomain domain>
static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd,
static void calculate_average_color(VPaintData *vpd,
Object *ob,
Mesh *me,
const Brush *brush,
Color *lcol,
const GSpan attribute,
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>(
".select_vert", ATTR_DOMAIN_POINT, false);
VPaintAverageAccum<Blend> *accum = (VPaintAverageAccum<Blend> *)MEM_mallocN(
sizeof(*accum) * nodes.size(), __func__);
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
for (int n : range) {
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;
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;
const Span<Color> colors = attribute.typed<T>().template cast<Color>();
StrokeCache *cache = ss->cache;
const bool use_vert_sel = (me->editflag &
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
Array<VPaintAverageAccum<Blend>> accum(nodes.size());
blender::threading::parallel_for(nodes.index_range(), 1LL, [&](IndexRange range) {
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;
accum2->len = 0;
memset(accum2->value, 0, sizeof(accum2->value));
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
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];
/* 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;
}
else {
elem_index = v_index;
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;
}
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. */
accum2->value[0] += col->r * col->r;
accum2->value[1] += col->g * col->g;
accum2->value[2] += col->b * col->b;
if (vpd->domain == ATTR_DOMAIN_CORNER) {
elem_index = gmap->vert_to_loop[v_index][j];
}
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,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
const float v_co[3],
Color *r_color)
{
@@ -3535,15 +3511,14 @@ static float paint_and_tex_color_alpha(VPaint *vp,
return rgba[3];
}
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_draw(bContext *C,
Sculpt * /*sd*/,
VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Span<PBVHNode *> nodes,
Color *lcol)
GMutableSpan attribute)
{
SculptSession *ss = ob->sculpt;
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 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>(
".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) {
for (int n : range) {
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);
Color paintcol = vpd->paintcol;
SculptBrushTest test = test_init;
/* For each vertex */
PBVHVertexIter vd;
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);
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. */
float tex_alpha = 1.0;
if (vpd->is_texbrush) {
/* NOTE: we may want to paint alpha as vertex color alpha. */
tex_alpha = paint_and_tex_color_alpha<Color, Traits, domain>(
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];
/* If we're painting with a texture, sample the texture color and alpha. */
float tex_alpha = 1.0;
if (vpd->is_texbrush) {
/* NOTE: we may want to paint alpha as vertex color alpha. */
tex_alpha = paint_and_tex_color_alpha<Color>(
vp, vpd, vpd->vertexcosnos[v_index].co, &color_final);
}
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,
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 */
Color color_orig(0, 0, 0, 0);
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 */
if (isZero(previous_color[l_index])) {
previous_color[l_index] = lcol[l_index];
if (isZero(previous_color[v_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 *
brush_alpha_pressure * grid_alpha;
/* Mix the new color with the original based on final_alpha. */
lcol[l_index] = vpaint_blend<Color, Traits>(vp,
lcol[l_index],
color_orig,
color_final,
final_alpha,
Traits::range * brush_strength);
colors[v_index] = vpaint_blend<Color, Traits>(vp,
colors[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.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;
}
});
}
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_blur(bContext *C,
Sculpt *sd,
VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Span<PBVHNode *> nodes,
Color *lcol)
GMutableSpan attribute)
{
if constexpr (domain == ATTR_DOMAIN_POINT) {
do_vpaint_brush_blur_verts<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, lcol);
if (vpd->domain == ATTR_DOMAIN_POINT) {
do_vpaint_brush_blur_verts(C, sd, vp, vpd, ob, me, nodes, attribute);
}
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,
Sculpt *sd,
VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Color *lcol,
GMutableSpan attribute,
Span<PBVHNode *> nodes)
{
for (PBVHNode *node : nodes) {
@@ -3714,27 +3694,26 @@ static void vpaint_paint_leaves(bContext *C,
switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) {
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;
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;
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;
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;
default:
break;
}
}
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_paint(bContext *C,
Sculpt *sd,
VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
Brush *brush,
@@ -3751,21 +3730,18 @@ static void vpaint_do_paint(bContext *C,
bke::GSpanAttributeWriter attribute = me->attributes_for_write().lookup_for_write_span(
me->active_color_attribute);
BLI_assert(attribute.domain == domain);
Color *color_data = static_cast<Color *>(attribute.span.data());
BLI_assert(attribute.domain == vpd->domain);
/* 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();
}
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_do_radial_symmetry(bContext *C,
Sculpt *sd,
VPaint *vp,
VPaintData<Color, Traits, domain> *vpd,
VPaintData *vpd,
Object *ob,
Mesh *me,
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++) {
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,
* 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
template<typename Color, typename Traits, eAttrDomain domain>
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);
Mesh *me = (Mesh *)ob->data;
@@ -3794,13 +3769,10 @@ static void vpaint_do_symmetrical_brush_actions(
/* initial stroke */
const ePaintSymmetryFlags initial_symm = 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_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, initial_symm, 'X');
vpaint_do_radial_symmetry<Color, Traits, domain>(
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');
vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, initial_symm, 'X', 0, 0);
vpaint_do_radial_symmetry(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(C, sd, vp, vpd, ob, me, brush, initial_symm, 'Z');
cache->symmetry = symm;
@@ -3814,22 +3786,16 @@ static void vpaint_do_symmetrical_brush_actions(
SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0);
if (i & (1 << 0)) {
vpaint_do_paint<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'X', 0, 0);
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'X');
vpaint_do_paint(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');
}
if (i & (1 << 1)) {
vpaint_do_paint<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Y', 0, 0);
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Y');
vpaint_do_paint(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');
}
if (i & (1 << 2)) {
vpaint_do_paint<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Z', 0, 0);
vpaint_do_radial_symmetry<Color, Traits, domain>(
C, sd, vp, vpd, ob, me, brush, symm_pass, 'Z');
vpaint_do_paint(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');
}
}
}
@@ -3838,13 +3804,14 @@ static void vpaint_do_symmetrical_brush_actions(
cache->is_last_valid = true;
}
template<typename Color, typename Traits, eAttrDomain domain>
static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, PointerRNA *itemptr)
static void vpaint_stroke_update_step(bContext *C,
wmOperator * /*op*/,
PaintStroke *stroke,
PointerRNA *itemptr)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(
paint_stroke_mode_data(stroke));
VPaintData *vpd = static_cast<VPaintData *>(paint_stroke_mode_data(stroke));
VPaint *vp = ts->vpaint;
ViewContext *vc = &vpd->vc;
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);
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);
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
Mesh *me = BKE_object_get_original_mesh(ob);
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);
vpd->smear.color_prev = vpd->smear.color_curr;
}
/* 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);
}
static void vpaint_stroke_update_step(bContext *C,
wmOperator * /*op*/,
PaintStroke *stroke,
PointerRNA *itemptr)
static void vpaint_stroke_done(const bContext *C, PaintStroke *stroke)
{
VPaintDataBase *vpd = static_cast<VPaintDataBase *>(paint_stroke_mode_data(stroke));
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);
VPaintData *vpd = static_cast<VPaintData *>(paint_stroke_mode_data(stroke));
Object *ob = vpd->vc.obact;
if (vpd->is_texbrush) {
ED_vpaint_proj_handle_free(vpd->vp_handle);
}
if (vpd->smear.color_prev) {
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);
}
}
MEM_delete(vpd);
SculptSession *ss = ob->sculpt;
if (ss->cache->alt_smooth) {
if (ss->cache && ss->cache->alt_smooth) {
ToolSettings *ts = CTX_data_tool_settings(C);
VPaint *vp = ts->vpaint;
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);
Object *obact = CTX_data_active_object(C);
// uint paintcol = vpaint_get_current_color(scene, scene->toolsettings->vpaint, false);
ColorPaint4f paintcol = vpaint_get_current_col<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
scene, scene->toolsettings->vpaint, false);
ColorPaint4f paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint, false);
if (paint_object_attributes_active_color_fill_ex(obact, paintcol)) {
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->prev_displacement);
MEM_SAFE_FREE(cache->limit_surface_co);
MEM_SAFE_FREE(cache->prev_colors_vpaint);
if (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);
}
MEM_freeN(cache);
MEM_delete(cache);
}
/* 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(
bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mval[2])
{
StrokeCache *cache = static_cast<StrokeCache *>(
MEM_callocN(sizeof(StrokeCache), "stroke cache"));
StrokeCache *cache = MEM_new<StrokeCache>(__func__);
ToolSettings *tool_settings = CTX_data_tool_settings(C);
UnifiedPaintSettings *ups = &tool_settings->unified_paint_settings;
Brush *brush = BKE_paint_brush(&sd->paint);

View File

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