Attributes: Add quaternion rotation type

Add a quaternion attribute type that will be used in combination with
rotation sockets for geometry nodes to give a more intuitive experience
and better performance when using rotations.

The most interesting part is probably the interpolation, the rest is
the same as the last attribute type addition, 988f23cec3.
We need to interpolate multiple values with different weights.
Based on Sybren's suggestion, this uses the `expmap` methods from
4805a54525 for that.

This also refactors `SimpleMixerWithAccumulationType` to use a
function rather than a cast to convert to the accumulation type.

See #92967

Pull Request: https://projects.blender.org/blender/blender/pulls/108678
This commit is contained in:
Hans Goudey
2023-06-12 15:49:50 +02:00
committed by Hans Goudey
parent ccbab842b7
commit 1e4b80fed9
16 changed files with 198 additions and 18 deletions

View File

@@ -9,7 +9,9 @@
#include "BLI_cpp_type.hh"
#include "BLI_generic_span.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_math_axis_angle.hh"
#include "BLI_math_color.hh"
#include "BLI_math_quaternion.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
@@ -31,7 +33,8 @@ inline void convert_to_static_type(const CPPType &cpp_type, const Func &func)
bool,
int8_t,
ColorGeometry4f,
ColorGeometry4b>([&](auto type_tag) {
ColorGeometry4b,
math::Quaternion>([&](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. */
@@ -400,7 +403,10 @@ class BooleanPropagationMixer {
* This mixer accumulates values in a type that is different from the one that is mixed.
* Some types cannot encode the floating point weights in their values (e.g. int and bool).
*/
template<typename T, typename AccumulationT, T (*ConvertToT)(const AccumulationT &value)>
template<typename T,
typename AccumulationT,
AccumulationT (*ValueToAccumulate)(const T &value),
T (*AccumulateToValue)(const AccumulationT &value)>
class SimpleMixerWithAccumulationType {
private:
struct Item {
@@ -432,7 +438,7 @@ class SimpleMixerWithAccumulationType {
void set(const int64_t index, const T &value, const float weight = 1.0f)
{
const AccumulationT converted_value = static_cast<AccumulationT>(value);
const AccumulationT converted_value = ValueToAccumulate(value);
Item &item = accumulation_buffer_[index];
item.value = converted_value * weight;
item.weight = weight;
@@ -440,7 +446,7 @@ class SimpleMixerWithAccumulationType {
void mix_in(const int64_t index, const T &value, const float weight = 1.0f)
{
const AccumulationT converted_value = static_cast<AccumulationT>(value);
const AccumulationT converted_value = ValueToAccumulate(value);
Item &item = accumulation_buffer_[index];
item.value += converted_value * weight;
item.weight += weight;
@@ -457,7 +463,7 @@ class SimpleMixerWithAccumulationType {
const Item &item = accumulation_buffer_[i];
if (item.weight > 0.0f) {
const float weight_inv = 1.0f / item.weight;
const T converted_value = ConvertToT(item.value * weight_inv);
const T converted_value = AccumulateToValue(item.value * weight_inv);
buffer_[i] = converted_value;
}
else {
@@ -532,40 +538,68 @@ template<> struct DefaultMixerStruct<ColorGeometry4b> {
using type = ColorGeometry4bMixer;
};
template<> struct DefaultMixerStruct<int> {
static double int_to_double(const int &value)
{
return double(value);
}
static int double_to_int(const double &value)
{
return int(std::round(value));
}
/* Store interpolated ints in a double temporarily, so that weights are handled correctly. It
* uses double instead of float so that it is accurate for all 32 bit integers. */
using type = SimpleMixerWithAccumulationType<int, double, double_to_int>;
using type = SimpleMixerWithAccumulationType<int, double, int_to_double, double_to_int>;
};
template<> struct DefaultMixerStruct<int2> {
static double2 int_to_double(const int2 &value)
{
return double2(value);
}
static int2 double_to_int(const double2 &value)
{
return int2(math::round(value));
}
/* Store interpolated ints in a double temporarily, so that weights are handled correctly. It
* uses double instead of float so that it is accurate for all 32 bit integers. */
using type = SimpleMixerWithAccumulationType<int2, double2, double_to_int>;
using type = SimpleMixerWithAccumulationType<int2, double2, int_to_double, double_to_int>;
};
template<> struct DefaultMixerStruct<bool> {
static float bool_to_float(const bool &value)
{
return value ? 1.0f : 0.0f;
}
static bool float_to_bool(const float &value)
{
return value >= 0.5f;
}
/* Store interpolated booleans in a float temporary.
* Otherwise information provided by weights is easily rounded away. */
using type = SimpleMixerWithAccumulationType<bool, float, float_to_bool>;
using type = SimpleMixerWithAccumulationType<bool, float, bool_to_float, float_to_bool>;
};
template<> struct DefaultMixerStruct<int8_t> {
static float int8_t_to_float(const int8_t &value)
{
return float(value);
}
static int8_t float_to_int8_t(const float &value)
{
return int8_t(std::round(value));
}
/* Store interpolated 8 bit integers in a float temporarily to increase accuracy. */
using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>;
using type = SimpleMixerWithAccumulationType<int8_t, float, int8_t_to_float, float_to_int8_t>;
};
template<> struct DefaultMixerStruct<math::Quaternion> {
static float3 quat_to_expmap(const math::Quaternion &value)
{
return value.expmap();
}
static math::Quaternion expmap_to_quat(const float3 &value)
{
return math::Quaternion::expmap(value);
}
using type =
SimpleMixerWithAccumulationType<math::Quaternion, float3, quat_to_expmap, expmap_to_quat>;
};
template<typename T> struct DefaultPropagationMixerStruct {

View File

@@ -107,11 +107,13 @@ static int attribute_data_type_complexity(const eCustomDataType data_type)
return 6;
case CD_PROP_BYTE_COLOR:
return 7;
case CD_PROP_COLOR:
case CD_PROP_QUATERNION:
return 8;
case CD_PROP_COLOR:
return 9;
#if 0 /* These attribute types are not supported yet. */
case CD_PROP_STRING:
return 9;
return 10;
#endif
default:
/* Only accept "generic" custom data types used by the attribute system. */

View File

@@ -3,11 +3,39 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BLI_math_quaternion.hh"
#include "BKE_attribute_math.hh"
namespace blender::bke::attribute_math {
template<>
math::Quaternion mix2(const float factor, const math::Quaternion &a, const math::Quaternion &b)
{
return math::interpolate(a, b, factor);
}
template<>
math::Quaternion mix3(const float3 &weights,
const math::Quaternion &v0,
const math::Quaternion &v1,
const math::Quaternion &v2)
{
const float3 expmap_mixed = mix3(weights, v0.expmap(), v1.expmap(), v2.expmap());
return math::Quaternion::expmap(expmap_mixed);
}
template<>
math::Quaternion mix4(const float4 &weights,
const math::Quaternion &v0,
const math::Quaternion &v1,
const math::Quaternion &v2,
const math::Quaternion &v3)
{
const float3 expmap_mixed = mix4(weights, v0.expmap(), v1.expmap(), v2.expmap(), v3.expmap());
return math::Quaternion::expmap(expmap_mixed);
}
ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color)
: ColorGeometry4fMixer(buffer, buffer.index_range(), default_color)

View File

@@ -24,6 +24,7 @@
#include "BLI_index_range.hh"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector.hh"
#include "BLI_mempool.h"
#include "BLI_path_util.h"
@@ -1933,6 +1934,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr},
/* 51: CD_HAIRLENGTH */
{sizeof(float), "float", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 52: CD_PROP_QUATERNION */
{sizeof(float[4]), "vec4f", 1, N_("Quaternion"), nullptr, nullptr, nullptr, nullptr, nullptr},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@@ -5354,6 +5357,8 @@ const blender::CPPType *custom_data_type_to_cpp_type(const eCustomDataType type)
return &CPPType::get<int8_t>();
case CD_PROP_BYTE_COLOR:
return &CPPType::get<ColorGeometry4b>();
case CD_PROP_QUATERNION:
return &CPPType::get<math::Quaternion>();
case CD_PROP_STRING:
return &CPPType::get<MStringProperty>();
default:
@@ -5390,6 +5395,9 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
if (type.is<ColorGeometry4b>()) {
return CD_PROP_BYTE_COLOR;
}
if (type.is<math::Quaternion>()) {
return CD_PROP_QUATERNION;
}
if (type.is<MStringProperty>()) {
return CD_PROP_STRING;
}

View File

@@ -661,6 +661,26 @@ static int customdata_compare(
}
break;
}
case CD_PROP_QUATERNION: {
const float(*l1_data)[4] = (float(*)[4])l1->data;
const float(*l2_data)[4] = (float(*)[4])l2->data;
for (int i = 0; i < total_length; i++) {
if (compare_threshold_relative(l1_data[i][0], l2_data[i][0], thresh)) {
return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
}
if (compare_threshold_relative(l1_data[i][1], l2_data[i][1], thresh)) {
return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
}
if (compare_threshold_relative(l1_data[i][2], l2_data[i][2], thresh)) {
return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
}
if (compare_threshold_relative(l1_data[i][3], l2_data[i][3], thresh)) {
return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
}
}
break;
}
case CD_PROP_INT32: {
const int *l1_data = (int *)l1->data;
const int *l2_data = (int *)l2->data;

View File

@@ -18,6 +18,7 @@
#include "BLI_endian_switch.h"
#include "BLI_fileops.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_quaternion_types.hh"
#include "BLI_path_util.h"
#include "RNA_access.h"
@@ -778,6 +779,10 @@ static std::shared_ptr<io::serialize::Value> serialize_primitive_value(
const ColorGeometry4f value = *static_cast<const ColorGeometry4f *>(value_ptr);
return serialize_float_array({&value.r, 4});
}
case CD_PROP_QUATERNION: {
const math::Quaternion value = *static_cast<const math::Quaternion *>(value_ptr);
return serialize_float_array({&value.x, 4});
}
default:
break;
}
@@ -966,6 +971,9 @@ template<typename T>
case CD_PROP_COLOR: {
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4});
}
case CD_PROP_QUATERNION: {
return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4});
}
default:
break;
}

View File

@@ -11,6 +11,7 @@
#include "BLI_index_mask.hh"
#include "BLI_math_base.hh"
#include "BLI_math_color.hh"
#include "BLI_math_quaternion.hh"
#include "BLI_math_vector.hh"
namespace blender::length_parameterize {

View File

@@ -6,6 +6,7 @@
#include "BLI_cpp_type_make.hh"
#include "BLI_cpp_types_make.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector_types.hh"
namespace blender {
@@ -65,6 +66,8 @@ BLI_CPP_TYPE_MAKE(uint64_t, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(blender::ColorGeometry4f, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(blender::ColorGeometry4b, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(blender::math::Quaternion, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(std::string, CPPTypeFlags::BasicType)
BLI_VECTOR_CPP_TYPE_MAKE(std::string)
@@ -94,6 +97,8 @@ void register_cpp_types()
BLI_CPP_TYPE_REGISTER(blender::ColorGeometry4f);
BLI_CPP_TYPE_REGISTER(blender::ColorGeometry4b);
BLI_CPP_TYPE_REGISTER(math::Quaternion);
BLI_CPP_TYPE_REGISTER(std::string);
BLI_VECTOR_CPP_TYPE_REGISTER(std::string);

View File

@@ -83,7 +83,7 @@ bool drw_custom_data_match_attribute(const CustomData *custom_data,
int *r_layer_index,
eCustomDataType *r_type)
{
const eCustomDataType possible_attribute_types[9] = {
const eCustomDataType possible_attribute_types[10] = {
CD_PROP_BOOL,
CD_PROP_INT8,
CD_PROP_INT32_2D,
@@ -92,6 +92,7 @@ bool drw_custom_data_match_attribute(const CustomData *custom_data,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
CD_PROP_COLOR,
CD_PROP_QUATERNION,
CD_PROP_BYTE_COLOR,
};

View File

@@ -392,6 +392,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
}
case CD_PROP_BYTE_COLOR:
case CD_PROP_COLOR:
case CD_PROP_QUATERNION:
case CD_PROP_FLOAT3:
case CD_PROP_BOOL:
case CD_PROP_INT8:

View File

@@ -107,6 +107,7 @@ static uint gpu_component_size_for_attribute_type(eCustomDataType type)
return 3;
case CD_PROP_COLOR:
case CD_PROP_BYTE_COLOR:
case CD_PROP_QUATERNION:
return 4;
default:
return 0;
@@ -315,6 +316,7 @@ static void extract_attr(const MeshRenderData *mr,
case CD_PROP_FLOAT3:
extract_attr_generic<float3>(mr, vbo, request);
break;
case CD_PROP_QUATERNION:
case CD_PROP_COLOR:
extract_attr_generic<float4>(mr, vbo, request);
break;

View File

@@ -8,6 +8,7 @@
#include "BLI_color.hh"
#include "BLI_generic_pointer.hh"
#include "BLI_math_quaternion.hh"
#include "BKE_attribute.h"
#include "BKE_context.h"
@@ -106,6 +107,8 @@ static StringRefNull rna_property_name_for_type(const eCustomDataType type)
return "value_int";
case CD_PROP_INT32_2D:
return "value_int_vector_2d";
case CD_PROP_QUATERNION:
return "value_quat";
default:
BLI_assert_unreachable();
return "";
@@ -198,6 +201,12 @@ static int mesh_set_attribute_exec(bContext *C, wmOperator *op)
case CD_PROP_COLOR:
RNA_float_get_array(op->ptr, prop_name.c_str(), static_cast<float *>(buffer));
break;
case CD_PROP_QUATERNION: {
float4 value;
RNA_float_get_array(op->ptr, prop_name.c_str(), value);
*static_cast<math::Quaternion *>(buffer) = math::normalize(math::Quaternion(value));
break;
}
case CD_PROP_BYTE_COLOR:
ColorGeometry4f value;
RNA_float_get_array(op->ptr, prop_name.c_str(), value);
@@ -330,6 +339,11 @@ static int mesh_set_attribute_invoke(bContext *C, wmOperator *op, const wmEvent
case CD_PROP_INT32_2D:
RNA_property_int_set_array(op->ptr, prop, *active_value.get<int2>());
break;
case CD_PROP_QUATERNION: {
const math::Quaternion value = math::normalize(*active_value.get<math::Quaternion>());
RNA_property_float_set_array(op->ptr, prop, float4(value));
break;
}
default:
BLI_assert_unreachable();
}
@@ -408,6 +422,16 @@ void MESH_OT_attribute_set(wmOperatorType *ot)
RNA_def_float_color(
ot->srna, "value_color", 4, color_default, -FLT_MAX, FLT_MAX, "Value", "", 0.0f, 1.0f);
RNA_def_boolean(ot->srna, "value_bool", false, "Value", "");
RNA_def_float_array(ot->srna,
"value_quat",
4,
rna_default_quaternion,
-FLT_MAX,
FLT_MAX,
"Value",
"",
FLT_MAX,
FLT_MAX);
}
/** \} */

View File

@@ -6,6 +6,7 @@
#include "BLI_cpp_type_make.hh"
#include "BLI_cpp_types_make.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_quaternion_types.hh"
#include "BLI_math_vector_types.hh"
#include "FN_field_cpp_type_make.hh"
@@ -16,6 +17,7 @@ FN_FIELD_CPP_TYPE_MAKE(blender::float2);
FN_FIELD_CPP_TYPE_MAKE(blender::float3);
FN_FIELD_CPP_TYPE_MAKE(blender::ColorGeometry4f);
FN_FIELD_CPP_TYPE_MAKE(blender::ColorGeometry4b);
FN_FIELD_CPP_TYPE_MAKE(blender::math::Quaternion);
FN_FIELD_CPP_TYPE_MAKE(bool);
FN_FIELD_CPP_TYPE_MAKE(int8_t);
FN_FIELD_CPP_TYPE_MAKE(int32_t);
@@ -31,6 +33,7 @@ void FN_register_cpp_types()
FN_FIELD_CPP_TYPE_REGISTER(blender::float3);
FN_FIELD_CPP_TYPE_REGISTER(blender::ColorGeometry4f);
FN_FIELD_CPP_TYPE_REGISTER(blender::ColorGeometry4b);
FN_FIELD_CPP_TYPE_REGISTER(blender::math::Quaternion);
FN_FIELD_CPP_TYPE_REGISTER(bool);
FN_FIELD_CPP_TYPE_REGISTER(int8_t);
FN_FIELD_CPP_TYPE_REGISTER(int32_t);

View File

@@ -84,8 +84,7 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we can't use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
int typemap[52];
char _pad[4];
int typemap[53];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@@ -181,8 +180,9 @@ typedef enum eCustomDataType {
CD_PROP_BOOL = 50,
CD_HAIRLENGTH = 51,
CD_PROP_QUATERNION = 52,
CD_NUMTYPES = 52,
CD_NUMTYPES = 53,
} eCustomDataType;
/* Bits for eCustomDataMask */
@@ -224,6 +224,7 @@ typedef enum eCustomDataType {
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
#define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8)
#define CD_MASK_PROP_INT32_2D (1ULL << CD_PROP_INT32_2D)
#define CD_MASK_PROP_QUATERNION (1ULL << CD_PROP_QUATERNION)
#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH)
@@ -237,7 +238,7 @@ typedef enum eCustomDataType {
#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_PROP_BYTE_COLOR | CD_MASK_PROP_BOOL | \
CD_MASK_PROP_INT8 | CD_MASK_PROP_INT32_2D)
CD_MASK_PROP_INT8 | CD_MASK_PROP_INT32_2D | CD_MASK_PROP_QUATERNION)
/* All color attributes */
#define CD_MASK_COLOR_ALL (CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR)

View File

@@ -49,11 +49,11 @@ typedef struct vec3d {
typedef struct vec4i {
int x, y, z, w;
} vec4i;
*/
typedef struct vec4f {
float x, y, z, w;
} vec4f;
/*
typedef struct vec4d {
double x, y, z, w;
} vec4d;

View File

@@ -42,6 +42,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{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"},
{CD_PROP_INT32_2D, "INT32_2D", 0, "2D Integer Vector", "32-bit signed integer vector"},
{CD_PROP_QUATERNION, "QUATERNION", 0, "Quaternion", "Floating point quaternion rotation"},
{0, NULL, 0, NULL, NULL},
};
@@ -70,6 +71,7 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
{CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
{CD_PROP_INT32_2D, "INT32_2D", 0, "2D Integer Vector", "32-bit signed integer vector"},
{CD_PROP_QUATERNION, "QUATERNION", 0, "Quaternion", "Floating point quaternion rotation"},
{0, NULL, 0, NULL, NULL},
};
@@ -168,6 +170,8 @@ static StructRNA *srna_by_custom_data_layer_type(const eCustomDataType type)
return &RNA_ByteIntAttribute;
case CD_PROP_INT32_2D:
return &RNA_Int2Attribute;
case CD_PROP_QUATERNION:
return &RNA_QuaternionAttribute;
default:
return NULL;
}
@@ -301,6 +305,9 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN
case CD_PROP_INT32_2D:
struct_size = sizeof(int[2]);
break;
case CD_PROP_QUATERNION:
struct_size = sizeof(float[4]);
break;
default:
struct_size = 0;
length = 0;
@@ -1069,6 +1076,40 @@ static void rna_def_attribute_int2(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
}
static void rna_def_attribute_quaternion(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "QuaternionAttribute", "Attribute");
RNA_def_struct_sdna(srna, "CustomDataLayer");
RNA_def_struct_ui_text(srna, "Quaternion Attribute", "Geometry attribute that stores rotation");
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "QuaternionAttributeValue");
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
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, "QuaternionAttributeValue", NULL);
RNA_def_struct_sdna(srna, "vec4f");
RNA_def_struct_ui_text(
srna, "Quaternion Attribute Value", "Rotation value in geometry attribute");
prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Value", "Quaternion");
RNA_def_property_float_sdna(prop, NULL, "x");
RNA_def_property_array(prop, 4);
RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
}
static void rna_def_attribute_float2(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1149,6 +1190,7 @@ static void rna_def_attribute(BlenderRNA *brna)
rna_def_attribute_byte_color(brna);
rna_def_attribute_int(brna);
rna_def_attribute_int2(brna);
rna_def_attribute_quaternion(brna);
rna_def_attribute_string(brna);
rna_def_attribute_bool(brna);
rna_def_attribute_float2(brna);