Geometry Nodes: better support for byte color attributes

Since {rBeae36be372a6b16ee3e76eff0485a47da4f3c230} the distinction
between float and byte colors is more explicit in the ui. So far, geometry
nodes couldn't really deal with byte colors in general. This patch fixes that.
There is still only one color socket, which contains float colors. Conversion
to and from byte colors is done when read from or writing to attributes.

* Support writing to byte color attributes in Store Named Attribute node.
* Support converting to/from byte color in attribute conversion operator.
* Support propagating byte color attributes.
* Add all the implicit conversions from byte colors to the other types.
* Display byte colors as integers in spreadsheet.

Differential Revision: https://developer.blender.org/D14705
This commit is contained in:
Jacques Lucke 2022-04-21 16:11:26 +02:00
parent aca083fbf3
commit b9799dfb8a
Notes: blender-bot 2023-11-15 13:35:42 +01:00
Referenced by issue #98803, Regression: Vertex colors are now broken in hair strands
Referenced by issue #97468, Update Attribute Conversion operator after recent color attribute changes.
Referenced by issue #94428, Geometry Nodes - Vertex Color Issues
Referenced by issue #114872, Regression: Cycles point density node with a vertex color source renders without vertex colors (if these come from a Store Named Attribute node))
25 changed files with 364 additions and 109 deletions

View File

@ -5,6 +5,7 @@
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_cpp_type.hh"
#include "BLI_math_color.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
@ -18,17 +19,23 @@ namespace blender::attribute_math {
template<typename Func>
inline void convert_to_static_type(const CPPType &cpp_type, const Func &func)
{
cpp_type.to_static_type_tag<float, float2, float3, int, bool, int8_t, ColorGeometry4f>(
[&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_same_v<T, void>) {
/* It's expected that the given cpp type is one of the supported ones. */
BLI_assert_unreachable();
}
else {
func(T());
}
});
cpp_type.to_static_type_tag<float,
float2,
float3,
int,
bool,
int8_t,
ColorGeometry4f,
ColorGeometry4b>([&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_same_v<T, void>) {
/* It's expected that the given cpp type is one of the supported ones. */
BLI_assert_unreachable();
}
else {
func(T());
}
});
}
template<typename Func>
@ -91,6 +98,22 @@ inline ColorGeometry4f mix3(const float3 &weights,
return result;
}
template<>
inline ColorGeometry4b mix3(const float3 &weights,
const ColorGeometry4b &v0,
const ColorGeometry4b &v1,
const ColorGeometry4b &v2)
{
const float4 v0_f{&v0.r};
const float4 v1_f{&v1.r};
const float4 v2_f{&v2.r};
const float4 mixed = v0_f * weights[0] + v1_f * weights[1] + v2_f * weights[2];
return ColorGeometry4b{static_cast<uint8_t>(mixed[0]),
static_cast<uint8_t>(mixed[1]),
static_cast<uint8_t>(mixed[2]),
static_cast<uint8_t>(mixed[3])};
}
/** \} */
/* -------------------------------------------------------------------- */
@ -134,9 +157,13 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3
template<>
inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
{
ColorGeometry4f result;
interp_v4_v4v4(result, a, b, factor);
return result;
return math::interpolate(a, b, factor);
}
template<>
inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b)
{
return math::interpolate(a, b, factor);
}
/** \} */
@ -278,19 +305,33 @@ class SimpleMixerWithAccumulationType {
}
};
class ColorGeometryMixer {
class ColorGeometry4fMixer {
private:
MutableSpan<ColorGeometry4f> buffer_;
ColorGeometry4f default_color_;
Array<float> total_weights_;
public:
ColorGeometryMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
void finalize();
};
class ColorGeometry4bMixer {
private:
MutableSpan<ColorGeometry4b> buffer_;
ColorGeometry4b default_color_;
Array<float> total_weights_;
Array<float4> accumulation_buffer_;
public:
ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
void finalize();
};
template<typename T> struct DefaultMixerStruct {
/* Use void by default. This can be checked for in `if constexpr` statements. */
using type = void;
@ -307,7 +348,10 @@ template<> struct DefaultMixerStruct<float3> {
template<> struct DefaultMixerStruct<ColorGeometry4f> {
/* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not
* something one should usually do with colors. */
using type = ColorGeometryMixer;
using type = ColorGeometry4fMixer;
};
template<> struct DefaultMixerStruct<ColorGeometry4b> {
using type = ColorGeometry4bMixer;
};
template<> struct DefaultMixerStruct<int> {
static int double_to_int(const double &value)

View File

@ -2,6 +2,7 @@
#pragma once
#include "FN_field.hh"
#include "FN_multi_function.hh"
namespace blender::bke {
@ -59,8 +60,8 @@ class DataTypeConversions {
void convert_to_initialized_n(GSpan from_span, GMutableSpan to_span) const;
GVArray try_convert(GVArray varray, const CPPType &to_type) const;
GVMutableArray try_convert(GVMutableArray varray, const CPPType &to_type) const;
fn::GField try_convert(fn::GField field, const CPPType &to_type) const;
};
const DataTypeConversions &get_implicit_type_conversions();

View File

@ -70,11 +70,11 @@ static int attribute_data_type_complexity(const CustomDataType data_type)
return 4;
case CD_PROP_FLOAT3:
return 5;
case CD_PROP_COLOR:
return 6;
#if 0 /* These attribute types are not supported yet. */
case CD_PROP_BYTE_COLOR:
return 3;
return 6;
case CD_PROP_COLOR:
return 7;
#if 0 /* These attribute types are not supported yet. */
case CD_PROP_STRING:
return 6;
#endif

View File

@ -127,7 +127,7 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL |
CD_MASK_PROP_INT8;
CD_MASK_PROP_INT8 | CD_MASK_PROP_BYTE_COLOR;
const AttributeDomain domain_;
const CustomDataAccessInfo custom_data_access_;

View File

@ -4,8 +4,8 @@
namespace blender::attribute_math {
ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffer,
ColorGeometry4f default_color)
ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> output_buffer,
ColorGeometry4f default_color)
: buffer_(output_buffer),
default_color_(default_color),
total_weights_(output_buffer.size(), 0.0f)
@ -13,9 +13,9 @@ ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffe
buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
void ColorGeometryMixer::mix_in(const int64_t index,
const ColorGeometry4f &color,
const float weight)
void ColorGeometry4fMixer::mix_in(const int64_t index,
const ColorGeometry4f &color,
const float weight)
{
BLI_assert(weight >= 0.0f);
ColorGeometry4f &output_color = buffer_[index];
@ -26,7 +26,7 @@ void ColorGeometryMixer::mix_in(const int64_t index,
total_weights_[index] += weight;
}
void ColorGeometryMixer::finalize()
void ColorGeometry4fMixer::finalize()
{
for (const int64_t i : buffer_.index_range()) {
const float weight = total_weights_[i];
@ -44,4 +44,43 @@ void ColorGeometryMixer::finalize()
}
}
ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
ColorGeometry4b default_color)
: buffer_(buffer),
default_color_(default_color),
total_weights_(buffer.size(), 0.0f),
accumulation_buffer_(buffer.size(), float4(0, 0, 0, 0))
{
}
void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, float weight)
{
BLI_assert(weight >= 0.0f);
float4 &accum_value = accumulation_buffer_[index];
accum_value[0] += color.r * weight;
accum_value[1] += color.g * weight;
accum_value[2] += color.b * weight;
accum_value[3] += color.a * weight;
total_weights_[index] += weight;
}
void ColorGeometry4bMixer::finalize()
{
for (const int64_t i : buffer_.index_range()) {
const float weight = total_weights_[i];
const float4 &accum_value = accumulation_buffer_[i];
ColorGeometry4b &output_color = buffer_[i];
if (weight > 0.0f) {
const float weight_inv = 1.0f / weight;
output_color.r = accum_value[0] * weight_inv;
output_color.g = accum_value[1] * weight_inv;
output_color.b = accum_value[2] * weight_inv;
output_color.a = accum_value[3] * weight_inv;
}
else {
output_color = default_color_;
}
}
}
} // namespace blender::attribute_math

View File

@ -5399,6 +5399,8 @@ const blender::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
return &CPPType::get<bool>();
case CD_PROP_INT8:
return &CPPType::get<int8_t>();
case CD_PROP_BYTE_COLOR:
return &CPPType::get<ColorGeometry4b>();
default:
return nullptr;
}
@ -5428,6 +5430,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
if (type.is<int8_t>()) {
return CD_PROP_INT8;
}
if (type.is<ColorGeometry4b>()) {
return CD_PROP_BYTE_COLOR;
}
return static_cast<CustomDataType>(-1);
}

View File

@ -903,22 +903,6 @@ static void set_loop_uv(MLoopUV &uv, float2 co)
copy_v2_v2(uv.uv, co);
}
static ColorGeometry4f get_loop_color(const MLoopCol &col)
{
ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a);
ColorGeometry4f linear_color = encoded_color.decode();
return linear_color;
}
static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color)
{
ColorGeometry4b encoded_color = linear_color.encode();
col.r = encoded_color.r;
col.g = encoded_color.g;
col.b = encoded_color.b;
col.a = encoded_color.a;
}
static float get_crease(const MEdge &edge)
{
return edge.crease / 255.0f;
@ -1293,14 +1277,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
static NamedLegacyCustomDataProvider vertex_colors(
ATTR_DOMAIN_CORNER,
CD_PROP_COLOR,
CD_PROP_BYTE_COLOR,
corner_access,
make_derived_read_attribute<MLoopCol, ColorGeometry4f, get_loop_color>,
make_derived_write_attribute<MLoopCol, ColorGeometry4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
@ -1310,7 +1286,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
return ComponentAttributeProviders(
{&position, &id, &material_index, &shade_smooth, &normal, &crease},
{&uvs,
&vertex_colors,
&corner_custom_data,
&vertex_groups,
&point_custom_data,

View File

@ -2,6 +2,8 @@
#include "BKE_type_conversions.hh"
#include "DNA_meshdata_types.h"
#include "FN_multi_function_builder.hh"
#include "BLI_color.hh"
@ -61,6 +63,10 @@ static ColorGeometry4f float_to_color(const float &a)
{
return ColorGeometry4f(a, a, a, 1.0f);
}
static ColorGeometry4b float_to_byte_color(const float &a)
{
return float_to_color(a).encode();
}
static float3 float2_to_float3(const float2 &a)
{
@ -86,6 +92,10 @@ static ColorGeometry4f float2_to_color(const float2 &a)
{
return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
}
static ColorGeometry4b float2_to_byte_color(const float2 &a)
{
return float2_to_color(a).encode();
}
static bool float3_to_bool(const float3 &a)
{
@ -111,6 +121,10 @@ static ColorGeometry4f float3_to_color(const float3 &a)
{
return ColorGeometry4f(a.x, a.y, a.z, 1.0f);
}
static ColorGeometry4b float3_to_byte_color(const float3 &a)
{
return float3_to_color(a).encode();
}
static bool int_to_bool(const int32_t &a)
{
@ -137,6 +151,10 @@ static ColorGeometry4f int_to_color(const int32_t &a)
{
return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
static ColorGeometry4b int_to_byte_color(const int32_t &a)
{
return int_to_color(a).encode();
}
static bool int8_to_bool(const int8_t &a)
{
@ -162,6 +180,10 @@ static ColorGeometry4f int8_to_color(const int8_t &a)
{
return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
static ColorGeometry4b int8_to_byte_color(const int8_t &a)
{
return int8_to_color(a).encode();
}
static float bool_to_float(const bool &a)
{
@ -187,6 +209,10 @@ static ColorGeometry4f bool_to_color(const bool &a)
{
return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f);
}
static ColorGeometry4b bool_to_byte_color(const bool &a)
{
return bool_to_color(a).encode();
}
static bool color_to_bool(const ColorGeometry4f &a)
{
@ -212,6 +238,39 @@ static float3 color_to_float3(const ColorGeometry4f &a)
{
return float3(a.r, a.g, a.b);
}
static ColorGeometry4b color_to_byte_color(const ColorGeometry4f &a)
{
return a.encode();
}
static bool byte_color_to_bool(const ColorGeometry4b &a)
{
return a.r > 0 || a.g > 0 || a.b > 0;
}
static float byte_color_to_float(const ColorGeometry4b &a)
{
return color_to_float(a.decode());
}
static int32_t byte_color_to_int(const ColorGeometry4b &a)
{
return color_to_int(a.decode());
}
static int8_t byte_color_to_int8(const ColorGeometry4b &a)
{
return color_to_int8(a.decode());
}
static float2 byte_color_to_float2(const ColorGeometry4b &a)
{
return color_to_float2(a.decode());
}
static float3 byte_color_to_float3(const ColorGeometry4b &a)
{
return color_to_float3(a.decode());
}
static ColorGeometry4f byte_color_to_color(const ColorGeometry4b &a)
{
return a.decode();
}
static DataTypeConversions create_implicit_conversions()
{
@ -223,6 +282,7 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<float, bool, float_to_bool>(conversions);
add_implicit_conversion<float, int8_t, float_to_int8>(conversions);
add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions);
add_implicit_conversion<float, ColorGeometry4b, float_to_byte_color>(conversions);
add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
add_implicit_conversion<float2, float, float2_to_float>(conversions);
@ -230,6 +290,7 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
add_implicit_conversion<float2, int8_t, float2_to_int8>(conversions);
add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions);
add_implicit_conversion<float2, ColorGeometry4b, float2_to_byte_color>(conversions);
add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
add_implicit_conversion<float3, int8_t, float3_to_int8>(conversions);
@ -237,6 +298,7 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions);
add_implicit_conversion<float3, ColorGeometry4b, float3_to_byte_color>(conversions);
add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
add_implicit_conversion<int32_t, int8_t, int_to_int8>(conversions);
@ -244,6 +306,7 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions);
add_implicit_conversion<int32_t, ColorGeometry4b, int_to_byte_color>(conversions);
add_implicit_conversion<int8_t, bool, int8_to_bool>(conversions);
add_implicit_conversion<int8_t, int32_t, int8_to_int>(conversions);
@ -251,6 +314,7 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<int8_t, float2, int8_to_float2>(conversions);
add_implicit_conversion<int8_t, float3, int8_to_float3>(conversions);
add_implicit_conversion<int8_t, ColorGeometry4f, int8_to_color>(conversions);
add_implicit_conversion<int8_t, ColorGeometry4b, int8_to_byte_color>(conversions);
add_implicit_conversion<bool, float, bool_to_float>(conversions);
add_implicit_conversion<bool, int8_t, bool_to_int8>(conversions);
@ -258,6 +322,7 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
add_implicit_conversion<bool, ColorGeometry4b, bool_to_byte_color>(conversions);
add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions);
add_implicit_conversion<ColorGeometry4f, int8_t, color_to_int8>(conversions);
@ -265,6 +330,15 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);
add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions);
add_implicit_conversion<ColorGeometry4f, ColorGeometry4b, color_to_byte_color>(conversions);
add_implicit_conversion<ColorGeometry4b, bool, byte_color_to_bool>(conversions);
add_implicit_conversion<ColorGeometry4b, int8_t, byte_color_to_int8>(conversions);
add_implicit_conversion<ColorGeometry4b, float, byte_color_to_float>(conversions);
add_implicit_conversion<ColorGeometry4b, int32_t, byte_color_to_int>(conversions);
add_implicit_conversion<ColorGeometry4b, float2, byte_color_to_float2>(conversions);
add_implicit_conversion<ColorGeometry4b, float3, byte_color_to_float3>(conversions);
add_implicit_conversion<ColorGeometry4b, ColorGeometry4f, byte_color_to_color>(conversions);
return conversions;
}
@ -411,4 +485,19 @@ GVMutableArray DataTypeConversions::try_convert(GVMutableArray varray,
std::move(varray), to_type, *this);
}
fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_type) const
{
const CPPType &from_type = field.cpp_type();
if (from_type == to_type) {
return field;
}
if (!this->is_convertible(from_type, to_type)) {
return {};
}
const fn::MultiFunction &fn =
*bke::get_implicit_type_conversions().get_conversion_multi_function(
fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type));
return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})};
}
} // namespace blender::bke

View File

@ -102,7 +102,10 @@ template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(cons
return a - std::floor(a);
}
template<typename T, typename FactorT, BLI_ENABLE_IF((is_math_float_type<FactorT>))>
template<typename T,
typename FactorT,
BLI_ENABLE_IF((std::is_arithmetic_v<T>)),
BLI_ENABLE_IF((is_math_float_type<FactorT>))>
inline T interpolate(const T &a, const T &b, const FactorT &t)
{
return a * (1 - t) + b * t;

View File

@ -15,9 +15,22 @@
namespace blender::math {
inline ColorGeometry4f interpolate(const ColorGeometry4f &a,
const ColorGeometry4f &b,
const float t)
template<eAlpha Alpha>
inline ColorSceneLinear4f<Alpha> interpolate(const ColorSceneLinear4f<Alpha> &a,
const ColorSceneLinear4f<Alpha> &b,
const float t)
{
return {math::interpolate(a.r, b.r, t),
math::interpolate(a.g, b.g, t),
math::interpolate(a.b, b.b, t),
math::interpolate(a.a, b.a, t)};
}
template<eAlpha Alpha>
inline ColorSceneLinearByteEncoded4b<Alpha> interpolate(
const ColorSceneLinearByteEncoded4b<Alpha> &a,
const ColorSceneLinearByteEncoded4b<Alpha> &b,
const float t)
{
return {math::interpolate(a.r, b.r, t),
math::interpolate(a.g, b.g, t),

View File

@ -201,6 +201,14 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size>
}
}
template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))>
explicit vec_base(const U *ptr)
{
for (int i = 0; i < Size; i++) {
(*this)[i] = ptr[i];
}
}
vec_base(const T (*ptr)[Size]) : vec_base(static_cast<const T *>(ptr[0]))
{
}

View File

@ -241,7 +241,6 @@ enum class ConvertAttributeMode {
Generic,
UVMap,
VertexGroup,
VertexColor,
};
static bool geometry_attribute_convert_poll(bContext *C)
@ -288,7 +287,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
const CustomDataType dst_type = static_cast<CustomDataType>(
RNA_enum_get(op->ptr, "data_type"));
if (ELEM(dst_type, CD_PROP_STRING, CD_PROP_BYTE_COLOR)) {
if (ELEM(dst_type, CD_PROP_STRING)) {
BKE_report(op->reports, RPT_ERROR, "Cannot convert to the selected type");
return OPERATOR_CANCELLED;
}
@ -314,20 +313,6 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str());
break;
}
case ConvertAttributeMode::VertexColor: {
MLoopCol *dst_colors = static_cast<MLoopCol *>(
MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopCol), __func__));
VArray<ColorGeometry4f> src_varray = mesh_component.attribute_get_for_read<ColorGeometry4f>(
name, ATTR_DOMAIN_CORNER, ColorGeometry4f{0.0f, 0.0f, 0.0f, 1.0f});
for (const int i : IndexRange(mesh->totloop)) {
ColorGeometry4b encoded_color = src_varray[i].encode();
copy_v4_v4_uchar(&dst_colors[i].r, &encoded_color.r);
}
mesh_component.attribute_try_delete(name);
CustomData_add_layer_named(
&mesh->ldata, CD_PROP_BYTE_COLOR, CD_ASSIGN, dst_colors, mesh->totloop, name.c_str());
break;
}
case ConvertAttributeMode::VertexGroup: {
Array<float> src_weights(mesh->totvert);
VArray<float> src_varray = mesh_component.attribute_get_for_read<float>(
@ -550,7 +535,6 @@ void GEOMETRY_OT_attribute_convert(wmOperatorType *ot)
{int(ConvertAttributeMode::Generic), "GENERIC", 0, "Generic", ""},
{int(ConvertAttributeMode::UVMap), "UV_MAP", 0, "UV Map", ""},
{int(ConvertAttributeMode::VertexGroup), "VERTEX_GROUP", 0, "Vertex Group", ""},
{int(ConvertAttributeMode::VertexColor), "VERTEX_COLOR", 0, "Color Attribute", ""},
{0, nullptr, 0, nullptr, nullptr},
};

View File

@ -300,6 +300,7 @@ static float get_default_column_width(const ColumnValues &values)
return values.default_width;
}
static const float float_width = 3;
static const float int_width = 2;
switch (values.type()) {
case SPREADSHEET_VALUE_TYPE_BOOL:
return 2.0f;
@ -317,6 +318,8 @@ static float get_default_column_width(const ColumnValues &values)
return 8.0f;
case SPREADSHEET_VALUE_TYPE_STRING:
return 5.0f;
case SPREADSHEET_VALUE_TYPE_BYTE_COLOR:
return 4.0f * int_width;
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
return 2.0f;
}

View File

@ -44,6 +44,9 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type)
if (type.is<InstanceReference>()) {
return SPREADSHEET_VALUE_TYPE_INSTANCES;
}
if (type.is<ColorGeometry4b>()) {
return SPREADSHEET_VALUE_TYPE_BYTE_COLOR;
}
return SPREADSHEET_VALUE_TYPE_UNKNOWN;
}

View File

@ -191,6 +191,10 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
const ColorGeometry4f value = data.get<ColorGeometry4f>(real_index);
this->draw_float_vector(params, Span(&value.r, 4));
}
else if (data.type().is<ColorGeometry4b>()) {
const ColorGeometry4b value = data.get<ColorGeometry4b>(real_index);
this->draw_int_vector(params, {value.r, value.g, value.b, value.a});
}
else if (data.type().is<InstanceReference>()) {
const InstanceReference value = data.get<InstanceReference>(real_index);
switch (value.type()) {
@ -304,6 +308,34 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
}
}
void draw_int_vector(const CellDrawParams &params, const Span<int> values) const
{
BLI_assert(!values.is_empty());
const float segment_width = (float)params.width / values.size();
for (const int i : values.index_range()) {
const int value = values[i];
const std::string value_str = std::to_string(value);
uiBut *but = uiDefIconTextBut(params.block,
UI_BTYPE_LABEL,
0,
ICON_NONE,
value_str.c_str(),
params.xmin + i * segment_width,
params.ymin,
segment_width,
params.height,
nullptr,
0,
0,
0,
0,
nullptr);
/* Right-align Ints. */
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
}
}
int column_width(int column_index) const final
{
return spreadsheet_layout_.columns[column_index].width;

View File

@ -106,10 +106,10 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
const float2 value = row_filter.value_float2;
switch (row_filter.operation) {
case SPREADSHEET_ROW_FILTER_EQUAL: {
const float threshold_sq = row_filter.threshold;
const float threshold_sq = pow2f(row_filter.threshold);
apply_filter_operation(
column_data.typed<float2>(),
[&](const float2 cell) { return math::distance_squared(cell, value) > threshold_sq; },
[&](const float2 cell) { return math::distance_squared(cell, value) <= threshold_sq; },
prev_mask,
new_indices);
break;
@ -136,10 +136,10 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
const float3 value = row_filter.value_float3;
switch (row_filter.operation) {
case SPREADSHEET_ROW_FILTER_EQUAL: {
const float threshold_sq = row_filter.threshold;
const float threshold_sq = pow2f(row_filter.threshold);
apply_filter_operation(
column_data.typed<float3>(),
[&](const float3 cell) { return math::distance_squared(cell, value) > threshold_sq; },
[&](const float3 cell) { return math::distance_squared(cell, value) <= threshold_sq; },
prev_mask,
new_indices);
break;
@ -168,19 +168,25 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
}
else if (column_data.type().is<ColorGeometry4f>()) {
const ColorGeometry4f value = row_filter.value_color;
switch (row_filter.operation) {
case SPREADSHEET_ROW_FILTER_EQUAL: {
const float threshold_sq = row_filter.threshold;
apply_filter_operation(
column_data.typed<ColorGeometry4f>(),
[&](const ColorGeometry4f cell) {
return len_squared_v4v4(cell, value) > threshold_sq;
},
prev_mask,
new_indices);
break;
}
}
const float threshold_sq = pow2f(row_filter.threshold);
apply_filter_operation(
column_data.typed<ColorGeometry4f>(),
[&](const ColorGeometry4f cell) { return len_squared_v4v4(cell, value) <= threshold_sq; },
prev_mask,
new_indices);
}
else if (column_data.type().is<ColorGeometry4b>()) {
const ColorGeometry4b value = row_filter.value_byte_color;
const float4 value_floats = {(float)value.r, (float)value.g, (float)value.b, (float)value.a};
const float threshold_sq = pow2f(row_filter.threshold);
apply_filter_operation(
column_data.typed<ColorGeometry4b>(),
[&](const ColorGeometry4b cell) {
const float4 cell_floats = {(float)cell.r, (float)cell.g, (float)cell.b, (float)cell.a};
return len_squared_v4v4(value_floats, cell_floats) <= threshold_sq;
},
prev_mask,
new_indices);
}
else if (column_data.type().is<InstanceReference>()) {
const StringRef value = row_filter.value_string;

View File

@ -42,7 +42,8 @@ static std::string operation_string(const eSpreadsheetColumnValueType data_type,
if (ELEM(data_type,
SPREADSHEET_VALUE_TYPE_BOOL,
SPREADSHEET_VALUE_TYPE_INSTANCES,
SPREADSHEET_VALUE_TYPE_COLOR)) {
SPREADSHEET_VALUE_TYPE_COLOR,
SPREADSHEET_VALUE_TYPE_BYTE_COLOR)) {
return "=";
}
@ -101,6 +102,14 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter,
}
case SPREADSHEET_VALUE_TYPE_STRING:
return row_filter.value_string;
case SPREADSHEET_VALUE_TYPE_BYTE_COLOR: {
std::ostringstream result;
result.precision(3);
result << std::fixed << "(" << (int)row_filter.value_byte_color[0] << ", "
<< (int)row_filter.value_byte_color[1] << ", " << (int)row_filter.value_byte_color[2]
<< ", " << (int)row_filter.value_byte_color[3] << ")";
return result.str();
}
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
return "";
}
@ -229,6 +238,10 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
case SPREADSHEET_VALUE_TYPE_STRING:
uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE);
break;
case SPREADSHEET_VALUE_TYPE_BYTE_COLOR:
uiItemR(layout, filter_ptr, "value_byte_color", 0, IFACE_("Value"), ICON_NONE);
uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
break;
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
uiItemL(layout, IFACE_("Unknown column type"), ICON_ERROR);
break;

View File

@ -38,7 +38,7 @@
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
#include "FN_multi_function_builder.hh"
#include "FN_multi_function.hh"
namespace blender::fn {

View File

@ -11,6 +11,7 @@ MAKE_FIELD_CPP_TYPE(FloatField, float);
MAKE_FIELD_CPP_TYPE(Float2Field, blender::float2);
MAKE_FIELD_CPP_TYPE(Float3Field, blender::float3);
MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f);
MAKE_FIELD_CPP_TYPE(ColorGeometry4bField, blender::ColorGeometry4b);
MAKE_FIELD_CPP_TYPE(BoolField, bool);
MAKE_FIELD_CPP_TYPE(Int8Field, int8_t);
MAKE_FIELD_CPP_TYPE(Int32Field, int32_t);

View File

@ -8,6 +8,7 @@
#include "BLI_vector_set.hh"
#include "FN_field.hh"
#include "FN_multi_function_builder.hh"
#include "FN_multi_function_procedure.hh"
#include "FN_multi_function_procedure_builder.hh"
#include "FN_multi_function_procedure_executor.hh"

View File

@ -1968,8 +1968,7 @@ typedef struct SpreadsheetRowFilter {
float value_float2[2];
float value_float3[3];
float value_color[4];
char _pad1[4];
uint8_t value_byte_color[4];
} SpreadsheetRowFilter;
typedef enum eSpaceSpreadsheet_RowFilterFlag {
@ -2006,6 +2005,7 @@ typedef enum eSpreadsheetColumnValueType {
SPREADSHEET_VALUE_TYPE_COLOR = 5,
SPREADSHEET_VALUE_TYPE_INSTANCES = 6,
SPREADSHEET_VALUE_TYPE_STRING = 7,
SPREADSHEET_VALUE_TYPE_BYTE_COLOR = 8,
} eSpreadsheetColumnValueType;
/**

View File

@ -2144,8 +2144,13 @@ static void rna_GeometryNodeCompare_data_type_update(Main *bmain, Scene *scene,
static bool generic_attribute_type_supported(const EnumPropertyItem *item)
{
return ELEM(
item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32);
return ELEM(item->value,
CD_PROP_FLOAT,
CD_PROP_FLOAT3,
CD_PROP_COLOR,
CD_PROP_BOOL,
CD_PROP_INT32,
CD_PROP_BYTE_COLOR);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
@ -2156,6 +2161,18 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_itemf(bContext
return itemf_function_check(rna_enum_attribute_type_items, generic_attribute_type_supported);
}
static bool generic_attribute_type_supported_with_socket(const EnumPropertyItem *item)
{
return generic_attribute_type_supported(item) && !ELEM(item->value, CD_PROP_BYTE_COLOR);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_with_socket_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
return itemf_function_check(rna_enum_attribute_type_items,
generic_attribute_type_supported_with_socket);
}
static bool transfer_attribute_type_supported(const EnumPropertyItem *item)
{
return ELEM(
@ -10219,7 +10236,8 @@ static void def_geo_raycast(StructRNA *srna)
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf");
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@ -10271,7 +10289,8 @@ static void def_geo_input_named_attribute(StructRNA *srna)
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf");
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "The data type used to read the attribute values");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@ -10285,7 +10304,8 @@ static void def_geo_attribute_capture(StructRNA *srna)
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf");
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@ -10512,7 +10532,8 @@ static void def_geo_viewer(StructRNA *srna)
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf");
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@ -10542,7 +10563,8 @@ static void def_geo_field_at_index(StructRNA *srna)
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom2");
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf");
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf");
RNA_def_property_ui_text(prop, "Data Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}

View File

@ -7752,6 +7752,12 @@ static void rna_def_spreadsheet_row_filter(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Color Value", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
prop = RNA_def_property(srna, "value_byte_color", PROP_INT, PROP_NONE);
RNA_def_property_array(prop, 4);
RNA_def_property_range(prop, 0, 255);
RNA_def_property_ui_text(prop, "Byte Color Value", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
prop = RNA_def_property(srna, "value_string", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Text Value", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);

View File

@ -1635,10 +1635,8 @@ class GeometryNodesEvaluator {
if (conversions_.is_convertible(from_base_type, to_base_type)) {
if (from_field_type->is_field(from_value)) {
const GField &from_field = *from_field_type->get_field_ptr(from_value);
const MultiFunction &fn = *conversions_.get_conversion_multi_function(
MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field});
to_field_type->construct_from_field(to_value, GField(std::move(operation), 0));
to_field_type->construct_from_field(to_value,
conversions_.try_convert(from_field, to_base_type));
}
else {
to_field_type->default_construct(to_value);

View File

@ -5,6 +5,8 @@
#include "NOD_socket_search_link.hh"
#include "BKE_type_conversions.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_store_named_attribute_cc {
@ -55,7 +57,8 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3);
nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT);
nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR);
nodeSetSocketAvailability(
ntree, socket_color4f, ELEM(data_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR));
nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL);
nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32);
}
@ -149,6 +152,12 @@ static void node_geo_exec(GeoNodeExecParams params)
case CD_PROP_COLOR:
field = params.get_input<Field<ColorGeometry4f>>("Value_Color");
break;
case CD_PROP_BYTE_COLOR: {
field = params.get_input<Field<ColorGeometry4f>>("Value_Color");
field = bke::get_implicit_type_conversions().try_convert(field,
CPPType::get<ColorGeometry4b>());
break;
}
case CD_PROP_BOOL:
field = params.get_input<Field<bool>>("Value_Bool");
break;