Attributes: Infrastructure for generic 8-bit integer data type

This commit adds infrastructure for 8 bit signed integer attributes.
This can be useful given the discussion in T94193, where we want to
store spline type, Bezier handle type, and other small enums as
attributes.

This is only exposed in the interface in the attribute lists, so it
shouldn't be an option in geometry nodes, at least for now.
I expect that this type won't be used directly very often, it
should mostly be cast to an enum type. However, with support
for 8 bit integers, it also makes sense to add things like mixing
implementations for consistency.

Differential Revision: https://developer.blender.org/D13721
This commit is contained in:
Hans Goudey 2022-02-04 10:29:11 -06:00
parent b4563ab2df
commit e7912dfa19
Notes: blender-bot 2023-02-14 09:02:40 +01:00
Referenced by commit 1a8db5b717, Fix: Only possible to create one 8 bit integer attribute
Referenced by issue #95610, Writing to an attribute that is also a vertexgroup results in black when used in a shader
14 changed files with 225 additions and 14 deletions

View File

@ -50,6 +50,9 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f
case CD_PROP_BOOL:
func(bool());
break;
case CD_PROP_INT8:
func(int8_t());
break;
case CD_PROP_COLOR:
func(ColorGeometry4f());
break;
@ -77,6 +80,9 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func
else if (cpp_type.is<bool>()) {
func(bool());
}
else if (cpp_type.is<int8_t>()) {
func(int8_t());
}
else if (cpp_type.is<ColorGeometry4f>()) {
func(ColorGeometry4f());
}
@ -93,6 +99,12 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func
template<typename T> T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2);
template<>
inline int8_t mix3(const float3 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2)
{
return static_cast<int8_t>(weights.x * v0 + weights.y * v1 + weights.z * v2);
}
template<> inline bool mix3(const float3 &weights, const bool &v0, const bool &v1, const bool &v2)
{
return (weights.x * v0 + weights.y * v1 + weights.z * v2) >= 0.5f;
@ -147,6 +159,11 @@ template<> inline bool mix2(const float factor, const bool &a, const bool &b)
return ((1.0f - factor) * a + factor * b) >= 0.5f;
}
template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b)
{
return static_cast<int8_t>((1.0f - factor) * a + factor * b);
}
template<> inline int mix2(const float factor, const int &a, const int &b)
{
return static_cast<int>((1.0f - factor) * a + factor * b);
@ -364,6 +381,15 @@ template<> struct DefaultMixerStruct<bool> {
using type = SimpleMixerWithAccumulationType<bool, float, float_to_bool>;
};
template<> struct DefaultMixerStruct<int8_t> {
static int8_t float_to_int8_t(const float &value)
{
return static_cast<int8_t>(value);
}
/* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */
using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>;
};
template<typename T> struct DefaultPropatationMixerStruct {
/* Use void by default. This can be checked for in `if constexpr` statements. */
using type = typename DefaultMixerStruct<T>::type;

View File

@ -83,6 +83,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
return &CPPType::get<ColorGeometry4f>();
case CD_PROP_BOOL:
return &CPPType::get<bool>();
case CD_PROP_INT8:
return &CPPType::get<int8_t>();
default:
return nullptr;
}
@ -109,6 +111,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<bool>()) {
return CD_PROP_BOOL;
}
if (type.is<int8_t>()) {
return CD_PROP_INT8;
}
return static_cast<CustomDataType>(-1);
}
@ -117,16 +122,18 @@ static int attribute_data_type_complexity(const CustomDataType data_type)
switch (data_type) {
case CD_PROP_BOOL:
return 0;
case CD_PROP_INT32:
case CD_PROP_INT8:
return 1;
case CD_PROP_FLOAT:
case CD_PROP_INT32:
return 2;
case CD_PROP_FLOAT2:
case CD_PROP_FLOAT:
return 3;
case CD_PROP_FLOAT3:
case CD_PROP_FLOAT2:
return 4;
case CD_PROP_COLOR:
case CD_PROP_FLOAT3:
return 5;
case CD_PROP_COLOR:
return 6;
#if 0 /* These attribute types are not supported yet. */
case CD_MLOOPCOL:
return 3;

View File

@ -140,7 +140,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
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_COLOR | CD_MASK_PROP_BOOL |
CD_MASK_PROP_INT8;
const AttributeDomain domain_;
const CustomDataAccessInfo custom_data_access_;

View File

@ -1793,8 +1793,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 44: CD_RADIUS */
{sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_HAIRCURVE */ /* UNUSED */
{-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_PROP_INT8 */
{sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 46: CD_HAIRMAPPING */ /* UNUSED */
{-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 47: CD_PROP_COLOR */
@ -1914,7 +1914,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDCustomLoopNormal",
"CDSculptFaceGroups",
/* 43-46 */ "CDHairPoint",
"CDHairCurve",
"CDPropInt8",
"CDHairMapping",
"CDPoint",
"CDPropCol",

View File

@ -685,6 +685,17 @@ static int customdata_compare(
}
break;
}
case CD_PROP_INT8: {
const int8_t *l1_data = (int8_t *)l1->data;
const int8_t *l2_data = (int8_t *)l2->data;
for (int i = 0; i < total_length; i++) {
if (l1_data[i] != l2_data[i]) {
return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
}
}
break;
}
case CD_PROP_BOOL: {
const bool *l1_data = (bool *)l1->data;
const bool *l2_data = (bool *)l2->data;

View File

@ -62,6 +62,11 @@ static bool float_to_bool(const float &a)
{
return a > 0.0f;
}
static int8_t float_to_int8(const float &a)
{
return std::clamp(
a, float(std::numeric_limits<int8_t>::min()), float(std::numeric_limits<int8_t>::max()));
}
static ColorGeometry4f float_to_color(const float &a)
{
return ColorGeometry4f(a, a, a, 1.0f);
@ -83,6 +88,10 @@ static bool float2_to_bool(const float2 &a)
{
return !is_zero_v2(a);
}
static int8_t float2_to_int8(const float2 &a)
{
return float_to_int8((a.x + a.y) / 2.0f);
}
static ColorGeometry4f float2_to_color(const float2 &a)
{
return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
@ -92,6 +101,10 @@ static bool float3_to_bool(const float3 &a)
{
return !is_zero_v3(a);
}
static int8_t float3_to_int8(const float3 &a)
{
return float_to_int8((a.x + a.y + a.z) / 3.0f);
}
static float float3_to_float(const float3 &a)
{
return (a.x + a.y + a.z) / 3.0f;
@ -113,6 +126,11 @@ static bool int_to_bool(const int32_t &a)
{
return a > 0;
}
static int8_t int_to_int8(const int32_t &a)
{
return std::clamp(
a, int(std::numeric_limits<int8_t>::min()), int(std::numeric_limits<int8_t>::max()));
}
static float int_to_float(const int32_t &a)
{
return (float)a;
@ -130,10 +148,39 @@ static ColorGeometry4f int_to_color(const int32_t &a)
return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
static bool int8_to_bool(const int8_t &a)
{
return a > 0;
}
static int int8_to_int(const int8_t &a)
{
return static_cast<int>(a);
}
static float int8_to_float(const int8_t &a)
{
return (float)a;
}
static float2 int8_to_float2(const int8_t &a)
{
return float2((float)a);
}
static float3 int8_to_float3(const int8_t &a)
{
return float3((float)a);
}
static ColorGeometry4f int8_to_color(const int8_t &a)
{
return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
static float bool_to_float(const bool &a)
{
return (bool)a;
}
static int8_t bool_to_int8(const bool &a)
{
return static_cast<int8_t>(a);
}
static int32_t bool_to_int(const bool &a)
{
return (int32_t)a;
@ -163,6 +210,10 @@ static int32_t color_to_int(const ColorGeometry4f &a)
{
return (int)rgb_to_grayscale(a);
}
static int8_t color_to_int8(const ColorGeometry4f &a)
{
return int_to_int8(color_to_int(a));
}
static float2 color_to_float2(const ColorGeometry4f &a)
{
return float2(a.r, a.g);
@ -180,33 +231,46 @@ static DataTypeConversions create_implicit_conversions()
add_implicit_conversion<float, float3, float_to_float3>(conversions);
add_implicit_conversion<float, int32_t, float_to_int>(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<float2, float3, float2_to_float3>(conversions);
add_implicit_conversion<float2, float, float2_to_float>(conversions);
add_implicit_conversion<float2, int32_t, float2_to_int>(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<float3, bool, float3_to_bool>(conversions);
add_implicit_conversion<float3, int8_t, float3_to_int8>(conversions);
add_implicit_conversion<float3, float, float3_to_float>(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<int32_t, bool, int_to_bool>(conversions);
add_implicit_conversion<int32_t, int8_t, int_to_int8>(conversions);
add_implicit_conversion<int32_t, float, int_to_float>(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<int8_t, bool, int8_to_bool>(conversions);
add_implicit_conversion<int8_t, int32_t, int8_to_int>(conversions);
add_implicit_conversion<int8_t, float, int8_to_float>(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<bool, float, bool_to_float>(conversions);
add_implicit_conversion<bool, int8_t, bool_to_int8>(conversions);
add_implicit_conversion<bool, int32_t, bool_to_int>(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<ColorGeometry4f, bool, color_to_bool>(conversions);
add_implicit_conversion<ColorGeometry4f, int8_t, color_to_int8>(conversions);
add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions);
add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);

View File

@ -505,8 +505,9 @@ static bool custom_data_match_attribute(const CustomData *custom_data,
int *r_layer_index,
int *r_type)
{
const int possible_attribute_types[6] = {
const int possible_attribute_types[7] = {
CD_PROP_BOOL,
CD_PROP_INT8,
CD_PROP_INT32,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
@ -655,6 +656,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
break;
}
case CD_PROP_BOOL:
case CD_PROP_INT8:
case CD_PROP_INT32:
case CD_PROP_FLOAT:
case CD_PROP_FLOAT2:

View File

@ -99,6 +99,7 @@ static uint gpu_component_size_for_attribute_type(CustomDataType type)
{
switch (type) {
case CD_PROP_BOOL:
case CD_PROP_INT8:
case CD_PROP_INT32:
case CD_PROP_FLOAT: {
/* TODO(@kevindietrich): should be 1 when scalar attributes conversion is handled by us. See
@ -326,6 +327,10 @@ static void extract_attr_init(const MeshRenderData *mr,
extract_attr_generic<bool, float3>(mr, vbo, request);
break;
}
case CD_PROP_INT8: {
extract_attr_generic<int8_t, float3>(mr, vbo, request);
break;
}
case CD_PROP_INT32: {
extract_attr_generic<int32_t, float3>(mr, vbo, request);
break;
@ -378,6 +383,10 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
extract_attr_generic<bool, float3>(mr, src_data, request);
break;
}
case CD_PROP_INT8: {
extract_attr_generic<int8_t, float3>(mr, src_data, request);
break;
}
case CD_PROP_INT32: {
extract_attr_generic<int32_t, float3>(mr, src_data, request);
break;

View File

@ -127,6 +127,28 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
}
if (data.type().is<int8_t>()) {
const int8_t value = data.get<int8_t>(real_index);
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,
params.ymin,
params.width,
params.height,
nullptr,
0,
0,
0,
0,
nullptr);
/* Right-align Integers. */
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
}
else if (data.type().is<float>()) {
const float value = data.get<float>(real_index);
std::stringstream ss;

View File

@ -31,6 +31,7 @@ MAKE_CPP_TYPE(float3, blender::float3, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(float4x4, blender::float4x4, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(int32, int32_t, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(int8, int8_t, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(uint32, uint32_t, CPPTypeFlags::BasicType)
MAKE_CPP_TYPE(uint8, uint8_t, CPPTypeFlags::BasicType)
@ -44,6 +45,7 @@ MAKE_FIELD_CPP_TYPE(Float2Field, float2);
MAKE_FIELD_CPP_TYPE(Float3Field, float3);
MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f);
MAKE_FIELD_CPP_TYPE(BoolField, bool);
MAKE_FIELD_CPP_TYPE(Int8Field, int8_t);
MAKE_FIELD_CPP_TYPE(Int32Field, int32_t);
MAKE_FIELD_CPP_TYPE(StringField, std::string);

View File

@ -160,9 +160,9 @@ typedef enum CustomDataType {
CD_CUSTOMLOOPNORMAL = 41,
CD_SCULPT_FACE_SETS = 42,
/* CD_LOCATION = 43, */ /* UNUSED */
/* CD_RADIUS = 44, */ /* UNUSED */
/* CD_HAIRCURVE = 45, */ /* UNUSED, can be reused. */
/* CD_LOCATION = 43, */ /* UNUSED */
/* CD_RADIUS = 44, */ /* UNUSED */
CD_PROP_INT8 = 45,
/* CD_HAIRMAPPING = 46, */ /* UNUSED, can be reused. */
CD_PROP_COLOR = 47,
@ -223,6 +223,7 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3)
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
#define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8)
#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH)
@ -235,7 +236,8 @@ typedef enum CustomDataType {
/* All generic attributes. */
#define CD_MASK_PROP_ALL \
(CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | \
CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL)
CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL | \
CD_MASK_PROP_INT8)
typedef struct CustomData_MeshMasks {
uint64_t vmask;

View File

@ -265,6 +265,9 @@ typedef struct MStringProperty {
typedef struct MBoolProperty {
uint8_t b;
} MBoolProperty;
typedef struct MInt8Property {
int8_t i;
} MInt8Property;
/** \} */

View File

@ -104,6 +104,8 @@ extern StructRNA RNA_BuildGpencilModifier;
extern StructRNA RNA_BuildModifier;
extern StructRNA RNA_ByteColorAttribute;
extern StructRNA RNA_ByteColorAttributeValue;
extern StructRNA RNA_ByteIntAttribute;
extern StructRNA RNA_ByteIntAttributeValue;
extern StructRNA RNA_CacheFile;
extern StructRNA RNA_CacheFileLayer;
extern StructRNA RNA_Camera;

View File

@ -46,6 +46,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{CD_PROP_STRING, "STRING", 0, "String", "Text string"},
{CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
{CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"},
{0, NULL, 0, NULL, NULL},
};
@ -59,6 +60,7 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
{CD_PROP_STRING, "STRING", 0, "String", "Text string"},
{CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
{CD_PROP_INT8, "INT8", 0, "8-Bit Integer", "Smaller integer with a range from -128 to 127"},
{0, NULL, 0, NULL, NULL},
};
@ -133,6 +135,8 @@ static StructRNA *srna_by_custom_data_layer_type(const CustomDataType type)
return &RNA_BoolAttribute;
case CD_PROP_FLOAT2:
return &RNA_Float2Attribute;
case CD_PROP_INT8:
return &RNA_ByteIntAttribute;
default:
return NULL;
}
@ -253,6 +257,9 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN
case CD_PROP_FLOAT2:
struct_size = sizeof(float[2]);
break;
case CD_PROP_INT8:
struct_size = sizeof(int8_t);
break;
default:
struct_size = 0;
length = 0;
@ -294,6 +301,28 @@ static void rna_ByteColorAttributeValue_color_set(PointerRNA *ptr, const float *
linearrgb_to_srgb_uchar4(&mlcol->r, values);
}
/* Int8 Attribute. */
static int rna_ByteIntAttributeValue_get(PointerRNA *ptr)
{
int8_t *value = (int8_t *)ptr->data;
return (int)(*value);
}
static void rna_ByteIntAttributeValue_set(PointerRNA *ptr, const int new_value)
{
int8_t *value = (int8_t *)ptr->data;
if (new_value > INT8_MAX) {
*value = INT8_MAX;
}
else if (new_value < INT8_MIN) {
*value = INT8_MIN;
}
else {
*value = (int8_t)new_value;
}
}
/* Attribute Group */
static PointerRNA rna_AttributeGroup_new(
@ -648,6 +677,36 @@ static void rna_def_attribute_bool(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "b", 0x01);
}
static void rna_def_attribute_int8(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "ByteIntAttribute", "Attribute");
RNA_def_struct_sdna(srna, "CustomDataLayer");
RNA_def_struct_ui_text(srna, "8-bit Int Attribute", "8-bit int geometry attribute");
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "ByteIntAttributeValue");
RNA_def_property_collection_funcs(prop,
"rna_Attribute_data_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Attribute_data_length",
NULL,
NULL,
NULL);
srna = RNA_def_struct(brna, "ByteIntAttributeValue", NULL);
RNA_def_struct_sdna(srna, "MInt8Property");
RNA_def_struct_ui_text(
srna, "8-bit Integer Attribute Value", "8-bit value in geometry attribute");
prop = RNA_def_property(srna, "value", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs(
prop, "rna_ByteIntAttributeValue_get", "rna_ByteIntAttributeValue_set", NULL);
}
static void rna_def_attribute_float2(BlenderRNA *brna)
{
StructRNA *srna;
@ -723,6 +782,7 @@ static void rna_def_attribute(BlenderRNA *brna)
rna_def_attribute_string(brna);
rna_def_attribute_bool(brna);
rna_def_attribute_float2(brna);
rna_def_attribute_int8(brna);
}
/* Mesh/PointCloud/Hair.attributes */