Sculpt: Data oriented refactor for color filter tool
Part of #118145. Move the operation switch out of the hot loop. Use a series of thread local arrays to split some loops into simpler parts.
This commit is contained in:
@@ -77,9 +77,21 @@ static EnumPropertyItem prop_color_filter_types[] = {
|
||||
|
||||
struct LocalData {
|
||||
Vector<float> factors;
|
||||
Vector<float4> colors;
|
||||
Vector<Vector<int>> vert_neighbors;
|
||||
Vector<float4> average_colors;
|
||||
Vector<float4> new_colors;
|
||||
};
|
||||
|
||||
BLI_NOINLINE static void clamp_factors(const MutableSpan<float> factors,
|
||||
const float min,
|
||||
const float max)
|
||||
{
|
||||
for (float &factor : factors) {
|
||||
factor = std::clamp(factor, min, max);
|
||||
}
|
||||
}
|
||||
|
||||
static void color_filter_task(Object &ob,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
@@ -107,87 +119,103 @@ static void color_filter_task(Object &ob,
|
||||
tls.new_colors.resize(verts.size());
|
||||
const MutableSpan<float4> new_colors = tls.new_colors;
|
||||
|
||||
/* Copy alpha. */
|
||||
for (const int i : verts.index_range()) {
|
||||
const int vert = verts[i];
|
||||
new_colors[i][3] = orig_colors[i][3];
|
||||
}
|
||||
|
||||
float3 orig_color;
|
||||
float3 hsv_color;
|
||||
int hue;
|
||||
float brightness, contrast, gain, delta, offset;
|
||||
float fade = factors[i];
|
||||
|
||||
copy_v3_v3(orig_color, orig_colors[i]);
|
||||
new_colors[i][3] = orig_colors[i][3]; /* Copy alpha */
|
||||
|
||||
switch (mode) {
|
||||
case FilterType::Fill: {
|
||||
switch (mode) {
|
||||
case FilterType::Fill: {
|
||||
clamp_factors(factors, 0.0f, 1.0f);
|
||||
for (const int i : verts.index_range()) {
|
||||
float fill_color_rgba[4];
|
||||
copy_v3_v3(fill_color_rgba, filter_fill_color);
|
||||
fill_color_rgba[3] = 1.0f;
|
||||
fade = clamp_f(fade, 0.0f, 1.0f);
|
||||
mul_v4_fl(fill_color_rgba, fade);
|
||||
mul_v4_fl(fill_color_rgba, factors[i]);
|
||||
blend_color_mix_float(new_colors[i], orig_colors[i], fill_color_rgba);
|
||||
break;
|
||||
}
|
||||
case FilterType::Hue: {
|
||||
rgb_to_hsv_v(orig_color, hsv_color);
|
||||
hue = hsv_color[0];
|
||||
hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue, 1);
|
||||
break;
|
||||
}
|
||||
case FilterType::Hue: {
|
||||
for (const int i : verts.index_range()) {
|
||||
float3 hsv_color;
|
||||
rgb_to_hsv_v(orig_colors[i], hsv_color);
|
||||
const float hue = hsv_color[0];
|
||||
hsv_color[0] = fmod((hsv_color[0] + fabs(factors[i])) - hue, 1);
|
||||
hsv_to_rgb_v(hsv_color, new_colors[i]);
|
||||
break;
|
||||
}
|
||||
case FilterType::Saturation: {
|
||||
rgb_to_hsv_v(orig_color, hsv_color);
|
||||
break;
|
||||
}
|
||||
case FilterType::Saturation: {
|
||||
for (const int i : verts.index_range()) {
|
||||
float3 hsv_color;
|
||||
rgb_to_hsv_v(orig_colors[i], hsv_color);
|
||||
|
||||
if (hsv_color[1] > 0.001f) {
|
||||
hsv_color[1] = clamp_f(hsv_color[1] + fade * hsv_color[1], 0.0f, 1.0f);
|
||||
hsv_color[1] = std::clamp(hsv_color[1] + factors[i] * hsv_color[1], 0.0f, 1.0f);
|
||||
hsv_to_rgb_v(hsv_color, new_colors[i]);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(new_colors[i], orig_color);
|
||||
copy_v3_v3(new_colors[i], orig_colors[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FilterType::Value: {
|
||||
rgb_to_hsv_v(orig_color, hsv_color);
|
||||
hsv_color[2] = clamp_f(hsv_color[2] + fade, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
case FilterType::Value: {
|
||||
for (const int i : verts.index_range()) {
|
||||
float3 hsv_color;
|
||||
rgb_to_hsv_v(orig_colors[i], hsv_color);
|
||||
hsv_color[2] = std::clamp(hsv_color[2] + factors[i], 0.0f, 1.0f);
|
||||
hsv_to_rgb_v(hsv_color, new_colors[i]);
|
||||
break;
|
||||
}
|
||||
case FilterType::Red: {
|
||||
orig_color[0] = clamp_f(orig_color[0] + fade, 0.0f, 1.0f);
|
||||
copy_v3_v3(new_colors[i], orig_color);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case FilterType::Red: {
|
||||
for (const int i : verts.index_range()) {
|
||||
copy_v3_v3(new_colors[i], orig_colors[i]);
|
||||
new_colors[i][0] = std::clamp(orig_colors[i][0] + factors[i], 0.0f, 1.0f);
|
||||
}
|
||||
case FilterType::Green: {
|
||||
orig_color[1] = clamp_f(orig_color[1] + fade, 0.0f, 1.0f);
|
||||
copy_v3_v3(new_colors[i], orig_color);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case FilterType::Green: {
|
||||
for (const int i : verts.index_range()) {
|
||||
copy_v3_v3(new_colors[i], orig_colors[i]);
|
||||
new_colors[i][1] = std::clamp(orig_colors[i][1] + factors[i], 0.0f, 1.0f);
|
||||
}
|
||||
case FilterType::Blue: {
|
||||
orig_color[2] = clamp_f(orig_color[2] + fade, 0.0f, 1.0f);
|
||||
copy_v3_v3(new_colors[i], orig_color);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case FilterType::Blue: {
|
||||
for (const int i : verts.index_range()) {
|
||||
copy_v3_v3(new_colors[i], orig_colors[i]);
|
||||
new_colors[i][2] = std::clamp(orig_colors[i][2] + factors[i], 0.0f, 1.0f);
|
||||
}
|
||||
case FilterType::Brightness: {
|
||||
fade = clamp_f(fade, -1.0f, 1.0f);
|
||||
brightness = fade;
|
||||
contrast = 0;
|
||||
delta = contrast / 2.0f;
|
||||
gain = 1.0f - delta * 2.0f;
|
||||
break;
|
||||
}
|
||||
case FilterType::Brightness: {
|
||||
clamp_factors(factors, -1.0f, 1.0f);
|
||||
for (const int i : verts.index_range()) {
|
||||
const float brightness = factors[i];
|
||||
const float contrast = 0;
|
||||
float delta = contrast / 2.0f;
|
||||
const float gain = 1.0f - delta * 2.0f;
|
||||
delta *= -1;
|
||||
offset = gain * (brightness + delta);
|
||||
const float offset = gain * (brightness + delta);
|
||||
for (int component = 0; component < 3; component++) {
|
||||
new_colors[i][component] = clamp_f(gain * orig_color[component] + offset, 0.0f, 1.0f);
|
||||
new_colors[i][component] = std::clamp(
|
||||
gain * orig_colors[i][component] + offset, 0.0f, 1.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FilterType::Contrast: {
|
||||
fade = clamp_f(fade, -1.0f, 1.0f);
|
||||
brightness = 0;
|
||||
contrast = fade;
|
||||
delta = contrast / 2.0f;
|
||||
gain = 1.0f - delta * 2.0f;
|
||||
break;
|
||||
}
|
||||
case FilterType::Contrast: {
|
||||
clamp_factors(factors, -1.0f, 1.0f);
|
||||
for (const int i : verts.index_range()) {
|
||||
const float brightness = 0;
|
||||
const float contrast = factors[i];
|
||||
float delta = contrast / 2.0f;
|
||||
float gain = 1.0f - delta * 2.0f;
|
||||
|
||||
float offset;
|
||||
if (contrast > 0) {
|
||||
gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
|
||||
offset = gain * (brightness - delta);
|
||||
@@ -197,55 +225,71 @@ static void color_filter_task(Object &ob,
|
||||
offset = gain * (brightness + delta);
|
||||
}
|
||||
for (int component = 0; component < 3; component++) {
|
||||
new_colors[i][component] = clamp_f(gain * orig_color[component] + offset, 0.0f, 1.0f);
|
||||
new_colors[i][component] = std::clamp(
|
||||
gain * orig_colors[i][component] + offset, 0.0f, 1.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FilterType::Smooth: {
|
||||
fade = clamp_f(fade, -1.0f, 1.0f);
|
||||
float4 smooth_color = smooth::neighbor_color_average(ss,
|
||||
faces,
|
||||
corner_verts,
|
||||
vert_to_face_map,
|
||||
color_attribute.span,
|
||||
color_attribute.domain,
|
||||
vert);
|
||||
break;
|
||||
}
|
||||
case FilterType::Smooth: {
|
||||
clamp_factors(factors, -1.0f, 1.0f);
|
||||
|
||||
float4 col = color_vert_get(faces,
|
||||
corner_verts,
|
||||
vert_to_face_map,
|
||||
color_attribute.span,
|
||||
color_attribute.domain,
|
||||
vert);
|
||||
tls.colors.resize(verts.size());
|
||||
const MutableSpan<float4> colors = tls.colors;
|
||||
for (const int i : verts.index_range()) {
|
||||
colors[i] = color_vert_get(faces,
|
||||
corner_verts,
|
||||
vert_to_face_map,
|
||||
color_attribute.span,
|
||||
color_attribute.domain,
|
||||
verts[i]);
|
||||
}
|
||||
|
||||
if (fade < 0.0f) {
|
||||
interp_v4_v4v4(smooth_color, smooth_color, col, 0.5f);
|
||||
tls.vert_neighbors.reinitialize(verts.size());
|
||||
calc_vert_neighbors(faces, corner_verts, vert_to_face_map, {}, verts, tls.vert_neighbors);
|
||||
const Span<Vector<int>> neighbors = tls.vert_neighbors;
|
||||
|
||||
tls.average_colors.resize(verts.size());
|
||||
const MutableSpan<float4> average_colors = tls.average_colors;
|
||||
smooth::neighbor_color_average(faces,
|
||||
corner_verts,
|
||||
vert_to_face_map,
|
||||
color_attribute.span,
|
||||
color_attribute.domain,
|
||||
neighbors,
|
||||
average_colors);
|
||||
|
||||
for (const int i : verts.index_range()) {
|
||||
const int vert = verts[i];
|
||||
|
||||
if (factors[i] < 0.0f) {
|
||||
interp_v4_v4v4(average_colors[i], average_colors[i], colors[i], 0.5f);
|
||||
}
|
||||
|
||||
bool copy_alpha = col[3] == smooth_color[3];
|
||||
bool copy_alpha = colors[i][3] == average_colors[i][3];
|
||||
|
||||
if (fade < 0.0f) {
|
||||
if (factors[i] < 0.0f) {
|
||||
float delta_color[4];
|
||||
|
||||
/* Unsharp mask. */
|
||||
copy_v4_v4(delta_color, ss.filter_cache->pre_smoothed_color[vert]);
|
||||
sub_v4_v4(delta_color, smooth_color);
|
||||
sub_v4_v4(delta_color, average_colors[i]);
|
||||
|
||||
copy_v4_v4(new_colors[i], col);
|
||||
madd_v4_v4fl(new_colors[i], delta_color, fade);
|
||||
copy_v4_v4(new_colors[i], colors[i]);
|
||||
madd_v4_v4fl(new_colors[i], delta_color, factors[i]);
|
||||
}
|
||||
else {
|
||||
blend_color_interpolate_float(new_colors[i], col, smooth_color, fade);
|
||||
blend_color_interpolate_float(new_colors[i], colors[i], average_colors[i], factors[i]);
|
||||
}
|
||||
|
||||
new_colors[i] = math::clamp(new_colors[i], 0.0f, 1.0f);
|
||||
|
||||
/* Prevent accumulated numeric error from corrupting alpha. */
|
||||
if (copy_alpha) {
|
||||
new_colors[i][3] = smooth_color[3];
|
||||
new_colors[i][3] = average_colors[i][3];
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1661,13 +1661,14 @@ void bmesh_four_neighbor_average(float avg[3], const float3 &direction, const BM
|
||||
|
||||
float3 neighbor_coords_average(SculptSession &ss, PBVHVertRef vertex);
|
||||
float neighbor_mask_average(SculptSession &ss, SculptMaskWriteInfo write_info, PBVHVertRef vertex);
|
||||
float4 neighbor_color_average(SculptSession &ss,
|
||||
OffsetIndices<int> faces,
|
||||
Span<int> corner_verts,
|
||||
GroupedSpan<int> vert_to_face_map,
|
||||
GSpan color_attribute,
|
||||
bke::AttrDomain color_domain,
|
||||
int vert);
|
||||
|
||||
void neighbor_color_average(OffsetIndices<int> faces,
|
||||
Span<int> corner_verts,
|
||||
GroupedSpan<int> vert_to_face_map,
|
||||
GSpan color_attribute,
|
||||
bke::AttrDomain color_domain,
|
||||
Span<Vector<int>> vert_neighbors,
|
||||
MutableSpan<float4> smooth_colors);
|
||||
|
||||
/**
|
||||
* Mask the mesh boundaries smoothing only the mesh surface without using auto-masking.
|
||||
|
||||
@@ -253,25 +253,6 @@ struct LocalData {
|
||||
Vector<Vector<int>> vert_neighbors;
|
||||
};
|
||||
|
||||
BLI_NOINLINE static void calc_smooth_colors(const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const GroupedSpan<int> vert_to_face_map,
|
||||
const Span<Vector<int>> vert_neighbors,
|
||||
const GSpan colors,
|
||||
const bke::AttrDomain color_domain,
|
||||
const MutableSpan<float4> smooth_colors)
|
||||
{
|
||||
for (const int i : vert_neighbors.index_range()) {
|
||||
float4 sum(0);
|
||||
const Span<int> neighbors = vert_neighbors[i];
|
||||
for (const int vert : neighbors) {
|
||||
sum += color::color_vert_get(
|
||||
faces, corner_verts, vert_to_face_map, colors, color_domain, vert);
|
||||
}
|
||||
smooth_colors[i] = sum / neighbors.size();
|
||||
}
|
||||
}
|
||||
|
||||
static void do_color_smooth_task(const Object &object,
|
||||
const Span<float3> vert_positions,
|
||||
const Span<float3> vert_normals,
|
||||
@@ -330,13 +311,13 @@ static void do_color_smooth_task(const Object &object,
|
||||
|
||||
tls.new_colors.reinitialize(verts.size());
|
||||
MutableSpan<float4> new_colors = tls.new_colors;
|
||||
calc_smooth_colors(faces,
|
||||
corner_verts,
|
||||
vert_to_face_map,
|
||||
vert_neighbors,
|
||||
color_attribute.span,
|
||||
color_attribute.domain,
|
||||
new_colors);
|
||||
smooth::neighbor_color_average(faces,
|
||||
corner_verts,
|
||||
vert_to_face_map,
|
||||
color_attribute.span,
|
||||
color_attribute.domain,
|
||||
vert_neighbors,
|
||||
new_colors);
|
||||
|
||||
for (const int i : colors.index_range()) {
|
||||
blend_color_interpolate_float(new_colors[i], colors[i], new_colors[i], factors[i]);
|
||||
|
||||
@@ -448,32 +448,25 @@ float neighbor_mask_average(SculptSession &ss,
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float4 neighbor_color_average(SculptSession &ss,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const GroupedSpan<int> vert_to_face_map,
|
||||
const GSpan color_attribute,
|
||||
const bke::AttrDomain color_domain,
|
||||
const int vert)
|
||||
void neighbor_color_average(const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const GroupedSpan<int> vert_to_face_map,
|
||||
const GSpan color_attribute,
|
||||
const bke::AttrDomain color_domain,
|
||||
const Span<Vector<int>> vert_neighbors,
|
||||
const MutableSpan<float4> smooth_colors)
|
||||
{
|
||||
float4 avg(0);
|
||||
int total = 0;
|
||||
BLI_assert(vert_neighbors.size() == smooth_colors.size());
|
||||
|
||||
SculptVertexNeighborIter ni;
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, PBVHVertRef{vert}, ni) {
|
||||
float4 tmp = color::color_vert_get(
|
||||
faces, corner_verts, vert_to_face_map, color_attribute, color_domain, ni.index);
|
||||
|
||||
avg += tmp;
|
||||
total++;
|
||||
for (const int i : vert_neighbors.index_range()) {
|
||||
float4 sum(0);
|
||||
const Span<int> neighbors = vert_neighbors[i];
|
||||
for (const int vert : neighbors) {
|
||||
sum += color::color_vert_get(
|
||||
faces, corner_verts, vert_to_face_map, color_attribute, color_domain, vert);
|
||||
}
|
||||
smooth_colors[i] = sum / neighbors.size();
|
||||
}
|
||||
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
|
||||
|
||||
if (total > 0) {
|
||||
return avg / total;
|
||||
}
|
||||
return color::color_vert_get(
|
||||
faces, corner_verts, vert_to_face_map, color_attribute, color_domain, vert);
|
||||
}
|
||||
|
||||
/* HC Smooth Algorithm. */
|
||||
|
||||
Reference in New Issue
Block a user