Attributes: Use AttributeStorage for curves and Grease Pencil

This commit moves Curves and Grease Pencil to use `AttributeStorage`
instead of `CustomData`, except for vertex groups. This PR mostly
involves extending the changes from the above commit for point clouds
to generalize to other geometry types.

This is mostly straightforward, though a couple non-trivial places of
note are the joining of Grease Pencil objects (`merge_attributes`), the
"default render fallback" UV for curves objects which was previously
unused at the UI level and just ended up being the first attribute, and
the `update_curve_types()` call in the curves versioning function.

Similar to:
- fa03c53d4a
- f74e304b00

Part of #122398.

Pull Request: https://projects.blender.org/blender/blender/pulls/140936
This commit is contained in:
Hans Goudey
2025-07-01 16:30:00 +02:00
committed by Hans Goudey
parent 8b9755b537
commit 68759af516
27 changed files with 1007 additions and 881 deletions

View File

@@ -44,18 +44,12 @@ void mesh_convert_storage_to_customdata(Mesh &mesh);
*/
void mesh_convert_customdata_to_storage(Mesh &mesh);
/** See #mesh_convert_storage_to_customdata. */
void curves_convert_storage_to_customdata(CurvesGeometry &curves);
/** See #mesh_convert_customdata_to_storage. */
void curves_convert_customdata_to_storage(CurvesGeometry &curves);
/** See #mesh_convert_customdata_to_storage. */
void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud);
/** See #mesh_convert_storage_to_customdata. */
void grease_pencil_convert_storage_to_customdata(GreasePencil &grease_pencil);
/** See #mesh_convert_customdata_to_storage. */
void grease_pencil_convert_customdata_to_storage(GreasePencil &grease_pencil);

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 32
#define BLENDER_FILE_SUBVERSION 33
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -152,6 +152,9 @@ static std::array<DomainInfo, ATTR_DOMAIN_NUM> get_domains(const AttributeOwner
std::array<DomainInfo, ATTR_DOMAIN_NUM> info;
switch (owner.type()) {
case AttributeOwnerType::Curves:
case AttributeOwnerType::GreasePencil:
case AttributeOwnerType::GreasePencilDrawing:
case AttributeOwnerType::PointCloud: {
/* This should be implemented with #AttributeStorage instead. */
BLI_assert_unreachable();
@@ -182,28 +185,6 @@ static std::array<DomainInfo, ATTR_DOMAIN_NUM> get_domains(const AttributeOwner
}
break;
}
case AttributeOwnerType::Curves: {
Curves *curves = owner.get_curves();
info[int(AttrDomain::Point)].customdata = &curves->geometry.point_data;
info[int(AttrDomain::Point)].length = curves->geometry.point_num;
info[int(AttrDomain::Curve)].customdata = &curves->geometry.curve_data;
info[int(AttrDomain::Curve)].length = curves->geometry.curve_num;
break;
}
case AttributeOwnerType::GreasePencil: {
GreasePencil *grease_pencil = owner.get_grease_pencil();
info[int(AttrDomain::Layer)].customdata = &grease_pencil->layers_data;
info[int(AttrDomain::Layer)].length = grease_pencil->layers().size();
break;
}
case AttributeOwnerType::GreasePencilDrawing: {
blender::bke::greasepencil::Drawing &drawing = owner.get_grease_pencil_drawing()->wrap();
info[int(AttrDomain::Point)].customdata = &drawing.geometry.point_data;
info[int(AttrDomain::Point)].length = drawing.geometry.point_num;
info[int(AttrDomain::Curve)].customdata = &drawing.geometry.curve_data;
info[int(AttrDomain::Curve)].length = drawing.geometry.curve_num;
break;
}
}
return info;
@@ -281,7 +262,7 @@ bool BKE_attribute_rename(AttributeOwner &owner,
return false;
}
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &attributes = *owner.get_storage();
if (!attributes.lookup(old_name)) {
BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
@@ -394,7 +375,7 @@ static bool attribute_name_exists(const AttributeOwner &owner, const StringRef n
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, const StringRef name)
{
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
blender::bke::AttributeStorage &storage = *owner.get_storage();
return storage.unique_name_calc(name);
}
@@ -822,7 +803,7 @@ std::optional<blender::StringRefNull> BKE_attributes_active_name_get(AttributeOw
if (active_index == -1) {
return std::nullopt;
}
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
if (active_index >= storage.count()) {
return std::nullopt;
@@ -863,7 +844,7 @@ std::optional<blender::StringRefNull> BKE_attributes_active_name_get(AttributeOw
void BKE_attributes_active_set(AttributeOwner &owner, const StringRef name)
{
using namespace blender;
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &attributes = *owner.get_storage();
*BKE_attributes_active_index_p(owner) = attributes.index_of(name);
return;

View File

@@ -269,18 +269,15 @@ void mesh_convert_customdata_to_storage(Mesh &mesh)
mesh.attribute_storage.wrap());
}
void curves_convert_storage_to_customdata(CurvesGeometry &curves)
{
convert_storage_to_customdata(curves.attribute_storage.wrap(),
{{AttrDomain::Point, {curves.point_data, curves.points_num()}},
{AttrDomain::Curve, {curves.curve_data, curves.curves_num()}}});
}
void curves_convert_customdata_to_storage(CurvesGeometry &curves)
{
attribute_legacy_convert_customdata_to_storage(
{{AttrDomain::Point, {curves.point_data, curves.points_num()}},
{AttrDomain::Curve, {curves.curve_data, curves.curves_num()}}},
{AttrDomain::Curve, {curves.curve_data_legacy, curves.curves_num()}}},
curves.attribute_storage.wrap());
/* Update the curve type count again (the first time was done on file-read, where
* #AttributeStorage data doesn't exist yet for older fiels). */
curves.update_curve_types();
}
void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud)
@@ -290,16 +287,11 @@ void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud)
pointcloud.attribute_storage.wrap());
}
void grease_pencil_convert_storage_to_customdata(GreasePencil &grease_pencil)
{
convert_storage_to_customdata(
grease_pencil.attribute_storage.wrap(),
{{AttrDomain::Layer, {grease_pencil.layers_data, int(grease_pencil.layers().size())}}});
}
void grease_pencil_convert_customdata_to_storage(GreasePencil &grease_pencil)
{
attribute_legacy_convert_customdata_to_storage(
{{AttrDomain::Layer, {grease_pencil.layers_data, int(grease_pencil.layers().size())}}},
{{AttrDomain::Layer,
{grease_pencil.layers_data_legacy, int(grease_pencil.layers().size())}}},
grease_pencil.attribute_storage.wrap());
}

View File

@@ -627,7 +627,7 @@ static std::optional<CurvesGeometry> try_load_curves_geometry(const DictionaryVa
}
CurvesGeometry curves;
CustomData_free_layer_named(&curves.point_data, "position");
curves.attribute_storage.wrap().remove("position");
curves.point_num = io_curves.lookup_int("num_points").value_or(0);
curves.curve_num = io_curves.lookup_int("num_curves").value_or(0);

View File

@@ -6,415 +6,433 @@
#include "DNA_object_types.h"
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_attribute_storage.hh"
#include "BKE_curves.hh"
#include "BKE_deform.hh"
#include "FN_multi_function_builder.hh"
#include "attribute_access_intern.hh"
#include "attribute_storage_access.hh"
namespace blender::bke::curves {
static void tag_component_topology_changed(void *owner)
static void tag_topology_changed(void *owner)
{
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
curves.tag_topology_changed();
}
static void tag_component_curve_types_changed(void *owner)
static void tag_curve_types_changed(void *owner)
{
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
curves.update_curve_types();
curves.tag_topology_changed();
}
static void tag_component_positions_changed(void *owner)
static void tag_positions_changed(void *owner)
{
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
curves.tag_positions_changed();
}
static void tag_component_radii_changed(void *owner)
static void tag_radii_changed(void *owner)
{
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
curves.tag_radii_changed();
}
static void tag_component_normals_changed(void *owner)
static void tag_normals_changed(void *owner)
{
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
curves.tag_normals_changed();
}
static void tag_component_material_index_changed(void *owner)
static void tag_material_index_changed(void *owner)
{
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
curves.tag_material_index_changed();
}
/**
* This provider makes vertex groups available as float attributes.
*/
class CurvesVertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
GAttributeReader try_get_for_read(const void *owner, const StringRef attribute_id) const final
{
const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(owner);
if (curves == nullptr) {
return {};
}
const int vertex_group_index = BKE_defgroup_name_index(&curves->vertex_group_names,
attribute_id);
if (vertex_group_index < 0) {
return {};
}
const Span<MDeformVert> dverts = curves->deform_verts();
return this->get_for_vertex_group_index(*curves, dverts, vertex_group_index);
static const auto &changed_tags()
{
static Map<StringRef, AttrUpdateOnChange> attributes{
{"position", tag_positions_changed},
{"radius", tag_radii_changed},
{"tilt", tag_normals_changed},
{"handle_left", tag_positions_changed},
{"handle_right", tag_positions_changed},
{"handle_type_left", tag_topology_changed},
{"handle_type_right", tag_topology_changed},
{"nurbs_weight", tag_positions_changed},
{"nurbs_order", tag_topology_changed},
{"normal_mode", tag_normals_changed},
{"custom_normal", tag_normals_changed},
{"curve_type", tag_curve_types_changed},
{"resolution", tag_topology_changed},
{"cyclic", tag_topology_changed},
{"material_index", tag_material_index_changed},
};
return attributes;
}
static int get_domain_size(const void *owner, const AttrDomain domain)
{
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
switch (domain) {
case AttrDomain::Point:
return curves.points_num();
case AttrDomain::Curve:
return curves.curves_num();
default:
return 0;
}
}
static GAttributeReader reader_for_vertex_group_index(const CurvesGeometry &curves,
const Span<MDeformVert> dverts,
const int vertex_group_index)
{
BLI_assert(vertex_group_index >= 0);
if (dverts.is_empty()) {
return {VArray<float>::ForSingle(0.0f, curves.points_num()), AttrDomain::Point};
}
return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
}
static GAttributeReader try_get_vertex_group(const void *owner, const StringRef attribute_id)
{
const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(owner);
if (curves == nullptr) {
return {};
}
const int vertex_group_index = BKE_defgroup_name_index(&curves->vertex_group_names,
attribute_id);
if (vertex_group_index < 0) {
return {};
}
const Span<MDeformVert> dverts = curves->deform_verts();
return reader_for_vertex_group_index(*curves, dverts, vertex_group_index);
}
static GAttributeWriter try_get_vertex_group_for_write(void *owner, const StringRef attribute_id)
{
CurvesGeometry *curves = static_cast<CurvesGeometry *>(owner);
if (curves == nullptr) {
return {};
}
const int vertex_group_index = BKE_defgroup_name_index(&curves->vertex_group_names,
attribute_id);
if (vertex_group_index < 0) {
return {};
}
MutableSpan<MDeformVert> dverts = curves->deform_verts_for_write();
return {varray_for_mutable_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
}
static bool try_delete_vertex_group(void *owner, const StringRef name)
{
CurvesGeometry *curves = static_cast<CurvesGeometry *>(owner);
if (curves == nullptr) {
return true;
}
GAttributeReader get_for_vertex_group_index(const CurvesGeometry &curves,
const Span<MDeformVert> dverts,
const int vertex_group_index) const
{
BLI_assert(vertex_group_index >= 0);
if (dverts.is_empty()) {
return {VArray<float>::ForSingle(0.0f, curves.points_num()), AttrDomain::Point};
}
return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
int index;
bDeformGroup *group;
if (!BKE_defgroup_listbase_name_find(&curves->vertex_group_names, name, &index, &group)) {
return false;
}
BLI_remlink(&curves->vertex_group_names, group);
MEM_freeN(group);
if (curves->deform_verts().is_empty()) {
return true;
}
GAttributeWriter try_get_for_write(void *owner, const StringRef attribute_id) const final
{
CurvesGeometry *curves = static_cast<CurvesGeometry *>(owner);
if (curves == nullptr) {
return {};
}
const int vertex_group_index = BKE_defgroup_name_index(&curves->vertex_group_names,
attribute_id);
if (vertex_group_index < 0) {
return {};
}
MutableSpan<MDeformVert> dverts = curves->deform_verts_for_write();
return {varray_for_mutable_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
MutableSpan<MDeformVert> dverts = curves->deform_verts_for_write();
remove_defgroup_index(dverts, index);
return true;
}
static bool foreach_vertex_group(const void *owner, FunctionRef<void(const AttributeIter &)> fn)
{
const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(owner);
if (curves == nullptr) {
return true;
}
const Span<MDeformVert> dverts = curves->deform_verts();
bool try_delete(void *owner, const StringRef name) const final
{
CurvesGeometry *curves = static_cast<CurvesGeometry *>(owner);
if (curves == nullptr) {
return true;
}
int index;
bDeformGroup *group;
if (!BKE_defgroup_listbase_name_find(&curves->vertex_group_names, name, &index, &group)) {
int group_index = 0;
LISTBASE_FOREACH_INDEX (const bDeformGroup *, group, &curves->vertex_group_names, group_index) {
const auto get_fn = [&]() {
return reader_for_vertex_group_index(*curves, dverts, group_index);
};
AttributeIter iter{group->name, AttrDomain::Point, CD_PROP_FLOAT, get_fn};
fn(iter);
if (iter.is_stopped()) {
return false;
}
BLI_remlink(&curves->vertex_group_names, group);
MEM_freeN(group);
if (curves->deform_verts().is_empty()) {
return true;
}
MutableSpan<MDeformVert> dverts = curves->deform_verts_for_write();
remove_defgroup_index(dverts, index);
return true;
}
return true;
}
bool foreach_attribute(const void *owner,
FunctionRef<void(const AttributeIter &)> fn) const final
{
const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(owner);
if (curves == nullptr) {
return true;
}
const Span<MDeformVert> dverts = curves->deform_verts();
int group_index = 0;
LISTBASE_FOREACH_INDEX (const bDeformGroup *, group, &curves->vertex_group_names, group_index)
{
const auto get_fn = [&]() {
return this->get_for_vertex_group_index(*curves, dverts, group_index);
};
AttributeIter iter{group->name, AttrDomain::Point, CD_PROP_FLOAT, get_fn};
fn(iter);
if (iter.is_stopped()) {
return false;
}
}
return true;
}
void foreach_domain(const FunctionRef<void(AttrDomain)> callback) const final
{
callback(AttrDomain::Point);
}
};
/**
* In this function all the attribute providers for a curves component are created.
* Most data in this function is statically allocated, because it does not change over time.
*/
static GeometryAttributeProviders create_attribute_providers_for_curve()
static const auto &builtin_attributes()
{
static CustomDataAccessInfo curve_access = {
[](void *owner) -> CustomData * {
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
return &curves.curve_data;
},
[](const void *owner) -> const CustomData * {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return &curves.curve_data;
},
[](const void *owner) -> int {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return curves.curves_num();
}};
static CustomDataAccessInfo point_access = {
[](void *owner) -> CustomData * {
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
return &curves.point_data;
},
[](const void *owner) -> const CustomData * {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return &curves.point_data;
},
[](const void *owner) -> int {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return curves.points_num();
}};
static auto attributes = []() {
Map<StringRef, AttrBuiltinInfo> map;
static BuiltinCustomDataLayerProvider position("position",
AttrDomain::Point,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::NonDeletable,
point_access,
tag_component_positions_changed);
AttrBuiltinInfo position(AttrDomain::Point, AttrType::Float3);
position.deletable = false;
map.add_new("position", std::move(position));
static BuiltinCustomDataLayerProvider radius("radius",
AttrDomain::Point,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_radii_changed);
AttrBuiltinInfo radius(AttrDomain::Point, AttrType::Float);
map.add_new("radius", std::move(radius));
static BuiltinCustomDataLayerProvider id("id",
AttrDomain::Point,
CD_PROP_INT32,
BuiltinAttributeProvider::Deletable,
point_access,
nullptr);
AttrBuiltinInfo id(AttrDomain::Point, AttrType::Int32);
map.add_new("id", std::move(id));
static BuiltinCustomDataLayerProvider tilt("tilt",
AttrDomain::Point,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_normals_changed);
AttrBuiltinInfo tilt(AttrDomain::Point, AttrType::Float);
map.add_new("tilt", std::move(tilt));
static BuiltinCustomDataLayerProvider handle_right("handle_right",
AttrDomain::Point,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_positions_changed);
AttrBuiltinInfo handle_left(AttrDomain::Point, AttrType::Float3);
map.add_new("handle_left", std::move(handle_left));
static BuiltinCustomDataLayerProvider handle_left("handle_left",
AttrDomain::Point,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_positions_changed);
AttrBuiltinInfo handle_right(AttrDomain::Point, AttrType::Float3);
map.add_new("handle_right", std::move(handle_right));
static auto handle_type_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Handle Type Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN);
},
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
AttrDomain::Point,
CD_PROP_INT8,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_topology_changed,
AttributeValidator{&handle_type_clamp});
static auto handle_type_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Handle Type Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN);
},
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
AttrDomain::Point,
CD_PROP_INT8,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_topology_changed,
AttributeValidator{&handle_type_clamp});
AttrBuiltinInfo handle_type_left(AttrDomain::Point, AttrType::Int8);
handle_type_left.validator = AttributeValidator{&handle_type_clamp};
map.add_new("handle_type_left", std::move(handle_type_left));
static float default_nurbs_weight = 1.0f;
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
AttrDomain::Point,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_positions_changed,
{},
&default_nurbs_weight);
AttrBuiltinInfo handle_type_right(AttrDomain::Point, AttrType::Int8);
handle_type_right.validator = AttributeValidator{&handle_type_clamp};
map.add_new("handle_type_right", std::move(handle_type_right));
static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"NURBS Order Validate",
[](int8_t value) { return std::max<int8_t>(value, 1); },
mf::build::exec_presets::AllSpanOrSingle());
static int nurbs_order_default = 4;
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
AttrDomain::Curve,
CD_PROP_INT8,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed,
AttributeValidator{&nurbs_order_clamp},
&nurbs_order_default);
static float default_nurbs_weight = 1.0f;
AttrBuiltinInfo nurbs_weight(AttrDomain::Point, AttrType::Float);
nurbs_weight.default_value = &default_nurbs_weight;
map.add_new("nurbs_weight", std::move(nurbs_weight));
static const auto normal_mode_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Normal Mode Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, NORMAL_MODE_MINIMUM_TWIST, NORMAL_MODE_FREE);
},
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
AttrDomain::Curve,
CD_PROP_INT8,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_normals_changed,
AttributeValidator{&normal_mode_clamp});
static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"NURBS Order Validate",
[](int8_t value) { return std::max<int8_t>(value, 1); },
mf::build::exec_presets::AllSpanOrSingle());
static int nurbs_order_default = 4;
AttrBuiltinInfo nurbs_order(AttrDomain::Curve, AttrType::Int8);
nurbs_order.default_value = &nurbs_order_default;
nurbs_order.validator = AttributeValidator{&nurbs_order_clamp};
map.add_new("nurbs_order", std::move(nurbs_order));
static BuiltinCustomDataLayerProvider custom_normal("custom_normal",
AttrDomain::Point,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_normals_changed);
static const auto normal_mode_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Normal Mode Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, NORMAL_MODE_MINIMUM_TWIST, NORMAL_MODE_FREE);
},
mf::build::exec_presets::AllSpanOrSingle());
AttrBuiltinInfo normal_mode(AttrDomain::Curve, AttrType::Int8);
normal_mode.validator = AttributeValidator{&normal_mode_clamp};
map.add_new("normal_mode", std::move(normal_mode));
static const auto knots_mode_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Knots Mode Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
},
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
AttrDomain::Curve,
CD_PROP_INT8,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed,
AttributeValidator{&knots_mode_clamp});
AttrBuiltinInfo custom_normal(AttrDomain::Point, AttrType::Float3);
map.add_new("custom_normal", std::move(custom_normal));
static const auto curve_type_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Curve Type Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, CURVE_TYPE_CATMULL_ROM, CURVE_TYPES_NUM);
},
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider curve_type("curve_type",
AttrDomain::Curve,
CD_PROP_INT8,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_curve_types_changed,
AttributeValidator{&curve_type_clamp});
static const auto knots_mode_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Knots Mode Validate",
[](int8_t value) {
return std::clamp<int8_t>(
value, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
},
mf::build::exec_presets::AllSpanOrSingle());
AttrBuiltinInfo knots_mode(AttrDomain::Curve, AttrType::Int8);
knots_mode.validator = AttributeValidator{&knots_mode_clamp};
map.add_new("knots_mode", std::move(knots_mode));
static const auto resolution_clamp = mf::build::SI1_SO<int, int>(
"Resolution Validate",
[](int value) { return std::max<int>(value, 1); },
mf::build::exec_presets::AllSpanOrSingle());
static int resolution_default = 12;
static BuiltinCustomDataLayerProvider resolution("resolution",
AttrDomain::Curve,
CD_PROP_INT32,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed,
AttributeValidator{&resolution_clamp},
&resolution_default);
static const auto curve_type_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"Curve Type Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, CURVE_TYPE_CATMULL_ROM, CURVE_TYPES_NUM);
},
mf::build::exec_presets::AllSpanOrSingle());
AttrBuiltinInfo curve_type(AttrDomain::Curve, AttrType::Int8);
curve_type.validator = AttributeValidator{&curve_type_clamp};
map.add_new("curve_type", std::move(curve_type));
static BuiltinCustomDataLayerProvider cyclic("cyclic",
AttrDomain::Curve,
CD_PROP_BOOL,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed);
static const auto resolution_clamp = mf::build::SI1_SO<int, int>(
"Resolution Validate",
[](int value) { return std::max<int>(value, 1); },
mf::build::exec_presets::AllSpanOrSingle());
static int resolution_default = 12;
AttrBuiltinInfo resolution(AttrDomain::Curve, AttrType::Int32);
resolution.default_value = &resolution_default;
resolution.validator = AttributeValidator{&resolution_clamp};
map.add_new("resolution", std::move(resolution));
static const auto material_index_clamp = mf::build::SI1_SO<int, int>(
"Material Index Validate",
[](int value) {
/* Use #short for the maximum since many areas still use that type for indices. */
return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
},
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider material_index("material_index",
AttrDomain::Curve,
CD_PROP_INT32,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_material_index_changed,
AttributeValidator{&material_index_clamp});
AttrBuiltinInfo cyclic(AttrDomain::Curve, AttrType::Bool);
map.add_new("cyclic", std::move(cyclic));
static CurvesVertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider curve_custom_data(AttrDomain::Curve, curve_access);
static CustomDataAttributeProvider point_custom_data(AttrDomain::Point, point_access);
static const auto material_index_clamp = mf::build::SI1_SO<int, int>(
"Material Index Validate",
[](int value) {
/* Use #short for the maximum since many areas still use that type for indices. */
return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
},
mf::build::exec_presets::AllSpanOrSingle());
AttrBuiltinInfo material_index(AttrDomain::Curve, AttrType::Int32);
material_index.validator = AttributeValidator{&material_index_clamp};
map.add_new("material_index", std::move(material_index));
return GeometryAttributeProviders({&position,
&radius,
&id,
&tilt,
&handle_right,
&handle_left,
&handle_type_right,
&handle_type_left,
&normal_mode,
&custom_normal,
&nurbs_order,
&nurbs_knots_mode,
&nurbs_weight,
&curve_type,
&resolution,
&cyclic,
&material_index},
{&vertex_groups, &curve_custom_data, &point_custom_data});
return map;
}();
return attributes;
}
/** \} */
static AttributeAccessorFunctions get_curves_accessor_functions()
{
static const GeometryAttributeProviders providers = create_attribute_providers_for_curve();
AttributeAccessorFunctions fn =
attribute_accessor_functions::accessor_functions_for_providers<providers>();
fn.domain_size = [](const void *owner, const AttrDomain domain) {
if (owner == nullptr) {
return 0;
}
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
switch (domain) {
case AttrDomain::Point:
return curves.points_num();
case AttrDomain::Curve:
return curves.curves_num();
default:
return 0;
}
};
AttributeAccessorFunctions fn{};
fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
return ELEM(domain, AttrDomain::Point, AttrDomain::Curve);
};
fn.domain_size = get_domain_size;
fn.builtin_domain_and_type = [](const void * /*owner*/,
const StringRef name) -> std::optional<AttributeDomainAndType> {
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
if (!info) {
return std::nullopt;
}
const std::optional<eCustomDataType> cd_type = attr_type_to_custom_data_type(info->type);
BLI_assert(cd_type.has_value());
return AttributeDomainAndType{info->domain, *cd_type};
};
fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer {
const AttrBuiltinInfo &info = builtin_attributes().lookup(name);
return info.default_value;
};
fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
if (GAttributeReader vertex_group = try_get_vertex_group(owner, name)) {
return vertex_group;
}
const AttributeStorage &storage = curves.attribute_storage.wrap();
const Attribute *attr = storage.lookup(name);
if (!attr) {
return {};
}
const int domain_size = get_domain_size(owner, attr->domain());
return attribute_to_reader(*attr, attr->domain(), domain_size);
};
fn.adapt_domain = [](const void *owner,
const GVArray &varray,
const AttrDomain from_domain,
const AttrDomain to_domain) -> GVArray {
if (owner == nullptr) {
return {};
}
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return curves.adapt_domain(varray, from_domain, to_domain);
};
fn.foreach_attribute = [](const void *owner,
const FunctionRef<void(const AttributeIter &)> fn,
const AttributeAccessor &accessor) {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
const bool should_continue = foreach_vertex_group(
owner, [&](const AttributeIter &iter) { fn(iter); });
if (!should_continue) {
return;
}
const AttributeStorage &storage = curves.attribute_storage.wrap();
storage.foreach_with_stop([&](const Attribute &attr) {
const auto get_fn = [&]() {
const int domain_size = get_domain_size(owner, attr.domain());
return attribute_to_reader(attr, attr.domain(), domain_size);
};
const std::optional<eCustomDataType> cd_type = attr_type_to_custom_data_type(
attr.data_type());
BLI_assert(cd_type.has_value());
AttributeIter iter(attr.name(), attr.domain(), *cd_type, get_fn);
iter.is_builtin = builtin_attributes().contains(attr.name());
iter.accessor = &accessor;
fn(iter);
return !iter.is_stopped();
});
};
fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator {
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
if (!info) {
return {};
}
return info->validator;
};
fn.lookup_for_write = [](void *owner, const StringRef name) -> GAttributeWriter {
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
if (GAttributeWriter vertex_group = try_get_vertex_group_for_write(owner, name)) {
return vertex_group;
}
AttributeStorage &storage = curves.attribute_storage.wrap();
Attribute *attr = storage.lookup(name);
if (!attr) {
return {};
}
const int domain_size = get_domain_size(owner, attr->domain());
return attribute_to_writer(&curves, changed_tags(), domain_size, *attr);
};
fn.remove = [](void *owner, const StringRef name) -> bool {
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
if (try_delete_vertex_group(owner, name)) {
return true;
}
AttributeStorage &storage = curves.attribute_storage.wrap();
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (!info->deletable) {
return false;
}
}
const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name);
const bool removed = storage.remove(name);
if (!removed) {
return false;
}
if (fn) {
(*fn)(owner);
}
return true;
};
fn.add = [](void *owner,
const StringRef name,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer) {
CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
const int domain_size = get_domain_size(owner, domain);
AttributeStorage &storage = curves.attribute_storage.wrap();
const std::optional<AttrType> type = custom_data_type_to_attr_type(data_type);
BLI_assert(type.has_value());
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (info->domain != domain || info->type != type) {
return false;
}
}
if (storage.lookup(name)) {
return false;
}
Attribute::DataVariant data = attribute_init_to_data(*type, domain_size, initializer);
storage.add(name, domain, *type, std::move(data));
return true;
};
return fn;
}

View File

@@ -37,6 +37,8 @@
#include "BKE_customdata.hh"
#include "BKE_deform.hh"
#include "attribute_storage_access.hh"
namespace blender::bke {
constexpr StringRef ATTR_POSITION = "position";
@@ -68,7 +70,7 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
this->point_num = point_num;
this->curve_num = curve_num;
CustomData_reset(&this->point_data);
CustomData_reset(&this->curve_data);
CustomData_reset(&this->curve_data_legacy);
new (&this->attribute_storage.wrap()) blender::bke::AttributeStorage();
BLI_listbase_clear(&this->vertex_group_names);
@@ -110,8 +112,7 @@ CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
other.runtime->custom_knots_sharing_info->add_user();
}
CustomData_init_from(&other.point_data, &this->point_data, CD_MASK_ALL, other.point_num);
CustomData_init_from(&other.curve_data, &this->curve_data, CD_MASK_ALL, other.curve_num);
CustomData_init_from(&other.point_data, &this->point_data, CD_MASK_MDEFORMVERT, other.point_num);
new (&this->attribute_storage.wrap()) AttributeStorage(other.attribute_storage.wrap());
@@ -171,9 +172,6 @@ CurvesGeometry::CurvesGeometry(CurvesGeometry &&other)
this->point_data = other.point_data;
CustomData_reset(&other.point_data);
this->curve_data = other.curve_data;
CustomData_reset(&other.curve_data);
new (&this->attribute_storage.wrap())
AttributeStorage(std::move(other.attribute_storage.wrap()));
@@ -209,7 +207,6 @@ CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other)
CurvesGeometry::~CurvesGeometry()
{
CustomData_free(&this->point_data);
CustomData_free(&this->curve_data);
this->attribute_storage.wrap().~AttributeStorage();
BLI_freelistN(&this->vertex_group_names);
if (this->runtime) {
@@ -227,88 +224,19 @@ CurvesGeometry::~CurvesGeometry()
/** \name Accessors
* \{ */
static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.points_num() : curves.curves_num();
}
static CustomData &domain_custom_data(CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
}
static const CustomData &domain_custom_data(const CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
}
template<typename T>
static VArray<T> get_varray_attribute(const CurvesGeometry &curves,
const AttrDomain domain,
const StringRef name,
const T default_value)
{
const int num = domain_num(curves, domain);
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
const CustomData &custom_data = domain_custom_data(curves, domain);
const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name);
if (data != nullptr) {
return VArray<T>::ForSpan(Span<T>(data, num));
}
return VArray<T>::ForSingle(default_value, num);
}
template<typename T>
static Span<T> get_span_attribute(const CurvesGeometry &curves,
const AttrDomain domain,
const StringRef name)
{
const int num = domain_num(curves, domain);
const CustomData &custom_data = domain_custom_data(curves, domain);
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
T *data = (T *)CustomData_get_layer_named(&custom_data, type, name);
if (data == nullptr) {
return {};
}
return {data, num};
}
template<typename T>
static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
const AttrDomain domain,
const StringRef name,
const T default_value = T())
{
const int num = domain_num(curves, domain);
if (num <= 0) {
return {};
}
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
CustomData &custom_data = domain_custom_data(curves, domain);
T *data = (T *)CustomData_get_layer_named_for_write(&custom_data, type, name, num);
if (data != nullptr) {
return {data, num};
}
data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name);
MutableSpan<T> span = {data, num};
if (num > 0 && span.first() != default_value) {
span.fill(default_value);
}
return span;
}
VArray<int8_t> CurvesGeometry::curve_types() const
{
return get_varray_attribute<int8_t>(
*this, AttrDomain::Curve, ATTR_CURVE_TYPE, CURVE_TYPE_CATMULL_ROM);
return get_varray_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Curve,
ATTR_CURVE_TYPE,
this->curves_num(),
CURVE_TYPE_CATMULL_ROM);
}
MutableSpan<int8_t> CurvesGeometry::curve_types_for_write()
{
return get_mutable_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_CURVE_TYPE);
return get_mutable_attribute<int8_t>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_CURVE_TYPE, this->curves_num());
}
void CurvesGeometry::fill_curve_types(const CurveType type)
@@ -383,20 +311,24 @@ void CurvesGeometry::update_curve_types()
Span<float3> CurvesGeometry::positions() const
{
return get_span_attribute<float3>(*this, AttrDomain::Point, ATTR_POSITION);
return get_span_attribute<float3>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_POSITION, this->points_num());
}
MutableSpan<float3> CurvesGeometry::positions_for_write()
{
return get_mutable_attribute<float3>(*this, AttrDomain::Point, ATTR_POSITION);
return get_mutable_attribute<float3>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_POSITION, this->points_num());
}
VArray<float> CurvesGeometry::radius() const
{
return get_varray_attribute<float>(*this, AttrDomain::Point, ATTR_RADIUS, 0.01f);
return get_varray_attribute<float>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_RADIUS, this->points_num(), 0.01f);
}
MutableSpan<float> CurvesGeometry::radius_for_write()
{
return get_mutable_attribute<float>(*this, AttrDomain::Point, ATTR_RADIUS, 0.01f);
return get_mutable_attribute<float>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_RADIUS, this->points_num(), 0.01f);
}
Span<int> CurvesGeometry::offsets() const
@@ -418,111 +350,165 @@ MutableSpan<int> CurvesGeometry::offsets_for_write()
VArray<bool> CurvesGeometry::cyclic() const
{
return get_varray_attribute<bool>(*this, AttrDomain::Curve, ATTR_CYCLIC, false);
return get_varray_attribute<bool>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_CYCLIC, this->curves_num(), false);
}
MutableSpan<bool> CurvesGeometry::cyclic_for_write()
{
return get_mutable_attribute<bool>(*this, AttrDomain::Curve, ATTR_CYCLIC, false);
return get_mutable_attribute<bool>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_CYCLIC, this->curves_num(), false);
}
VArray<int> CurvesGeometry::resolution() const
{
return get_varray_attribute<int>(*this, AttrDomain::Curve, ATTR_RESOLUTION, 12);
return get_varray_attribute<int>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_RESOLUTION, this->curves_num(), 12);
}
MutableSpan<int> CurvesGeometry::resolution_for_write()
{
return get_mutable_attribute<int>(*this, AttrDomain::Curve, ATTR_RESOLUTION, 12);
return get_mutable_attribute<int>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_RESOLUTION, this->curves_num(), 12);
}
VArray<int8_t> CurvesGeometry::normal_mode() const
{
return get_varray_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_NORMAL_MODE, 0);
return get_varray_attribute<int8_t>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NORMAL_MODE, this->curves_num(), 0);
}
MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
{
return get_mutable_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_NORMAL_MODE);
return get_mutable_attribute<int8_t>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NORMAL_MODE, this->curves_num());
}
VArray<float> CurvesGeometry::tilt() const
{
return get_varray_attribute<float>(*this, AttrDomain::Point, ATTR_TILT, 0.0f);
return get_varray_attribute<float>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_TILT, this->points_num(), 0.0f);
}
MutableSpan<float> CurvesGeometry::tilt_for_write()
{
return get_mutable_attribute<float>(*this, AttrDomain::Point, ATTR_TILT);
return get_mutable_attribute<float>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_TILT, this->points_num());
}
VArray<int8_t> CurvesGeometry::handle_types_left() const
{
return get_varray_attribute<int8_t>(*this, AttrDomain::Point, ATTR_HANDLE_TYPE_LEFT, 0);
return get_varray_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_TYPE_LEFT,
this->points_num(),
0);
}
MutableSpan<int8_t> CurvesGeometry::handle_types_left_for_write()
{
return get_mutable_attribute<int8_t>(*this, AttrDomain::Point, ATTR_HANDLE_TYPE_LEFT, 0);
return get_mutable_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_TYPE_LEFT,
this->points_num(),
0);
}
VArray<int8_t> CurvesGeometry::handle_types_right() const
{
return get_varray_attribute<int8_t>(*this, AttrDomain::Point, ATTR_HANDLE_TYPE_RIGHT, 0);
return get_varray_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_TYPE_RIGHT,
this->points_num(),
0);
}
MutableSpan<int8_t> CurvesGeometry::handle_types_right_for_write()
{
return get_mutable_attribute<int8_t>(*this, AttrDomain::Point, ATTR_HANDLE_TYPE_RIGHT, 0);
return get_mutable_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_TYPE_RIGHT,
this->points_num(),
0);
}
Span<float3> CurvesGeometry::handle_positions_left() const
{
return get_span_attribute<float3>(*this, AttrDomain::Point, ATTR_HANDLE_POSITION_LEFT);
return get_span_attribute<float3>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_POSITION_LEFT,
this->points_num());
}
MutableSpan<float3> CurvesGeometry::handle_positions_left_for_write()
{
return get_mutable_attribute<float3>(*this, AttrDomain::Point, ATTR_HANDLE_POSITION_LEFT);
return get_mutable_attribute<float3>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_POSITION_LEFT,
this->points_num());
}
Span<float3> CurvesGeometry::handle_positions_right() const
{
return get_span_attribute<float3>(*this, AttrDomain::Point, ATTR_HANDLE_POSITION_RIGHT);
return get_span_attribute<float3>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_POSITION_RIGHT,
this->points_num());
}
MutableSpan<float3> CurvesGeometry::handle_positions_right_for_write()
{
return get_mutable_attribute<float3>(*this, AttrDomain::Point, ATTR_HANDLE_POSITION_RIGHT);
return get_mutable_attribute<float3>(this->attribute_storage.wrap(),
AttrDomain::Point,
ATTR_HANDLE_POSITION_RIGHT,
this->points_num());
}
VArray<int8_t> CurvesGeometry::nurbs_orders() const
{
return get_varray_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_NURBS_ORDER, 4);
return get_varray_attribute<int8_t>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NURBS_ORDER, this->curves_num(), 4);
}
MutableSpan<int8_t> CurvesGeometry::nurbs_orders_for_write()
{
return get_mutable_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_NURBS_ORDER, 4);
return get_mutable_attribute<int8_t>(
this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NURBS_ORDER, this->curves_num(), 4);
}
Span<float> CurvesGeometry::nurbs_weights() const
{
return get_span_attribute<float>(*this, AttrDomain::Point, ATTR_NURBS_WEIGHT);
return get_span_attribute<float>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_NURBS_WEIGHT, this->points_num());
}
MutableSpan<float> CurvesGeometry::nurbs_weights_for_write()
{
return get_mutable_attribute<float>(*this, AttrDomain::Point, ATTR_NURBS_WEIGHT);
return get_mutable_attribute<float>(
this->attribute_storage.wrap(), AttrDomain::Point, ATTR_NURBS_WEIGHT, this->points_num());
}
VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const
{
return get_varray_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_NURBS_KNOTS_MODE, 0);
return get_varray_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Curve,
ATTR_NURBS_KNOTS_MODE,
this->curves_num(),
0);
}
MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes_for_write()
{
return get_mutable_attribute<int8_t>(*this, AttrDomain::Curve, ATTR_NURBS_KNOTS_MODE, 0);
return get_mutable_attribute<int8_t>(this->attribute_storage.wrap(),
AttrDomain::Curve,
ATTR_NURBS_KNOTS_MODE,
this->curves_num(),
0);
}
Span<float2> CurvesGeometry::surface_uv_coords() const
{
return get_span_attribute<float2>(*this, AttrDomain::Curve, ATTR_SURFACE_UV_COORDINATE);
return get_span_attribute<float2>(this->attribute_storage.wrap(),
AttrDomain::Curve,
ATTR_SURFACE_UV_COORDINATE,
this->curves_num());
}
MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
{
return get_mutable_attribute<float2>(*this, AttrDomain::Curve, ATTR_SURFACE_UV_COORDINATE);
return get_mutable_attribute<float2>(this->attribute_storage.wrap(),
AttrDomain::Curve,
ATTR_SURFACE_UV_COORDINATE,
this->curves_num());
}
Span<float> CurvesGeometry::nurbs_custom_knots() const
@@ -1186,11 +1172,12 @@ void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
void CurvesGeometry::resize(const int points_num, const int curves_num)
{
if (points_num != this->point_num) {
this->attribute_storage.wrap().resize(AttrDomain::Point, points_num);
CustomData_realloc(&this->point_data, this->points_num(), points_num);
this->point_num = points_num;
}
if (curves_num != this->curve_num) {
CustomData_realloc(&this->curve_data, this->curves_num(), curves_num);
this->attribute_storage.wrap().resize(AttrDomain::Curve, curves_num);
implicit_sharing::resize_trivial_array(&this->curve_offsets,
&this->runtime->curve_offsets_sharing_info,
this->curve_num == 0 ? 0 : (this->curve_num + 1),
@@ -1390,8 +1377,8 @@ void CurvesGeometry::count_memory(MemoryCounter &memory) const
memory.add_shared(this->runtime->curve_offsets_sharing_info, this->offsets().size_in_bytes());
memory.add_shared(this->runtime->custom_knots_sharing_info,
this->nurbs_custom_knots().size_in_bytes());
this->attribute_storage.wrap().count_memory(memory);
CustomData_count_memory(this->point_data, this->point_num, memory);
CustomData_count_memory(this->curve_data, this->curve_num, memory);
}
static void copy_point_selection_custom_knots(const CurvesGeometry &curves,
@@ -1732,7 +1719,7 @@ CurvesGeometry curves_new_no_attributes(int point_num, int curve_num)
{
CurvesGeometry curves(0, curve_num);
curves.point_num = point_num;
CustomData_free_layer_named(&curves.point_data, "position");
curves.attribute_storage.wrap().remove("position");
return curves;
}
@@ -1888,7 +1875,7 @@ void CurvesGeometry::blend_read(BlendDataReader &reader)
this->runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
CustomData_blend_read(&reader, &this->point_data, this->point_num);
CustomData_blend_read(&reader, &this->curve_data, this->curve_num);
CustomData_blend_read(&reader, &this->curve_data_legacy, this->curve_num);
this->attribute_storage.wrap().blend_read(reader);
if (this->curve_offsets) {
@@ -1899,9 +1886,6 @@ void CurvesGeometry::blend_read(BlendDataReader &reader)
});
}
/* Forward compatibility. To be removed when runtime format changes. */
curves_convert_storage_to_customdata(*this);
BLO_read_struct_list(&reader, bDeformGroup, &this->vertex_group_names);
if (this->custom_knot_num) {
@@ -1932,11 +1916,6 @@ void CurvesGeometry::blend_write_prepare(CurvesGeometry::BlendWriteData &write_d
this->points_num(),
write_data.point_layers,
write_data.attribute_data);
CustomData_blend_write_prepare(this->curve_data,
AttrDomain::Curve,
this->curves_num(),
write_data.curve_layers,
write_data.attribute_data);
this->attribute_storage.dna_attributes = write_data.attribute_data.attributes.data();
this->attribute_storage.dna_attributes_num = write_data.attribute_data.attributes.size();
}
@@ -1947,8 +1926,6 @@ void CurvesGeometry::blend_write(BlendWriter &writer,
{
CustomData_blend_write(
&writer, &this->point_data, write_data.point_layers, this->point_num, CD_MASK_ALL, &id);
CustomData_blend_write(
&writer, &this->curve_data, write_data.curve_layers, this->curve_num, CD_MASK_ALL, &id);
this->attribute_storage.wrap().blend_write(writer, write_data.attribute_data);
if (this->curve_offsets) {

View File

@@ -40,8 +40,11 @@ void fill_points(const OffsetIndices<int> points_by_curve,
CurvesGeometry copy_only_curve_domain(const CurvesGeometry &src_curves)
{
CurvesGeometry dst_curves(0, src_curves.curves_num());
CustomData_init_from(
&src_curves.curve_data, &dst_curves.curve_data, CD_MASK_ALL, src_curves.curves_num());
copy_attributes(src_curves.attributes(),
AttrDomain::Curve,
AttrDomain::Curve,
{},
dst_curves.attributes_for_write());
dst_curves.runtime->type_counts = src_curves.runtime->type_counts;
return dst_curves;
}

View File

@@ -78,6 +78,8 @@
#include "MEM_guardedalloc.h"
#include "attribute_storage_access.hh"
using blender::float3;
using blender::int3;
using blender::Span;
@@ -107,7 +109,7 @@ static void grease_pencil_init_data(ID *id)
grease_pencil->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
grease_pencil->set_active_node(nullptr);
CustomData_reset(&grease_pencil->layers_data);
CustomData_reset(&grease_pencil->layers_data_legacy);
new (&grease_pencil->attribute_storage.wrap()) blender::bke::AttributeStorage();
grease_pencil->runtime = MEM_new<GreasePencilRuntime>(__func__);
@@ -200,10 +202,6 @@ static void grease_pencil_copy_data(Main * /*bmain*/,
grease_pencil_dst->set_active_node(active_node);
}
CustomData_init_from(&grease_pencil_src->layers_data,
&grease_pencil_dst->layers_data,
CD_MASK_ALL,
grease_pencil_dst->layers().size());
new (&grease_pencil_dst->attribute_storage.wrap())
blender::bke::AttributeStorage(grease_pencil_src->attribute_storage.wrap());
@@ -228,7 +226,6 @@ static void grease_pencil_free_data(ID *id)
MEM_SAFE_FREE(grease_pencil->material_array);
CustomData_free(&grease_pencil->layers_data);
grease_pencil->attribute_storage.wrap().~AttributeStorage();
free_drawing_array(*grease_pencil);
@@ -273,11 +270,6 @@ static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *i
blender::Vector<CustomDataLayer, 16> layers_data_layers;
blender::bke::AttributeStorage::BlendWriteData attribute_data{scope};
attribute_storage_blend_write_prepare(grease_pencil->attribute_storage.wrap(), attribute_data);
CustomData_blend_write_prepare(grease_pencil->layers_data,
AttrDomain::Layer,
grease_pencil->layers().size(),
layers_data_layers,
attribute_data);
grease_pencil->attribute_storage.dna_attributes = attribute_data.attributes.data();
grease_pencil->attribute_storage.dna_attributes_num = attribute_data.attributes.size();
@@ -285,12 +277,6 @@ static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *i
BLO_write_id_struct(writer, GreasePencil, id_address, &grease_pencil->id);
BKE_id_blend_write(writer, &grease_pencil->id);
CustomData_blend_write(writer,
&grease_pencil->layers_data,
layers_data_layers,
grease_pencil->layers().size(),
CD_MASK_ALL,
id);
grease_pencil->attribute_storage.wrap().blend_write(*writer, attribute_data);
/* Write drawings. */
@@ -315,12 +301,10 @@ static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id)
/* Read layer tree. */
read_layer_tree(*grease_pencil, reader);
CustomData_blend_read(reader, &grease_pencil->layers_data, grease_pencil->layers().size());
CustomData_blend_read(
reader, &grease_pencil->layers_data_legacy, grease_pencil->layers().size());
grease_pencil->attribute_storage.wrap().blend_read(*reader);
/* Forward compatibility. To be removed when runtime format changes. */
blender::bke::grease_pencil_convert_storage_to_customdata(*grease_pencil);
/* Read materials. */
BLO_read_pointer_array(reader,
grease_pencil->material_array_num,
@@ -368,40 +352,6 @@ constexpr StringRef ATTR_OPACITY = "opacity";
constexpr StringRef ATTR_VERTEX_COLOR = "vertex_color";
constexpr StringRef ATTR_FILL_COLOR = "fill_color";
/* Curves attributes getters */
static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.points_num() : curves.curves_num();
}
static CustomData &domain_custom_data(CurvesGeometry &curves, const AttrDomain domain)
{
return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
}
template<typename T>
static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
const AttrDomain domain,
const StringRef name,
const T default_value = T())
{
const int num = domain_num(curves, domain);
if (num <= 0) {
return {};
}
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
CustomData &custom_data = domain_custom_data(curves, domain);
T *data = (T *)CustomData_get_layer_named_for_write(&custom_data, type, name, num);
if (data != nullptr) {
return {data, num};
}
data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name);
MutableSpan<T> span = {data, num};
if (span.first() != default_value) {
span.fill(default_value);
}
return span;
}
Drawing::Drawing()
{
this->base.type = GP_DRAWING;
@@ -823,8 +773,12 @@ VArray<float> Drawing::radii() const
MutableSpan<float> Drawing::radii_for_write()
{
return get_mutable_attribute<float>(
this->strokes_for_write(), AttrDomain::Point, ATTR_RADIUS, 0.01f);
return blender::bke::get_mutable_attribute<float>(
this->strokes_for_write().attribute_storage.wrap(),
AttrDomain::Point,
ATTR_RADIUS,
this->strokes().points_num(),
0.01f);
}
VArray<float> Drawing::opacities() const
@@ -835,8 +789,12 @@ VArray<float> Drawing::opacities() const
MutableSpan<float> Drawing::opacities_for_write()
{
return get_mutable_attribute<float>(
this->strokes_for_write(), AttrDomain::Point, ATTR_OPACITY, 1.0f);
return blender::bke::get_mutable_attribute<float>(
this->strokes_for_write().attribute_storage.wrap(),
AttrDomain::Point,
ATTR_OPACITY,
this->strokes().points_num(),
1.0f);
}
VArray<ColorGeometry4f> Drawing::vertex_colors() const
@@ -847,10 +805,12 @@ VArray<ColorGeometry4f> Drawing::vertex_colors() const
MutableSpan<ColorGeometry4f> Drawing::vertex_colors_for_write()
{
return get_mutable_attribute<ColorGeometry4f>(this->strokes_for_write(),
AttrDomain::Point,
ATTR_VERTEX_COLOR,
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
return blender::bke::get_mutable_attribute<ColorGeometry4f>(
this->strokes_for_write().attribute_storage.wrap(),
AttrDomain::Point,
ATTR_VERTEX_COLOR,
this->strokes().points_num(),
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
VArray<ColorGeometry4f> Drawing::fill_colors() const
@@ -861,10 +821,12 @@ VArray<ColorGeometry4f> Drawing::fill_colors() const
MutableSpan<ColorGeometry4f> Drawing::fill_colors_for_write()
{
return get_mutable_attribute<ColorGeometry4f>(this->strokes_for_write(),
AttrDomain::Curve,
ATTR_FILL_COLOR,
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
return blender::bke::get_mutable_attribute<ColorGeometry4f>(
this->strokes_for_write().attribute_storage.wrap(),
AttrDomain::Curve,
ATTR_FILL_COLOR,
this->strokes().curves_num(),
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
void Drawing::tag_texture_matrices_changed()
@@ -2122,7 +2084,6 @@ void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src,
}
/* Layers. */
CustomData_free(&grease_pencil_dst->layers_data);
if (grease_pencil_dst->root_group_ptr) {
MEM_delete(&grease_pencil_dst->root_group());
}
@@ -2134,10 +2095,8 @@ void BKE_grease_pencil_nomain_to_grease_pencil(GreasePencil *grease_pencil_src,
/* Reset the active node. */
grease_pencil_dst->active_node = nullptr;
CustomData_init_from(&grease_pencil_src->layers_data,
&grease_pencil_dst->layers_data,
eCustomDataMask(CD_MASK_ALL),
grease_pencil_src->layers().size());
grease_pencil_dst->attribute_storage.wrap() = std::move(
grease_pencil_src->attribute_storage.wrap());
DEG_id_tag_update(&grease_pencil_dst->id, ID_RECALC_GEOMETRY);
@@ -3724,7 +3683,7 @@ blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::String
using namespace blender;
std::string unique_name = check_name_is_unique ? unique_layer_name(name) : std::string(name);
const int numLayers = layers().size();
CustomData_realloc(&layers_data, numLayers, numLayers + 1, CD_SET_DEFAULT);
this->attribute_storage.wrap().resize(bke::AttrDomain::Layer, numLayers + 1);
bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__, unique_name);
/* Hide masks by default. */
new_layer->base.flag |= GP_LAYER_TREE_NODE_HIDE_MASKS;
@@ -3755,7 +3714,7 @@ void GreasePencil::add_layers_for_eval(const int num_new_layers)
{
using namespace blender;
const int num_layers = this->layers().size();
CustomData_realloc(&layers_data, num_layers, num_layers + num_new_layers);
this->attribute_storage.wrap().resize(bke::AttrDomain::Layer, num_layers + num_new_layers);
for ([[maybe_unused]] const int i : IndexRange(num_new_layers)) {
bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__);
/* Hide masks by default. */
@@ -3772,11 +3731,13 @@ blender::bke::greasepencil::Layer &GreasePencil::duplicate_layer(
std::optional<int> duplicate_layer_idx = get_layer_index(duplicate_layer);
BLI_assert(duplicate_layer_idx.has_value());
const int numLayers = layers().size();
CustomData_realloc(&layers_data, numLayers, numLayers + 1);
for (const int layer_index : IndexRange(layers_data.totlayer)) {
CustomData_copy_data_layer(
&layers_data, &layers_data, layer_index, layer_index, *duplicate_layer_idx, numLayers, 1);
}
this->attribute_storage.wrap().resize(bke::AttrDomain::Layer, numLayers + 1);
bke::MutableAttributeAccessor attributes = this->attributes_for_write();
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
bke::GSpanAttributeWriter attr = attributes.lookup_for_write_span(iter.name);
GMutableSpan span = attr.span;
span.type().copy_assign(span[*duplicate_layer_idx], span[numLayers]);
});
bke::greasepencil::Layer *new_layer = MEM_new<bke::greasepencil::Layer>(__func__,
duplicate_layer);
root_group().add_node(new_layer->as_node());
@@ -3817,17 +3778,30 @@ blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group(
return new_group;
}
static void reorder_customdata(CustomData &data, const Span<int> new_by_old_map)
static void reorder_attribute_domain(blender::bke::AttributeStorage &data,
const blender::bke::AttrDomain domain,
const Span<int> new_by_old_map)
{
CustomData new_data;
CustomData_init_layout_from(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, new_by_old_map.size());
for (const int old_i : new_by_old_map.index_range()) {
const int new_i = new_by_old_map[old_i];
CustomData_copy_data(&data, &new_data, old_i, new_i, 1);
}
CustomData_free(&data);
data = new_data;
using namespace blender;
data.foreach([&](bke::Attribute &attr) {
if (attr.domain() != domain) {
return;
}
const CPPType &type = bke::attribute_type_to_cpp_type(attr.data_type());
switch (attr.storage_type()) {
case bke::AttrStorageType::Array: {
const auto &data = std::get<bke::Attribute::ArrayData>(attr.data());
auto new_data = bke::Attribute::ArrayData::ForConstructed(type, new_by_old_map.size());
bke::attribute_math::gather(GSpan(type, data.data, data.size),
new_by_old_map,
GMutableSpan(type, new_data.data, new_data.size));
attr.assign_data(std::move(new_data));
}
case bke::AttrStorageType::Single: {
return;
}
}
});
}
static void reorder_layer_data(GreasePencil &grease_pencil,
@@ -3859,7 +3833,8 @@ static void reorder_layer_data(GreasePencil &grease_pencil,
BLI_assert(old_layer_index_by_layer.is_empty());
/* Use the mapping to re-order the custom data */
reorder_customdata(grease_pencil.layers_data, new_by_old_map);
reorder_attribute_domain(
grease_pencil.attribute_storage.wrap(), bke::AttrDomain::Layer, new_by_old_map);
}
void GreasePencil::move_node_up(blender::bke::greasepencil::TreeNode &node, const int step)
@@ -4143,27 +4118,34 @@ void GreasePencil::rename_node(Main &bmain,
}
}
static void shrink_customdata(CustomData &data, const int index_to_remove, const int size)
static void shrink_attribute_storage(blender::bke::AttributeStorage &storage,
const int index_to_remove,
const int size)
{
using namespace blender;
CustomData new_data;
CustomData_init_layout_from(&data, &new_data, CD_MASK_ALL, CD_CONSTRUCT, size);
CustomData_realloc(&new_data, size, size - 1);
const IndexRange range_before(index_to_remove);
const IndexRange range_after(index_to_remove + 1, size - index_to_remove - 1);
if (!range_before.is_empty()) {
CustomData_copy_data(
&data, &new_data, range_before.start(), range_before.start(), range_before.size());
}
if (!range_after.is_empty()) {
CustomData_copy_data(
&data, &new_data, range_after.start(), range_after.start() - 1, range_after.size());
}
storage.foreach([&](bke::Attribute &attr) {
const CPPType &type = bke::attribute_type_to_cpp_type(attr.data_type());
switch (attr.storage_type()) {
case bke::AttrStorageType::Array: {
const auto &data = std::get<bke::Attribute::ArrayData>(attr.data());
CustomData_free(&data);
data = new_data;
auto new_data = bke::Attribute::ArrayData::ForUninitialized(type, size - 1);
type.copy_construct_n(data.data, new_data.data, range_before.size());
type.copy_construct_n(
POINTER_OFFSET(data.data, type.size * range_after.start()),
POINTER_OFFSET(new_data.data, type.size * (range_before.start() - 1)),
range_after.size());
attr.assign_data(std::move(new_data));
}
case bke::AttrStorageType::Single: {
return;
}
}
});
}
static void update_active_node_from_node_to_remove(
@@ -4200,7 +4182,7 @@ void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
/* Remove all the layer attributes and shrink the `CustomData`. */
const int layer_index = *this->get_layer_index(layer);
shrink_customdata(this->layers_data, layer_index, this->layers().size());
shrink_attribute_storage(this->attribute_storage.wrap(), layer_index, this->layers().size());
/* Unlink the layer from the parent group. */
layer.parent_group().unlink_node(layer.as_node());

View File

@@ -2,77 +2,148 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_attribute_storage.hh"
#include "BKE_grease_pencil.hh"
#include "DNA_grease_pencil_types.h"
#include "attribute_access_intern.hh"
#include "attribute_storage_access.hh"
namespace blender ::bke::greasepencil {
namespace blender::bke::greasepencil {
static GeometryAttributeProviders create_attribute_providers_for_grease_pencil()
static const auto &changed_tags()
{
static CustomDataAccessInfo layers_access = {
[](void *owner) -> CustomData * {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(owner);
return &grease_pencil.layers_data;
},
[](const void *owner) -> const CustomData * {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return &grease_pencil.layers_data;
},
[](const void *owner) -> int {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return grease_pencil.layers().size();
}};
static CustomDataAttributeProvider layer_custom_data(AttrDomain::Layer, layers_access);
return GeometryAttributeProviders({}, {&layer_custom_data});
static Map<StringRef, AttrUpdateOnChange> attributes;
return attributes;
}
static GVArray adapt_grease_pencil_attribute_domain(const GreasePencil & /*grease_pencil*/,
const GVArray &varray,
const AttrDomain from,
const AttrDomain to)
static const auto &builtin_attributes()
{
if (from == to) {
return varray;
}
return {};
static auto attributes = []() {
Map<StringRef, AttrBuiltinInfo> map;
return map;
}();
return attributes;
}
static int get_domain_size(const void *owner, const AttrDomain domain)
{
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return domain == AttrDomain::Layer ? grease_pencil.layers().size() : 0;
}
static AttributeAccessorFunctions get_grease_pencil_accessor_functions()
{
static const GeometryAttributeProviders providers =
create_attribute_providers_for_grease_pencil();
AttributeAccessorFunctions fn =
attribute_accessor_functions::accessor_functions_for_providers<providers>();
fn.domain_size = [](const void *owner, const AttrDomain domain) {
if (owner == nullptr) {
return 0;
}
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
switch (domain) {
case AttrDomain::Layer:
return int(grease_pencil.layers().size());
default:
return 0;
}
};
AttributeAccessorFunctions fn{};
fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
return domain == AttrDomain::Layer;
};
fn.adapt_domain = [](const void *owner,
const GVArray &varray,
const AttrDomain from_domain,
const AttrDomain to_domain) -> GVArray {
if (owner == nullptr) {
fn.domain_size = get_domain_size;
fn.builtin_domain_and_type = [](const void * /*owner*/, const StringRef /*name*/)
-> std::optional<AttributeDomainAndType> { return std::nullopt; };
fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
const AttributeStorage &storage = grease_pencil.attribute_storage.wrap();
const Attribute *attribute = storage.lookup(name);
if (!attribute) {
return {};
}
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
return adapt_grease_pencil_attribute_domain(grease_pencil, varray, from_domain, to_domain);
const int domain_size = get_domain_size(owner, AttrDomain::Layer);
return attribute_to_reader(*attribute, AttrDomain::Layer, domain_size);
};
fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer {
const AttrBuiltinInfo &info = builtin_attributes().lookup(name);
return info.default_value;
};
fn.adapt_domain = [](const void * /*owner*/,
const GVArray &varray,
const AttrDomain from_domain,
const AttrDomain to_domain) {
if (from_domain == to_domain && from_domain == AttrDomain::Layer) {
return varray;
}
return GVArray{};
};
fn.foreach_attribute = [](const void *owner,
const FunctionRef<void(const AttributeIter &)> fn,
const AttributeAccessor &accessor) {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(owner);
const AttributeStorage &storage = grease_pencil.attribute_storage.wrap();
storage.foreach_with_stop([&](const Attribute &attribute) {
const auto get_fn = [&]() {
const int domain_size = get_domain_size(owner, AttrDomain::Layer);
return attribute_to_reader(attribute, AttrDomain::Layer, domain_size);
};
const std::optional<eCustomDataType> cd_type = attr_type_to_custom_data_type(
attribute.data_type());
BLI_assert(cd_type.has_value());
AttributeIter iter(attribute.name(), attribute.domain(), *cd_type, get_fn);
iter.is_builtin = builtin_attributes().contains(attribute.name());
iter.accessor = &accessor;
fn(iter);
return !iter.is_stopped();
});
};
fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator {
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
if (!info) {
return {};
}
return info->validator;
};
fn.lookup_for_write = [](void *owner, const StringRef name) -> GAttributeWriter {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(owner);
AttributeStorage &storage = grease_pencil.attribute_storage.wrap();
Attribute *attribute = storage.lookup(name);
if (!attribute) {
return {};
}
const int domain_size = get_domain_size(owner, AttrDomain::Layer);
return attribute_to_writer(&grease_pencil, {}, domain_size, *attribute);
};
fn.remove = [](void *owner, const StringRef name) -> bool {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(owner);
AttributeStorage &storage = grease_pencil.attribute_storage.wrap();
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (!info->deletable) {
return false;
}
}
const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name);
const bool removed = storage.remove(name);
if (!removed) {
return false;
}
if (fn) {
(*fn)(owner);
}
return true;
};
fn.add = [](void *owner,
const StringRef name,
const AttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer) {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(owner);
const int domain_size = get_domain_size(owner, domain);
AttributeStorage &storage = grease_pencil.attribute_storage.wrap();
const std::optional<AttrType> type = custom_data_type_to_attr_type(data_type);
BLI_assert(type.has_value());
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (info->domain != domain || info->type != type) {
return false;
}
}
if (storage.lookup(name)) {
return false;
}
Attribute::DataVariant data = attribute_init_to_data(*type, domain_size, initializer);
storage.add(name, domain, *type, std::move(data));
return true;
};
return fn;
}

View File

@@ -7,7 +7,6 @@
#include "BLI_string.h"
#include "BKE_curves.hh"
#include "BKE_customdata.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
@@ -129,7 +128,7 @@ struct GreasePencilHelper : public ::GreasePencil {
this->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
this->active_node = nullptr;
CustomData_reset(&this->layers_data);
new (&this->attribute_storage.wrap()) blender::bke::AttributeStorage();
this->drawing_array = nullptr;
this->drawing_array_num = 0;
@@ -139,7 +138,7 @@ struct GreasePencilHelper : public ::GreasePencil {
~GreasePencilHelper()
{
CustomData_free(&this->layers_data);
this->attribute_storage.wrap().~AttributeStorage();
MEM_delete(&this->root_group());
MEM_delete(this->runtime);
this->runtime = nullptr;

View File

@@ -745,17 +745,20 @@ static void convert_grease_pencil_stroke_hardness_to_softness(GreasePencil *grea
}
bke::greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
const int layer_index = CustomData_get_named_layer_index(
&drawing.geometry.curve_data, CD_PROP_FLOAT, "hardness");
&drawing.geometry.curve_data_legacy, CD_PROP_FLOAT, "hardness");
if (layer_index == -1) {
continue;
}
float *data = static_cast<float *>(CustomData_get_layer_named_for_write(
&drawing.geometry.curve_data, CD_PROP_FLOAT, "hardness", drawing.geometry.curve_num));
float *data = static_cast<float *>(
CustomData_get_layer_named_for_write(&drawing.geometry.curve_data_legacy,
CD_PROP_FLOAT,
"hardness",
drawing.geometry.curve_num));
for (const int i : IndexRange(drawing.geometry.curve_num)) {
data[i] = 1.0f - data[i];
}
/* Rename the layer. */
STRNCPY(drawing.geometry.curve_data.layers[layer_index].name, "softness");
STRNCPY(drawing.geometry.curve_data_legacy.layers[layer_index].name, "softness");
}
}

View File

@@ -98,14 +98,14 @@ static void fix_built_in_curve_attribute_defaults(Main *bmain)
LISTBASE_FOREACH (Curves *, curves, &bmain->hair_curves) {
const int curves_num = curves->geometry.curve_num;
if (int *resolutions = static_cast<int *>(CustomData_get_layer_named_for_write(
&curves->geometry.curve_data, CD_PROP_INT32, "resolution", curves_num)))
&curves->geometry.curve_data_legacy, CD_PROP_INT32, "resolution", curves_num)))
{
for (int &resolution : blender::MutableSpan{resolutions, curves_num}) {
resolution = std::max(resolution, 1);
}
}
if (int8_t *nurb_orders = static_cast<int8_t *>(CustomData_get_layer_named_for_write(
&curves->geometry.curve_data, CD_PROP_INT8, "nurbs_order", curves_num)))
&curves->geometry.curve_data_legacy, CD_PROP_INT8, "nurbs_order", curves_num)))
{
for (int8_t &nurbs_order : blender::MutableSpan{nurb_orders, curves_num}) {
nurbs_order = std::max<int8_t>(nurbs_order, 1);

View File

@@ -87,7 +87,7 @@ static void fix_curve_nurbs_knot_mode_custom(Main *bmain)
}
int8_t *knot_modes = static_cast<int8_t *>(CustomData_get_layer_named_for_write(
&curves.curve_data, CD_PROP_INT8, "knots_mode", curves.curve_num));
&curves.curve_data_legacy, CD_PROP_INT8, "knots_mode", curves.curve_num));
if (knot_modes == nullptr) {
return;
}

View File

@@ -11,6 +11,8 @@
#include <fmt/format.h>
#include "DNA_ID.h"
#include "DNA_curves_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
@@ -29,6 +31,7 @@
#include "BKE_animsys.h"
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_colortools.hh"
#include "BKE_curves.hh"
#include "BKE_idprop.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
@@ -1215,7 +1218,7 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 31)) {
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 32)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
@@ -1261,6 +1264,22 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 33)) {
LISTBASE_FOREACH (Curves *, curves, &bmain->hair_curves) {
blender::bke::curves_convert_customdata_to_storage(curves->geometry.wrap());
}
LISTBASE_FOREACH (GreasePencil *, grease_pencil, &bmain->grease_pencils) {
blender::bke::grease_pencil_convert_customdata_to_storage(*grease_pencil);
for (const int i : IndexRange(grease_pencil->drawing_array_num)) {
GreasePencilDrawingBase *drawing_base = grease_pencil->drawing_array[i];
if (drawing_base->type == GP_DRAWING) {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
blender::bke::curves_convert_customdata_to_storage(drawing->geometry.wrap());
}
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@@ -732,120 +732,39 @@ static void calc_final_indices(const bke::CurvesGeometry &curves,
cache.final.proc_hairs = GPU_batch_create_ex(prim_type, vbo, ibo, owns_flag);
}
static std::optional<StringRef> get_first_uv_name(const bke::AttributeAccessor &attributes)
{
std::optional<StringRef> name;
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
if (iter.data_type == CD_PROP_FLOAT2) {
name = iter.name;
iter.stop();
}
});
return name;
}
static bool ensure_attributes(const Curves &curves,
CurvesBatchCache &cache,
const GPUMaterial *gpu_material)
{
const CustomData &cd_curve = curves.geometry.curve_data;
const CustomData &cd_point = curves.geometry.point_data;
const bke::AttributeAccessor attributes = curves.geometry.wrap().attributes();
CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
if (gpu_material) {
/* The following code should be kept in sync with `mesh_cd_calc_used_gpu_layers`. */
VectorSet<std::string> attrs_needed;
ListBase gpu_attrs = GPU_material_attributes(gpu_material);
LISTBASE_FOREACH (const GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
StringRef name = gpu_attr->name;
eCustomDataType type = eCustomDataType(gpu_attr->type);
int layer = -1;
std::optional<bke::AttrDomain> domain;
if (gpu_attr->type == CD_AUTO_FROM_NAME) {
/* We need to deduce what exact layer is used.
*
* We do it based on the specified name.
*/
if (!name.is_empty()) {
layer = CustomData_get_named_layer(&cd_curve, CD_PROP_FLOAT2, name);
type = CD_MTFACE;
domain = bke::AttrDomain::Curve;
if (layer == -1) {
/* Try to match a generic attribute, we use the first attribute domain with a
* matching name. */
if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
domain = bke::AttrDomain::Point;
}
else if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
domain = bke::AttrDomain::Curve;
}
else {
domain.reset();
layer = -1;
}
}
if (layer == -1) {
continue;
}
}
else {
/* Fall back to the UV layer, which matches old behavior. */
type = CD_MTFACE;
if (name.is_empty()) {
if (std::optional<StringRef> uv_name = get_first_uv_name(attributes)) {
drw_attributes_add_request(&attrs_needed, *uv_name);
}
}
else {
if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
domain = bke::AttrDomain::Curve;
}
else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
domain = bke::AttrDomain::Point;
}
}
switch (type) {
case CD_MTFACE: {
if (layer == -1) {
layer = !name.is_empty() ?
CustomData_get_named_layer(&cd_curve, CD_PROP_FLOAT2, name) :
CustomData_get_render_layer(&cd_curve, CD_PROP_FLOAT2);
if (layer != -1) {
domain = bke::AttrDomain::Curve;
}
}
if (layer == -1) {
layer = !name.is_empty() ?
CustomData_get_named_layer(&cd_point, CD_PROP_FLOAT2, name) :
CustomData_get_render_layer(&cd_point, CD_PROP_FLOAT2);
if (layer != -1) {
domain = bke::AttrDomain::Point;
}
}
if (layer != -1 && !name.is_empty() && domain.has_value()) {
name = CustomData_get_layer_name(
domain == bke::AttrDomain::Curve ? &cd_curve : &cd_point, CD_PROP_FLOAT2, layer);
}
if (layer != -1 && domain.has_value()) {
drw_attributes_add_request(&attrs_needed, name);
}
break;
}
case CD_TANGENT:
case CD_ORCO:
break;
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:
case CD_PROP_INT32:
case CD_PROP_INT16_2D:
case CD_PROP_INT32_2D:
case CD_PROP_FLOAT:
case CD_PROP_FLOAT2: {
if (layer != -1 && domain.has_value()) {
drw_attributes_add_request(&attrs_needed, name);
}
break;
}
default:
break;
if (!attributes.contains(name)) {
continue;
}
drw_attributes_add_request(&attrs_needed, name);
}
if (!drw_attributes_overlap(&final_cache.attr_used, &attrs_needed)) {

View File

@@ -270,6 +270,18 @@ gpu::VertBuf *curves_pos_buffer_get(Scene *scene, Object *object)
return cache->final.proc_buf;
}
static std::optional<StringRef> get_first_uv_name(const bke::AttributeAccessor &attributes)
{
std::optional<StringRef> name;
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
if (iter.data_type == CD_PROP_FLOAT2) {
name = iter.name;
iter.stop();
}
});
return name;
}
template<typename PassT>
gpu::Batch *curves_sub_pass_setup_implementation(PassT &sub_ps,
const Scene *scene,
@@ -326,17 +338,8 @@ gpu::Batch *curves_sub_pass_setup_implementation(PassT &sub_ps,
sub_ps.bind_texture("l", curves_cache->proc_length_buf);
}
StringRef curve_data_render_uv;
StringRef point_data_render_uv;
if (CustomData_has_layer(&curves_id.geometry.curve_data, CD_PROP_FLOAT2)) {
curve_data_render_uv = CustomData_get_render_layer_name(&curves_id.geometry.curve_data,
CD_PROP_FLOAT2);
}
if (CustomData_has_layer(&curves_id.geometry.point_data, CD_PROP_FLOAT2)) {
point_data_render_uv = CustomData_get_render_layer_name(&curves_id.geometry.point_data,
CD_PROP_FLOAT2);
}
const std::optional<StringRef> uv_name = get_first_uv_name(
curves_id.geometry.wrap().attributes());
const VectorSet<std::string> &attrs = curves_cache->final.attr_used;
for (const int i : attrs.index_range()) {
const StringRef name = attrs[i];
@@ -348,7 +351,7 @@ gpu::Batch *curves_sub_pass_setup_implementation(PassT &sub_ps,
continue;
}
sub_ps.bind_texture(sampler_name, curves_cache->proc_attributes_buf[i]);
if (name == curve_data_render_uv) {
if (name == uv_name) {
sub_ps.bind_texture("a", curves_cache->proc_attributes_buf[i]);
}
}
@@ -357,7 +360,7 @@ gpu::Batch *curves_sub_pass_setup_implementation(PassT &sub_ps,
continue;
}
sub_ps.bind_texture(sampler_name, curves_cache->final.attributes_buf[i]);
if (name == point_data_render_uv) {
if (name == uv_name) {
sub_ps.bind_texture("a", curves_cache->final.attributes_buf[i]);
}
}

View File

@@ -213,7 +213,7 @@ bool attribute_set_poll(bContext &C, const ID &object_data)
return false;
}
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeAccessor attributes = *owner.get_accessor();
std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(*name);
if (!meta_data) {
@@ -298,7 +298,7 @@ static wmOperatorStatus geometry_attribute_add_exec(bContext *C, wmOperator *op)
bke::AttrDomain domain = bke::AttrDomain(RNA_enum_get(op->ptr, "domain"));
AttributeOwner owner = AttributeOwner::from_id(id);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::MutableAttributeAccessor accessor = *owner.get_accessor();
if (!accessor.domain_supported(bke::AttrDomain(domain))) {
BKE_report(op->reports, RPT_ERROR, "Attribute domain not supported by this geometry type");

View File

@@ -36,6 +36,7 @@
#include "BKE_anim_data.hh"
#include "BKE_animsys.h"
#include "BKE_attribute.hh"
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_context.hh"
#include "BKE_curves_utils.hh"
#include "BKE_customdata.hh"
@@ -4798,6 +4799,46 @@ static void remap_vertex_groups(bke::greasepencil::Drawing &drawing,
* Only the names of the groups change. */
}
static bke::AttributeStorage merge_attributes(const bke::AttributeAccessor &a,
const bke::AttributeAccessor &b,
const int dst_size)
{
Map<std::string, eCustomDataType> new_types;
const auto add_or_upgrade_types = [&](const bke::AttributeAccessor &attributes) {
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
new_types.add_or_modify(
iter.name,
[&](eCustomDataType *value) { *value = iter.data_type; },
[&](eCustomDataType *value) {
*value = bke::attribute_data_type_highest_complexity({*value, iter.data_type});
});
});
};
add_or_upgrade_types(a);
add_or_upgrade_types(b);
const int64_t domain_size_a = a.domain_size(bke::AttrDomain::Layer);
bke::AttributeStorage new_storage;
for (const auto &[name, type] : new_types.items()) {
const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(type);
auto new_data = bke::Attribute::ArrayData::ForUninitialized(cpp_type, dst_size);
const GVArray data_a = *a.lookup_or_default(name, bke::AttrDomain::Layer, type);
data_a.materialize_to_uninitialized(new_data.data);
const GVArray data_b = *b.lookup_or_default(name, bke::AttrDomain::Layer, type);
data_b.materialize_to_uninitialized(
POINTER_OFFSET(new_data.data, cpp_type.size * domain_size_a));
new_storage.add(name,
bke::AttrDomain::Layer,
*bke::custom_data_type_to_attr_type(type),
std::move(new_data));
}
return new_storage;
}
static void join_object_with_active(Main &bmain,
Object &ob_src,
Object &ob_dst,
@@ -4849,17 +4890,9 @@ static void join_object_with_active(Main &bmain,
grease_pencil_src.root_group(),
layer_name_map);
/* Copy custom attributes for new layers. */
CustomData_merge_layout(&grease_pencil_src.layers_data,
&grease_pencil_dst.layers_data,
CD_MASK_ALL,
CD_SET_DEFAULT,
grease_pencil_dst.layers().size());
CustomData_copy_data(&grease_pencil_src.layers_data,
&grease_pencil_dst.layers_data,
0,
orig_layers_num,
grease_pencil_src.layers().size());
grease_pencil_dst.attribute_storage.wrap() = merge_attributes(grease_pencil_src.attributes(),
grease_pencil_dst.attributes(),
grease_pencil_dst.layers().size());
/* Fix names, indices and transforms to keep relationships valid. */
for (const int layer_index : grease_pencil_dst.layers().index_range()) {

View File

@@ -196,7 +196,7 @@ class StepObject {
int layers_num_ = 0;
bke::greasepencil::LayerGroup root_group_;
std::string active_node_name_;
CustomData layers_data_ = {};
bke::AttributeStorage layer_attributes_;
void encode_drawings(const GreasePencil &grease_pencil, StepEncodeStatus &encode_status)
{
@@ -252,9 +252,7 @@ class StepObject {
void encode_layers(const GreasePencil &grease_pencil, StepEncodeStatus & /*encode_status*/)
{
layers_num_ = int(grease_pencil.layers().size());
CustomData_init_from(
&grease_pencil.layers_data, &layers_data_, eCustomDataMask(CD_MASK_ALL), layers_num_);
layer_attributes_ = grease_pencil.attribute_storage.wrap();
if (grease_pencil.active_node != nullptr) {
active_node_name_ = grease_pencil.get_active_node()->name();
@@ -281,16 +279,11 @@ class StepObject {
}
}
CustomData_free(&grease_pencil.layers_data);
CustomData_init_from(
&layers_data_, &grease_pencil.layers_data, eCustomDataMask(CD_MASK_ALL), layers_num_);
grease_pencil.attribute_storage.wrap() = layer_attributes_;
}
public:
~StepObject()
{
CustomData_free(&layers_data_);
}
~StepObject() = default;
void encode(Object *ob, StepEncodeStatus &encode_status)
{

View File

@@ -178,6 +178,44 @@ static void reorder_customdata_groups(CustomData &data,
data = new_data;
}
static void reorder_attribute_groups(bke::AttributeStorage &storage,
const bke::AttrDomain domain,
const OffsetIndices<int> old_offsets,
const OffsetIndices<int> new_offsets,
const Span<int> new_by_old_map)
{
const int groups_num = new_by_old_map.size();
storage.foreach([&](bke::Attribute &attr) {
if (attr.domain() != domain) {
return;
}
const CPPType &type = bke::attribute_type_to_cpp_type(attr.data_type());
switch (attr.storage_type()) {
case bke::AttrStorageType::Array: {
const auto &data = std::get<bke::Attribute::ArrayData>(attr.data());
auto new_data = bke::Attribute::ArrayData::ForUninitialized(type, new_by_old_map.size());
threading::parallel_for(IndexRange(groups_num), 1024, [&](const IndexRange range) {
for (const int old_i : range) {
const int new_i = new_by_old_map[old_i];
const IndexRange old_range = old_offsets[old_i];
const IndexRange new_range = new_offsets[new_i];
BLI_assert(old_range.size() == new_range.size());
type.copy_construct_n(POINTER_OFFSET(data.data, old_range.start() * type.size),
POINTER_OFFSET(new_data.data, new_range.start() * type.size),
old_range.size());
}
});
attr.assign_data(std::move(new_data));
}
case bke::AttrStorageType::Single: {
return;
}
}
});
}
void debug_randomize_face_order(Mesh *mesh)
{
if (mesh == nullptr || mesh->faces_num == 0 || !use_debug_randomization()) {
@@ -226,7 +264,9 @@ void debug_randomize_curve_order(bke::CurvesGeometry *curves)
const Array<int> new_by_old_map = get_permutation(curves->curve_num, seed);
const Array<int> old_by_new_map = invert_permutation(new_by_old_map);
reorder_customdata(curves->curve_data, new_by_old_map);
bke::AttributeStorage &attributes = curves->attribute_storage.wrap();
reorder_attribute_domain(attributes, bke::AttrDomain::Curve, new_by_old_map);
const OffsetIndices old_points_by_curve = curves->points_by_curve();
Array<int> new_curve_offsets = make_new_offset_indices(old_points_by_curve, old_by_new_map);
@@ -234,6 +274,11 @@ void debug_randomize_curve_order(bke::CurvesGeometry *curves)
reorder_customdata_groups(
curves->point_data, old_points_by_curve, new_points_by_curve, new_by_old_map);
reorder_attribute_groups(attributes,
bke::AttrDomain::Point,
old_points_by_curve,
new_points_by_curve,
new_by_old_map);
curves->offsets_for_write().copy_from(new_curve_offsets);

View File

@@ -95,7 +95,7 @@ typedef enum NormalMode {
* A reusable data structure for geometry consisting of many curves. All control point data is
* stored contiguously for better efficiency when there are many curves. Multiple curve types are
* supported, as described in #CurveType. Data for each curve is accessed by slicing the main
* #point_data arrays.
* point attribute data arrays.
*
* The data structure is meant to separate geometry data storage and processing from Blender
* focused ID data-block handling. The struct can also be embedded to allow reusing it.
@@ -117,22 +117,16 @@ typedef struct CurvesGeometry {
*/
int *curve_offsets;
/**
* Curve and point domain attributes. Currently unused at runtime, but used for forward
* compatibility when reading files (see #122398).
*/
/** Curve and point domain attributes. */
struct AttributeStorage attribute_storage;
/**
* All attributes stored on control points (#AttrDomain::Point).
* This might not contain a layer for positions if there are no points.
* Generic attributes are stored in #attribute_storage. This is still used for vertex groups.
*/
CustomData point_data;
/**
* All attributes stored on curves (#AttrDomain::Curve).
*/
CustomData curve_data;
/** Used only for backward compatibility with old files. */
CustomData curve_data_legacy;
/**
* The total number of control points in all curves.

View File

@@ -463,14 +463,11 @@ typedef struct GreasePencil {
/* Root group of the layer tree. */
GreasePencilLayerTreeGroup *root_group_ptr;
/**
* All attributes stored on the grease pencil layers (#AttrDomain::Layer).
*/
CustomData layers_data;
/** Used only for backward compatibility with old files. */
CustomData layers_data_legacy;
/**
* Layer domain attributes. Currently unused at runtime, but used for forward
* compatibility when reading files (see #122398).
* Layer domain attributes.
*/
struct AttributeStorage attribute_storage;

View File

@@ -89,6 +89,7 @@ DNA_STRUCT_RENAME_MEMBER(Curve, texflag, texspace_flag)
DNA_STRUCT_RENAME_MEMBER(Curve, type, ob_type)
DNA_STRUCT_RENAME_MEMBER(Curve, width, offset)
DNA_STRUCT_RENAME_MEMBER(Curves, attributes_active_index, attributes_active_index_legacy)
DNA_STRUCT_RENAME_MEMBER(CurvesGeometry, curve_data, curve_data_legacy)
DNA_STRUCT_RENAME_MEMBER(CurvesGeometry, curve_size, curve_num)
DNA_STRUCT_RENAME_MEMBER(CurvesGeometry, point_size, point_num)
DNA_STRUCT_RENAME_MEMBER(CustomDataExternal, filename, filepath)
@@ -107,6 +108,7 @@ DNA_STRUCT_RENAME_MEMBER(FluidDomainSettings, guiding_source, guide_source)
DNA_STRUCT_RENAME_MEMBER(FluidDomainSettings, guiding_vel_factor, guide_vel_factor)
DNA_STRUCT_RENAME_MEMBER(FluidEffectorSettings, guiding_mode, guide_mode)
DNA_STRUCT_RENAME_MEMBER(GreasePencil, drawing_array_size, drawing_array_num)
DNA_STRUCT_RENAME_MEMBER(GreasePencil, layers_data, layers_data_legacy)
DNA_STRUCT_RENAME_MEMBER(GreasePencil, material_array_size, material_array_num)
DNA_STRUCT_RENAME_MEMBER(GreasePencilLayerFramesMapStorage, size, num)
DNA_STRUCT_RENAME_MEMBER(HookModifierData, totindex, indexar_num)

View File

@@ -191,6 +191,7 @@ const EnumPropertyItem rna_enum_attribute_curves_domain_items[] = {
# include "BLI_string.h"
# include "BKE_attribute_legacy_convert.hh"
# include "BKE_curves.hh"
# include "BKE_customdata.hh"
# include "BKE_report.hh"
@@ -206,36 +207,42 @@ using blender::StringRef;
/* Attribute */
static bool find_attr_with_pointer(const blender::bke::AttributeStorage &storage,
const blender::bke::Attribute &attr)
{
bool found_attr = false;
storage.foreach_with_stop([&](const blender::bke::Attribute &attr_iter) {
if (&attr_iter == &attr) {
found_attr = true;
return false;
}
return true;
});
return found_attr;
}
static AttributeOwner owner_from_attribute_pointer_rna(PointerRNA *ptr)
{
using namespace blender;
ID *owner_id = ptr->owner_id;
/* TODO: Because we don't know the path to the `ptr`, we need to look though all possible
* candidates and search for the `layer` currently. This should be just a simple lookup. */
if (GS(owner_id->name) == ID_GP) {
const CustomDataLayer *layer = static_cast<const CustomDataLayer *>(ptr->data);
bke::Attribute *attr = static_cast<bke::Attribute *>(ptr->data);
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(owner_id);
/* First check the layer attributes. */
CustomData *layers_data = &grease_pencil->layers_data;
for (int i = 0; i < layers_data->totlayer; i++) {
if (&layers_data->layers[i] == layer) {
return AttributeOwner(AttributeOwnerType::GreasePencil, grease_pencil);
}
if (find_attr_with_pointer(grease_pencil->attribute_storage.wrap(), *attr)) {
return AttributeOwner(AttributeOwnerType::GreasePencil, grease_pencil);
}
/* Now check all the drawings. */
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type == GP_DRAWING) {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(base);
CustomData *curve_data = &drawing->geometry.curve_data;
for (int i = 0; i < curve_data->totlayer; i++) {
if (&curve_data->layers[i] == layer) {
return AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
}
}
CustomData *point_data = &drawing->geometry.point_data;
for (int i = 0; i < point_data->totlayer; i++) {
if (&point_data->layers[i] == layer) {
return AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
}
const bke::CurvesGeometry &curves = drawing->geometry.wrap();
if (find_attr_with_pointer(curves.attribute_storage.wrap(), *attr)) {
return AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
}
}
}
@@ -256,7 +263,7 @@ static AttributeOwner owner_from_pointer_rna(PointerRNA *ptr)
static std::optional<std::string> rna_Attribute_path(const PointerRNA *ptr)
{
using namespace blender;
if (GS(ptr->owner_id->name) == ID_PT) {
if (GS(ptr->owner_id->name) != ID_ME) {
bke::Attribute *attr = ptr->data_as<bke::Attribute>();
const std::string escaped_name = BLI_str_escape(attr->name().c_str());
return fmt::format("attributes[\"{}\"]", escaped_name);
@@ -305,7 +312,7 @@ static StructRNA *srna_by_custom_data_layer_type(const eCustomDataType type)
static StructRNA *rna_Attribute_refine(PointerRNA *ptr)
{
using namespace blender;
if (GS(ptr->owner_id->name) == ID_PT) {
if (GS(ptr->owner_id->name) != ID_ME) {
bke::Attribute *attr = ptr->data_as<bke::Attribute>();
const eCustomDataType data_type = *bke::attr_type_to_custom_data_type(attr->data_type());
return srna_by_custom_data_layer_type(data_type);
@@ -319,7 +326,7 @@ static void rna_Attribute_name_get(PointerRNA *ptr, char *value)
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
attr->name().copy_unsafe(value);
return;
@@ -332,7 +339,7 @@ static int rna_Attribute_name_length(PointerRNA *ptr)
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
return attr->name().size();
}
@@ -345,7 +352,7 @@ static void rna_Attribute_name_set(PointerRNA *ptr, const char *value)
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
BKE_attribute_rename(owner, attr->name(), value, nullptr);
return;
@@ -359,7 +366,7 @@ static int rna_Attribute_name_editable(const PointerRNA *ptr, const char **r_inf
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(const_cast<PointerRNA *>(ptr));
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::Attribute *attr = ptr->data_as<bke::Attribute>();
if (BKE_attribute_required(owner, attr->name())) {
*r_info = N_("Cannot modify name of required geometry attribute");
@@ -380,7 +387,7 @@ static int rna_Attribute_name_editable(const PointerRNA *ptr, const char **r_inf
static int rna_Attribute_type_get(PointerRNA *ptr)
{
using namespace blender;
if (GS(ptr->owner_id->name) == ID_PT) {
if (GS(ptr->owner_id->name) != ID_ME) {
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
return *bke::attr_type_to_custom_data_type(attr->data_type());
}
@@ -392,7 +399,7 @@ static int rna_Attribute_type_get(PointerRNA *ptr)
static int rna_Attribute_storage_type_get(PointerRNA *ptr)
{
using namespace blender;
if (GS(ptr->owner_id->name) == ID_PT) {
if (GS(ptr->owner_id->name) != ID_ME) {
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
return int(attr->storage_type());
}
@@ -464,7 +471,7 @@ static int rna_Attribute_domain_get(PointerRNA *ptr)
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
return int(attr->domain());
}
@@ -475,7 +482,7 @@ static int rna_Attribute_domain_get(PointerRNA *ptr)
static bool rna_Attribute_is_internal_get(PointerRNA *ptr)
{
using namespace blender;
if (GS(ptr->owner_id->name) == ID_PT) {
if (GS(ptr->owner_id->name) != ID_ME) {
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
return !bke::allow_procedural_attribute_access(attr->name());
}
@@ -488,7 +495,7 @@ static bool rna_Attribute_is_required_get(PointerRNA *ptr)
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
return BKE_attribute_required(owner, attr->name());
}
@@ -501,7 +508,7 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::MutableAttributeAccessor accessor = *owner.get_accessor();
bke::Attribute *attr = ptr->data_as<bke::Attribute>();
@@ -538,7 +545,7 @@ static int rna_Attribute_data_length(PointerRNA *ptr)
{
using namespace blender;
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
const bke::AttributeAccessor accessor = *owner.get_accessor();
return accessor.domain_size(attr->domain());
@@ -638,7 +645,7 @@ static PointerRNA rna_AttributeGroupID_new(
{
using namespace blender;
AttributeOwner owner = AttributeOwner::from_id(id);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::AttributeAccessor accessor = *owner.get_accessor();
if (!accessor.domain_supported(AttrDomain(domain))) {
BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
@@ -688,7 +695,7 @@ static void rna_AttributeGroupID_remove(ID *id, ReportList *reports, PointerRNA
{
using namespace blender;
AttributeOwner owner = AttributeOwner::from_id(id);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
const bke::Attribute *attr = static_cast<const bke::Attribute *>(attribute_ptr->data);
if (BKE_attribute_required(owner, attr->name())) {
BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed");
@@ -774,7 +781,7 @@ void rna_AttributeGroup_iterator_begin(CollectionPropertyIterator *iter, Pointer
using namespace blender;
memset(&iter->internal.array, 0, sizeof(iter->internal.array));
AttributeOwner owner = owner_from_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
Vector<bke::Attribute *> attributes;
storage.foreach([&](bke::Attribute &attr) { attributes.append(&attr); });
@@ -791,7 +798,7 @@ void rna_AttributeGroup_iterator_next(CollectionPropertyIterator *iter)
{
rna_iterator_array_next(iter);
AttributeOwner owner = owner_from_pointer_rna(&iter->parent);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
return;
}
@@ -804,7 +811,7 @@ PointerRNA rna_AttributeGroup_iterator_get(CollectionPropertyIterator *iter)
{
using namespace blender;
AttributeOwner owner = owner_from_pointer_rna(&iter->parent);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::Attribute *attr = *static_cast<bke::Attribute **>(rna_iterator_array_get(iter));
const eCustomDataType data_type = *bke::attr_type_to_custom_data_type(attr->data_type());
StructRNA *type = srna_by_custom_data_layer_type(data_type);
@@ -852,7 +859,7 @@ void rna_AttributeStorage_color_iterator_begin(CollectionPropertyIterator *iter,
using namespace blender;
memset(&iter->internal.array, 0, sizeof(iter->internal.array));
AttributeOwner owner = owner_from_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
Vector<bke::Attribute *> attributes;
storage.foreach([&](bke::Attribute &attr) { attributes.append(&attr); });
@@ -870,9 +877,9 @@ void rna_AttributeStorage_color_iterator_begin(CollectionPropertyIterator *iter,
int rna_AttributeGroup_color_length(PointerRNA *ptr)
{
using namespace blender;
if (GS(ptr->owner_id->name) == ID_PT) {
PointCloud &pointcloud = *reinterpret_cast<PointCloud *>(ptr->owner_id);
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
AttributeOwner owner = owner_from_pointer_rna(ptr);
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
int count = 0;
storage.foreach([&](bke::Attribute &attr) {
if (!(ATTR_DOMAIN_AS_MASK(attr.domain()) & ATTR_DOMAIN_MASK_COLOR)) {
@@ -885,7 +892,6 @@ int rna_AttributeGroup_color_length(PointerRNA *ptr)
});
return count;
}
AttributeOwner owner = owner_from_pointer_rna(ptr);
return BKE_attributes_length(owner, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
}
@@ -893,7 +899,7 @@ int rna_AttributeGroup_length(PointerRNA *ptr)
{
using namespace blender;
AttributeOwner owner = owner_from_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
int count = 0;
storage.foreach([&](bke::Attribute & /*attr*/) { count++; });
@@ -906,7 +912,7 @@ bool rna_AttributeGroup_lookup_string(PointerRNA *ptr, const char *key, PointerR
{
using namespace blender;
AttributeOwner owner = owner_from_pointer_rna(ptr);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
bke::Attribute *attr = storage.lookup(key);
if (!attr) {
@@ -942,7 +948,7 @@ static PointerRNA rna_AttributeGroupID_active_get(PointerRNA *ptr)
if (!name) {
return PointerRNA_NULL;
}
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeStorage &storage = *owner.get_storage();
bke::Attribute *attr = storage.lookup(*name);
return RNA_pointer_create_with_parent(*ptr, &RNA_Attribute, attr);
@@ -959,7 +965,7 @@ static void rna_AttributeGroupID_active_set(PointerRNA *ptr,
{
using namespace blender;
AttributeOwner owner = AttributeOwner::from_id(ptr->owner_id);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::Attribute *attr = attribute_ptr.data_as<bke::Attribute>();
BKE_attributes_active_set(owner, attr->name());
return;
@@ -1012,7 +1018,7 @@ static int rna_AttributeGroupID_domain_size(ID *id, const int domain)
{
using namespace blender;
AttributeOwner owner = AttributeOwner::from_id(id);
if (owner.type() == AttributeOwnerType::PointCloud) {
if (owner.type() != AttributeOwnerType::Mesh) {
bke::AttributeAccessor attributes = *owner.get_accessor();
return attributes.domain_size(bke::AttrDomain(domain));
}
@@ -1177,19 +1183,27 @@ static PointerRNA rna_AttributeGroupGreasePencilDrawing_new(ID *grease_pencil_id
const int type,
const int domain)
{
using namespace blender;
AttributeOwner owner = AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
CustomDataLayer *layer = BKE_attribute_new(
owner, name, eCustomDataType(type), AttrDomain(domain), reports);
if (!layer) {
const bke::AttributeAccessor accessor = *owner.get_accessor();
if (!accessor.domain_supported(AttrDomain(domain))) {
BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
return PointerRNA_NULL;
}
const int domain_size = accessor.domain_size(AttrDomain(domain));
bke::AttributeStorage &attributes = *owner.get_storage();
const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(eCustomDataType(type));
bke::Attribute &attr = attributes.add(
attributes.unique_name_calc(name),
AttrDomain(domain),
*bke::custom_data_type_to_attr_type(eCustomDataType(type)),
bke::Attribute::ArrayData::ForDefaultValue(cpp_type, domain_size));
DEG_id_tag_update(grease_pencil_id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, grease_pencil_id);
PointerRNA ptr = RNA_pointer_create_discrete(grease_pencil_id, &RNA_Attribute, layer);
return ptr;
return RNA_pointer_create_discrete(grease_pencil_id, &RNA_Attribute, &attr);
}
static void rna_AttributeGroupGreasePencilDrawing_remove(ID *grease_pencil_id,
@@ -1197,9 +1211,16 @@ static void rna_AttributeGroupGreasePencilDrawing_remove(ID *grease_pencil_id,
ReportList *reports,
PointerRNA *attribute_ptr)
{
using namespace blender;
AttributeOwner owner = AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
const CustomDataLayer *layer = (const CustomDataLayer *)attribute_ptr->data;
BKE_attribute_remove(owner, layer->name, reports);
const bke::Attribute *attr = static_cast<const bke::Attribute *>(attribute_ptr->data);
if (BKE_attribute_required(owner, attr->name())) {
BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed");
return;
}
bke::MutableAttributeAccessor accessor = *owner.get_accessor();
accessor.remove(attr->name());
attribute_ptr->invalidate();
DEG_id_tag_update(grease_pencil_id, ID_RECALC_GEOMETRY);
@@ -1208,15 +1229,16 @@ static void rna_AttributeGroupGreasePencilDrawing_remove(ID *grease_pencil_id,
static PointerRNA rna_AttributeGroupGreasePencilDrawing_active_get(PointerRNA *ptr)
{
using namespace blender;
GreasePencilDrawing *drawing = static_cast<GreasePencilDrawing *>(ptr->data);
AttributeOwner owner = AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
const std::optional<blender::StringRef> name = BKE_attributes_active_name_get(owner);
if (!name) {
return PointerRNA_NULL;
}
CustomDataLayer *layer = BKE_attribute_search_for_write(
owner, *name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
return RNA_pointer_create_discrete(ptr->owner_id, &RNA_Attribute, layer);
bke::AttributeStorage &storage = *owner.get_storage();
bke::Attribute *attr = storage.lookup(*name);
return RNA_pointer_create_discrete(ptr->owner_id, &RNA_Attribute, attr);
}
static void rna_AttributeGroupGreasePencilDrawing_active_set(PointerRNA *ptr,
@@ -1262,7 +1284,7 @@ static void rna_AttributeGroupGreasePencilDrawing_active_index_range(
GreasePencilDrawing *drawing = static_cast<GreasePencilDrawing *>(ptr->data);
AttributeOwner owner = AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
*min = -1;
*max = BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL);
*max = owner.get_storage()->count();
*softmin = *min;
*softmax = *max;
@@ -1272,7 +1294,7 @@ static int rna_AttributeGroupGreasePencilDrawing_domain_size(GreasePencilDrawing
const int domain)
{
AttributeOwner owner = AttributeOwner(AttributeOwnerType::GreasePencilDrawing, drawing);
return BKE_attribute_domain_size(owner, domain);
return owner.get_accessor()->domain_size(blender::bke::AttrDomain(domain));
}
#else

View File

@@ -405,6 +405,11 @@ if(TEST_SRC_DIR_EXISTS)
)
endif()
add_blender_test(
geometry_attributes
--python ${CMAKE_CURRENT_LIST_DIR}/bl_geometry_attributes.py
)
# ------------------------------------------------------------------------------
# MODIFIERS TESTS
# ------------------------------------------------------------------------------
@@ -911,7 +916,7 @@ if(TEST_SRC_DIR_EXISTS)
output
color
filter
utilities
utilities
vector
pixel_nodes
@@ -946,7 +951,7 @@ if(WITH_GPU_COMPOSITOR_TESTS AND TEST_SRC_DIR_EXISTS)
output
color
filter
utilities
utilities
vector
pixel_nodes

View File

@@ -0,0 +1,74 @@
# SPDX-FileCopyrightText: 2024 Blender Authors
#
# SPDX-License-Identifier: Apache-2.0
# ./blender.bin --background --python tests/python/bl_geometry_attributes.py -- --verbose
import bpy
import unittest
class TestCurves(unittest.TestCase):
def setUp(self):
self.curves = bpy.data.hair_curves.new("test")
# 50 points, 4 curves
self.curves.add_curves([5, 10, 15, 20])
def tearDown(self):
bpy.data.hair_curves.remove(self.curves)
del self.curves
def test_add_attribute(self):
a = self.curves.attributes.new("a", 'FLOAT', 'POINT')
self.assertTrue(a.name == "a")
self.assertTrue(a.data_type == 'FLOAT')
self.assertTrue(a.domain == 'POINT')
self.assertTrue(a.storage_type == 'ARRAY')
self.assertFalse(a.is_internal)
self.assertTrue(len(a.data) == 50)
def test_is_required(self):
a = self.curves.attributes.new("a", 'FLOAT', 'POINT')
self.assertFalse(a.is_required)
self.assertTrue(self.curves.attributes["position"].is_required)
def test_pointer_stability_on_add(self):
attrs = [self.curves.attributes.new("a" + str(i), 'FLOAT', 'POINT') for i in range(100)]
for i in range(100):
self.assertTrue(attrs[i].name == "a" + str(i))
self.assertTrue(attrs[i].data_type == 'FLOAT')
self.assertTrue(attrs[i].domain == 'POINT')
# Remove some attributes
for i in range(50):
self.curves.attributes.remove(attrs[i])
del attrs[i]
self.assertTrue(len(self.curves.attributes) == 51)
self.assertTrue(self.curves.attributes["a51"].name == "a51")
def test_add_same_name(self):
a = self.curves.attributes.new("a", 'FLOAT', 'POINT')
b = self.curves.attributes.new("a", 'BOOLEAN', 'CURVE')
self.assertFalse(a.name == b.name)
def test_add_wrong_domain(self):
with self.assertRaises(RuntimeError):
self.curves.attributes.new("a", 'FLOAT', 'CORNER')
def rename_attribute(self, name, new_name):
with self.assertRaises(RuntimeError):
self.curves.attributes["position"].name = "asjhfksjhdfkjsh"
a = self.curves.attributes.new("a", 'FLOAT', 'POINT')
a.name = "better_name"
self.assertTrue(a.name == "better_name")
self.assertTrue(self.curves.attributes["better_name"].name == "better_name")
def test_long_name(self):
self.curves.attributes.new("a" * 100, 'FLOAT', 'POINT')
self.assertTrue(self.curves.attributes["a" * 100].name == "a" * 100)
if __name__ == '__main__':
import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
unittest.main()