Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/attribute_access.cc
| Show All 28 Lines | |||||
| #include "DNA_pointcloud_types.h" | #include "DNA_pointcloud_types.h" | ||||
| #include "BLI_color.hh" | #include "BLI_color.hh" | ||||
| #include "BLI_float2.hh" | #include "BLI_float2.hh" | ||||
| #include "BLI_span.hh" | #include "BLI_span.hh" | ||||
| #include "CLG_log.h" | #include "CLG_log.h" | ||||
| #include "NOD_node_tree_multi_function.hh" | #include "NOD_type_conversions.hh" | ||||
| #include "attribute_access_intern.hh" | #include "attribute_access_intern.hh" | ||||
| static CLG_LogRef LOG = {"bke.attribute_access"}; | static CLG_LogRef LOG = {"bke.attribute_access"}; | ||||
| using blender::float3; | using blender::float3; | ||||
| using blender::Set; | using blender::Set; | ||||
| using blender::StringRef; | using blender::StringRef; | ||||
| using blender::StringRefNull; | using blender::StringRefNull; | ||||
| using blender::bke::ReadAttributePtr; | |||||
| using blender::bke::WriteAttributePtr; | |||||
| using blender::fn::GMutableSpan; | using blender::fn::GMutableSpan; | ||||
| namespace blender::bke { | namespace blender::bke { | ||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Attribute Accessor implementations | |||||
| * \{ */ | |||||
| ReadAttribute::~ReadAttribute() | |||||
| { | |||||
| if (array_is_temporary_ && array_buffer_ != nullptr) { | |||||
| cpp_type_.destruct_n(array_buffer_, size_); | |||||
| MEM_freeN(array_buffer_); | |||||
| } | |||||
| } | |||||
| fn::GSpan ReadAttribute::get_span() const | |||||
| { | |||||
| if (size_ == 0) { | |||||
| return fn::GSpan(cpp_type_); | |||||
| } | |||||
| if (array_buffer_ == nullptr) { | |||||
| std::lock_guard lock{span_mutex_}; | |||||
| if (array_buffer_ == nullptr) { | |||||
| this->initialize_span(); | |||||
| } | |||||
| } | |||||
| return fn::GSpan(cpp_type_, array_buffer_, size_); | |||||
| } | |||||
| void ReadAttribute::initialize_span() const | |||||
| { | |||||
| const int element_size = cpp_type_.size(); | |||||
| array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); | |||||
| array_is_temporary_ = true; | |||||
| for (const int i : IndexRange(size_)) { | |||||
| this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); | |||||
| } | |||||
| } | |||||
| WriteAttribute::~WriteAttribute() | |||||
| { | |||||
| if (array_should_be_applied_) { | |||||
| CLOG_ERROR(&LOG, "Forgot to call apply_span."); | |||||
| } | |||||
| if (array_is_temporary_ && array_buffer_ != nullptr) { | |||||
| cpp_type_.destruct_n(array_buffer_, size_); | |||||
| MEM_freeN(array_buffer_); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Get a mutable span that can be modified. When all modifications to the attribute are done, | |||||
| * #apply_span should be called. */ | |||||
| fn::GMutableSpan WriteAttribute::get_span() | |||||
| { | |||||
| if (size_ == 0) { | |||||
| return fn::GMutableSpan(cpp_type_); | |||||
| } | |||||
| if (array_buffer_ == nullptr) { | |||||
| this->initialize_span(false); | |||||
| } | |||||
| array_should_be_applied_ = true; | |||||
| return fn::GMutableSpan(cpp_type_, array_buffer_, size_); | |||||
| } | |||||
| fn::GMutableSpan WriteAttribute::get_span_for_write_only() | |||||
| { | |||||
| if (size_ == 0) { | |||||
| return fn::GMutableSpan(cpp_type_); | |||||
| } | |||||
| if (array_buffer_ == nullptr) { | |||||
| this->initialize_span(true); | |||||
| } | |||||
| array_should_be_applied_ = true; | |||||
| return fn::GMutableSpan(cpp_type_, array_buffer_, size_); | |||||
| } | |||||
| void WriteAttribute::initialize_span(const bool write_only) | |||||
| { | |||||
| const int element_size = cpp_type_.size(); | |||||
| array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__); | |||||
| array_is_temporary_ = true; | |||||
| if (write_only) { | |||||
| /* This does nothing for trivial types, but is necessary for general correctness. */ | |||||
| cpp_type_.construct_default_n(array_buffer_, size_); | |||||
| } | |||||
| else { | |||||
| for (const int i : IndexRange(size_)) { | |||||
| this->get(i, POINTER_OFFSET(array_buffer_, i * element_size)); | |||||
| } | |||||
| } | |||||
| } | |||||
| void WriteAttribute::apply_span() | |||||
| { | |||||
| this->apply_span_if_necessary(); | |||||
| array_should_be_applied_ = false; | |||||
| } | |||||
| void WriteAttribute::apply_span_if_necessary() | |||||
| { | |||||
| /* Only works when the span has been initialized beforehand. */ | |||||
| BLI_assert(array_buffer_ != nullptr); | |||||
| const int element_size = cpp_type_.size(); | |||||
| for (const int i : IndexRange(size_)) { | |||||
| this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); | |||||
| } | |||||
| } | |||||
| /* This is used by the #OutputAttributePtr class. */ | |||||
| class TemporaryWriteAttribute final : public WriteAttribute { | |||||
| public: | |||||
| GMutableSpan data; | |||||
| GeometryComponent &component; | |||||
| std::string final_name; | |||||
| TemporaryWriteAttribute(AttributeDomain domain, | |||||
| GMutableSpan data, | |||||
| GeometryComponent &component, | |||||
| std::string final_name) | |||||
| : WriteAttribute(domain, data.type(), data.size()), | |||||
| data(data), | |||||
| component(component), | |||||
| final_name(std::move(final_name)) | |||||
| { | |||||
| } | |||||
| ~TemporaryWriteAttribute() override | |||||
| { | |||||
| if (data.data() != nullptr) { | |||||
| cpp_type_.destruct_n(data.data(), data.size()); | |||||
| MEM_freeN(data.data()); | |||||
| } | |||||
| } | |||||
| void get_internal(const int64_t index, void *r_value) const override | |||||
| { | |||||
| data.type().copy_to_uninitialized(data[index], r_value); | |||||
| } | |||||
| void set_internal(const int64_t index, const void *value) override | |||||
| { | |||||
| data.type().copy_to_initialized(value, data[index]); | |||||
| } | |||||
| void initialize_span(const bool UNUSED(write_only)) override | |||||
| { | |||||
| array_buffer_ = data.data(); | |||||
| array_is_temporary_ = false; | |||||
| } | |||||
| void apply_span_if_necessary() override | |||||
| { | |||||
| /* Do nothing, because the span contains the attribute itself already. */ | |||||
| } | |||||
| }; | |||||
| class ConvertedReadAttribute final : public ReadAttribute { | |||||
| private: | |||||
| const CPPType &from_type_; | |||||
| const CPPType &to_type_; | |||||
| ReadAttributePtr base_attribute_; | |||||
| const nodes::DataTypeConversions &conversions_; | |||||
| public: | |||||
| ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type) | |||||
| : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()), | |||||
| from_type_(base_attribute->cpp_type()), | |||||
| to_type_(to_type), | |||||
| base_attribute_(std::move(base_attribute)), | |||||
| conversions_(nodes::get_implicit_type_conversions()) | |||||
| { | |||||
| } | |||||
| void get_internal(const int64_t index, void *r_value) const override | |||||
| { | |||||
| BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); | |||||
| base_attribute_->get(index, buffer); | |||||
| conversions_.convert(from_type_, to_type_, buffer, r_value); | |||||
| } | |||||
| }; | |||||
| /** \} */ | |||||
| const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) | const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) | ||||
| { | { | ||||
| switch (type) { | switch (type) { | ||||
| case CD_PROP_FLOAT: | case CD_PROP_FLOAT: | ||||
| return &CPPType::get<float>(); | return &CPPType::get<float>(); | ||||
| case CD_PROP_FLOAT2: | case CD_PROP_FLOAT2: | ||||
| return &CPPType::get<float2>(); | return &CPPType::get<float2>(); | ||||
| case CD_PROP_FLOAT3: | case CD_PROP_FLOAT3: | ||||
| ▲ Show 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | if (priority > highest_priority) { | ||||
| highest_priority = priority; | highest_priority = priority; | ||||
| highest_priority_domain = domain; | highest_priority_domain = domain; | ||||
| } | } | ||||
| } | } | ||||
| return highest_priority_domain; | return highest_priority_domain; | ||||
| } | } | ||||
| ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( | void OutputAttribute::save() | ||||
| { | |||||
| if (optional_span_varray_.has_value()) { | |||||
| optional_span_varray_->save(); | |||||
| } | |||||
| if (save_) { | |||||
| save_(*this); | |||||
| } | |||||
| } | |||||
| GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( | |||||
| const GeometryComponent &component) const | const GeometryComponent &component) const | ||||
| { | { | ||||
| const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| const void *data = CustomData_get_layer(custom_data, stored_type_); | const void *data = CustomData_get_layer(custom_data, stored_type_); | ||||
| if (data == nullptr) { | if (data == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| return as_read_attribute_(data, domain_size); | return as_read_attribute_(data, domain_size); | ||||
| } | } | ||||
| WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write( | GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( | ||||
| GeometryComponent &component) const | GeometryComponent &component) const | ||||
| { | { | ||||
| if (writable_ != Writable) { | if (writable_ != Writable) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| CustomData *custom_data = custom_data_access_.get_custom_data(component); | CustomData *custom_data = custom_data_access_.get_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return {}; | return {}; | ||||
| Show All 29 Lines | bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const | ||||
| const bool delete_success = CustomData_free_layer( | const bool delete_success = CustomData_free_layer( | ||||
| custom_data, stored_type_, domain_size, layer_index); | custom_data, stored_type_, domain_size, layer_index); | ||||
| if (delete_success) { | if (delete_success) { | ||||
| custom_data_access_.update_custom_data_pointers(component); | custom_data_access_.update_custom_data_pointers(component); | ||||
| } | } | ||||
| return delete_success; | return delete_success; | ||||
| } | } | ||||
| bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const | static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data, | ||||
| const CustomDataType data_type, | |||||
| const int domain_size, | |||||
| const AttributeInit &initializer) | |||||
| { | |||||
| switch (initializer.type) { | |||||
| case AttributeInit::Type::Default: { | |||||
| void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); | |||||
| return data != nullptr; | |||||
| } | |||||
| case AttributeInit::Type::VArray: { | |||||
| void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); | |||||
| if (data == nullptr) { | |||||
| return false; | |||||
| } | |||||
| const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; | |||||
| varray->materialize_to_uninitialized(IndexRange(varray->size()), data); | |||||
| return true; | |||||
| } | |||||
| case AttributeInit::Type::MoveArray: { | |||||
| void *source_data = static_cast<const AttributeInitMove &>(initializer).data; | |||||
| void *data = CustomData_add_layer( | |||||
| &custom_data, data_type, CD_ASSIGN, source_data, domain_size); | |||||
| if (data == nullptr) { | |||||
| MEM_freeN(source_data); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, | |||||
| const AttributeInit &initializer) const | |||||
| { | { | ||||
| if (createable_ != Creatable) { | if (createable_ != Creatable) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| CustomData *custom_data = custom_data_access_.get_custom_data(component); | CustomData *custom_data = custom_data_access_.get_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { | if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { | ||||
| /* Exists already. */ | /* Exists already. */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| const void *data = CustomData_add_layer( | const bool success = add_custom_data_layer_from_attribute_init( | ||||
| custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size); | *custom_data, stored_type_, domain_size, initializer); | ||||
| const bool success = data != nullptr; | |||||
| if (success) { | if (success) { | ||||
| custom_data_access_.update_custom_data_pointers(component); | custom_data_access_.update_custom_data_pointers(component); | ||||
| } | } | ||||
| return success; | return success; | ||||
| } | } | ||||
| bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const | bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const | ||||
| { | { | ||||
| const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const void *data = CustomData_get_layer(custom_data, stored_type_); | const void *data = CustomData_get_layer(custom_data, stored_type_); | ||||
| return data != nullptr; | return data != nullptr; | ||||
| } | } | ||||
| ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( | ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( | ||||
| const GeometryComponent &component, const StringRef attribute_name) const | const GeometryComponent &component, const StringRef attribute_name) const | ||||
| { | { | ||||
| const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | ||||
| Show All 16 Lines | switch (data_type) { | ||||
| return this->layer_to_read_attribute<bool>(layer, domain_size); | return this->layer_to_read_attribute<bool>(layer, domain_size); | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| WriteAttributePtr CustomDataAttributeProvider::try_get_for_write( | WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( | ||||
| GeometryComponent &component, const StringRef attribute_name) const | GeometryComponent &component, const StringRef attribute_name) const | ||||
| { | { | ||||
| CustomData *custom_data = custom_data_access_.get_custom_data(component); | CustomData *custom_data = custom_data_access_.get_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { | for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { | ||||
| Show All 35 Lines | for (const int i : IndexRange(custom_data->totlayer)) { | ||||
| if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) { | if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) { | ||||
| CustomData_free_layer(custom_data, layer.type, domain_size, i); | CustomData_free_layer(custom_data, layer.type, domain_size, i); | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name, | |||||
| CustomData &custom_data, | |||||
| const CustomDataType data_type, | |||||
| const int domain_size, | |||||
| const AttributeInit &initializer) | |||||
| { | |||||
| char attribute_name_c[MAX_NAME]; | |||||
| attribute_name.copy(attribute_name_c); | |||||
| switch (initializer.type) { | |||||
| case AttributeInit::Type::Default: { | |||||
| void *data = CustomData_add_layer_named( | |||||
| &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); | |||||
| return data != nullptr; | |||||
| } | |||||
| case AttributeInit::Type::VArray: { | |||||
| void *data = CustomData_add_layer_named( | |||||
| &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); | |||||
| if (data == nullptr) { | |||||
| return false; | |||||
| } | |||||
| const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; | |||||
| varray->materialize_to_uninitialized(IndexRange(varray->size()), data); | |||||
| return true; | |||||
| } | |||||
| case AttributeInit::Type::MoveArray: { | |||||
| void *source_data = static_cast<const AttributeInitMove &>(initializer).data; | |||||
| void *data = CustomData_add_layer_named( | |||||
| &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c); | |||||
| if (data == nullptr) { | |||||
| MEM_freeN(source_data); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| bool CustomDataAttributeProvider::try_create(GeometryComponent &component, | bool CustomDataAttributeProvider::try_create(GeometryComponent &component, | ||||
| const StringRef attribute_name, | const StringRef attribute_name, | ||||
| const AttributeDomain domain, | const AttributeDomain domain, | ||||
| const CustomDataType data_type) const | const CustomDataType data_type, | ||||
| const AttributeInit &initializer) const | |||||
| { | { | ||||
| if (domain_ != domain) { | if (domain_ != domain) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (!this->type_is_supported(data_type)) { | if (!this->type_is_supported(data_type)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| CustomData *custom_data = custom_data_access_.get_custom_data(component); | CustomData *custom_data = custom_data_access_.get_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | ||||
| if (layer.name == attribute_name) { | if (layer.name == attribute_name) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| char attribute_name_c[MAX_NAME]; | add_named_custom_data_layer_from_attribute_init( | ||||
| attribute_name.copy(attribute_name_c); | attribute_name, *custom_data, data_type, domain_size, initializer); | ||||
| CustomData_add_layer_named( | |||||
| custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); | |||||
| return true; | return true; | ||||
| } | } | ||||
| bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component, | bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component, | ||||
| const AttributeForeachCallback callback) const | const AttributeForeachCallback callback) const | ||||
| { | { | ||||
| const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | ||||
| const CustomDataType data_type = (CustomDataType)layer.type; | const CustomDataType data_type = (CustomDataType)layer.type; | ||||
| if (this->type_is_supported(data_type)) { | if (this->type_is_supported(data_type)) { | ||||
| AttributeMetaData meta_data{domain_, data_type}; | AttributeMetaData meta_data{domain_, data_type}; | ||||
| if (!callback(layer.name, meta_data)) { | if (!callback(layer.name, meta_data)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( | ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( | ||||
| const GeometryComponent &component, const StringRef attribute_name) const | const GeometryComponent &component, const StringRef attribute_name) const | ||||
| { | { | ||||
| const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { | ||||
| if (layer.type == stored_type_) { | if (layer.type == stored_type_) { | ||||
| if (layer.name == attribute_name) { | if (layer.name == attribute_name) { | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| return as_read_attribute_(layer.data, domain_size); | return {as_read_attribute_(layer.data, domain_size), domain_}; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( | WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( | ||||
| GeometryComponent &component, const StringRef attribute_name) const | GeometryComponent &component, const StringRef attribute_name) const | ||||
| { | { | ||||
| CustomData *custom_data = custom_data_access_.get_custom_data(component); | CustomData *custom_data = custom_data_access_.get_custom_data(component); | ||||
| if (custom_data == nullptr) { | if (custom_data == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { | for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { | ||||
| if (layer.type == stored_type_) { | if (layer.type == stored_type_) { | ||||
| if (layer.name == attribute_name) { | if (layer.name == attribute_name) { | ||||
| const int domain_size = component.attribute_domain_size(domain_); | const int domain_size = component.attribute_domain_size(domain_); | ||||
| void *data_old = layer.data; | void *data_old = layer.data; | ||||
| void *data_new = CustomData_duplicate_referenced_layer_named( | void *data_new = CustomData_duplicate_referenced_layer_named( | ||||
| custom_data, stored_type_, layer.name, domain_size); | custom_data, stored_type_, layer.name, domain_size); | ||||
| if (data_old != data_new) { | if (data_old != data_new) { | ||||
| custom_data_access_.update_custom_data_pointers(component); | custom_data_access_.update_custom_data_pointers(component); | ||||
| } | } | ||||
| return as_write_attribute_(layer.data, domain_size); | return {as_write_attribute_(layer.data, domain_size), domain_}; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, | bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, | ||||
| const StringRef attribute_name) const | const StringRef attribute_name) const | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) const | ||||
| return providers->supported_domains().contains(domain); | return providers->supported_domains().contains(domain); | ||||
| } | } | ||||
| int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const | int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const | ||||
| { | { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| ReadAttributePtr GeometryComponent::attribute_try_get_for_read( | bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const | ||||
| { | |||||
| using namespace blender::bke; | |||||
| const ComponentAttributeProviders *providers = this->get_attribute_providers(); | |||||
| if (providers == nullptr) { | |||||
| return false; | |||||
| } | |||||
| return providers->builtin_attribute_providers().contains_as(attribute_name); | |||||
| } | |||||
| blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( | |||||
| const StringRef attribute_name) const | const StringRef attribute_name) const | ||||
| { | { | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| const ComponentAttributeProviders *providers = this->get_attribute_providers(); | const ComponentAttributeProviders *providers = this->get_attribute_providers(); | ||||
| if (providers == nullptr) { | if (providers == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| const BuiltinAttributeProvider *builtin_provider = | const BuiltinAttributeProvider *builtin_provider = | ||||
| providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | ||||
| if (builtin_provider != nullptr) { | if (builtin_provider != nullptr) { | ||||
| return builtin_provider->try_get_for_read(*this); | return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; | ||||
| } | } | ||||
| for (const DynamicAttributesProvider *dynamic_provider : | for (const DynamicAttributesProvider *dynamic_provider : | ||||
| providers->dynamic_attribute_providers()) { | providers->dynamic_attribute_providers()) { | ||||
| ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name); | ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name); | ||||
| if (attribute) { | if (attribute) { | ||||
| return attribute; | return attribute; | ||||
| } | } | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| ReadAttributePtr GeometryComponent::attribute_try_adapt_domain( | std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain( | ||||
| ReadAttributePtr attribute, const AttributeDomain new_domain) const | std::unique_ptr<blender::fn::GVArray> varray, | ||||
| const AttributeDomain from_domain, | |||||
| const AttributeDomain to_domain) const | |||||
| { | { | ||||
| if (attribute && attribute->domain() == new_domain) { | if (from_domain == to_domain) { | ||||
| return attribute; | return varray; | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name) | blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( | ||||
| const StringRef attribute_name) | |||||
| { | { | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| const ComponentAttributeProviders *providers = this->get_attribute_providers(); | const ComponentAttributeProviders *providers = this->get_attribute_providers(); | ||||
| if (providers == nullptr) { | if (providers == nullptr) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| const BuiltinAttributeProvider *builtin_provider = | const BuiltinAttributeProvider *builtin_provider = | ||||
| providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | ||||
| if (builtin_provider != nullptr) { | if (builtin_provider != nullptr) { | ||||
| return builtin_provider->try_get_for_write(*this); | return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; | ||||
| } | } | ||||
| for (const DynamicAttributesProvider *dynamic_provider : | for (const DynamicAttributesProvider *dynamic_provider : | ||||
| providers->dynamic_attribute_providers()) { | providers->dynamic_attribute_providers()) { | ||||
| WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name); | WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name); | ||||
| if (attribute) { | if (attribute) { | ||||
| return attribute; | return attribute; | ||||
| } | } | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) | bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) | ||||
| Show All 13 Lines | for (const DynamicAttributesProvider *dynamic_provider : | ||||
| providers->dynamic_attribute_providers()) { | providers->dynamic_attribute_providers()) { | ||||
| success = dynamic_provider->try_delete(*this, attribute_name) || success; | success = dynamic_provider->try_delete(*this, attribute_name) || success; | ||||
| } | } | ||||
| return success; | return success; | ||||
| } | } | ||||
| bool GeometryComponent::attribute_try_create(const StringRef attribute_name, | bool GeometryComponent::attribute_try_create(const StringRef attribute_name, | ||||
| const AttributeDomain domain, | const AttributeDomain domain, | ||||
| const CustomDataType data_type) | const CustomDataType data_type, | ||||
| const AttributeInit &initializer) | |||||
| { | { | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| if (attribute_name.is_empty()) { | if (attribute_name.is_empty()) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const ComponentAttributeProviders *providers = this->get_attribute_providers(); | const ComponentAttributeProviders *providers = this->get_attribute_providers(); | ||||
| if (providers == nullptr) { | if (providers == nullptr) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const BuiltinAttributeProvider *builtin_provider = | const BuiltinAttributeProvider *builtin_provider = | ||||
| providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | ||||
| if (builtin_provider != nullptr) { | if (builtin_provider != nullptr) { | ||||
| if (builtin_provider->domain() != domain) { | if (builtin_provider->domain() != domain) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (builtin_provider->data_type() != data_type) { | if (builtin_provider->data_type() != data_type) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| return builtin_provider->try_create(*this); | return builtin_provider->try_create(*this, initializer); | ||||
| } | } | ||||
| for (const DynamicAttributesProvider *dynamic_provider : | for (const DynamicAttributesProvider *dynamic_provider : | ||||
| providers->dynamic_attribute_providers()) { | providers->dynamic_attribute_providers()) { | ||||
| if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) { | if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name, | |||||
| const AttributeInit &initializer) | |||||
| { | |||||
| using namespace blender::bke; | |||||
| if (attribute_name.is_empty()) { | |||||
| return false; | |||||
| } | |||||
| const ComponentAttributeProviders *providers = this->get_attribute_providers(); | |||||
| if (providers == nullptr) { | |||||
| return false; | |||||
| } | |||||
| const BuiltinAttributeProvider *builtin_provider = | |||||
| providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); | |||||
| if (builtin_provider == nullptr) { | |||||
| return false; | |||||
| } | |||||
| return builtin_provider->try_create(*this, initializer); | |||||
| } | |||||
| Set<std::string> GeometryComponent::attribute_names() const | Set<std::string> GeometryComponent::attribute_names() const | ||||
| { | { | ||||
| Set<std::string> attributes; | Set<std::string> attributes; | ||||
| this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { | this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { | ||||
| attributes.add(name); | attributes.add(name); | ||||
| return true; | return true; | ||||
| }); | }); | ||||
| return attributes; | return attributes; | ||||
| } | } | ||||
| void GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const | /** | ||||
| * \return False if the callback explicitly returned false at any point, otherwise true, | |||||
| * meaning the callback made it all the way through. | |||||
| */ | |||||
| bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const | |||||
| { | { | ||||
| using namespace blender::bke; | using namespace blender::bke; | ||||
| const ComponentAttributeProviders *providers = this->get_attribute_providers(); | const ComponentAttributeProviders *providers = this->get_attribute_providers(); | ||||
| if (providers == nullptr) { | if (providers == nullptr) { | ||||
| return; | return true; | ||||
| } | } | ||||
| /* Keep track handled attribute names to make sure that we do not return the same name twice. */ | /* Keep track handled attribute names to make sure that we do not return the same name twice. */ | ||||
| Set<std::string> handled_attribute_names; | Set<std::string> handled_attribute_names; | ||||
| for (const BuiltinAttributeProvider *provider : | for (const BuiltinAttributeProvider *provider : | ||||
| providers->builtin_attribute_providers().values()) { | providers->builtin_attribute_providers().values()) { | ||||
| if (provider->exists(*this)) { | if (provider->exists(*this)) { | ||||
| AttributeMetaData meta_data{provider->domain(), provider->data_type()}; | AttributeMetaData meta_data{provider->domain(), provider->data_type()}; | ||||
| if (!callback(provider->name(), meta_data)) { | if (!callback(provider->name(), meta_data)) { | ||||
| return; | return false; | ||||
| } | } | ||||
| handled_attribute_names.add_new(provider->name()); | handled_attribute_names.add_new(provider->name()); | ||||
| } | } | ||||
| } | } | ||||
| for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { | for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { | ||||
| const bool continue_loop = provider->foreach_attribute( | const bool continue_loop = provider->foreach_attribute( | ||||
| *this, [&](StringRefNull name, const AttributeMetaData &meta_data) { | *this, [&](StringRefNull name, const AttributeMetaData &meta_data) { | ||||
| if (handled_attribute_names.add(name)) { | if (handled_attribute_names.add(name)) { | ||||
| return callback(name, meta_data); | return callback(name, meta_data); | ||||
| } | } | ||||
| return true; | return true; | ||||
| }); | }); | ||||
| if (!continue_loop) { | if (!continue_loop) { | ||||
| return; | return false; | ||||
| } | } | ||||
| } | } | ||||
| return true; | |||||
| } | } | ||||
| bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const | bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const | ||||
| { | { | ||||
| ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); | blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); | ||||
| if (attribute) { | if (attribute) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, | std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( | ||||
| const blender::fn::CPPType &to_type) | const StringRef attribute_name) const | ||||
| { | { | ||||
| const blender::fn::CPPType &from_type = attribute->cpp_type(); | std::optional<AttributeMetaData> result{std::nullopt}; | ||||
| if (from_type == to_type) { | this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { | ||||
| return attribute; | if (attribute_name == name) { | ||||
| result = meta_data; | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| }); | |||||
| return result; | |||||
| } | } | ||||
| static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type( | |||||
| std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type) | |||||
| { | |||||
| const blender::nodes::DataTypeConversions &conversions = | const blender::nodes::DataTypeConversions &conversions = | ||||
| blender::nodes::get_implicit_type_conversions(); | blender::nodes::get_implicit_type_conversions(); | ||||
| if (!conversions.is_convertible(from_type, to_type)) { | return conversions.try_convert(std::move(varray), to_type); | ||||
| return {}; | |||||
| } | |||||
| return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type); | |||||
| } | } | ||||
| ReadAttributePtr GeometryComponent::attribute_try_get_for_read( | std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read( | ||||
| const StringRef attribute_name, | const StringRef attribute_name, | ||||
| const AttributeDomain domain, | const AttributeDomain domain, | ||||
| const CustomDataType data_type) const | const CustomDataType data_type) const | ||||
| { | { | ||||
| ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); | blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); | ||||
| if (!attribute) { | if (!attribute) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) { | std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray); | ||||
| attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); | if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) { | ||||
| if (!attribute) { | varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); | ||||
| if (!varray) { | |||||
| return {}; | return {}; | ||||
| } | } | ||||
| } | } | ||||
| const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); | const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); | ||||
| BLI_assert(cpp_type != nullptr); | BLI_assert(cpp_type != nullptr); | ||||
| if (attribute->cpp_type() != *cpp_type) { | if (varray->type() != *cpp_type) { | ||||
| attribute = try_adapt_data_type(std::move(attribute), *cpp_type); | varray = try_adapt_data_type(std::move(varray), *cpp_type); | ||||
| if (!attribute) { | if (!varray) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| } | } | ||||
| return attribute; | return varray; | ||||
| } | } | ||||
| ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name, | std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read( | ||||
| const AttributeDomain domain) const | const StringRef attribute_name, const AttributeDomain domain) const | ||||
| { | { | ||||
| if (!this->attribute_domain_supported(domain)) { | if (!this->attribute_domain_supported(domain)) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); | blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); | ||||
| if (!attribute) { | if (!attribute) { | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| if (attribute->domain() != domain) { | if (attribute.domain != domain) { | ||||
| attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); | return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain); | ||||
| if (!attribute) { | |||||
| return {}; | |||||
| } | } | ||||
| return std::move(attribute.varray); | |||||
| } | } | ||||
| blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( | |||||
| const blender::StringRef attribute_name, const CustomDataType data_type) const | |||||
| { | |||||
| blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); | |||||
| if (!attribute) { | |||||
| return {}; | |||||
| } | |||||
| const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); | |||||
| BLI_assert(type != nullptr); | |||||
| if (attribute.varray->type() == *type) { | |||||
| return attribute; | return attribute; | ||||
| } | } | ||||
| const blender::nodes::DataTypeConversions &conversions = | |||||
| blender::nodes::get_implicit_type_conversions(); | |||||
| return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; | |||||
| } | |||||
| ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name, | std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read( | ||||
| const StringRef attribute_name, | |||||
| const AttributeDomain domain, | const AttributeDomain domain, | ||||
| const CustomDataType data_type, | const CustomDataType data_type, | ||||
| const void *default_value) const | const void *default_value) const | ||||
| { | { | ||||
| ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type); | std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read( | ||||
| if (attribute) { | attribute_name, domain, data_type); | ||||
| return attribute; | if (varray) { | ||||
| return varray; | |||||
| } | |||||
| const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); | |||||
| if (default_value == nullptr) { | |||||
| default_value = type->default_value(); | |||||
| } | } | ||||
| return this->attribute_get_constant_for_read(domain, data_type, default_value); | const int domain_size = this->attribute_domain_size(domain); | ||||
| return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value); | |||||
| } | } | ||||
| blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read( | class GVMutableAttribute_For_OutputAttribute | ||||
| const AttributeDomain domain, const CustomDataType data_type, const void *value) const | : public blender::fn::GVMutableArray_For_GMutableSpan { | ||||
| public: | |||||
| GeometryComponent *component; | |||||
| std::string final_name; | |||||
| GVMutableAttribute_For_OutputAttribute(GMutableSpan data, | |||||
| GeometryComponent &component, | |||||
| std::string final_name) | |||||
| : blender::fn::GVMutableArray_For_GMutableSpan(data), | |||||
| component(&component), | |||||
| final_name(std::move(final_name)) | |||||
| { | { | ||||
| BLI_assert(this->attribute_domain_supported(domain)); | |||||
| const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); | |||||
| BLI_assert(cpp_type != nullptr); | |||||
| if (value == nullptr) { | |||||
| value = cpp_type->default_value(); | |||||
| } | |||||
| const int domain_size = this->attribute_domain_size(domain); | |||||
| return std::make_unique<blender::bke::ConstantReadAttribute>( | |||||
| domain, domain_size, *cpp_type, value); | |||||
| } | } | ||||
| blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted( | ~GVMutableAttribute_For_OutputAttribute() override | ||||
| const AttributeDomain domain, | { | ||||
| const CustomDataType in_data_type, | type_->destruct_n(data_, size_); | ||||
| const CustomDataType out_data_type, | MEM_freeN(data_); | ||||
| const void *value) const | |||||
| { | |||||
| BLI_assert(this->attribute_domain_supported(domain)); | |||||
| if (value == nullptr || in_data_type == out_data_type) { | |||||
| return this->attribute_get_constant_for_read(domain, out_data_type, value); | |||||
| } | } | ||||
| }; | |||||
| const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type( | static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) | ||||
| in_data_type); | { | ||||
| const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type( | using namespace blender; | ||||
| out_data_type); | using namespace blender::fn; | ||||
| BLI_assert(in_cpp_type != nullptr); | using namespace blender::bke; | ||||
| BLI_assert(out_cpp_type != nullptr); | |||||
| const blender::nodes::DataTypeConversions &conversions = | |||||
| blender::nodes::get_implicit_type_conversions(); | |||||
| BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type)); | |||||
| void *out_value = alloca(out_cpp_type->size()); | |||||
| conversions.convert(*in_cpp_type, *out_cpp_type, value, out_value); | |||||
| const int domain_size = this->attribute_domain_size(domain); | GVMutableAttribute_For_OutputAttribute &varray = | ||||
| blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>( | dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray()); | ||||
| domain, domain_size, *out_cpp_type, out_value); | |||||
| out_cpp_type->destruct(out_value); | GeometryComponent &component = *varray.component; | ||||
| return attribute; | const StringRefNull name = varray.final_name; | ||||
| const AttributeDomain domain = output_attribute.domain(); | |||||
| const CustomDataType data_type = output_attribute.custom_data_type(); | |||||
| const CPPType &cpp_type = output_attribute.cpp_type(); | |||||
| component.attribute_try_delete(name); | |||||
| if (!component.attribute_try_create( | |||||
| varray.final_name, domain, data_type, AttributeInitDefault())) { | |||||
| CLOG_WARN(&LOG, | |||||
| "Could not create the '%s' attribute with type '%s'.", | |||||
| name.c_str(), | |||||
| cpp_type.name().c_str()); | |||||
| return; | |||||
| } | |||||
| WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); | |||||
| BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); | |||||
| for (const int i : IndexRange(varray.size())) { | |||||
| varray.get(i, buffer); | |||||
| write_attribute.varray->set_by_relocate(i, buffer); | |||||
| } | |||||
| } | } | ||||
| OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name, | static blender::bke::OutputAttribute create_output_attribute( | ||||
| GeometryComponent &component, | |||||
| const blender::StringRef attribute_name, | |||||
| const AttributeDomain domain, | const AttributeDomain domain, | ||||
| const CustomDataType data_type, | const CustomDataType data_type, | ||||
| const bool ignore_old_values, | |||||
| const void *default_value) | const void *default_value) | ||||
| { | { | ||||
| const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); | using namespace blender; | ||||
| BLI_assert(cpp_type != nullptr); | using namespace blender::fn; | ||||
| using namespace blender::bke; | |||||
| WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name); | if (attribute_name.is_empty()) { | ||||
| return {}; | |||||
| } | |||||
| const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type); | |||||
| BLI_assert(cpp_type != nullptr); | |||||
| const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); | |||||
| /* If the attribute doesn't exist, make a new one with the correct type. */ | if (component.attribute_is_builtin(attribute_name)) { | ||||
| WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); | |||||
| if (!attribute) { | if (!attribute) { | ||||
| this->attribute_try_create(attribute_name, domain, data_type); | if (default_value) { | ||||
| attribute = this->attribute_try_get_for_write(attribute_name); | const int64_t domain_size = component.attribute_domain_size(domain); | ||||
| if (attribute && default_value != nullptr) { | const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; | ||||
| void *data = attribute->get_span_for_write_only().data(); | component.attribute_try_create_builtin(attribute_name, | ||||
| cpp_type->fill_initialized(default_value, data, attribute->size()); | AttributeInitVArray(&default_varray)); | ||||
| attribute->apply_span(); | |||||
| } | } | ||||
| return OutputAttributePtr(std::move(attribute)); | else { | ||||
| component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); | |||||
| } | } | ||||
| attribute = component.attribute_try_get_for_write(attribute_name); | |||||
| /* If an existing attribute has a matching domain and type, just use that. */ | if (!attribute) { | ||||
| if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) { | /* Builtin attribute does not exist and can't be created. */ | ||||
| return OutputAttributePtr(std::move(attribute)); | return {}; | ||||
| } | } | ||||
| /* Otherwise create a temporary buffer to use before saving the new attribute. */ | |||||
| return OutputAttributePtr(*this, domain, attribute_name, data_type); | |||||
| } | } | ||||
| if (attribute.domain != domain) { | |||||
| /* Construct from an attribute that already exists in the geometry component. */ | /* Builtin attribute is on different domain. */ | ||||
| OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute) | return {}; | ||||
| : attribute_(std::move(attribute)) | } | ||||
| { | GVMutableArrayPtr varray = std::move(attribute.varray); | ||||
| if (varray->type() == *cpp_type) { | |||||
| /* Builtin attribute matches exactly. */ | |||||
| return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); | |||||
| } | |||||
| /* Builtin attribute is on the same domain but has a different data type. */ | |||||
| varray = conversions.try_convert(std::move(varray), *cpp_type); | |||||
| return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); | |||||
| } | } | ||||
| /* Construct a temporary attribute that has to replace an existing one later on. */ | |||||
| OutputAttributePtr::OutputAttributePtr(GeometryComponent &component, | |||||
| AttributeDomain domain, | |||||
| std::string final_name, | |||||
| CustomDataType data_type) | |||||
| { | |||||
| const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); | |||||
| BLI_assert(cpp_type != nullptr); | |||||
| const int domain_size = component.attribute_domain_size(domain); | const int domain_size = component.attribute_domain_size(domain); | ||||
| void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__); | |||||
| GMutableSpan new_span{*cpp_type, buffer, domain_size}; | |||||
| /* Copy converted values from conflicting attribute, in case the value is read. | WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); | ||||
| * TODO: An optimization could be to not do this, when the caller says that the attribute will | if (!attribute) { | ||||
| * only be written. */ | if (default_value) { | ||||
| ReadAttributePtr src_attribute = component.attribute_get_for_read( | const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; | ||||
| final_name, domain, data_type, nullptr); | component.attribute_try_create( | ||||
| for (const int i : blender::IndexRange(domain_size)) { | attribute_name, domain, data_type, AttributeInitVArray(&default_varray)); | ||||
| src_attribute->get(i, new_span[i]); | |||||
| } | } | ||||
| else { | |||||
| attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>( | component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); | ||||
| domain, new_span, component, std::move(final_name)); | |||||
| } | } | ||||
| /* Store the computed attribute. If it was stored from the beginning already, nothing is done. This | attribute = component.attribute_try_get_for_write(attribute_name); | ||||
| * might delete another attribute with the same name. */ | if (!attribute) { | ||||
| void OutputAttributePtr::save() | /* Can't create the attribute. */ | ||||
| { | return {}; | ||||
| if (!attribute_) { | |||||
| CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore."); | |||||
| return; | |||||
| } | } | ||||
| blender::bke::TemporaryWriteAttribute *attribute = | |||||
| dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get()); | |||||
| if (attribute == nullptr) { | |||||
| /* The attribute is saved already. */ | |||||
| attribute_.reset(); | |||||
| return; | |||||
| } | } | ||||
| if (attribute.domain == domain && attribute.varray->type() == *cpp_type) { | |||||
| StringRefNull name = attribute->final_name; | /* Existing generic attribute matches exactly. */ | ||||
| const blender::fn::CPPType &cpp_type = attribute->cpp_type(); | return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values); | ||||
| /* Delete an existing attribute with the same name if necessary. */ | |||||
| attribute->component.attribute_try_delete(name); | |||||
| if (!attribute->component.attribute_try_create( | |||||
| name, attribute_->domain(), attribute_->custom_data_type())) { | |||||
| /* Cannot create the target attribute for some reason. */ | |||||
| CLOG_WARN(&LOG, | |||||
| "Creating the '%s' attribute with type '%s' failed.", | |||||
| name.c_str(), | |||||
| cpp_type.name().c_str()); | |||||
| attribute_.reset(); | |||||
| return; | |||||
| } | } | ||||
| WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name); | /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing | ||||
| * attribute after processing is done. */ | |||||
| GMutableSpan temp_span = attribute->data; | void *data = MEM_mallocN_aligned( | ||||
| GMutableSpan new_span = new_attribute->get_span_for_write_only(); | cpp_type->size() * domain_size, cpp_type->alignment(), __func__); | ||||
| BLI_assert(temp_span.size() == new_span.size()); | if (ignore_old_values) { | ||||
| /* This does nothing for trivially constructible types, but is necessary for correctness. */ | |||||
| /* Currently we copy over the attribute. In the future we want to reuse the buffer. */ | cpp_type->construct_default_n(data, domain); | ||||
| cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size()); | } | ||||
| new_attribute->apply_span(); | else { | ||||
| /* Fill the temporary array with values from the existing attribute. */ | |||||
| GVArrayPtr old_varray = component.attribute_get_for_read( | |||||
| attribute_name, domain, data_type, default_value); | |||||
| old_varray->materialize_to_uninitialized(IndexRange(domain_size), data); | |||||
| } | |||||
| GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>( | |||||
| GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name); | |||||
| attribute_.reset(); | return OutputAttribute(std::move(varray), domain, save_output_attribute, true); | ||||
| } | } | ||||
| OutputAttributePtr::~OutputAttributePtr() | blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output( | ||||
| const StringRef attribute_name, | |||||
| const AttributeDomain domain, | |||||
| const CustomDataType data_type, | |||||
| const void *default_value) | |||||
| { | { | ||||
| if (attribute_) { | return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); | ||||
| CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save."); | |||||
| } | |||||
| } | } | ||||
| /* Utility function to call #apply_span and #save in the right order. */ | blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( | ||||
| void OutputAttributePtr::apply_span_and_save() | const blender::StringRef attribute_name, | ||||
| const AttributeDomain domain, | |||||
| const CustomDataType data_type) | |||||
| { | { | ||||
| BLI_assert(attribute_); | return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); | ||||
| attribute_->apply_span(); | |||||
| this->save(); | |||||
| } | } | ||||
| /** \} */ | |||||