Attributes: Use AttributeStorage instead of CustomData for Instances
Part of #122398. This commit moves the runtime instances type to use `AttributeStorage`. Besides sharing some utility functions with point clouds, a resize method is implemented. Pull Request: https://projects.blender.org/blender/blender/pulls/140991
This commit is contained in:
@@ -185,6 +185,12 @@ class AttributeStorage : public ::AttributeStorage {
|
||||
/** Change the name of a single existing attribute. */
|
||||
void rename(StringRef old_name, std::string new_name);
|
||||
|
||||
/**
|
||||
* Resize the data for a given domain. New values will be default initialized (meaning no zero
|
||||
* initialization for trivial types).
|
||||
*/
|
||||
void resize(AttrDomain domain, int64_t new_size);
|
||||
|
||||
/**
|
||||
* Read data owned by the #AttributeStorage struct. This works by converting the DNA-specific
|
||||
* types stored in the files to the runtime data structures.
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_virtual_array_fwd.hh"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "BKE_attribute_filter.hh"
|
||||
#include "BKE_attribute_storage.hh"
|
||||
|
||||
struct Object;
|
||||
struct Collection;
|
||||
@@ -120,7 +119,7 @@ class Instances {
|
||||
|
||||
int instances_num_ = 0;
|
||||
|
||||
CustomData attributes_;
|
||||
bke::AttributeStorage attributes_;
|
||||
|
||||
/**
|
||||
* Caches how often each reference is used.
|
||||
@@ -210,8 +209,8 @@ class Instances {
|
||||
bke::AttributeAccessor attributes() const;
|
||||
bke::MutableAttributeAccessor attributes_for_write();
|
||||
|
||||
CustomData &custom_data_attributes();
|
||||
const CustomData &custom_data_attributes() const;
|
||||
bke::AttributeStorage &attribute_storage();
|
||||
const bke::AttributeStorage &attribute_storage() const;
|
||||
|
||||
void foreach_referenced_geometry(
|
||||
FunctionRef<void(const GeometrySet &geometry_set)> callback) const;
|
||||
@@ -306,12 +305,12 @@ inline const GeometrySet &InstanceReference::geometry_set() const
|
||||
return *geometry_set_;
|
||||
}
|
||||
|
||||
inline CustomData &Instances::custom_data_attributes()
|
||||
inline AttributeStorage &Instances::attribute_storage()
|
||||
{
|
||||
return attributes_;
|
||||
}
|
||||
|
||||
inline const CustomData &Instances::custom_data_attributes() const
|
||||
inline const AttributeStorage &Instances::attribute_storage() const
|
||||
{
|
||||
return attributes_;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ set(SRC
|
||||
intern/attribute_legacy_convert.cc
|
||||
intern/attribute_math.cc
|
||||
intern/attribute_storage.cc
|
||||
intern/attribute_storage_access.cc
|
||||
intern/autoexec.cc
|
||||
intern/bake_data_block_map.cc
|
||||
intern/bake_geometry_nodes_modifier.cc
|
||||
|
||||
@@ -179,9 +179,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
|
||||
* if the stored type is the same as the attribute type.
|
||||
*/
|
||||
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
using UpdateOnChange = void (*)(void *owner);
|
||||
using AttrUpdateOnChange = void (*)(void *owner);
|
||||
const CustomDataAccessInfo custom_data_access_;
|
||||
const UpdateOnChange update_on_change_;
|
||||
const AttrUpdateOnChange update_on_change_;
|
||||
|
||||
public:
|
||||
BuiltinCustomDataLayerProvider(std::string attribute_name,
|
||||
@@ -189,7 +189,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
|
||||
const eCustomDataType data_type,
|
||||
const DeletableEnum deletable,
|
||||
const CustomDataAccessInfo custom_data_access,
|
||||
const UpdateOnChange update_on_change,
|
||||
const AttrUpdateOnChange update_on_change,
|
||||
const AttributeValidator validator = {},
|
||||
const GPointer default_value = {})
|
||||
: BuiltinAttributeProvider(
|
||||
|
||||
@@ -314,6 +314,34 @@ void AttributeStorage::rename(const StringRef old_name, std::string new_name)
|
||||
}
|
||||
}
|
||||
|
||||
void AttributeStorage::resize(const AttrDomain domain, const int64_t new_size)
|
||||
{
|
||||
this->foreach([&](Attribute &attr) {
|
||||
if (attr.domain() != domain) {
|
||||
return;
|
||||
}
|
||||
const CPPType &type = 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());
|
||||
const int64_t old_size = data.size;
|
||||
|
||||
auto new_data = bke::Attribute::ArrayData::ForUninitialized(type, new_size);
|
||||
type.copy_construct_n(data.data, new_data.data, std::min(old_size, new_size));
|
||||
if (old_size < new_size) {
|
||||
type.default_construct_n(POINTER_OFFSET(new_data.data, type.size * old_size),
|
||||
new_size - old_size);
|
||||
}
|
||||
|
||||
attr.assign_data(std::move(new_data));
|
||||
}
|
||||
case bke::AttrStorageType::Single: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void read_array_data(BlendDataReader &reader,
|
||||
const int8_t dna_attr_type,
|
||||
const int64_t size,
|
||||
|
||||
206
source/blender/blenkernel/intern/attribute_storage_access.cc
Normal file
206
source/blender/blenkernel/intern/attribute_storage_access.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_storage.hh"
|
||||
|
||||
#include "attribute_storage_access.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
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()};
|
||||
}
|
||||
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 {};
|
||||
}
|
||||
|
||||
GAttributeWriter attribute_to_writer(void *owner,
|
||||
const Map<StringRef, AttrUpdateOnChange> &changed_tags,
|
||||
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 AttrUpdateOnChange update_fn = changed_tags.lookup_default(attribute.name(),
|
||||
nullptr))
|
||||
{
|
||||
tag_modified_fn = [owner, update_fn]() { update_fn(owner); };
|
||||
};
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
GVArray get_varray_attribute(const AttributeStorage &storage,
|
||||
AttrDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
StringRef name,
|
||||
int64_t domain_size,
|
||||
const void *default_value)
|
||||
{
|
||||
const bke::Attribute *attr = storage.wrap().lookup(name);
|
||||
|
||||
const auto return_default = [&]() {
|
||||
return GVArray::ForSingle(cpp_type, domain_size, default_value);
|
||||
};
|
||||
|
||||
if (!attr) {
|
||||
return return_default();
|
||||
}
|
||||
if (attr->domain() != domain) {
|
||||
return return_default();
|
||||
}
|
||||
if (attr->data_type() != cpp_type_to_attribute_type(cpp_type)) {
|
||||
return return_default();
|
||||
}
|
||||
switch (attr->storage_type()) {
|
||||
case bke::AttrStorageType::Array: {
|
||||
const auto &data = std::get<bke::Attribute::ArrayData>(attr->data());
|
||||
const GSpan span(cpp_type, data.data, data.size);
|
||||
return GVArray::ForSpan(span);
|
||||
}
|
||||
case bke::AttrStorageType::Single: {
|
||||
const auto &data = std::get<bke::Attribute::SingleData>(attr->data());
|
||||
return GVArray::ForSingle(cpp_type, domain_size, data.value);
|
||||
}
|
||||
}
|
||||
return return_default();
|
||||
}
|
||||
|
||||
GSpan get_span_attribute(const AttributeStorage &storage,
|
||||
const AttrDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
const StringRef name,
|
||||
const int64_t domain_size)
|
||||
{
|
||||
const bke::Attribute *attr = storage.wrap().lookup(name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
if (attr->domain() != domain) {
|
||||
return {};
|
||||
}
|
||||
if (const auto *array_data = std::get_if<bke::Attribute::ArrayData>(&attr->data())) {
|
||||
BLI_assert(array_data->size == domain_size);
|
||||
UNUSED_VARS_NDEBUG(domain_size);
|
||||
return GSpan(cpp_type, array_data->data, array_data->size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
GMutableSpan get_mutable_attribute(AttributeStorage &storage,
|
||||
const AttrDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
const StringRef name,
|
||||
const int64_t domain_size,
|
||||
const void *default_value)
|
||||
{
|
||||
if (domain_size <= 0) {
|
||||
return {};
|
||||
}
|
||||
const bke::AttrType type = bke::cpp_type_to_attribute_type(cpp_type);
|
||||
if (bke::Attribute *attr = 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(cpp_type, single_data->value);
|
||||
attr->assign_data(bke::Attribute::ArrayData::ForValue(g_value, domain_size));
|
||||
}
|
||||
auto &array_data = std::get<bke::Attribute::ArrayData>(attr->data_for_write());
|
||||
return GMutableSpan(cpp_type, array_data.data, domain_size);
|
||||
}
|
||||
/* The attribute has the wrong type. This shouldn't happen for builtin attributes, but just
|
||||
* in case, remove it. */
|
||||
storage.wrap().remove(name);
|
||||
}
|
||||
bke::Attribute &attr = storage.wrap().add(
|
||||
name,
|
||||
domain,
|
||||
type,
|
||||
bke::Attribute::ArrayData::ForValue({cpp_type, &default_value}, domain_size));
|
||||
auto &array_data = std::get<bke::Attribute::ArrayData>(attr.data_for_write());
|
||||
BLI_assert(array_data.size == domain_size);
|
||||
return GMutableSpan(cpp_type, array_data.data, domain_size);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
88
source/blender/blenkernel/intern/attribute_storage_access.hh
Normal file
88
source/blender/blenkernel/intern/attribute_storage_access.hh
Normal file
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_storage.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
using AttrUpdateOnChange = void (*)(void *owner);
|
||||
|
||||
struct AttrBuiltinInfo {
|
||||
AttrDomain domain;
|
||||
AttrType type;
|
||||
GPointer default_value = {};
|
||||
AttributeValidator validator = {};
|
||||
bool deletable = true;
|
||||
AttrBuiltinInfo(AttrDomain domain, AttrType type) : domain(domain), type(type) {}
|
||||
};
|
||||
|
||||
GAttributeReader attribute_to_reader(const Attribute &attribute,
|
||||
const AttrDomain domain,
|
||||
const int64_t domain_size);
|
||||
|
||||
GAttributeWriter attribute_to_writer(void *owner,
|
||||
const Map<StringRef, AttrUpdateOnChange> &changed_tags,
|
||||
const int64_t domain_size,
|
||||
Attribute &attribute);
|
||||
|
||||
Attribute::DataVariant attribute_init_to_data(const bke::AttrType data_type,
|
||||
const int64_t domain_size,
|
||||
const AttributeInit &initializer);
|
||||
|
||||
GVArray get_varray_attribute(const AttributeStorage &storage,
|
||||
AttrDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
StringRef name,
|
||||
int64_t domain_size,
|
||||
const void *default_value);
|
||||
|
||||
template<typename T>
|
||||
inline VArray<T> get_varray_attribute(const AttributeStorage &storage,
|
||||
const AttrDomain domain,
|
||||
const StringRef name,
|
||||
const int64_t domain_size,
|
||||
const T &default_value)
|
||||
{
|
||||
GVArray varray = get_varray_attribute(
|
||||
storage, domain, CPPType::get<T>(), name, domain_size, &default_value);
|
||||
return varray.typed<T>();
|
||||
}
|
||||
|
||||
GSpan get_span_attribute(const AttributeStorage &storage,
|
||||
AttrDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
StringRef name,
|
||||
const int64_t domain_size);
|
||||
|
||||
template<typename T>
|
||||
inline Span<T> get_span_attribute(const AttributeStorage &storage,
|
||||
const AttrDomain domain,
|
||||
const StringRef name,
|
||||
const int64_t domain_size)
|
||||
{
|
||||
const GSpan span = get_span_attribute(storage, domain, CPPType::get<T>(), name, domain_size);
|
||||
return span.typed<T>();
|
||||
}
|
||||
|
||||
GMutableSpan get_mutable_attribute(AttributeStorage &storage,
|
||||
const AttrDomain domain,
|
||||
const CPPType &cpp_type,
|
||||
const StringRef name,
|
||||
const int64_t domain_size,
|
||||
const void *default_value);
|
||||
|
||||
template<typename T>
|
||||
inline MutableSpan<T> get_mutable_attribute(AttributeStorage &storage,
|
||||
const AttrDomain domain,
|
||||
const StringRef name,
|
||||
const int64_t domain_size,
|
||||
const T &default_value = T())
|
||||
{
|
||||
const GMutableSpan span = get_mutable_attribute(
|
||||
storage, domain, CPPType::get<T>(), name, domain_size, &default_value);
|
||||
return span.typed<T>();
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
@@ -10,11 +10,12 @@
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_geometry_set_instances.hh"
|
||||
#include "BKE_instances.hh"
|
||||
|
||||
#include "attribute_storage_access.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
InstanceReference::InstanceReference(GeometrySet geometry_set)
|
||||
@@ -145,34 +146,27 @@ uint64_t InstanceReference::hash() const
|
||||
return get_default_hash(geometry_hash, type_, data_);
|
||||
}
|
||||
|
||||
Instances::Instances()
|
||||
{
|
||||
CustomData_reset(&attributes_);
|
||||
}
|
||||
Instances::Instances() = default;
|
||||
|
||||
Instances::Instances(Instances &&other)
|
||||
: references_(std::move(other.references_)),
|
||||
instances_num_(other.instances_num_),
|
||||
attributes_(other.attributes_),
|
||||
attributes_(std::move(other.attributes_)),
|
||||
reference_user_counts_(std::move(other.reference_user_counts_)),
|
||||
almost_unique_ids_cache_(std::move(other.almost_unique_ids_cache_))
|
||||
{
|
||||
CustomData_reset(&other.attributes_);
|
||||
}
|
||||
|
||||
Instances::Instances(const Instances &other)
|
||||
: references_(other.references_),
|
||||
instances_num_(other.instances_num_),
|
||||
attributes_(other.attributes_),
|
||||
reference_user_counts_(other.reference_user_counts_),
|
||||
almost_unique_ids_cache_(other.almost_unique_ids_cache_)
|
||||
{
|
||||
CustomData_init_from(&other.attributes_, &attributes_, CD_MASK_ALL, other.instances_num_);
|
||||
}
|
||||
|
||||
Instances::~Instances()
|
||||
{
|
||||
CustomData_free(&attributes_);
|
||||
}
|
||||
Instances::~Instances() = default;
|
||||
|
||||
Instances &Instances::operator=(const Instances &other)
|
||||
{
|
||||
@@ -196,17 +190,21 @@ Instances &Instances::operator=(Instances &&other)
|
||||
|
||||
void Instances::resize(int capacity)
|
||||
{
|
||||
CustomData_realloc(&attributes_, instances_num_, capacity, CD_SET_DEFAULT);
|
||||
const int old_size = this->instances_num();
|
||||
attributes_.resize(AttrDomain::Instance, capacity);
|
||||
instances_num_ = capacity;
|
||||
fill_attribute_range_default(this->attributes_for_write(),
|
||||
AttrDomain::Instance,
|
||||
{},
|
||||
IndexRange::from_begin_end(old_size, capacity));
|
||||
}
|
||||
|
||||
void Instances::add_instance(const int instance_handle, const float4x4 &transform)
|
||||
{
|
||||
BLI_assert(instance_handle >= 0);
|
||||
BLI_assert(instance_handle < references_.size());
|
||||
const int old_size = instances_num_;
|
||||
instances_num_++;
|
||||
CustomData_realloc(&attributes_, old_size, instances_num_);
|
||||
attributes_.resize(AttrDomain::Instance, instances_num_);
|
||||
this->reference_handles_for_write().last() = instance_handle;
|
||||
this->transforms_for_write().last() = transform;
|
||||
this->tag_reference_handles_changed();
|
||||
@@ -214,38 +212,26 @@ void Instances::add_instance(const int instance_handle, const float4x4 &transfor
|
||||
|
||||
Span<int> Instances::reference_handles() const
|
||||
{
|
||||
return {static_cast<const int *>(
|
||||
CustomData_get_layer_named(&attributes_, CD_PROP_INT32, ".reference_index")),
|
||||
instances_num_};
|
||||
return get_span_attribute<int>(
|
||||
attributes_, AttrDomain::Instance, ".reference_index", instances_num_);
|
||||
}
|
||||
|
||||
MutableSpan<int> Instances::reference_handles_for_write()
|
||||
{
|
||||
int *data = static_cast<int *>(CustomData_get_layer_named_for_write(
|
||||
&attributes_, CD_PROP_INT32, ".reference_index", instances_num_));
|
||||
if (!data) {
|
||||
data = static_cast<int *>(CustomData_add_layer_named(
|
||||
&attributes_, CD_PROP_INT32, CD_SET_DEFAULT, instances_num_, ".reference_index"));
|
||||
}
|
||||
return {data, instances_num_};
|
||||
return get_mutable_attribute<int>(
|
||||
attributes_, AttrDomain::Instance, ".reference_index", instances_num_);
|
||||
}
|
||||
|
||||
Span<float4x4> Instances::transforms() const
|
||||
{
|
||||
return {static_cast<const float4x4 *>(
|
||||
CustomData_get_layer_named(&attributes_, CD_PROP_FLOAT4X4, "instance_transform")),
|
||||
instances_num_};
|
||||
return get_span_attribute<float4x4>(
|
||||
attributes_, AttrDomain::Instance, "instance_transform", instances_num_);
|
||||
}
|
||||
|
||||
MutableSpan<float4x4> Instances::transforms_for_write()
|
||||
{
|
||||
float4x4 *data = static_cast<float4x4 *>(CustomData_get_layer_named_for_write(
|
||||
&attributes_, CD_PROP_FLOAT4X4, "instance_transform", instances_num_));
|
||||
if (!data) {
|
||||
data = static_cast<float4x4 *>(CustomData_add_layer_named(
|
||||
&attributes_, CD_PROP_FLOAT4X4, CD_SET_DEFAULT, instances_num_, "instance_transform"));
|
||||
}
|
||||
return {data, instances_num_};
|
||||
return get_mutable_attribute<float4x4>(
|
||||
attributes_, AttrDomain::Instance, "instance_transform", instances_num_);
|
||||
}
|
||||
|
||||
GeometrySet &Instances::geometry_set_from_reference(const int reference_index)
|
||||
@@ -427,7 +413,7 @@ void Instances::ensure_owns_direct_data()
|
||||
|
||||
void Instances::count_memory(MemoryCounter &memory) const
|
||||
{
|
||||
CustomData_count_memory(attributes_, instances_num_, memory);
|
||||
attributes_.count_memory(memory);
|
||||
for (const InstanceReference &reference : references_) {
|
||||
reference.count_memory(memory);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_instances.hh"
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
#include "attribute_storage_access.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
@@ -14,77 +16,76 @@ static void tag_component_reference_index_changed(void *owner)
|
||||
instances.tag_reference_handles_changed();
|
||||
}
|
||||
|
||||
static GeometryAttributeProviders create_attribute_providers_for_instances()
|
||||
static const auto &changed_tags()
|
||||
{
|
||||
static CustomDataAccessInfo instance_custom_data_access = {
|
||||
[](void *owner) -> CustomData * {
|
||||
Instances *instances = static_cast<Instances *>(owner);
|
||||
return &instances->custom_data_attributes();
|
||||
},
|
||||
[](const void *owner) -> const CustomData * {
|
||||
const Instances *instances = static_cast<const Instances *>(owner);
|
||||
return &instances->custom_data_attributes();
|
||||
},
|
||||
[](const void *owner) -> int {
|
||||
const Instances *instances = static_cast<const Instances *>(owner);
|
||||
return instances->instances_num();
|
||||
}};
|
||||
|
||||
/**
|
||||
* IDs of the instances. They are used for consistency over multiple frames for things like
|
||||
* motion blur. Proper stable ID data that actually helps when rendering can only be generated
|
||||
* in some situations, so this vector is allowed to be empty, in which case the index of each
|
||||
* instance will be used for the final ID.
|
||||
*/
|
||||
static BuiltinCustomDataLayerProvider id("id",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Deletable,
|
||||
instance_custom_data_access,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider instance_transform("instance_transform",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_FLOAT4X4,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
instance_custom_data_access,
|
||||
nullptr);
|
||||
|
||||
/** Indices into `Instances::references_`. Determines what data is instanced. */
|
||||
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
instance_custom_data_access,
|
||||
tag_component_reference_index_changed);
|
||||
|
||||
static CustomDataAttributeProvider instance_custom_data(AttrDomain::Instance,
|
||||
instance_custom_data_access);
|
||||
|
||||
return GeometryAttributeProviders({&instance_transform, &id, &reference_index},
|
||||
{&instance_custom_data});
|
||||
static Map<StringRef, AttrUpdateOnChange> attributes{
|
||||
{".reference_index", tag_component_reference_index_changed},
|
||||
};
|
||||
return attributes;
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_instances_accessor_functions()
|
||||
static const auto &builtin_attributes()
|
||||
{
|
||||
static const GeometryAttributeProviders providers = create_attribute_providers_for_instances();
|
||||
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 Instances *instances = static_cast<const Instances *>(owner);
|
||||
switch (domain) {
|
||||
case AttrDomain::Instance:
|
||||
return instances->instances_num();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
static auto attributes = []() {
|
||||
Map<StringRef, AttrBuiltinInfo> map;
|
||||
|
||||
/**
|
||||
* IDs of the instances. They are used for consistency over multiple frames for things like
|
||||
* motion blur. Proper stable ID data that actually helps when rendering can only be generated
|
||||
* in some situations, so this vector is allowed to be empty, in which case the index of each
|
||||
* instance will be used for the final ID.
|
||||
*/
|
||||
AttrBuiltinInfo id(bke::AttrDomain::Instance, bke::AttrType::Int32);
|
||||
map.add_new("id", std::move(id));
|
||||
|
||||
AttrBuiltinInfo instance_transform(bke::AttrDomain::Instance, bke::AttrType::Float4x4);
|
||||
instance_transform.deletable = false;
|
||||
map.add_new("instance_transform", std::move(instance_transform));
|
||||
|
||||
/** Indices into `Instances::references_`. Determines what data is instanced. */
|
||||
AttrBuiltinInfo reference_index(bke::AttrDomain::Instance, bke::AttrType::Int32);
|
||||
reference_index.deletable = false;
|
||||
map.add_new(".reference_index", std::move(reference_index));
|
||||
|
||||
return map;
|
||||
}();
|
||||
return attributes;
|
||||
}
|
||||
|
||||
static constexpr AttributeAccessorFunctions get_instances_accessor_functions()
|
||||
{
|
||||
AttributeAccessorFunctions fn{};
|
||||
fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
|
||||
return domain == AttrDomain::Instance;
|
||||
};
|
||||
fn.domain_size = [](const void *owner, const AttrDomain domain) {
|
||||
return domain == AttrDomain::Instance ?
|
||||
static_cast<const Instances *>(owner)->instances_num() :
|
||||
0;
|
||||
};
|
||||
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 Instances &instances = *static_cast<const Instances *>(owner);
|
||||
const AttributeStorage &storage = instances.attribute_storage();
|
||||
const Attribute *attribute = storage.lookup(name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
return attribute_to_reader(*attribute, AttrDomain::Instance, instances.instances_num());
|
||||
};
|
||||
fn.adapt_domain = [](const void * /*owner*/,
|
||||
const GVArray &varray,
|
||||
const AttrDomain from_domain,
|
||||
@@ -94,6 +95,82 @@ static AttributeAccessorFunctions get_instances_accessor_functions()
|
||||
}
|
||||
return GVArray{};
|
||||
};
|
||||
fn.foreach_attribute = [](const void *owner,
|
||||
const FunctionRef<void(const AttributeIter &)> fn,
|
||||
const AttributeAccessor &accessor) {
|
||||
const Instances &instances = *static_cast<const Instances *>(owner);
|
||||
const AttributeStorage &storage = instances.attribute_storage();
|
||||
storage.foreach_with_stop([&](const Attribute &attribute) {
|
||||
const auto get_fn = [&]() {
|
||||
return attribute_to_reader(attribute, AttrDomain::Instance, instances.instances_num());
|
||||
};
|
||||
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 {
|
||||
Instances &instances = *static_cast<Instances *>(owner);
|
||||
AttributeStorage &storage = instances.attribute_storage();
|
||||
Attribute *attribute = storage.lookup(name);
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
return attribute_to_writer(&instances, changed_tags(), instances.instances_num(), *attribute);
|
||||
};
|
||||
fn.remove = [](void *owner, const StringRef name) -> bool {
|
||||
Instances &instances = *static_cast<Instances *>(owner);
|
||||
AttributeStorage &storage = instances.attribute_storage();
|
||||
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) {
|
||||
Instances &instances = *static_cast<Instances *>(owner);
|
||||
const int domain_size = instances.instances_num();
|
||||
AttributeStorage &storage = instances.attribute_storage();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
|
||||
#include "attribute_storage_access.hh"
|
||||
|
||||
using blender::CPPType;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
@@ -177,98 +179,32 @@ IDTypeInfo IDType_ID_PT = {
|
||||
/*lib_override_apply_post*/ nullptr,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static VArray<T> get_varray_attribute(const PointCloud &pointcloud,
|
||||
const StringRef name,
|
||||
const T default_value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Span<T> get_span_attribute(const PointCloud &pointcloud, const StringRef name)
|
||||
{
|
||||
using namespace blender;
|
||||
const bke::Attribute *attr = pointcloud.attribute_storage.wrap().lookup(name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
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>
|
||||
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 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->assign_data(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);
|
||||
}
|
||||
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
|
||||
{
|
||||
return get_span_attribute<float3>(*this, "position");
|
||||
return blender::bke::get_span_attribute<float3>(
|
||||
this->attribute_storage.wrap(), blender::bke::AttrDomain::Point, "position", this->totpoint);
|
||||
}
|
||||
MutableSpan<float3> PointCloud::positions_for_write()
|
||||
{
|
||||
return get_mutable_attribute<float3>(*this, "position");
|
||||
return blender::bke::get_mutable_attribute<float3>(
|
||||
this->attribute_storage.wrap(), blender::bke::AttrDomain::Point, "position", this->totpoint);
|
||||
}
|
||||
|
||||
VArray<float> PointCloud::radius() const
|
||||
{
|
||||
return get_varray_attribute<float>(*this, "radius", 0.01f);
|
||||
return blender::bke::get_varray_attribute<float>(this->attribute_storage.wrap(),
|
||||
blender::bke::AttrDomain::Point,
|
||||
"radius",
|
||||
this->totpoint,
|
||||
0.01f);
|
||||
}
|
||||
MutableSpan<float> PointCloud::radius_for_write()
|
||||
{
|
||||
return get_mutable_attribute<float>(*this, "radius", 0.01f);
|
||||
return blender::bke::get_mutable_attribute<float>(this->attribute_storage.wrap(),
|
||||
blender::bke::AttrDomain::Point,
|
||||
"radius",
|
||||
this->totpoint,
|
||||
0.01f);
|
||||
}
|
||||
|
||||
PointCloud *BKE_pointcloud_add(Main *bmain, const char *name)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "BKE_pointcloud.hh"
|
||||
|
||||
#include "attribute_access_intern.hh"
|
||||
#include "attribute_storage_access.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
@@ -24,38 +25,23 @@ static void tag_radius_changed(void *owner)
|
||||
points.tag_radii_changed();
|
||||
}
|
||||
|
||||
using UpdateOnChange = void (*)(void *owner);
|
||||
|
||||
static const auto &changed_tags()
|
||||
{
|
||||
static Map<StringRef, UpdateOnChange> attributes{{"position", tag_position_changed},
|
||||
{"radius", tag_radius_changed}};
|
||||
static Map<StringRef, AttrUpdateOnChange> attributes{{"position", tag_position_changed},
|
||||
{"radius", tag_radius_changed}};
|
||||
return attributes;
|
||||
}
|
||||
|
||||
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 auto attributes = []() {
|
||||
Map<StringRef, BuiltinInfo> map;
|
||||
Map<StringRef, AttrBuiltinInfo> map;
|
||||
|
||||
BuiltinInfo position(bke::AttrDomain::Point, bke::AttrType::Float3);
|
||||
AttrBuiltinInfo 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);
|
||||
AttrBuiltinInfo radius(bke::AttrDomain::Point, bke::AttrType::Float);
|
||||
map.add_new("radius", std::move(radius));
|
||||
|
||||
return map;
|
||||
@@ -63,107 +49,6 @@ static const auto &builtin_attributes()
|
||||
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()};
|
||||
}
|
||||
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{};
|
||||
@@ -175,7 +60,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
};
|
||||
fn.builtin_domain_and_type = [](const void * /*owner*/,
|
||||
const StringRef name) -> std::optional<AttributeDomainAndType> {
|
||||
const BuiltinInfo *info = builtin_attributes().lookup_ptr(name);
|
||||
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
|
||||
if (!info) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -184,7 +69,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
return AttributeDomainAndType{info->domain, *cd_type};
|
||||
};
|
||||
fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer {
|
||||
const BuiltinInfo &info = builtin_attributes().lookup(name);
|
||||
const AttrBuiltinInfo &info = builtin_attributes().lookup(name);
|
||||
return info.default_value;
|
||||
};
|
||||
fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader {
|
||||
@@ -225,7 +110,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
});
|
||||
};
|
||||
fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator {
|
||||
const BuiltinInfo *info = builtin_attributes().lookup_ptr(name);
|
||||
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
|
||||
if (!info) {
|
||||
return {};
|
||||
}
|
||||
@@ -238,17 +123,17 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
if (!attribute) {
|
||||
return {};
|
||||
}
|
||||
return attribute_to_writer(pointcloud, pointcloud.totpoint, *attribute);
|
||||
return attribute_to_writer(&pointcloud, changed_tags(), 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 (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
|
||||
if (!info->deletable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const std::optional<UpdateOnChange> fn = changed_tags().lookup_try(name);
|
||||
const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name);
|
||||
const bool removed = storage.remove(name);
|
||||
if (!removed) {
|
||||
return false;
|
||||
@@ -268,7 +153,7 @@ static constexpr AttributeAccessorFunctions get_pointcloud_accessor_functions()
|
||||
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 (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
|
||||
if (info->domain != domain || info->type != type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -259,7 +259,8 @@ void debug_randomize_instance_order(bke::Instances *instances)
|
||||
const int instances_num = instances->instances_num();
|
||||
const int seed = seed_from_instances(*instances);
|
||||
const Array<int> new_by_old_map = get_permutation(instances_num, seed);
|
||||
reorder_customdata(instances->custom_data_attributes(), new_by_old_map);
|
||||
reorder_attribute_domain(
|
||||
instances->attribute_storage(), bke::AttrDomain::Instance, new_by_old_map);
|
||||
}
|
||||
|
||||
bool use_debug_randomization()
|
||||
|
||||
Reference in New Issue
Block a user