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-f74e304b00Part of #122398. Pull Request: https://projects.blender.org/blender/blender/pulls/140936
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
74
tests/python/bl_geometry_attributes.py
Normal file
74
tests/python/bl_geometry_attributes.py
Normal 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()
|
||||
Reference in New Issue
Block a user