Point Cloud: Use AttributeStorage instead of CustomData
This moves `PointCloud` to use the recently added `AttributeStorage` at runtime. Mainly this involves implementing the higher level attribute API on top, and implementing the RNA API as well. The attribute RNA type is now backed by either CustomDataLayer or bke::Attribute. For now the new code is specific to point clouds but next steps can reuse it for Grease Pencil layer attributes, curves, and eventually meshes. Point cloud attributes no longer have a name length limit. Internally, the `AttributeStorage` API is extended with a few additions: - The data structs have static constructors for convenience. - A few functions give index-based access to attributes - A "rename" function is added. The `Attribute` RNA type now exposes a `storage_type` property. For now the "single value" option is still unused at runtime, and accessing the single value data isn't implemented yet. Pull Request: https://projects.blender.org/blender/blender/pulls/139165
This commit is contained in:
@@ -50,9 +50,6 @@ 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_storage_to_customdata. */
|
||||
void pointcloud_convert_storage_to_customdata(PointCloud &pointcloud);
|
||||
|
||||
/** See #mesh_convert_customdata_to_storage. */
|
||||
void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud);
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
namespace blender {
|
||||
class GPointer;
|
||||
class CPPType;
|
||||
class ResourceScope;
|
||||
}
|
||||
} // namespace blender
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
@@ -41,6 +43,9 @@ class Attribute {
|
||||
/* The number of elements in the array. */
|
||||
int64_t size;
|
||||
ImplicitSharingPtr<> sharing_info;
|
||||
static ArrayData ForValue(const GPointer &value, int64_t domain_size);
|
||||
static ArrayData ForDefaultValue(const CPPType &type, int64_t domain_size);
|
||||
static ArrayData ForConstructed(const CPPType &type, int64_t domain_size);
|
||||
};
|
||||
/** Data for an attribute stored as a single value for the entire domain. */
|
||||
struct SingleData {
|
||||
@@ -48,6 +53,8 @@ class Attribute {
|
||||
* It's not necessary to manage a single value. */
|
||||
void *value;
|
||||
ImplicitSharingPtr<> sharing_info;
|
||||
static SingleData ForValue(const GPointer &value);
|
||||
static SingleData ForDefaultValue(const CPPType &type);
|
||||
};
|
||||
using DataVariant = std::variant<ArrayData, SingleData>;
|
||||
friend AttributeStorage;
|
||||
@@ -133,6 +140,18 @@ class AttributeStorage : public ::AttributeStorage {
|
||||
*/
|
||||
void foreach(FunctionRef<void(Attribute &)> fn);
|
||||
void foreach(FunctionRef<void(const Attribute &)> fn) const;
|
||||
void foreach_with_stop(FunctionRef<bool(Attribute &)> fn);
|
||||
void foreach_with_stop(FunctionRef<bool(const Attribute &)> fn) const;
|
||||
|
||||
/** Return the number of attributes. */
|
||||
int count() const;
|
||||
|
||||
/** Return the attribute at the given index. */
|
||||
Attribute &at_index(int index);
|
||||
const Attribute &at_index(int index) const;
|
||||
|
||||
/** Return the index of the attribute with the given name, or -1 if not found. */
|
||||
int index_of(StringRef name) const;
|
||||
|
||||
/**
|
||||
* Try to find the attribute with a given name. The non-const overload does not make the
|
||||
@@ -159,6 +178,9 @@ class AttributeStorage : public ::AttributeStorage {
|
||||
/** Return a possibly changed version of the input name that is unique within existing names. */
|
||||
std::string unique_name_calc(StringRef name);
|
||||
|
||||
/** Change the name of a single existing attribute. */
|
||||
void rename(StringRef old_name, std::string new_name);
|
||||
|
||||
/**
|
||||
* Read data owned by the #AttributeStorage struct. This works by converting the DNA-specific
|
||||
* types stored in the files to the runtime data structures.
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 1
|
||||
#define BLENDER_FILE_SUBVERSION 2
|
||||
|
||||
/* 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
|
||||
@@ -36,8 +36,8 @@
|
||||
* See
|
||||
* https://developer.blender.org/docs/handbook/guidelines/compatibility_handling_for_blend_files/
|
||||
* for details. */
|
||||
#define BLENDER_FILE_MIN_VERSION 306
|
||||
#define BLENDER_FILE_MIN_SUBVERSION 13
|
||||
#define BLENDER_FILE_MIN_VERSION 405
|
||||
#define BLENDER_FILE_MIN_SUBVERSION 85
|
||||
|
||||
/** User readable version string. */
|
||||
const char *BKE_blender_version_string(void);
|
||||
|
||||
@@ -115,9 +115,8 @@ static std::array<DomainInfo, ATTR_DOMAIN_NUM> get_domains(const AttributeOwner
|
||||
|
||||
switch (owner.type()) {
|
||||
case AttributeOwnerType::PointCloud: {
|
||||
PointCloud *pointcloud = owner.get_pointcloud();
|
||||
info[int(AttrDomain::Point)].customdata = &pointcloud->pdata;
|
||||
info[int(AttrDomain::Point)].length = pointcloud->totpoint;
|
||||
/* This should be implemented with #AttributeStorage instead. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
case AttributeOwnerType::Mesh: {
|
||||
@@ -279,6 +278,17 @@ bool BKE_attribute_rename(AttributeOwner &owner,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &attributes = pointcloud.attribute_storage.wrap();
|
||||
if (!attributes.lookup(old_name)) {
|
||||
BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
|
||||
return false;
|
||||
}
|
||||
attributes.rename(old_name, new_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* NOTE: Checking if the new name matches the old name only makes sense when the name
|
||||
* is clamped to it's maximum length, otherwise assigning an over-long name multiple times
|
||||
* will add `.001` suffix unnecessarily. */
|
||||
@@ -382,6 +392,9 @@ 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) {
|
||||
return owner.get_pointcloud()->attribute_storage.wrap().unique_name_calc(name);
|
||||
}
|
||||
return BLI_uniquename_cb(
|
||||
[&](const StringRef new_name) { return attribute_name_exists(owner, new_name); },
|
||||
'.',
|
||||
@@ -533,9 +546,8 @@ bool BKE_attribute_remove(AttributeOwner &owner, const StringRef name, ReportLis
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
|
||||
|
||||
if (owner.type() == AttributeOwnerType::Mesh) {
|
||||
const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
|
||||
Mesh *mesh = owner.get_mesh();
|
||||
if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
|
||||
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
||||
@@ -801,10 +813,21 @@ bool BKE_attribute_required(const AttributeOwner &owner, const StringRef name)
|
||||
|
||||
std::optional<blender::StringRefNull> BKE_attributes_active_name_get(AttributeOwner &owner)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
int active_index = *BKE_attributes_active_index_p(owner);
|
||||
if (active_index == -1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
if (active_index >= storage.count()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return storage.at_index(active_index).name();
|
||||
}
|
||||
|
||||
if (active_index > BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) {
|
||||
active_index = 0;
|
||||
}
|
||||
@@ -837,6 +860,14 @@ 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) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &attributes = pointcloud.attribute_storage.wrap();
|
||||
*BKE_attributes_active_index_p(owner) = attributes.index_of(name);
|
||||
return;
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = BKE_attribute_search(
|
||||
owner, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
|
||||
BLI_assert(layer != nullptr);
|
||||
|
||||
@@ -268,16 +268,10 @@ void curves_convert_customdata_to_storage(CurvesGeometry &curves)
|
||||
{AttrDomain::Curve, {curves.curve_data, curves.curves_num()}}});
|
||||
}
|
||||
|
||||
void pointcloud_convert_storage_to_customdata(PointCloud &pointcloud)
|
||||
{
|
||||
convert_storage_to_customdata(pointcloud.attribute_storage.wrap(),
|
||||
{{AttrDomain::Point, {pointcloud.pdata, pointcloud.totpoint}}});
|
||||
}
|
||||
|
||||
void pointcloud_convert_customdata_to_storage(PointCloud &pointcloud)
|
||||
{
|
||||
pointcloud.attribute_storage.wrap() = attribute_legacy_convert_customdata_to_storage(
|
||||
{{AttrDomain::Point, {pointcloud.pdata, pointcloud.totpoint}}});
|
||||
{{AttrDomain::Point, {pointcloud.pdata_legacy, pointcloud.totpoint}}});
|
||||
}
|
||||
|
||||
void grease_pencil_convert_storage_to_customdata(GreasePencil &grease_pencil)
|
||||
|
||||
@@ -57,6 +57,62 @@ class ArrayDataImplicitSharing : public ImplicitSharingInfo {
|
||||
}
|
||||
};
|
||||
|
||||
Attribute::ArrayData Attribute::ArrayData::ForValue(const GPointer &value,
|
||||
const int64_t domain_size)
|
||||
{
|
||||
Attribute::ArrayData data{};
|
||||
const CPPType &type = *value.type();
|
||||
const void *value_ptr = type.default_value();
|
||||
|
||||
/* Prefer `calloc` to zeroing after allocation since it is faster. */
|
||||
if (BLI_memory_is_zero(value_ptr, type.size)) {
|
||||
data.data = MEM_calloc_arrayN_aligned(domain_size, type.size, type.alignment, __func__);
|
||||
}
|
||||
else {
|
||||
data.data = MEM_malloc_arrayN_aligned(domain_size, type.size, type.alignment, __func__);
|
||||
type.fill_construct_n(value_ptr, data.data, domain_size);
|
||||
}
|
||||
|
||||
data.size = domain_size;
|
||||
BLI_assert(type.is_trivially_destructible);
|
||||
data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data));
|
||||
return data;
|
||||
}
|
||||
|
||||
Attribute::ArrayData Attribute::ArrayData::ForDefaultValue(const CPPType &type,
|
||||
const int64_t domain_size)
|
||||
{
|
||||
return ForValue(GPointer(type, type.default_value()), domain_size);
|
||||
}
|
||||
|
||||
Attribute::ArrayData Attribute::ArrayData::ForConstructed(const CPPType &type,
|
||||
const int64_t domain_size)
|
||||
{
|
||||
Attribute::ArrayData data{};
|
||||
data.data = MEM_malloc_arrayN_aligned(domain_size, type.size, type.alignment, __func__);
|
||||
type.default_construct_n(data.data, domain_size);
|
||||
data.size = domain_size;
|
||||
BLI_assert(type.is_trivially_destructible);
|
||||
data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data));
|
||||
return data;
|
||||
}
|
||||
|
||||
Attribute::SingleData Attribute::SingleData::ForValue(const GPointer &value)
|
||||
{
|
||||
Attribute::SingleData data{};
|
||||
const CPPType &type = *value.type();
|
||||
data.value = MEM_mallocN_aligned(type.size, type.alignment, __func__);
|
||||
type.copy_construct(value.get(), data.value);
|
||||
BLI_assert(type.is_trivially_destructible);
|
||||
data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.value));
|
||||
return data;
|
||||
}
|
||||
|
||||
Attribute::SingleData Attribute::SingleData::ForDefaultValue(const CPPType &type)
|
||||
{
|
||||
return ForValue(GPointer(type, type.default_value()));
|
||||
}
|
||||
|
||||
void AttributeStorage::foreach(FunctionRef<void(Attribute &)> fn)
|
||||
{
|
||||
for (const std::unique_ptr<Attribute> &attribute : this->runtime->attributes) {
|
||||
@@ -70,11 +126,21 @@ void AttributeStorage::foreach(FunctionRef<void(const Attribute &)> fn) const
|
||||
}
|
||||
}
|
||||
|
||||
static ImplicitSharingInfo *create_sharing_info_for_array(void *data,
|
||||
const int64_t size,
|
||||
const CPPType &type)
|
||||
void AttributeStorage::foreach_with_stop(FunctionRef<bool(Attribute &)> fn)
|
||||
{
|
||||
return MEM_new<ArrayDataImplicitSharing>(__func__, data, size, type);
|
||||
for (const std::unique_ptr<Attribute> &attribute : this->runtime->attributes) {
|
||||
if (!fn(*attribute)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void AttributeStorage::foreach_with_stop(FunctionRef<bool(const Attribute &)> fn) const
|
||||
{
|
||||
for (const std::unique_ptr<Attribute> &attribute : this->runtime->attributes) {
|
||||
if (!fn(*attribute)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AttrStorageType Attribute::storage_type() const
|
||||
@@ -96,19 +162,18 @@ Attribute::DataVariant &Attribute::data_for_write()
|
||||
data->sharing_info->tag_ensured_mutable();
|
||||
return data_;
|
||||
}
|
||||
|
||||
const CPPType &cpp_type = attribute_type_to_cpp_type(type_);
|
||||
void *new_data = MEM_malloc_arrayN_aligned(
|
||||
data->size, cpp_type.size, cpp_type.alignment, __func__);
|
||||
cpp_type.copy_construct_n(data->data, new_data, data->size);
|
||||
|
||||
data->data = new_data;
|
||||
data->sharing_info = ImplicitSharingPtr<>(
|
||||
create_sharing_info_for_array(data->data, data->size, cpp_type));
|
||||
const CPPType &type = attribute_type_to_cpp_type(type_);
|
||||
ArrayData new_data = ArrayData::ForConstructed(type, data->size);
|
||||
type.copy_construct_n(data->data, new_data.data, data->size);
|
||||
*data = std::move(new_data);
|
||||
}
|
||||
else if (std::get_if<Attribute::SingleData>(&data_)) {
|
||||
/* Not yet implemented because #SingleData isn't used at runtime yet. */
|
||||
BLI_assert_unreachable();
|
||||
else if (auto *data = std::get_if<Attribute::SingleData>(&data_)) {
|
||||
if (data->sharing_info->is_mutable()) {
|
||||
data->sharing_info->tag_ensured_mutable();
|
||||
return data_;
|
||||
}
|
||||
const CPPType &type = attribute_type_to_cpp_type(type_);
|
||||
*data = SingleData::ForValue(GPointer(type, data->value));
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
@@ -163,6 +228,25 @@ AttributeStorage::~AttributeStorage()
|
||||
MEM_delete(this->runtime);
|
||||
}
|
||||
|
||||
int AttributeStorage::count() const
|
||||
{
|
||||
return this->runtime->attributes.size();
|
||||
}
|
||||
|
||||
Attribute &AttributeStorage::at_index(int index)
|
||||
{
|
||||
return *this->runtime->attributes[index];
|
||||
}
|
||||
const Attribute &AttributeStorage::at_index(int index) const
|
||||
{
|
||||
return *this->runtime->attributes[index];
|
||||
}
|
||||
|
||||
int AttributeStorage::index_of(StringRef name) const
|
||||
{
|
||||
return this->runtime->attributes.index_of_try_as(name);
|
||||
}
|
||||
|
||||
const Attribute *AttributeStorage::lookup(const StringRef name) const
|
||||
{
|
||||
const std::unique_ptr<blender::bke::Attribute> *attribute =
|
||||
@@ -210,6 +294,19 @@ std::string AttributeStorage::unique_name_calc(const StringRef name)
|
||||
[&](const StringRef check_name) { return this->lookup(check_name) != nullptr; }, '.', name);
|
||||
}
|
||||
|
||||
void AttributeStorage::rename(const StringRef old_name, std::string new_name)
|
||||
{
|
||||
/* The VectorSet must be rebuilt from scratch because the data used to create the hash is
|
||||
* changed. */
|
||||
const int index = this->runtime->attributes.index_of_try_as(old_name);
|
||||
Vector<std::unique_ptr<Attribute>> old_vector = this->runtime->attributes.extract_vector();
|
||||
old_vector[index]->name_ = std::move(new_name);
|
||||
this->runtime->attributes.reserve(old_vector.size());
|
||||
for (std::unique_ptr<Attribute> &attribute : old_vector) {
|
||||
this->runtime->attributes.add_new(std::move(attribute));
|
||||
}
|
||||
}
|
||||
|
||||
static void read_array_data(BlendDataReader &reader,
|
||||
const int8_t dna_attr_type,
|
||||
const int64_t size,
|
||||
@@ -443,7 +540,7 @@ void attribute_storage_blend_write_prepare(
|
||||
}
|
||||
}
|
||||
data.foreach([&](Attribute &attr) {
|
||||
if (!U.experimental.use_attribute_storage_write) {
|
||||
if (!U.experimental.use_attribute_storage_write && !layers_to_write.is_empty()) {
|
||||
/* In version 4.5, all attribute data is written in the #CustomData format (at least when the
|
||||
* debug option is not enabled), so the #Attribute needs to be converted to a
|
||||
* #CustomDataLayer in the proper list. This is only relevant when #AttributeStorage is
|
||||
|
||||
@@ -63,8 +63,6 @@ static void pointcloud_init_data(ID *id)
|
||||
|
||||
new (&pointcloud->attribute_storage.wrap()) blender::bke::AttributeStorage();
|
||||
pointcloud->runtime = new blender::bke::PointCloudRuntime();
|
||||
|
||||
CustomData_reset(&pointcloud->pdata);
|
||||
}
|
||||
|
||||
static void pointcloud_copy_data(Main * /*bmain*/,
|
||||
@@ -77,8 +75,6 @@ static void pointcloud_copy_data(Main * /*bmain*/,
|
||||
const PointCloud *pointcloud_src = (const PointCloud *)id_src;
|
||||
pointcloud_dst->mat = static_cast<Material **>(MEM_dupallocN(pointcloud_src->mat));
|
||||
|
||||
CustomData_init_from(
|
||||
&pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, pointcloud_dst->totpoint);
|
||||
new (&pointcloud_dst->attribute_storage.wrap())
|
||||
blender::bke::AttributeStorage(pointcloud_src->attribute_storage.wrap());
|
||||
|
||||
@@ -101,7 +97,6 @@ static void pointcloud_free_data(ID *id)
|
||||
PointCloud *pointcloud = (PointCloud *)id;
|
||||
BKE_animdata_free(&pointcloud->id, false);
|
||||
BKE_pointcloud_batch_cache_free(pointcloud);
|
||||
CustomData_free(&pointcloud->pdata);
|
||||
pointcloud->attribute_storage.wrap().~AttributeStorage();
|
||||
MEM_SAFE_FREE(pointcloud->mat);
|
||||
delete pointcloud->runtime;
|
||||
@@ -122,12 +117,9 @@ static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_a
|
||||
PointCloud *pointcloud = (PointCloud *)id;
|
||||
|
||||
ResourceScope scope;
|
||||
Vector<CustomDataLayer, 16> point_layers;
|
||||
bke::AttributeStorage::BlendWriteData attribute_data{scope};
|
||||
attribute_storage_blend_write_prepare(
|
||||
pointcloud->attribute_storage.wrap(), {{AttrDomain::Point, &point_layers}}, attribute_data);
|
||||
CustomData_blend_write_prepare(
|
||||
pointcloud->pdata, AttrDomain::Point, pointcloud->totpoint, point_layers, attribute_data);
|
||||
attribute_storage_blend_write_prepare(pointcloud->attribute_storage.wrap(), {}, attribute_data);
|
||||
BLI_assert(pointcloud->pdata_legacy.totlayer == 0);
|
||||
pointcloud->attribute_storage.dna_attributes = attribute_data.attributes.data();
|
||||
pointcloud->attribute_storage.dna_attributes_num = attribute_data.attributes.size();
|
||||
|
||||
@@ -136,12 +128,6 @@ static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_a
|
||||
BKE_id_blend_write(writer, &pointcloud->id);
|
||||
|
||||
/* Direct data */
|
||||
CustomData_blend_write(writer,
|
||||
&pointcloud->pdata,
|
||||
point_layers,
|
||||
pointcloud->totpoint,
|
||||
CD_MASK_ALL,
|
||||
&pointcloud->id);
|
||||
pointcloud->attribute_storage.wrap().blend_write(*writer, attribute_data);
|
||||
|
||||
BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat);
|
||||
@@ -152,12 +138,9 @@ static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
PointCloud *pointcloud = (PointCloud *)id;
|
||||
|
||||
/* Geometry */
|
||||
CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint);
|
||||
CustomData_blend_read(reader, &pointcloud->pdata_legacy, pointcloud->totpoint);
|
||||
pointcloud->attribute_storage.wrap().blend_read(*reader);
|
||||
|
||||
/* Forward compatibility. To be removed when runtime format changes. */
|
||||
blender::bke::pointcloud_convert_storage_to_customdata(*pointcloud);
|
||||
|
||||
/* Materials */
|
||||
BLO_read_pointer_array(reader, pointcloud->totcol, (void **)&pointcloud->mat);
|
||||
|
||||
@@ -199,11 +182,22 @@ static VArray<T> get_varray_attribute(const PointCloud &pointcloud,
|
||||
const StringRef name,
|
||||
const T default_value)
|
||||
{
|
||||
const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(CPPType::get<T>());
|
||||
|
||||
const T *data = (const T *)CustomData_get_layer_named(&pointcloud.pdata, type, name);
|
||||
if (data != nullptr) {
|
||||
return VArray<T>::ForSpan(Span<T>(data, pointcloud.totpoint));
|
||||
using namespace blender;
|
||||
const bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name);
|
||||
if (!attr) {
|
||||
return VArray<T>::ForSingle(default_value, pointcloud.totpoint);
|
||||
}
|
||||
switch (attr->storage_type()) {
|
||||
case bke::AttrStorageType::Array: {
|
||||
const auto &data = std::get<bke::Attribute::ArrayData>(attr->data());
|
||||
const Span span(static_cast<const T *>(data.data), data.size);
|
||||
BLI_assert(data.size == pointcloud.totpoint);
|
||||
return VArray<T>::ForSpan(span);
|
||||
}
|
||||
case bke::AttrStorageType::Single: {
|
||||
const auto &data = std::get<bke::Attribute::SingleData>(attr->data());
|
||||
return VArray<T>::ForSingle(*static_cast<const T *>(data.value), pointcloud.totpoint);
|
||||
}
|
||||
}
|
||||
return VArray<T>::ForSingle(default_value, pointcloud.totpoint);
|
||||
}
|
||||
@@ -211,13 +205,16 @@ static VArray<T> get_varray_attribute(const PointCloud &pointcloud,
|
||||
template<typename T>
|
||||
static Span<T> get_span_attribute(const PointCloud &pointcloud, const StringRef name)
|
||||
{
|
||||
const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(CPPType::get<T>());
|
||||
|
||||
T *data = (T *)CustomData_get_layer_named(&pointcloud.pdata, type, name);
|
||||
if (data == nullptr) {
|
||||
using namespace blender;
|
||||
const bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
return {data, pointcloud.totpoint};
|
||||
if (const auto *array_data = std::get_if<bke::Attribute::ArrayData>(&attr->data())) {
|
||||
BLI_assert(array_data->size == pointcloud.totpoint);
|
||||
return Span(static_cast<const T *>(array_data->data), array_data->size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -225,23 +222,35 @@ static MutableSpan<T> get_mutable_attribute(PointCloud &pointcloud,
|
||||
const StringRef name,
|
||||
const T default_value = T())
|
||||
{
|
||||
using namespace blender;
|
||||
if (pointcloud.totpoint <= 0) {
|
||||
return {};
|
||||
}
|
||||
const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(CPPType::get<T>());
|
||||
|
||||
T *data = (T *)CustomData_get_layer_named_for_write(
|
||||
&pointcloud.pdata, type, name, pointcloud.totpoint);
|
||||
if (data != nullptr) {
|
||||
return {data, pointcloud.totpoint};
|
||||
const bke::AttrType type = bke::cpp_type_to_attribute_type(CPPType::get<T>());
|
||||
if (bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name)) {
|
||||
if (attr->data_type() == type) {
|
||||
if (const auto *single_data = std::get_if<bke::Attribute::SingleData>(&attr->data())) {
|
||||
/* Convert single value storage to array storage. */
|
||||
const GPointer g_value(CPPType::get<T>(), single_data->value);
|
||||
attr->data_for_write() = bke::Attribute::ArrayData::ForValue(g_value, pointcloud.totpoint);
|
||||
}
|
||||
auto &array_data = std::get<bke::Attribute::ArrayData>(attr->data_for_write());
|
||||
BLI_assert(array_data.size == pointcloud.totpoint);
|
||||
return MutableSpan(static_cast<T *>(array_data.data), pointcloud.totpoint);
|
||||
}
|
||||
/* The attribute has the wrong type. This shouldn't happen for builtin attributes, but just in
|
||||
* case, remove it. */
|
||||
pointcloud.attribute_storage.wrap().remove(name);
|
||||
}
|
||||
data = (T *)CustomData_add_layer_named(
|
||||
&pointcloud.pdata, type, CD_SET_DEFAULT, pointcloud.totpoint, name);
|
||||
MutableSpan<T> span = {data, pointcloud.totpoint};
|
||||
if (pointcloud.totpoint > 0 && span.first() != default_value) {
|
||||
span.fill(default_value);
|
||||
}
|
||||
return span;
|
||||
bke::Attribute &attr = pointcloud.attribute_storage.wrap().add(
|
||||
name,
|
||||
bke::AttrDomain::Point,
|
||||
type,
|
||||
bke::Attribute::ArrayData::ForValue({CPPType::get<T>(), &default_value},
|
||||
pointcloud.totpoint));
|
||||
auto &array_data = std::get<bke::Attribute::ArrayData>(attr.data_for_write());
|
||||
BLI_assert(array_data.size == pointcloud.totpoint);
|
||||
return MutableSpan(static_cast<T *>(array_data.data), pointcloud.totpoint);
|
||||
}
|
||||
|
||||
Span<float3> PointCloud::positions() const
|
||||
@@ -288,11 +297,8 @@ void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud
|
||||
{
|
||||
BLI_assert(pointcloud_src->id.tag & ID_TAG_NO_MAIN);
|
||||
|
||||
CustomData_free(&pointcloud_dst->pdata);
|
||||
|
||||
const int totpoint = pointcloud_dst->totpoint = pointcloud_src->totpoint;
|
||||
CustomData_init_from(&pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, totpoint);
|
||||
|
||||
pointcloud_dst->totpoint = pointcloud_src->totpoint;
|
||||
pointcloud_dst->attribute_storage.wrap() = std::move(pointcloud_src->attribute_storage.wrap());
|
||||
pointcloud_dst->runtime->bounds_cache = pointcloud_src->runtime->bounds_cache;
|
||||
pointcloud_dst->runtime->bounds_with_radius_cache =
|
||||
pointcloud_src->runtime->bounds_with_radius_cache;
|
||||
@@ -344,7 +350,7 @@ std::optional<int> PointCloud::material_index_max() const
|
||||
|
||||
void PointCloud::count_memory(blender::MemoryCounter &memory) const
|
||||
{
|
||||
CustomData_count_memory(this->pdata, this->totpoint, memory);
|
||||
this->attribute_storage.wrap().count_memory(memory);
|
||||
}
|
||||
|
||||
blender::bke::AttributeAccessor PointCloud::attributes() const
|
||||
|
||||
@@ -4,86 +4,194 @@
|
||||
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_attribute_storage.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
static void tag_component_positions_changed(void *owner)
|
||||
static void tag_position_changed(void *owner)
|
||||
{
|
||||
PointCloud &points = *static_cast<PointCloud *>(owner);
|
||||
points.tag_positions_changed();
|
||||
}
|
||||
|
||||
static void tag_component_radius_changed(void *owner)
|
||||
static void tag_radius_changed(void *owner)
|
||||
{
|
||||
PointCloud &points = *static_cast<PointCloud *>(owner);
|
||||
points.tag_radii_changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* In this function all the attribute providers for a point cloud component are created. Most data
|
||||
* in this function is statically allocated, because it does not change over time.
|
||||
*/
|
||||
static GeometryAttributeProviders create_attribute_providers_for_pointcloud()
|
||||
{
|
||||
static CustomDataAccessInfo point_access = {
|
||||
[](void *owner) -> CustomData * {
|
||||
PointCloud *pointcloud = static_cast<PointCloud *>(owner);
|
||||
return &pointcloud->pdata;
|
||||
},
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
|
||||
return &pointcloud->pdata;
|
||||
},
|
||||
[](const void *owner) -> int {
|
||||
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
|
||||
return pointcloud->totpoint;
|
||||
}};
|
||||
using UpdateOnChange = void (*)(void *owner);
|
||||
|
||||
static BuiltinCustomDataLayerProvider position("position",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT3,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
point_access,
|
||||
tag_component_positions_changed);
|
||||
static BuiltinCustomDataLayerProvider radius("radius",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_FLOAT,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
tag_component_radius_changed);
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
AttrDomain::Point,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
point_access,
|
||||
nullptr);
|
||||
static CustomDataAttributeProvider point_custom_data(AttrDomain::Point, point_access);
|
||||
return GeometryAttributeProviders({&position, &radius, &id}, {&point_custom_data});
|
||||
static const auto &changed_tags()
|
||||
{
|
||||
static Map<StringRef, UpdateOnChange> attributes{{"position", tag_position_changed},
|
||||
{"radius", tag_radius_changed}};
|
||||
return attributes;
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
namespace {
|
||||
|
||||
struct BuiltinInfo {
|
||||
bke::AttrDomain domain;
|
||||
bke::AttrType type;
|
||||
GPointer default_value = {};
|
||||
AttributeValidator validator = {};
|
||||
bool deletable = false;
|
||||
BuiltinInfo(bke::AttrDomain domain, bke::AttrType type) : domain(domain), type(type) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static const auto &builtin_attributes()
|
||||
{
|
||||
static const GeometryAttributeProviders providers = create_attribute_providers_for_pointcloud();
|
||||
AttributeAccessorFunctions fn =
|
||||
attribute_accessor_functions::accessor_functions_for_providers<providers>();
|
||||
fn.domain_size = [](const void *owner, const AttrDomain domain) {
|
||||
if (owner == nullptr) {
|
||||
return 0;
|
||||
static auto attributes = []() {
|
||||
Map<StringRef, BuiltinInfo> map;
|
||||
|
||||
BuiltinInfo position(bke::AttrDomain::Point, bke::AttrType::Float3);
|
||||
position.deletable = false;
|
||||
map.add_new("position", std::move(position));
|
||||
|
||||
BuiltinInfo radius(bke::AttrDomain::Point, bke::AttrType::Float);
|
||||
map.add_new("radius", std::move(radius));
|
||||
|
||||
return map;
|
||||
}();
|
||||
return attributes;
|
||||
}
|
||||
|
||||
static GAttributeReader attribute_to_reader(const Attribute &attribute,
|
||||
const AttrDomain domain,
|
||||
const int64_t domain_size)
|
||||
{
|
||||
const CPPType &cpp_type = attribute_type_to_cpp_type(attribute.data_type());
|
||||
switch (attribute.storage_type()) {
|
||||
case AttrStorageType::Array: {
|
||||
const auto &data = std::get<Attribute::ArrayData>(attribute.data());
|
||||
return GAttributeReader{GVArray::ForSpan(GSpan(cpp_type, data.data, data.size)),
|
||||
domain,
|
||||
data.sharing_info.get()};
|
||||
}
|
||||
const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner);
|
||||
switch (domain) {
|
||||
case AttrDomain::Point:
|
||||
return pointcloud.totpoint;
|
||||
default:
|
||||
return 0;
|
||||
case AttrStorageType::Single: {
|
||||
const auto &data = std::get<Attribute::SingleData>(attribute.data());
|
||||
return GAttributeReader{GVArray::ForSingleRef(cpp_type, domain_size, data.value),
|
||||
domain,
|
||||
data.sharing_info.get()};
|
||||
}
|
||||
};
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
|
||||
static GAttributeWriter attribute_to_writer(PointCloud &pointcloud,
|
||||
const int64_t domain_size,
|
||||
Attribute &attribute)
|
||||
{
|
||||
const CPPType &cpp_type = attribute_type_to_cpp_type(attribute.data_type());
|
||||
switch (attribute.storage_type()) {
|
||||
case AttrStorageType::Array: {
|
||||
auto &data = std::get<Attribute::ArrayData>(attribute.data_for_write());
|
||||
BLI_assert(data.size == domain_size);
|
||||
|
||||
std::function<void()> tag_modified_fn;
|
||||
if (const UpdateOnChange update_fn = changed_tags().lookup_default(attribute.name(),
|
||||
nullptr))
|
||||
{
|
||||
tag_modified_fn = [pointcloud = &pointcloud, update_fn]() { update_fn(pointcloud); };
|
||||
};
|
||||
|
||||
return GAttributeWriter{
|
||||
GVMutableArray::ForSpan(GMutableSpan(cpp_type, data.data, domain_size)),
|
||||
attribute.domain(),
|
||||
std::move(tag_modified_fn)};
|
||||
}
|
||||
case AttrStorageType::Single: {
|
||||
/* Not yet implemented. */
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
|
||||
static Attribute::DataVariant attribute_init_to_data(const bke::AttrType data_type,
|
||||
const int64_t domain_size,
|
||||
const AttributeInit &initializer)
|
||||
{
|
||||
switch (initializer.type) {
|
||||
case AttributeInit::Type::Construct: {
|
||||
const CPPType &type = bke::attribute_type_to_cpp_type(data_type);
|
||||
return Attribute::ArrayData::ForConstructed(type, domain_size);
|
||||
}
|
||||
case AttributeInit::Type::DefaultValue: {
|
||||
const CPPType &type = bke::attribute_type_to_cpp_type(data_type);
|
||||
return Attribute::ArrayData::ForDefaultValue(type, domain_size);
|
||||
}
|
||||
case AttributeInit::Type::VArray: {
|
||||
const auto &init = static_cast<const AttributeInitVArray &>(initializer);
|
||||
const GVArray &varray = init.varray;
|
||||
BLI_assert(varray.size() == domain_size);
|
||||
const CPPType &type = varray.type();
|
||||
Attribute::ArrayData data;
|
||||
data.data = MEM_malloc_arrayN_aligned(domain_size, type.size, type.alignment, __func__);
|
||||
varray.materialize_to_uninitialized(varray.index_range(), data.data);
|
||||
data.size = domain_size;
|
||||
data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data));
|
||||
return data;
|
||||
}
|
||||
case AttributeInit::Type::MoveArray: {
|
||||
const auto &init = static_cast<const AttributeInitMoveArray &>(initializer);
|
||||
Attribute::ArrayData data;
|
||||
data.data = init.data;
|
||||
data.size = domain_size;
|
||||
data.sharing_info = ImplicitSharingPtr<>(implicit_sharing::info_for_mem_free(data.data));
|
||||
return data;
|
||||
}
|
||||
case AttributeInit::Type::Shared: {
|
||||
const auto &init = static_cast<const AttributeInitShared &>(initializer);
|
||||
Attribute::ArrayData data;
|
||||
data.data = const_cast<void *>(init.data);
|
||||
data.size = domain_size;
|
||||
data.sharing_info = ImplicitSharingPtr<>(init.sharing_info);
|
||||
data.sharing_info->add_user();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
{
|
||||
AttributeAccessorFunctions fn{};
|
||||
fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
|
||||
return domain == AttrDomain::Point;
|
||||
};
|
||||
fn.domain_size = [](const void *owner, const AttrDomain domain) {
|
||||
return domain == AttrDomain::Point ? static_cast<const PointCloud *>(owner)->totpoint : 0;
|
||||
};
|
||||
fn.builtin_domain_and_type = [](const void * /*owner*/,
|
||||
const StringRef name) -> std::optional<AttributeDomainAndType> {
|
||||
const BuiltinInfo *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.lookup = [](const void *owner, const StringRef name) -> GAttributeReader {
|
||||
const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner);
|
||||
const AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
const Attribute *attribute = storage.lookup(name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
return attribute_to_reader(*attribute, AttrDomain::Point, pointcloud.totpoint);
|
||||
};
|
||||
fn.adapt_domain = [](const void * /*owner*/,
|
||||
const GVArray &varray,
|
||||
const AttrDomain from_domain,
|
||||
@@ -93,12 +201,88 @@ static AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
}
|
||||
return GVArray{};
|
||||
};
|
||||
fn.foreach_attribute = [](const void *owner,
|
||||
const FunctionRef<void(const AttributeIter &)> fn,
|
||||
const AttributeAccessor &accessor) {
|
||||
const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner);
|
||||
const AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
storage.foreach_with_stop([&](const Attribute &attribute) {
|
||||
const auto get_fn = [&]() {
|
||||
return attribute_to_reader(attribute, AttrDomain::Point, pointcloud.totpoint);
|
||||
};
|
||||
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 BuiltinInfo *info = builtin_attributes().lookup_ptr(name);
|
||||
if (!info) {
|
||||
return {};
|
||||
}
|
||||
return info->validator;
|
||||
};
|
||||
fn.lookup_for_write = [](void *owner, const StringRef name) -> GAttributeWriter {
|
||||
PointCloud &pointcloud = *static_cast<PointCloud *>(owner);
|
||||
AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
Attribute *attribute = storage.lookup(name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
return attribute_to_writer(pointcloud, pointcloud.totpoint, *attribute);
|
||||
};
|
||||
fn.remove = [](void *owner, const StringRef name) -> bool {
|
||||
PointCloud &pointcloud = *static_cast<PointCloud *>(owner);
|
||||
AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
if (const BuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
|
||||
if (!info->deletable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const std::optional<UpdateOnChange> 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) {
|
||||
PointCloud &pointcloud = *static_cast<PointCloud *>(owner);
|
||||
const int domain_size = pointcloud.totpoint;
|
||||
AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
const std::optional<AttrType> type = custom_data_type_to_attr_type(data_type);
|
||||
BLI_assert(type.has_value());
|
||||
if (const BuiltinInfo *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;
|
||||
}
|
||||
|
||||
const AttributeAccessorFunctions &pointcloud_attribute_accessor_functions()
|
||||
{
|
||||
static const AttributeAccessorFunctions fn = get_pointcloud_accessor_functions();
|
||||
static constexpr AttributeAccessorFunctions fn = get_pointcloud_accessor_functions();
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
@@ -1182,7 +1182,7 @@ void blo_do_versions_290(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
|
||||
/* PointCloud attributes. */
|
||||
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
|
||||
do_versions_point_attributes(&pointcloud->pdata);
|
||||
do_versions_point_attributes(&pointcloud->pdata_legacy);
|
||||
}
|
||||
|
||||
/* Show outliner mode column by default. */
|
||||
@@ -1488,7 +1488,7 @@ void blo_do_versions_290(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
|
||||
/* PointCloud attributes names. */
|
||||
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
|
||||
do_versions_point_attribute_names(&pointcloud->pdata);
|
||||
do_versions_point_attribute_names(&pointcloud->pdata_legacy);
|
||||
}
|
||||
|
||||
/* Cryptomatte render pass */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "DNA_light_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_force_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_mesh_legacy_convert.hh"
|
||||
|
||||
@@ -96,6 +97,12 @@ void blo_do_versions_500(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 2)) {
|
||||
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
|
||||
blender::bke::pointcloud_convert_customdata_to_storage(*pointcloud);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
@@ -211,6 +212,22 @@ bool attribute_set_poll(bContext &C, const ID &object_data)
|
||||
CTX_wm_operator_poll_msg_set(&C, "No active attribute");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeAccessor attributes = pointcloud.attributes();
|
||||
std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(*name);
|
||||
if (!meta_data) {
|
||||
CTX_wm_operator_poll_msg_set(&C, "No active attribute");
|
||||
return false;
|
||||
}
|
||||
if (ELEM(meta_data->data_type, CD_PROP_STRING, CD_PROP_FLOAT4X4, CD_PROP_QUATERNION)) {
|
||||
CTX_wm_operator_poll_msg_set(&C, "The active attribute has an unsupported type");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = BKE_attribute_search(
|
||||
owner, *name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
|
||||
if (ELEM(layer->type, CD_PROP_STRING, CD_PROP_FLOAT4X4, CD_PROP_QUATERNION)) {
|
||||
@@ -281,6 +298,32 @@ static wmOperatorStatus geometry_attribute_add_exec(bContext *C, wmOperator *op)
|
||||
eCustomDataType type = eCustomDataType(RNA_enum_get(op->ptr, "data_type"));
|
||||
bke::AttrDomain domain = bke::AttrDomain(RNA_enum_get(op->ptr, "domain"));
|
||||
AttributeOwner owner = AttributeOwner::from_id(id);
|
||||
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::MutableAttributeAccessor accessor = pointcloud.attributes_for_write();
|
||||
if (!accessor.domain_supported(bke::AttrDomain(domain))) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
bke::AttributeStorage &attributes = pointcloud.attribute_storage.wrap();
|
||||
const int domain_size = accessor.domain_size(bke::AttrDomain(domain));
|
||||
|
||||
const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(type);
|
||||
bke::Attribute &attr = attributes.add(
|
||||
attributes.unique_name_calc(name),
|
||||
bke::AttrDomain(domain),
|
||||
*bke::custom_data_type_to_attr_type(type),
|
||||
bke::Attribute::ArrayData::ForDefaultValue(cpp_type, domain_size));
|
||||
|
||||
BKE_attributes_active_set(owner, attr.name());
|
||||
|
||||
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, id);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = BKE_attribute_new(owner, name, type, domain, op->reports);
|
||||
|
||||
if (layer == nullptr) {
|
||||
|
||||
@@ -321,7 +321,7 @@ static void store_result_geometry(const bContext &C,
|
||||
PointCloud *new_points =
|
||||
geometry.get_component_for_write<bke::PointCloudComponent>().release();
|
||||
if (!new_points) {
|
||||
CustomData_free(&points.pdata);
|
||||
new_points->attribute_storage.wrap() = {};
|
||||
points.totpoint = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2138,7 +2138,6 @@ static wmOperatorStatus object_pointcloud_add_exec(bContext *C, wmOperator *op)
|
||||
Object *object = add_type(C, OB_POINTCLOUD, nullptr, loc, rot, false, local_view_bits);
|
||||
PointCloud &pointcloud = *static_cast<PointCloud *>(object->data);
|
||||
pointcloud.totpoint = 400;
|
||||
CustomData_realloc(&pointcloud.pdata, 0, pointcloud.totpoint);
|
||||
|
||||
bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
|
||||
bke::SpanAttributeWriter<float3> position = attributes.lookup_or_add_for_write_only_span<float3>(
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_attribute_storage.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
@@ -38,7 +38,7 @@ namespace undo {
|
||||
|
||||
struct StepObject {
|
||||
UndoRefID_Object obedit_ref = {};
|
||||
CustomData custom_data = {};
|
||||
bke::AttributeStorage attribute_storage;
|
||||
int totpoint = 0;
|
||||
/* Store the bounds caches because they are small. */
|
||||
SharedCache<Bounds<float3>> bounds_cache;
|
||||
@@ -67,10 +67,9 @@ static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
|
||||
for (const int i : range) {
|
||||
Object *ob = objects[i];
|
||||
StepObject &object = us->objects[i];
|
||||
PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
|
||||
const PointCloud &pointcloud = *static_cast<const PointCloud *>(ob->data);
|
||||
object.obedit_ref.ptr = ob;
|
||||
CustomData_init_from(
|
||||
&pointcloud.pdata, &object.custom_data, CD_MASK_ALL, pointcloud.totpoint);
|
||||
object.attribute_storage.wrap() = pointcloud.attribute_storage.wrap();
|
||||
object.bounds_cache = pointcloud.runtime->bounds_cache;
|
||||
object.bounds_with_radius_cache = pointcloud.runtime->bounds_with_radius_cache;
|
||||
object.totpoint = pointcloud.totpoint;
|
||||
@@ -101,12 +100,22 @@ static void step_decode(
|
||||
|
||||
for (const StepObject &object : us->objects) {
|
||||
PointCloud &pointcloud = *static_cast<PointCloud *>(object.obedit_ref.ptr->data);
|
||||
const bool positions_changed =
|
||||
CustomData_get_layer_named(&pointcloud.pdata, CD_PROP_FLOAT3, "position") !=
|
||||
CustomData_get_layer_named(&object.custom_data, CD_PROP_FLOAT3, "position");
|
||||
|
||||
CustomData_free(&pointcloud.pdata);
|
||||
CustomData_init_from(&object.custom_data, &pointcloud.pdata, CD_MASK_ALL, object.totpoint);
|
||||
const bool positions_changed = [&]() {
|
||||
const bke::Attribute *attr_a = pointcloud.attribute_storage.wrap().lookup("position");
|
||||
const bke::Attribute *attr_b = object.attribute_storage.wrap().lookup("position");
|
||||
if (!attr_b && !attr_a) {
|
||||
return false;
|
||||
}
|
||||
if (!attr_a || !attr_b) {
|
||||
return true;
|
||||
}
|
||||
return std::get<bke::Attribute::ArrayData>(attr_a->data()).data !=
|
||||
std::get<bke::Attribute::ArrayData>(attr_b->data()).data;
|
||||
}();
|
||||
|
||||
pointcloud.attribute_storage.wrap() = object.attribute_storage.wrap();
|
||||
|
||||
pointcloud.totpoint = object.totpoint;
|
||||
pointcloud.runtime->bounds_cache = object.bounds_cache;
|
||||
pointcloud.runtime->bounds_with_radius_cache = object.bounds_with_radius_cache;
|
||||
@@ -127,9 +136,6 @@ static void step_decode(
|
||||
static void step_free(UndoStep *us_p)
|
||||
{
|
||||
PointCloudUndoStep *us = reinterpret_cast<PointCloudUndoStep *>(us_p);
|
||||
for (StepObject &object : us->objects) {
|
||||
CustomData_free(&object.custom_data);
|
||||
}
|
||||
us->objects.~Array();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BKE_attribute_storage.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
@@ -81,6 +82,31 @@ static void reorder_customdata(CustomData &data, const Span<int> new_by_old_map)
|
||||
data = new_data;
|
||||
}
|
||||
|
||||
static void reorder_attribute_domain(bke::AttributeStorage &data,
|
||||
const bke::AttrDomain domain,
|
||||
const Span<int> new_by_old_map)
|
||||
{
|
||||
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.data_for_write() = std::move(new_data);
|
||||
}
|
||||
case bke::AttrStorageType::Single: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void debug_randomize_vert_order(Mesh *mesh)
|
||||
{
|
||||
if (mesh == nullptr || !use_debug_randomization()) {
|
||||
@@ -183,8 +209,8 @@ void debug_randomize_point_order(PointCloud *pointcloud)
|
||||
|
||||
const int seed = seed_from_pointcloud(*pointcloud);
|
||||
const Array<int> new_by_old_map = get_permutation(pointcloud->totpoint, seed);
|
||||
|
||||
reorder_customdata(pointcloud->pdata, new_by_old_map);
|
||||
reorder_attribute_domain(
|
||||
pointcloud->attribute_storage.wrap(), bke::AttrDomain::Point, new_by_old_map);
|
||||
|
||||
pointcloud->tag_positions_changed();
|
||||
pointcloud->tag_radii_changed();
|
||||
|
||||
@@ -51,14 +51,11 @@ typedef struct PointCloud {
|
||||
/* Geometry */
|
||||
int totpoint;
|
||||
|
||||
/**
|
||||
* Storage for generic attributes. Currently unused at runtime, but used for forward
|
||||
* compatibility when reading files (see #122398).
|
||||
*/
|
||||
/** Storage for generic attributes. */
|
||||
struct AttributeStorage attribute_storage;
|
||||
|
||||
/* Custom Data */
|
||||
struct CustomData pdata;
|
||||
struct CustomData pdata_legacy;
|
||||
/** Set to -1 when none is active. */
|
||||
int attributes_active_index;
|
||||
int _pad4;
|
||||
|
||||
@@ -182,6 +182,7 @@ DNA_STRUCT_RENAME_MEMBER(ParticleSettings, dup_group, instance_collection)
|
||||
DNA_STRUCT_RENAME_MEMBER(ParticleSettings, dup_ob, instance_object)
|
||||
DNA_STRUCT_RENAME_MEMBER(ParticleSettings, dupliweights, instance_weights)
|
||||
DNA_STRUCT_RENAME_MEMBER(ParticleSettings, ren_child_nbr, child_render_percent)
|
||||
DNA_STRUCT_RENAME_MEMBER(PointCloud, pdata, pdata_legacy)
|
||||
DNA_STRUCT_RENAME_MEMBER(RenderData, bake_filter, bake_margin)
|
||||
DNA_STRUCT_RENAME_MEMBER(RenderData, blurfac, motion_blur_shutter)
|
||||
DNA_STRUCT_RENAME_MEMBER(RigidBodyWorld, steps_per_second, substeps_per_frame)
|
||||
|
||||
@@ -73,6 +73,20 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem rna_enum_attr_storage_type[] = {
|
||||
{int(blender::bke::AttrStorageType::Array),
|
||||
"ARRAY",
|
||||
0,
|
||||
"Array",
|
||||
"Store a value for every element"},
|
||||
{int(blender::bke::AttrStorageType::Single),
|
||||
"SINGLE",
|
||||
0,
|
||||
"Single",
|
||||
"Store a single value for the entire domain"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_attribute_domain_items[] = {
|
||||
/* Not implement yet */
|
||||
// {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"},
|
||||
@@ -171,10 +185,16 @@ const EnumPropertyItem rna_enum_attribute_curves_domain_items[] = {
|
||||
# include "DNA_grease_pencil_types.h"
|
||||
# include "DNA_mesh_types.h"
|
||||
# include "DNA_meshdata_types.h"
|
||||
# include "DNA_pointcloud_types.h"
|
||||
|
||||
# include "BLI_math_color.h"
|
||||
# include "BLI_string.h"
|
||||
|
||||
# include "BKE_attribute_legacy_convert.hh"
|
||||
# include "BKE_customdata.hh"
|
||||
# include "BKE_report.hh"
|
||||
|
||||
# include "RNA_prototypes.hh"
|
||||
|
||||
# include "DEG_depsgraph.hh"
|
||||
|
||||
@@ -189,10 +209,10 @@ using blender::StringRef;
|
||||
static AttributeOwner owner_from_attribute_pointer_rna(PointerRNA *ptr)
|
||||
{
|
||||
ID *owner_id = ptr->owner_id;
|
||||
const CustomDataLayer *layer = static_cast<const CustomDataLayer *>(ptr->data);
|
||||
/* 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);
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(owner_id);
|
||||
/* First check the layer attributes. */
|
||||
CustomData *layers_data = &grease_pencil->layers_data;
|
||||
@@ -235,6 +255,13 @@ 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = static_cast<const CustomDataLayer *>(ptr->data);
|
||||
char layer_name_esc[sizeof(layer->name) * 2];
|
||||
BLI_str_escape(layer_name_esc, layer->name, sizeof(layer_name_esc));
|
||||
@@ -277,21 +304,71 @@ 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = static_cast<CustomDataLayer *>(ptr->data);
|
||||
return srna_by_custom_data_layer_type(eCustomDataType(layer->type));
|
||||
}
|
||||
|
||||
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) {
|
||||
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
|
||||
attr->name().copy_unsafe(value);
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(value, ptr->data_as<CustomDataLayer>()->name);
|
||||
}
|
||||
|
||||
static int rna_Attribute_name_length(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
|
||||
return attr->name().size();
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = ptr->data_as<CustomDataLayer>();
|
||||
return strlen(layer->name);
|
||||
}
|
||||
|
||||
static void rna_Attribute_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
const CustomDataLayer *layer = (const CustomDataLayer *)ptr->data;
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
const bke::Attribute *attr = ptr->data_as<bke::Attribute>();
|
||||
BKE_attribute_rename(owner, attr->name(), value, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = (const CustomDataLayer *)ptr->data;
|
||||
BKE_attribute_rename(owner, layer->name, value, nullptr);
|
||||
}
|
||||
|
||||
static int rna_Attribute_name_editable(const PointerRNA *ptr, const char **r_info)
|
||||
{
|
||||
CustomDataLayer *layer = static_cast<CustomDataLayer *>(ptr->data);
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(const_cast<PointerRNA *>(ptr));
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = static_cast<CustomDataLayer *>(ptr->data);
|
||||
if (BKE_attribute_required(owner, layer->name)) {
|
||||
*r_info = N_("Cannot modify name of required geometry attribute");
|
||||
return false;
|
||||
@@ -302,10 +379,27 @@ 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) {
|
||||
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
|
||||
return *bke::attr_type_to_custom_data_type(attr->data_type());
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = static_cast<CustomDataLayer *>(ptr->data);
|
||||
return layer->type;
|
||||
}
|
||||
|
||||
static int rna_Attribute_storage_type_get(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
if (GS(ptr->owner_id->name) == ID_PT) {
|
||||
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
|
||||
return int(attr->storage_type());
|
||||
}
|
||||
|
||||
return int(bke::AttrStorageType::Array);
|
||||
}
|
||||
|
||||
const EnumPropertyItem *rna_enum_attribute_domain_itemf(const AttributeOwner &owner,
|
||||
bool include_instances,
|
||||
bool *r_free)
|
||||
@@ -368,26 +462,67 @@ static const EnumPropertyItem *rna_Attribute_domain_itemf(bContext * /*C*/,
|
||||
|
||||
static int rna_Attribute_domain_get(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
|
||||
return int(attr->domain());
|
||||
}
|
||||
|
||||
return int(BKE_attribute_domain(owner, static_cast<const CustomDataLayer *>(ptr->data)));
|
||||
}
|
||||
|
||||
static bool rna_Attribute_is_internal_get(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
if (GS(ptr->owner_id->name) == ID_PT) {
|
||||
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
|
||||
return !bke::allow_procedural_attribute_access(attr->name());
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = (const CustomDataLayer *)ptr->data;
|
||||
return !blender::bke::allow_procedural_attribute_access(layer->name);
|
||||
}
|
||||
|
||||
static bool rna_Attribute_is_required_get(PointerRNA *ptr)
|
||||
{
|
||||
const CustomDataLayer *layer = (const CustomDataLayer *)ptr->data;
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
const bke::Attribute *attr = static_cast<const bke::Attribute *>(ptr->data);
|
||||
return BKE_attribute_required(owner, attr->name());
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = (const CustomDataLayer *)ptr->data;
|
||||
return BKE_attribute_required(owner, layer->name);
|
||||
}
|
||||
|
||||
static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::MutableAttributeAccessor accessor = pointcloud.attributes_for_write();
|
||||
|
||||
bke::Attribute *attr = ptr->data_as<bke::Attribute>();
|
||||
const int domain_size = accessor.domain_size(attr->domain());
|
||||
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_for_write());
|
||||
rna_iterator_array_begin(iter, ptr, data.data, type.size, domain_size, false, nullptr);
|
||||
break;
|
||||
}
|
||||
case bke::AttrStorageType::Single: {
|
||||
/* TODO: Access to single values is unimplemented for now. */
|
||||
iter->valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
|
||||
if (!(CD_TYPE_AS_MASK(layer->type) & CD_MASK_PROP_ALL)) {
|
||||
iter->valid = false;
|
||||
@@ -402,7 +537,13 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN
|
||||
|
||||
static int rna_Attribute_data_length(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_attribute_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
const PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
return pointcloud.totpoint;
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
|
||||
return BKE_attribute_data_length(owner, layer);
|
||||
}
|
||||
@@ -495,7 +636,31 @@ static void rna_StringAttributeValue_s_set(PointerRNA *ptr, const char *value)
|
||||
static PointerRNA rna_AttributeGroupID_new(
|
||||
ID *id, ReportList *reports, const char *name, const int type, const int domain)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = AttributeOwner::from_id(id);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::MutableAttributeAccessor accessor = pointcloud.attributes_for_write();
|
||||
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 = pointcloud.attribute_storage.wrap();
|
||||
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(id, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, id);
|
||||
|
||||
return RNA_pointer_create_discrete(id, &RNA_Attribute, &attr);
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = BKE_attribute_new(
|
||||
owner, name, eCustomDataType(type), AttrDomain(domain), reports);
|
||||
|
||||
@@ -522,7 +687,26 @@ static PointerRNA rna_AttributeGroupID_new(
|
||||
|
||||
static void rna_AttributeGroupID_remove(ID *id, ReportList *reports, PointerRNA *attribute_ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = AttributeOwner::from_id(id);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
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;
|
||||
}
|
||||
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::MutableAttributeAccessor accessor = pointcloud.attributes_for_write();
|
||||
accessor.remove(attr->name());
|
||||
attribute_ptr->invalidate();
|
||||
|
||||
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const CustomDataLayer *layer = (const CustomDataLayer *)attribute_ptr->data;
|
||||
BKE_attribute_remove(owner, layer->name, reports);
|
||||
attribute_ptr->invalidate();
|
||||
@@ -552,6 +736,19 @@ static bool rna_Attributes_noncolor_layer_skip(CollectionPropertyIterator *iter,
|
||||
return !(CD_TYPE_AS_MASK(layer->type) & CD_MASK_COLOR_ALL) || (layer->flag & CD_FLAG_TEMPORARY);
|
||||
}
|
||||
|
||||
static bool rna_AttributeStorage_noncolor_skip(CollectionPropertyIterator * /*iter*/, void *data)
|
||||
{
|
||||
using namespace blender;
|
||||
bke::Attribute *attr = static_cast<bke::Attribute *>(data);
|
||||
if (!(ATTR_DOMAIN_AS_MASK(attr->domain()) & ATTR_DOMAIN_MASK_COLOR)) {
|
||||
return true;
|
||||
}
|
||||
if (!(CD_TYPE_AS_MASK(attr->data_type()) & CD_MASK_COLOR_ALL)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Attributes are spread over multiple domains in separate CustomData, we use repeated
|
||||
* array iterators to loop over all. */
|
||||
static void rna_AttributeGroup_next_domain(AttributeOwner &owner,
|
||||
@@ -576,24 +773,47 @@ static void rna_AttributeGroup_next_domain(AttributeOwner &owner,
|
||||
|
||||
void rna_AttributeGroup_iterator_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
memset(&iter->internal.array, 0, sizeof(iter->internal.array));
|
||||
AttributeOwner owner = owner_from_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
Vector<bke::Attribute *> attributes;
|
||||
storage.foreach([&](bke::Attribute &attr) { attributes.append(&attr); });
|
||||
VectorData data = attributes.release();
|
||||
rna_iterator_array_begin(
|
||||
iter, ptr, data.data, sizeof(bke::Attribute *), data.size, true, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
rna_AttributeGroup_next_domain(owner, iter, ptr, rna_Attributes_layer_skip);
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iter->valid) {
|
||||
AttributeOwner owner = owner_from_pointer_rna(&iter->parent);
|
||||
rna_AttributeGroup_next_domain(owner, iter, &iter->parent, rna_Attributes_layer_skip);
|
||||
}
|
||||
}
|
||||
|
||||
PointerRNA rna_AttributeGroup_iterator_get(CollectionPropertyIterator *iter)
|
||||
{
|
||||
/* Refine to the proper type. */
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_pointer_rna(&iter->parent);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
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);
|
||||
return RNA_pointer_create_with_parent(iter->parent, type, attr);
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = static_cast<CustomDataLayer *>(rna_iterator_array_get(iter));
|
||||
StructRNA *type = srna_by_custom_data_layer_type(eCustomDataType(layer->type));
|
||||
if (type == nullptr) {
|
||||
@@ -630,21 +850,78 @@ PointerRNA rna_AttributeGroup_color_iterator_get(CollectionPropertyIterator *ite
|
||||
return RNA_pointer_create_with_parent(iter->parent, type, layer);
|
||||
}
|
||||
|
||||
void rna_AttributeStorage_color_iterator_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
memset(&iter->internal.array, 0, sizeof(iter->internal.array));
|
||||
AttributeOwner owner = owner_from_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
Vector<bke::Attribute *> attributes;
|
||||
storage.foreach([&](bke::Attribute &attr) { attributes.append(&attr); });
|
||||
VectorData data = attributes.release();
|
||||
rna_iterator_array_begin(iter,
|
||||
ptr,
|
||||
data.data,
|
||||
sizeof(bke::Attribute *),
|
||||
data.size,
|
||||
true,
|
||||
rna_AttributeStorage_noncolor_skip);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
int count = 0;
|
||||
storage.foreach([&](bke::Attribute &attr) {
|
||||
if (!(ATTR_DOMAIN_AS_MASK(attr.domain()) & ATTR_DOMAIN_MASK_COLOR)) {
|
||||
return;
|
||||
}
|
||||
if (!(CD_TYPE_AS_MASK(attr.data_type()) & CD_MASK_COLOR_ALL)) {
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
AttributeOwner owner = owner_from_pointer_rna(ptr);
|
||||
return BKE_attributes_length(owner, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
|
||||
}
|
||||
|
||||
int rna_AttributeGroup_length(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
int count = 0;
|
||||
storage.foreach([&](bke::Attribute & /*attr*/) { count++; });
|
||||
return count;
|
||||
}
|
||||
return BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL);
|
||||
}
|
||||
|
||||
bool rna_AttributeGroup_lookup_string(PointerRNA *ptr, const char *key, PointerRNA *r_ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = owner_from_pointer_rna(ptr);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
bke::Attribute *attr = storage.lookup(key);
|
||||
if (!attr) {
|
||||
*r_ptr = PointerRNA_NULL;
|
||||
return false;
|
||||
}
|
||||
rna_pointer_create_with_ancestors(*ptr, &RNA_Attribute, attr, *r_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CustomDataLayer *layer = BKE_attribute_search_for_write(
|
||||
owner, key, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL))
|
||||
@@ -665,11 +942,19 @@ static int rna_AttributeGroupID_active_index_get(PointerRNA *ptr)
|
||||
|
||||
static PointerRNA rna_AttributeGroupID_active_get(PointerRNA *ptr)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = AttributeOwner::from_id(ptr->owner_id);
|
||||
const std::optional<blender::StringRef> name = BKE_attributes_active_name_get(owner);
|
||||
if (!name) {
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeStorage &storage = pointcloud.attribute_storage.wrap();
|
||||
bke::Attribute *attr = storage.lookup(*name);
|
||||
return RNA_pointer_create_with_parent(*ptr, &RNA_Attribute, attr);
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = BKE_attribute_search_for_write(
|
||||
owner, *name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
|
||||
return RNA_pointer_create_with_parent(*ptr, &RNA_Attribute, layer);
|
||||
@@ -679,7 +964,14 @@ static void rna_AttributeGroupID_active_set(PointerRNA *ptr,
|
||||
PointerRNA attribute_ptr,
|
||||
ReportList * /*reports*/)
|
||||
{
|
||||
using namespace blender;
|
||||
AttributeOwner owner = AttributeOwner::from_id(ptr->owner_id);
|
||||
if (owner.type() == AttributeOwnerType::PointCloud) {
|
||||
bke::Attribute *attr = attribute_ptr.data_as<bke::Attribute>();
|
||||
BKE_attributes_active_set(owner, attr->name());
|
||||
return;
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = static_cast<CustomDataLayer *>(attribute_ptr.data);
|
||||
if (layer) {
|
||||
BKE_attributes_active_set(owner, layer->name);
|
||||
@@ -698,9 +990,8 @@ static void rna_AttributeGroupID_active_index_set(PointerRNA *ptr, int value)
|
||||
static void rna_AttributeGroupID_active_index_range(
|
||||
PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
|
||||
{
|
||||
AttributeOwner owner = AttributeOwner::from_id(ptr->owner_id);
|
||||
*min = -1;
|
||||
*max = BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL);
|
||||
*max = rna_AttributeGroup_length(ptr);
|
||||
|
||||
*softmin = *min;
|
||||
*softmax = *max;
|
||||
@@ -726,7 +1017,14 @@ static void rna_AttributeGroup_update_active_color(Main * /*bmain*/,
|
||||
|
||||
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) {
|
||||
PointCloud &pointcloud = *owner.get_pointcloud();
|
||||
bke::AttributeAccessor attributes = pointcloud.attributes();
|
||||
return attributes.domain_size(bke::AttrDomain(domain));
|
||||
}
|
||||
|
||||
return BKE_attribute_domain_size(owner, domain);
|
||||
}
|
||||
|
||||
@@ -1475,24 +1773,29 @@ static void rna_def_attribute(BlenderRNA *brna)
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "Attribute", nullptr);
|
||||
RNA_def_struct_sdna(srna, "CustomDataLayer");
|
||||
RNA_def_struct_ui_text(srna, "Attribute", "Geometry attribute");
|
||||
RNA_def_struct_path_func(srna, "rna_Attribute_path");
|
||||
RNA_def_struct_refine_func(srna, "rna_Attribute_refine");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_Attribute_name_set");
|
||||
RNA_def_property_string_funcs(
|
||||
prop, "rna_Attribute_name_get", "rna_Attribute_name_length", "rna_Attribute_name_set");
|
||||
RNA_def_property_editable_func(prop, "rna_Attribute_name_editable");
|
||||
RNA_def_property_ui_text(prop, "Name", "Name of the Attribute");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "type");
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
|
||||
RNA_def_property_enum_funcs(prop, "rna_Attribute_type_get", nullptr, nullptr);
|
||||
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "storage_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_attr_storage_type);
|
||||
RNA_def_property_enum_funcs(prop, "rna_Attribute_storage_type_get", nullptr, nullptr);
|
||||
RNA_def_property_ui_text(prop, "Storage Type", "Method used to store the data");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
|
||||
RNA_def_property_enum_funcs(
|
||||
|
||||
Reference in New Issue
Block a user