diff options
Diffstat (limited to 'source/blender/blenkernel/intern')
39 files changed, 3116 insertions, 1129 deletions
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index c8416ac897e..ca1d92c001b 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -1859,9 +1859,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, BKE_id_free(nullptr, mesh_orco); } - /* Ensure normals calculation below is correct. */ - BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH)); - BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh); + /* Ensure normals calculation below is correct (normal settings have transferred properly). + * However, nodes modifiers might create meshes from scratch or transfer meshes from other + * objects with different settings, and in general it doesn't make sense to guarantee that + * the settings are the same as the original mesh. If necessary, this could become a modifier + * type flag. */ BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh); /* Compute normals. */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 0b38e2d9f75..3f3aa0386e2 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name) * \note Use with care, not on Armature poses but for temporal ones. * \note (currently used for action constraints and in rebuild_pose). */ -bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name) +bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name) { bPoseChannel *chan; @@ -815,7 +815,7 @@ void BKE_pose_copy_data_ex(bPose **dst, */ if (outPose->chanbase.first != outPose->chanbase.last) { outPose->chanhash = NULL; - BKE_pose_channels_hash_make(outPose); + BKE_pose_channels_hash_ensure(outPose); } outPose->iksolver = src->iksolver; @@ -945,7 +945,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan) * Removes the hash for quick lookup of channels, must * be done when adding/removing channels. */ -void BKE_pose_channels_hash_make(bPose *pose) +void BKE_pose_channels_hash_ensure(bPose *pose) { if (!pose->chanhash) { bPoseChannel *pchan; @@ -1191,7 +1191,7 @@ void BKE_pose_free(bPose *pose) * and ID-Props, used when duplicating bones in editmode. * (unlike copy_pose_channel_data which only does posing-related stuff). * - * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify) + * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure) */ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from) { @@ -1774,7 +1774,7 @@ void what_does_obaction(Object *ob, * allocation and also will make lookup slower. */ if (pose->chanbase.first != pose->chanbase.last) { - BKE_pose_channels_hash_make(pose); + BKE_pose_channels_hash_ensure(pose); } if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(pose); diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 447ed8fbe14..68de3e93a8e 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -354,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag) } /* duplicate NLA data */ - BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag); + BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag); /* duplicate drivers (F-Curves) */ BKE_fcurves_copy(&dadt->drivers, &adt->drivers); diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 1075a46e72b..bcfd34ab42f 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version) { static char version_str[5]; BLI_assert(version < 1000); - BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100); + BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100); return version_str; } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index da8a3b49f3c..80992cff34d 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) static int rebuild_pose_bone( bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p) { - bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */ + bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */ pchan->bone = bone; pchan->parent = parchan; @@ -2562,7 +2562,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ /* and a check for garbage */ BKE_pose_channels_clear_with_null_bone(pose, do_id_user); - BKE_pose_channels_hash_make(pose); + BKE_pose_channels_hash_ensure(pose); for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { /* Find the custom B-Bone handles. */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index ac582fc30e7..c24630c5666 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -44,196 +44,10 @@ using blender::float3; using blender::Set; using blender::StringRef; using blender::StringRefNull; -using blender::bke::ReadAttributePtr; -using blender::bke::WriteAttributePtr; using blender::fn::GMutableSpan; 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_; - void (*convert_)(const void *src, void *dst); - - 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)) - { - const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - convert_ = conversions.get_conversion_functions(base_attribute_->cpp_type(), to_type) - ->convert_single_to_uninitialized; - } - - 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); - convert_(buffer, r_value); - } -}; - -/** \} */ - const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) { switch (type) { @@ -329,10 +143,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_ static int attribute_domain_priority(const AttributeDomain domain) { switch (domain) { -#if 0 case ATTR_DOMAIN_CURVE: return 0; -#endif case ATTR_DOMAIN_FACE: return 1; case ATTR_DOMAIN_EDGE: @@ -368,7 +180,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) return highest_priority_domain; } -ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( +void OutputAttribute::save() +{ + save_has_been_called_ = true; + if (optional_span_varray_.has_value()) { + optional_span_varray_->save(); + } + if (save_) { + save_(*this); + } +} + +OutputAttribute::~OutputAttribute() +{ + if (!save_has_been_called_) { + if (varray_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } + } +} + +GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( const GeometryComponent &component) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -384,7 +216,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( return as_read_attribute_(data, domain_size); } -WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write( +GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( GeometryComponent &component) const { if (writable_ != Writable) { @@ -430,7 +262,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co 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) { return false; @@ -443,10 +311,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co /* Exists already. */ return false; } + const int domain_size = component.attribute_domain_size(domain_); - const void *data = CustomData_add_layer( - custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size); - const bool success = data != nullptr; + const bool success = add_custom_data_layer_from_attribute_init( + *custom_data, stored_type_, domain_size, initializer); if (success) { custom_data_access_.update_custom_data_pointers(component); } @@ -463,7 +331,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } -ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( +ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( const GeometryComponent &component, const StringRef attribute_name) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -496,7 +364,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( return {}; } -WriteAttributePtr CustomDataAttributeProvider::try_get_for_write( +WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( GeometryComponent &component, const StringRef attribute_name) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); @@ -548,10 +416,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, 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, const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) const + const CustomDataType data_type, + const AttributeInit &initializer) const { if (domain_ != domain) { return false; @@ -569,10 +479,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, } } const int domain_size = component.attribute_domain_size(domain_); - char attribute_name_c[MAX_NAME]; - attribute_name.copy(attribute_name_c); - CustomData_add_layer_named( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + add_named_custom_data_layer_from_attribute_init( + attribute_name, *custom_data, data_type, domain_size, initializer); return true; } @@ -595,7 +503,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com return true; } -ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( +ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( const GeometryComponent &component, const StringRef attribute_name) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -606,14 +514,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( if (layer.type == stored_type_) { if (layer.name == attribute_name) { 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 {}; } -WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( +WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( GeometryComponent &component, const StringRef attribute_name) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); @@ -630,7 +538,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( if (data_old != data_new) { 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_}; } } } @@ -708,7 +616,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain 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 { using namespace blender::bke; @@ -719,11 +637,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_name, 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 : 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) { return attribute; } @@ -731,16 +649,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( return {}; } -ReadAttributePtr GeometryComponent::attribute_try_adapt_domain( - ReadAttributePtr attribute, const AttributeDomain new_domain) const +std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { - if (attribute && attribute->domain() == new_domain) { - return attribute; + if (from_domain == to_domain) { + return varray; } 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; const ComponentAttributeProviders *providers = this->get_attribute_providers(); @@ -750,11 +671,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_name, 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 : 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) { return attribute; } @@ -784,7 +705,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) bool GeometryComponent::attribute_try_create(const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) + const CustomDataType data_type, + const AttributeInit &initializer) { using namespace blender::bke; if (attribute_name.is_empty()) { @@ -803,17 +725,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name, if (builtin_provider->data_type() != data_type) { return false; } - return builtin_provider->try_create(*this); + return builtin_provider->try_create(*this, initializer); } for (const DynamicAttributesProvider *dynamic_provider : 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 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> attributes; @@ -867,264 +808,283 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac 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) { return true; } return false; } -static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, - const blender::fn::CPPType &to_type) +std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( + const StringRef attribute_name) const { - const blender::fn::CPPType &from_type = attribute->cpp_type(); - if (from_type == to_type) { - return attribute; - } + std::optional<AttributeMetaData> result{std::nullopt}; + this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { + 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 = blender::nodes::get_implicit_type_conversions(); - if (!conversions.is_convertible(from_type, to_type)) { - return {}; - } - - return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type); + return conversions.try_convert(std::move(varray), 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 AttributeDomain domain, 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) { return {}; } - if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) { - attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); - if (!attribute) { + std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray); + if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) { + varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); + if (!varray) { return {}; } } const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); BLI_assert(cpp_type != nullptr); - if (attribute->cpp_type() != *cpp_type) { - attribute = try_adapt_data_type(std::move(attribute), *cpp_type); - if (!attribute) { + if (varray->type() != *cpp_type) { + varray = try_adapt_data_type(std::move(varray), *cpp_type); + if (!varray) { return {}; } } - return attribute; + return varray; } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name, - const AttributeDomain domain) const +std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read( + const StringRef attribute_name, const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { 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) { return {}; } - if (attribute->domain() != domain) { - attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); - if (!attribute) { - return {}; - } + if (attribute.domain != domain) { + return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain); } - return attribute; + return std::move(attribute.varray); } -ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) const +blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( + const blender::StringRef attribute_name, const CustomDataType data_type) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type); - if (attribute) { - return attribute; + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + if (!attribute) { + return {}; } - return this->attribute_get_constant_for_read(domain, data_type, default_value); -} - -blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read( - const AttributeDomain domain, const CustomDataType data_type, const void *value) const -{ - 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 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; } - const int domain_size = this->attribute_domain_size(domain); - return std::make_unique<blender::bke::ConstantReadAttribute>( - domain, domain_size, *cpp_type, value); + const blender::nodes::DataTypeConversions &conversions = + blender::nodes::get_implicit_type_conversions(); + return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; } -blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted( +std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read( + const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType in_data_type, - const CustomDataType out_data_type, - const void *value) const + const CustomDataType data_type, + const void *default_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); + std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read( + attribute_name, domain, data_type); + 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(); } - - const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type( - in_data_type); - const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type( - out_data_type); - BLI_assert(in_cpp_type != nullptr); - 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_to_uninitialized(*in_cpp_type, *out_cpp_type, value, out_value); - const int domain_size = this->attribute_domain_size(domain); - blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>( - domain, domain_size, *out_cpp_type, out_value); - - out_cpp_type->destruct(out_value); - return attribute; + return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value); } -OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) -{ - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - - WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name); +class GVMutableAttribute_For_OutputAttribute + : public blender::fn::GVMutableArray_For_GMutableSpan { + public: + GeometryComponent *component; + std::string final_name; - /* If the attribute doesn't exist, make a new one with the correct type. */ - if (!attribute) { - this->attribute_try_create(attribute_name, domain, data_type); - attribute = this->attribute_try_get_for_write(attribute_name); - if (attribute && default_value != nullptr) { - void *data = attribute->get_span_for_write_only().data(); - cpp_type->fill_initialized(default_value, data, attribute->size()); - attribute->apply_span(); - } - return OutputAttributePtr(std::move(attribute)); + 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)) + { } - /* If an existing attribute has a matching domain and type, just use that. */ - if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) { - return OutputAttributePtr(std::move(attribute)); + ~GVMutableAttribute_For_OutputAttribute() override + { + type_->destruct_n(data_, size_); + MEM_freeN(data_); } +}; - /* Otherwise create a temporary buffer to use before saving the new attribute. */ - return OutputAttributePtr(*this, domain, attribute_name, data_type); -} - -/* Construct from an attribute that already exists in the geometry component. */ -OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute) - : attribute_(std::move(attribute)) +static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) { -} + using namespace blender; + using namespace blender::fn; + using namespace blender::bke; -/* 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); + GVMutableAttribute_For_OutputAttribute &varray = + dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray()); - 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}; + GeometryComponent &component = *varray.component; + 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(); - /* Copy converted values from conflicting attribute, in case the value is read. - * TODO: An optimization could be to not do this, when the caller says that the attribute will - * only be written. */ - ReadAttributePtr src_attribute = component.attribute_get_for_read( - final_name, domain, data_type, nullptr); - for (const int i : blender::IndexRange(domain_size)) { - src_attribute->get(i, new_span[i]); + 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); } - - attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>( - domain, new_span, component, std::move(final_name)); } -/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This - * might delete another attribute with the same name. */ -void OutputAttributePtr::save() +static blender::bke::OutputAttribute create_output_attribute( + GeometryComponent &component, + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const bool ignore_old_values, + const void *default_value) { - if (!attribute_) { - CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore."); - return; + using namespace blender; + using namespace blender::fn; + using namespace blender::bke; + + if (attribute_name.is_empty()) { + return {}; } - blender::bke::TemporaryWriteAttribute *attribute = - dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get()); + 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 (attribute == nullptr) { - /* The attribute is saved already. */ - attribute_.reset(); - return; + if (component.attribute_is_builtin(attribute_name)) { + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + if (default_value) { + const int64_t domain_size = component.attribute_domain_size(domain); + const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; + component.attribute_try_create_builtin(attribute_name, + AttributeInitVArray(&default_varray)); + } + else { + component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); + } + attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + /* Builtin attribute does not exist and can't be created. */ + return {}; + } + } + if (attribute.domain != domain) { + /* Builtin attribute is on different domain. */ + return {}; + } + 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); } - StringRefNull name = attribute->final_name; - const blender::fn::CPPType &cpp_type = attribute->cpp_type(); + const int domain_size = component.attribute_domain_size(domain); - /* Delete an existing attribute with the same name if necessary. */ - attribute->component.attribute_try_delete(name); + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + if (default_value) { + const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; + component.attribute_try_create( + attribute_name, domain, data_type, AttributeInitVArray(&default_varray)); + } + else { + component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); + } - 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; + attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + /* Can't create the attribute. */ + return {}; + } + } + if (attribute.domain == domain && attribute.varray->type() == *cpp_type) { + /* Existing generic attribute matches exactly. */ + return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values); } - WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name); - - GMutableSpan temp_span = attribute->data; - GMutableSpan new_span = new_attribute->get_span_for_write_only(); - BLI_assert(temp_span.size() == new_span.size()); - - /* Currently we copy over the attribute. In the future we want to reuse the buffer. */ - cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size()); - new_attribute->apply_span(); + /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing + * attribute after processing is done. */ + void *data = MEM_mallocN_aligned( + cpp_type->size() * domain_size, cpp_type->alignment(), __func__); + if (ignore_old_values) { + /* This does nothing for trivially constructible types, but is necessary for correctness. */ + cpp_type->construct_default_n(data, domain); + } + 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_) { - CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save."); - } + return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); } -/* Utility function to call #apply_span and #save in the right order. */ -void OutputAttributePtr::apply_span_and_save() +blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) { - BLI_assert(attribute_); - attribute_->apply_span(); - this->save(); + return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); } - -/** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 806d10e9e89..b3a795faa30 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -24,166 +24,8 @@ namespace blender::bke { -class ConstantReadAttribute final : public ReadAttribute { - private: - void *value_; - - public: - ConstantReadAttribute(AttributeDomain domain, - const int64_t size, - const CPPType &type, - const void *value) - : ReadAttribute(domain, type, size) - { - value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); - type.copy_to_uninitialized(value, value_); - } - - ~ConstantReadAttribute() override - { - this->cpp_type_.destruct(value_); - MEM_freeN(value_); - } - - void get_internal(const int64_t UNUSED(index), void *r_value) const override - { - this->cpp_type_.copy_to_uninitialized(value_, r_value); - } - - void initialize_span() const override - { - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - cpp_type_.fill_uninitialized(value_, array_buffer_, size_); - } -}; - -template<typename T> class ArrayReadAttribute final : public ReadAttribute { - private: - Span<T> data_; - - public: - ArrayReadAttribute(AttributeDomain domain, Span<T> data) - : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void initialize_span() const override - { - /* The data will not be modified, so this const_cast is fine. */ - array_buffer_ = const_cast<T *>(data_.data()); - array_is_temporary_ = false; - } -}; - -template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute { - private: - Array<T> data_; - - public: - OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data) - : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data)) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void initialize_span() const override - { - /* The data will not be modified, so this const_cast is fine. */ - array_buffer_ = const_cast<T *>(data_.data()); - array_is_temporary_ = false; - } -}; - -template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -class DerivedArrayReadAttribute final : public ReadAttribute { - private: - Span<StructT> data_; - - public: - DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data) - : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - const StructT &struct_value = data_[index]; - const ElemT value = GetFunc(struct_value); - new (r_value) ElemT(value); - } -}; - -template<typename T> class ArrayWriteAttribute final : public WriteAttribute { - private: - MutableSpan<T> data_; - - public: - ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data) - : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void set_internal(const int64_t index, const void *value) override - { - data_[index] = *reinterpret_cast<const T *>(value); - } - - 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. */ - } -}; - -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, const ElemT &)> -class DerivedArrayWriteAttribute final : public WriteAttribute { - private: - MutableSpan<StructT> data_; - - public: - DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data) - : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - const StructT &struct_value = data_[index]; - const ElemT value = GetFunc(struct_value); - new (r_value) ElemT(value); - } - - void set_internal(const int64_t index, const void *value) override - { - StructT &struct_value = data_[index]; - const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value); - SetFunc(struct_value, typed_value); - } -}; +using fn::GVArrayPtr; +using fn::GVMutableArrayPtr; /** * Utility to group together multiple functions that are used to access custom data on geometry @@ -244,10 +86,11 @@ class BuiltinAttributeProvider { { } - virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0; - virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0; + virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0; + virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0; virtual bool try_delete(GeometryComponent &component) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0; + virtual bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const = 0; virtual bool exists(const GeometryComponent &component) const = 0; StringRefNull name() const @@ -272,15 +115,16 @@ class BuiltinAttributeProvider { */ class DynamicAttributesProvider { public: - virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const = 0; - virtual WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const = 0; + virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const = 0; + virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const = 0; virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), const StringRef UNUSED(attribute_name), const AttributeDomain UNUSED(domain), - const CustomDataType UNUSED(data_type)) const + const CustomDataType UNUSED(data_type), + const AttributeInit &UNUSED(initializer)) const { /* Some providers should not create new attributes. */ return false; @@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final; bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; bool try_create(GeometryComponent &component, const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) const final; + const CustomDataType data_type, + const AttributeInit &initializer) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; @@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { private: template<typename T> - ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer, - const int domain_size) const + ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer, + const int domain_size) const { - return std::make_unique<ArrayReadAttribute<T>>( - domain_, Span(static_cast<const T *>(layer.data), domain_size)); + return {std::make_unique<fn::GVArray_For_Span<T>>( + Span(static_cast<const T *>(layer.data), domain_size)), + domain_}; } template<typename T> - WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const + WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer, + const int domain_size) const { - return std::make_unique<ArrayWriteAttribute<T>>( - domain_, MutableSpan(static_cast<T *>(layer.data), domain_size)); + return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>( + MutableSpan(static_cast<T *>(layer.data), domain_size)), + domain_}; } bool type_is_supported(CustomDataType data_type) const @@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); - using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); + using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final; bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; @@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { * the #MVert struct, but is exposed as float3 attribute. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); - using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); + using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; @@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component) const final; + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final; + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final; bool try_delete(GeometryComponent &component) const final; - bool try_create(GeometryComponent &component) const final; + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; bool exists(const GeometryComponent &component) const final; }; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index e8879cdda8f..e84b485c466 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -132,7 +132,7 @@ static void blender_version_init(void) BLI_snprintf(blender_version_string, ARRAY_SIZE(blender_version_string), - "%d.%02d.%d%s", + "%d.%01d.%d%s", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_VERSION_PATCH, diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index a61e7a2d1d8..54fd3f55c31 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -399,7 +399,8 @@ static void setup_app_data(bContext *C, BKE_lib_override_library_main_resync( bmain, curscene, - bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene)); + bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene), + reports); /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ BKE_lib_override_library_main_operations_create(bmain, true); } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index ef567044282..20c5af0efb6 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -989,6 +989,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_smoothfac = 0.1f; brush->gpencil_settings->draw_smoothlvl = 1; brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->dilate_pixels = 1; brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 2ee030ca83f..17f36bd0860 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2836,7 +2836,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph, * including rotation order, otherwise this fails. */ pchan = cob->pchan; - tchan = BKE_pose_channel_verify(&pose, pchan->name); + tchan = BKE_pose_channel_ensure(&pose, pchan->name); tchan->rotmode = pchan->rotmode; /* evaluate action using workob (it will only set the PoseChannel in question) */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index cbf7a4483c0..81830f5bb61 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -80,7 +80,17 @@ struct bContext { struct ARegion *menu; struct wmGizmoGroup *gizmo_group; struct bContextStore *store; - const char *operator_poll_msg; /* reason for poll failing */ + + /* Operator poll. */ + /** + * Store the reason the poll function fails (static string, not allocated). + * For more advanced formatting use `operator_poll_msg_dyn_params`. + */ + const char *operator_poll_msg; + /** + * Store values to dynamically to create the string (called when a tool-tip is shown). + */ + struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params; } wm; /* data context */ @@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C) { bContext *newC = MEM_dupallocN((void *)C); + memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params)); + return newC; } void CTX_free(bContext *C) { + /* This may contain a dynamically allocated message, free. */ + CTX_wm_operator_poll_msg_clear(C); + MEM_freeN(C); } @@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup) C->wm.gizmo_group = gzgroup; } +void CTX_wm_operator_poll_msg_clear(bContext *C) +{ + struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; + if (params->free_fn != NULL) { + params->free_fn(C, params->user_data); + } + params->get_fn = NULL; + params->free_fn = NULL; + params->user_data = NULL; + + C->wm.operator_poll_msg = NULL; +} void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg) { + CTX_wm_operator_poll_msg_clear(C); + C->wm.operator_poll_msg = msg; } -const char *CTX_wm_operator_poll_msg_get(bContext *C) +void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, + const struct bContextPollMsgDyn_Params *params) { + CTX_wm_operator_poll_msg_clear(C); + + C->wm.operator_poll_msg_dyn_params = *params; +} + +const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free) +{ + struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; + if (params->get_fn != NULL) { + char *msg = params->get_fn(C, params->user_data); + if (msg != NULL) { + *r_free = true; + } + return msg; + } + + *r_free = false; return IFACE_(C->wm.operator_poll_msg); } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc new file mode 100644 index 00000000000..4c63c7f05ee --- /dev/null +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -0,0 +1,184 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_listbase.h" +#include "BLI_span.hh" + +#include "DNA_curve_types.h" + +#include "BKE_curve.h" +#include "BKE_spline.hh" + +using blender::float3; +using blender::float4x4; +using blender::Span; + +CurveEval *CurveEval::copy() +{ + CurveEval *new_curve = new CurveEval(); + + for (SplinePtr &spline : this->splines) { + new_curve->splines.append(spline->copy()); + } + + return new_curve; +} + +void CurveEval::translate(const float3 &translation) +{ + for (SplinePtr &spline : this->splines) { + spline->translate(translation); + spline->mark_cache_invalid(); + } +} + +void CurveEval::transform(const float4x4 &matrix) +{ + for (SplinePtr &spline : this->splines) { + spline->transform(matrix); + } +} + +void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const +{ + for (const SplinePtr &spline : this->splines) { + spline->bounds_min_max(min, max, use_evaluated); + } +} + +static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) +{ + switch (dna_handle_type) { + case HD_FREE: + return BezierSpline::HandleType::Free; + case HD_AUTO: + return BezierSpline::HandleType::Auto; + case HD_VECT: + return BezierSpline::HandleType::Vector; + case HD_ALIGN: + return BezierSpline::HandleType::Align; + case HD_AUTO_ANIM: + return BezierSpline::HandleType::Auto; + case HD_ALIGN_DOUBLESIDE: + return BezierSpline::HandleType::Align; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode) +{ + switch (twist_mode) { + case CU_TWIST_Z_UP: + return Spline::NormalCalculationMode::ZUp; + case CU_TWIST_MINIMUM: + return Spline::NormalCalculationMode::Minimum; + case CU_TWIST_TANGENT: + return Spline::NormalCalculationMode::Tangent; + } + BLI_assert_unreachable(); + return Spline::NormalCalculationMode::Minimum; +} + +static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) +{ + switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) { + case CU_NURB_ENDPOINT: + return NURBSpline::KnotsMode::EndPoint; + case CU_NURB_BEZIER: + return NURBSpline::KnotsMode::Bezier; + default: + return NURBSpline::KnotsMode::Normal; + } + + BLI_assert_unreachable(); + return NURBSpline::KnotsMode::Normal; +} + +std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + + const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve)); + + curve->splines.reserve(BLI_listbase_count(nurbs)); + + /* TODO: Optimize by reserving the correct points size. */ + LISTBASE_FOREACH (const Nurb *, nurb, nurbs) { + switch (nurb->type) { + case CU_BEZIER: { + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->set_resolution(nurb->resolu); + spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); + + for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) { + spline->add_point(bezt.vec[1], + handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1), + bezt.vec[0], + handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2), + bezt.vec[2], + bezt.radius, + bezt.tilt); + } + + curve->splines.append(std::move(spline)); + break; + } + case CU_NURBS: { + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->set_resolution(nurb->resolu); + spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); + spline->set_order(nurb->orderu); + spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu); + + for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { + spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]); + } + + curve->splines.append(std::move(spline)); + break; + } + case CU_POLY: { + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC); + + for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) { + spline->add_point(bp.vec, bp.radius, bp.tilt); + } + + curve->splines.append(std::move(spline)); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + } + + /* Note: Normal mode is stored separately in each spline to facilitate combining splines + * from multiple curve objects, where the value may be different. */ + const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve( + dna_curve.twist_mode); + for (SplinePtr &spline : curve->splines) { + spline->normal_mode = normal_mode; + } + + return curve; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c index 752e0d4dfcf..00cdc7b3031 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.c @@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, { const float total_length = BKE_curveprofile_total_length(profile); const float segment_length = total_length / n_segments; - float length_travelled = 0.0f; float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); float distance_to_previous_table_point = 0.0f; int i_table = 0; @@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, for (int i = 1; i < n_segments; i++) { /* Travel over all of the points that fit inside this segment. */ while (distance_to_next_table_point < segment_left) { - length_travelled += distance_to_next_table_point; segment_left -= distance_to_next_table_point; i_table++; distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table); @@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, /* We sampled in between this table point and the next, so the next travel step is smaller. */ distance_to_next_table_point -= segment_left; distance_to_previous_table_point += segment_left; - length_travelled += segment_left; segment_left = segment_length; } } diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.cc index ad8939fa5d1..20534ef933b 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.cc @@ -21,9 +21,9 @@ * \ingroup bke */ -#include <math.h> -#include <stdio.h> -#include <string.h> +#include <cmath> +#include <cstdio> +#include <cstring> #include "MEM_guardedalloc.h" @@ -82,7 +82,7 @@ void BKE_displist_free(ListBase *lb) { DispList *dl; - while ((dl = BLI_pophead(lb))) { + while ((dl = (DispList *)BLI_pophead(lb))) { BKE_displist_elem_free(dl); } } @@ -95,7 +95,7 @@ DispList *BKE_displist_find_or_create(ListBase *lb, int type) } } - DispList *dl = MEM_callocN(sizeof(DispList), "find_disp"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "find_disp"); dl->type = type; BLI_addtail(lb, dl); @@ -110,7 +110,7 @@ DispList *BKE_displist_find(ListBase *lb, int type) } } - return NULL; + return nullptr; } bool BKE_displist_has_faces(const ListBase *lb) @@ -129,11 +129,11 @@ void BKE_displist_copy(ListBase *lbn, const ListBase *lb) BKE_displist_free(lbn); LISTBASE_FOREACH (const DispList *, dl, lb) { - DispList *dln = MEM_dupallocN(dl); + DispList *dln = (DispList *)MEM_dupallocN(dl); BLI_addtail(lbn, dln); - dln->verts = MEM_dupallocN(dl->verts); - dln->nors = MEM_dupallocN(dl->nors); - dln->index = MEM_dupallocN(dl->index); + dln->verts = (float *)MEM_dupallocN(dl->verts); + dln->nors = (float *)MEM_dupallocN(dl->nors); + dln->index = (int *)MEM_dupallocN(dl->index); } } @@ -146,8 +146,8 @@ void BKE_displist_normals_add(ListBase *lb) LISTBASE_FOREACH (DispList *, dl, lb) { if (dl->type == DL_INDEX3) { - if (dl->nors == NULL) { - dl->nors = MEM_callocN(sizeof(float[3]), "dlnors"); + if (dl->nors == nullptr) { + dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors"); if (dl->flag & DL_BACK_CURVE) { dl->nors[2] = -1.0f; @@ -158,8 +158,8 @@ void BKE_displist_normals_add(ListBase *lb) } } else if (dl->type == DL_SURF) { - if (dl->nors == NULL) { - dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors"); + if (dl->nors == nullptr) { + dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors"); vdata = dl->verts; ndata = dl->nors; @@ -338,9 +338,9 @@ static void curve_to_displist(const Curve *cu, * and resolution > 1. */ const bool use_cyclic_sample = is_cyclic && (samples_len != 2); - DispList *dl = MEM_callocN(sizeof(DispList), __func__); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__); /* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */ - dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts"); BLI_addtail(r_dispbase, dl); dl->parts = 1; dl->nr = samples_len; @@ -393,8 +393,8 @@ static void curve_to_displist(const Curve *cu, } else if (nu->type == CU_NURBS) { const int len = (resolution * SEGMENTSU(nu)); - DispList *dl = MEM_callocN(sizeof(DispList), __func__); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(r_dispbase, dl); dl->parts = 1; dl->nr = len; @@ -402,12 +402,12 @@ static void curve_to_displist(const Curve *cu, dl->charidx = nu->charidx; dl->type = is_cyclic ? DL_POLY : DL_SEGM; - BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3])); + BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3])); } else if (nu->type == CU_POLY) { const int len = nu->pntsu; - DispList *dl = MEM_callocN(sizeof(DispList), __func__); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(r_dispbase, dl); dl->parts = 1; dl->nr = len; @@ -435,7 +435,7 @@ void BKE_displist_fill(const ListBase *dispbase, const float normal_proj[3], const bool flip_normal) { - if (dispbase == NULL) { + if (dispbase == nullptr) { return; } if (BLI_listbase_is_empty(dispbase)) { @@ -471,14 +471,14 @@ void BKE_displist_fill(const ListBase *dispbase, sf_ctx.poly_nr++; /* Make verts and edges. */ - ScanFillVert *sf_vert = NULL; - ScanFillVert *sf_vert_last = NULL; - ScanFillVert *sf_vert_new = NULL; + ScanFillVert *sf_vert = nullptr; + ScanFillVert *sf_vert_last = nullptr; + ScanFillVert *sf_vert_new = nullptr; for (int i = 0; i < dl->nr; i++) { sf_vert_last = sf_vert; sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]); totvert++; - if (sf_vert_last == NULL) { + if (sf_vert_last == nullptr) { sf_vert_new = sf_vert; } else { @@ -486,7 +486,7 @@ void BKE_displist_fill(const ListBase *dispbase, } } - if (sf_vert != NULL && sf_vert_new != NULL) { + if (sf_vert != nullptr && sf_vert_new != nullptr) { BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new); } } @@ -503,7 +503,7 @@ void BKE_displist_fill(const ListBase *dispbase, const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj); if (totvert != 0 && triangles_len != 0) { - DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist"); + DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist"); dlnew->type = DL_INDEX3; dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE)); dlnew->rt = (dl_rt_accum & CU_SMOOTH); @@ -511,8 +511,8 @@ void BKE_displist_fill(const ListBase *dispbase, dlnew->nr = totvert; dlnew->parts = triangles_len; - dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex"); - dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts"); + dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex"); + dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts"); /* vert data */ int i; @@ -551,16 +551,16 @@ void BKE_displist_fill(const ListBase *dispbase, static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase) { - ListBase front = {NULL, NULL}; - ListBase back = {NULL, NULL}; + ListBase front = {nullptr, nullptr}; + ListBase back = {nullptr, nullptr}; LISTBASE_FOREACH (const DispList *, dl, dispbase) { if (dl->type == DL_SURF) { if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) { if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) { - DispList *dlnew = MEM_callocN(sizeof(DispList), __func__); + DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__); BLI_addtail(&front, dlnew); - dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); + dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); dlnew->nr = dl->parts; dlnew->parts = 1; dlnew->type = DL_POLY; @@ -577,9 +577,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase) } } if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) { - DispList *dlnew = MEM_callocN(sizeof(DispList), __func__); + DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__); BLI_addtail(&back, dlnew); - dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); + dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__); dlnew->nr = dl->parts; dlnew->parts = 1; dlnew->type = DL_POLY; @@ -634,16 +634,16 @@ static float displist_calc_taper(Depsgraph *depsgraph, Object *taperobj, float fac) { - DispList *dl; - - if (taperobj == NULL || taperobj->type != OB_CURVE) { + if (taperobj == nullptr || taperobj->type != OB_CURVE) { return 1.0; } - dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL; - if (dl == NULL) { + DispList *dl = taperobj->runtime.curve_cache ? + (DispList *)taperobj->runtime.curve_cache->disp.first : + nullptr; + if (dl == nullptr) { BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false); - dl = taperobj->runtime.curve_cache->disp.first; + dl = (DispList *)taperobj->runtime.curve_cache->disp.first; } if (dl) { float minx, dx, *fp; @@ -693,7 +693,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_displist_free(&(ob->runtime.curve_cache->disp)); } else { - ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall"); + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for MBall"); } BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp); @@ -738,9 +739,9 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene, required_mode |= eModifierMode_Editmode; } - pretessellatePoint = NULL; + pretessellatePoint = nullptr; for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; @@ -777,22 +778,22 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, VirtualModifierData virtualModifierData; ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); ModifierData *pretessellatePoint; - Curve *cu = ob->data; + Curve *cu = (Curve *)ob->data; int numElems = 0, numVerts = 0; const bool editmode = (!for_render && (cu->editnurb || cu->editfont)); - ModifierApplyFlag apply_flag = 0; - float(*deformedVerts)[3] = NULL; - float *keyVerts = NULL; + ModifierApplyFlag apply_flag = (ModifierApplyFlag)0; + float(*deformedVerts)[3] = nullptr; + float *keyVerts = nullptr; int required_mode; bool modified = false; BKE_modifiers_clear_errors(ob); if (editmode) { - apply_flag |= MOD_APPLY_USECACHE; + apply_flag = MOD_APPLY_USECACHE; } if (for_render) { - apply_flag |= MOD_APPLY_RENDER; + apply_flag = MOD_APPLY_RENDER; required_mode = eModifierMode_Render; } else { @@ -823,7 +824,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, if (pretessellatePoint) { for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; @@ -836,7 +837,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts); } - mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts); + mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts); modified = true; if (md == pretessellatePoint) { @@ -869,7 +870,7 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[ *r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr; } - allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts"); + allverts = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__); fp = (float *)allverts; LISTBASE_FOREACH (DispList *, dl, dispbase) { int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr); @@ -903,16 +904,16 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, VirtualModifierData virtualModifierData; ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); ModifierData *pretessellatePoint; - const Curve *cu = ob->data; + const Curve *cu = (const Curve *)ob->data; int required_mode = 0, totvert = 0; const bool editmode = (!for_render && (cu->editnurb || cu->editfont)); - Mesh *modified = NULL, *mesh_applied; - float(*vertCos)[3] = NULL; + Mesh *modified = nullptr, *mesh_applied; + float(*vertCos)[3] = nullptr; int useCache = !for_render; - ModifierApplyFlag apply_flag = 0; + ModifierApplyFlag apply_flag = (ModifierApplyFlag)0; if (for_render) { - apply_flag |= MOD_APPLY_RENDER; + apply_flag = MOD_APPLY_RENDER; required_mode = eModifierMode_Render; } else { @@ -920,9 +921,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, } const ModifierEvalContext mectx_deform = { - depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag}; + depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag}; const ModifierEvalContext mectx_apply = { - depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag}; + depsgraph, ob, useCache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag}; pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode); @@ -935,22 +936,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, } if (r_final && *r_final) { - BKE_id_free(NULL, *r_final); + BKE_id_free(nullptr, *r_final); } for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; } /* If we need normals, no choice, have to convert to mesh now. */ - bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md); + bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md); /* XXX 2.8 : now that batch cache is stored inside the ob->data * we need to create a Mesh for each curve that uses modifiers. */ - if (modified == NULL /* && need_normal */) { - if (vertCos != NULL) { + if (modified == nullptr /* && need_normal */) { + if (vertCos != nullptr) { displist_vert_coords_apply(dispbase, vertCos); } @@ -976,7 +977,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (!vertCos) { vertCos = displist_vert_coords_alloc(dispbase, &totvert); } - mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert); + mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert); } } else { @@ -991,8 +992,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (modified) { if (vertCos) { Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex( - NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE); - BKE_id_free(NULL, modified); + nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE); + BKE_id_free(nullptr, modified); modified = temp_mesh; BKE_mesh_vert_coords_apply(modified, vertCos); @@ -1013,7 +1014,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (vertCos) { /* Vertex coordinates were applied to necessary data, could free it */ MEM_freeN(vertCos); - vertCos = NULL; + vertCos = nullptr; } if (need_normal) { @@ -1025,7 +1026,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, /* Modifier returned a new derived mesh */ if (modified && modified != mesh_applied) { /* Modifier */ - BKE_id_free(NULL, modified); + BKE_id_free(nullptr, modified); } modified = mesh_applied; } @@ -1034,8 +1035,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (vertCos) { if (modified) { - Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE); - BKE_id_free(NULL, modified); + Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex( + nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE); + BKE_id_free(nullptr, modified); modified = temp_mesh; BKE_mesh_vert_coords_apply(modified, vertCos); @@ -1046,7 +1048,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, else { displist_vert_coords_apply(dispbase, vertCos); MEM_freeN(vertCos); - vertCos = NULL; + vertCos = nullptr; } } @@ -1081,18 +1083,18 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, MEM_SAFE_FREE(modified->mat); /* Set flag which makes it easier to see what's going on in a debugger. */ modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT; - modified->mat = MEM_dupallocN(cu->mat); + modified->mat = (Material **)MEM_dupallocN(cu->mat); modified->totcol = cu->totcol; (*r_final) = modified; } else { - (*r_final) = NULL; + (*r_final) = nullptr; } } - else if (modified != NULL) { + else if (modified != nullptr) { /* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */ - BKE_id_free(NULL, modified); + BKE_id_free(nullptr, modified); } } @@ -1103,8 +1105,8 @@ static void displist_surf_indices(DispList *dl) dl->totindex = 0; - index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1), - "index array nurbs"); + index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1), + "index array nurbs"); for (a = 0; a < dl->parts; a++) { @@ -1136,8 +1138,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, const bool for_render, const bool for_orco) { - ListBase nubase = {NULL, NULL}; - Curve *cu = ob->data; + ListBase nubase = {nullptr, nullptr}; + Curve *cu = (Curve *)ob->data; DispList *dl; float *data; int len; @@ -1174,8 +1176,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, if (nu->pntsv == 1) { len = SEGMENTSU(nu) * resolu; - dl = MEM_callocN(sizeof(DispList), "makeDispListsurf"); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf"); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(dispbase, dl); dl->parts = 1; @@ -1195,13 +1197,13 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, dl->type = DL_SEGM; } - BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3])); + BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3])); } else { len = (nu->pntsu * resolu) * (nu->pntsv * resolv); - dl = MEM_callocN(sizeof(DispList), "makeDispListsurf"); - dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts"); + dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf"); + dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts"); BLI_addtail(dispbase, dl); dl->col = nu->mat_nr; @@ -1258,7 +1260,7 @@ static void rotateBevelPiece(const Curve *cu, vec[1] = fp[2]; vec[2] = 0.0; - if (nbevp == NULL) { + if (nbevp == nullptr) { copy_v3_v3(data, bevp->vec); copy_qt_qt(quat, bevp->quat); } @@ -1276,7 +1278,7 @@ static void rotateBevelPiece(const Curve *cu, else { float sina, cosa; - if (nbevp == NULL) { + if (nbevp == nullptr) { copy_v3_v3(data, bevp->vec); sina = bevp->sina; cosa = bevp->cosa; @@ -1307,8 +1309,8 @@ static void fillBevelCap(const Nurb *nu, { DispList *dl; - dl = MEM_callocN(sizeof(DispList), "makeDispListbev2"); - dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts"); + dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts"); memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr); dl->type = DL_POLY; @@ -1469,7 +1471,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, const bool for_orco, Mesh **r_final) { - Curve *cu = ob->data; + Curve *cu = (Curve *)ob->data; /* we do allow duplis... this is only displist on curve level */ if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { @@ -1481,7 +1483,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } else if (ELEM(ob->type, OB_CURVE, OB_FONT)) { ListBase dlbev; - ListBase nubase = {NULL, NULL}; + ListBase nubase = {nullptr, nullptr}; bool force_mesh_conversion = false; BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); @@ -1494,7 +1496,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, if (ob->runtime.curve_cache->anim_path_accum_length) { MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length); } - ob->runtime.curve_cache->anim_path_accum_length = NULL; + ob->runtime.curve_cache->anim_path_accum_length = nullptr; } if (ob->type == OB_FONT) { @@ -1520,8 +1522,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } else { const float widfac = cu->width - 1.0f; - BevList *bl = ob->runtime.curve_cache->bev.first; - Nurb *nu = nubase.first; + BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first; + Nurb *nu = (Nurb *)nubase.first; for (; bl && nu; bl = bl->next, nu = nu->next) { float *data; @@ -1532,8 +1534,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, /* exception handling; curve without bevel or extrude, with width correction */ if (BLI_listbase_is_empty(&dlbev)) { - DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev"); - dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev"); + dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts"); BLI_addtail(dispbase, dl); if (bl->poly != -1) { @@ -1565,8 +1567,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } } else { - ListBase bottom_capbase = {NULL, NULL}; - ListBase top_capbase = {NULL, NULL}; + ListBase bottom_capbase = {nullptr, nullptr}; + ListBase top_capbase = {nullptr, nullptr}; float bottom_no[3] = {0.0f}; float top_no[3] = {0.0f}; float first_blend = 0.0f, last_blend = 0.0f; @@ -1585,8 +1587,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, LISTBASE_FOREACH (DispList *, dlb, &dlbev) { /* for each part of the bevel use a separate displblock */ - DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1"); - dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts"); + DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1"); + dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts"); BLI_addtail(dispbase, dl); dl->type = DL_SURF; @@ -1616,7 +1618,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, float radius_factor = 1.0; float *cur_data = data; - if (cu->taperobj == NULL) { + if (cu->taperobj == nullptr) { radius_factor = bevp->radius; } else { @@ -1666,7 +1668,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data); } else { - rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data); + rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data); } if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) { @@ -1737,15 +1739,16 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, BKE_object_free_derived_caches(ob); if (!ob->runtime.curve_cache) { - ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for curve types"); } dispbase = &(ob->runtime.curve_cache->disp); - Mesh *mesh_eval = NULL; + Mesh *mesh_eval = nullptr; do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval); - if (mesh_eval != NULL) { + if (mesh_eval != nullptr) { BKE_object_eval_assign_data(ob, &mesh_eval->id, true); } @@ -1759,8 +1762,9 @@ void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph, Mesh **r_final, const bool for_orco) { - if (ob->runtime.curve_cache == NULL) { - ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + if (ob->runtime.curve_cache == nullptr) { + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), + "CurveCache for Curve"); } do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final); @@ -1797,8 +1801,8 @@ static void boundbox_displist_object(Object *ob) */ /* object's BB is calculated from final displist */ - if (ob->runtime.bb == NULL) { - ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox"); + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox"); } Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc new file mode 100644 index 00000000000..0490d577b88 --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" + +#include "attribute_access_intern.hh" + +/* -------------------------------------------------------------------- */ +/** \name Geometry Component Implementation + * \{ */ + +CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) +{ +} + +CurveComponent::~CurveComponent() +{ + this->clear(); +} + +GeometryComponent *CurveComponent::copy() const +{ + CurveComponent *new_component = new CurveComponent(); + if (curve_ != nullptr) { + new_component->curve_ = curve_->copy(); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void CurveComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (curve_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + delete curve_; + } + curve_ = nullptr; + } +} + +bool CurveComponent::has_curve() const +{ + return curve_ != nullptr; +} + +/* Clear the component and replace it with the new curve. */ +void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + curve_ = curve; + ownership_ = ownership; +} + +CurveEval *CurveComponent::release() +{ + BLI_assert(this->is_mutable()); + CurveEval *curve = curve_; + curve_ = nullptr; + return curve; +} + +const CurveEval *CurveComponent::get_for_read() const +{ + return curve_; +} + +CurveEval *CurveComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + curve_ = curve_->copy(); + ownership_ = GeometryOwnershipType::Owned; + } + return curve_; +} + +bool CurveComponent::is_empty() const +{ + return curve_ == nullptr; +} + +bool CurveComponent::owns_direct_data() const +{ + return ownership_ == GeometryOwnershipType::Owned; +} + +void CurveComponent::ensure_owns_direct_data() +{ + BLI_assert(this->is_mutable()); + if (ownership_ != GeometryOwnershipType::Owned) { + curve_ = curve_->copy(); + ownership_ = GeometryOwnershipType::Owned; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attribute Access + * \{ */ + +int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +{ + if (curve_ == nullptr) { + return 0; + } + if (domain == ATTR_DOMAIN_POINT) { + int total = 0; + for (const SplinePtr &spline : curve_->splines) { + total += spline->size(); + } + return total; + } + if (domain == ATTR_DOMAIN_CURVE) { + return curve_->splines.size(); + } + return 0; +} + +namespace blender::bke { + +class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { + using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data); + using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data); + using UpdateOnWrite = void (*)(Spline &spline); + const AsReadAttribute as_read_attribute_; + const AsWriteAttribute as_write_attribute_; + + public: + BuiltinSplineAttributeProvider(std::string attribute_name, + const CustomDataType attribute_type, + const WritableEnum writable, + const AsReadAttribute as_read_attribute, + const AsWriteAttribute as_write_attribute) + : BuiltinAttributeProvider(std::move(attribute_name), + ATTR_DOMAIN_CURVE, + attribute_type, + BuiltinAttributeProvider::NonCreatable, + writable, + BuiltinAttributeProvider::NonDeletable), + as_read_attribute_(as_read_attribute), + as_write_attribute_(as_write_attribute) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve == nullptr) { + return {}; + } + + return as_read_attribute_(*curve); + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + { + if (writable_ != Writable) { + return {}; + } + CurveComponent &curve_component = static_cast<CurveComponent &>(component); + CurveEval *curve = curve_component.get_for_write(); + if (curve == nullptr) { + return {}; + } + + return as_write_attribute_(*curve); + } + + bool try_delete(GeometryComponent &UNUSED(component)) const final + { + return false; + } + + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final + { + return false; + } + + bool exists(const GeometryComponent &component) const final + { + return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0; + } +}; + +static int get_spline_resolution(const SplinePtr &spline) +{ + if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) { + return bezier_spline->resolution(); + } + if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) { + return nurb_spline->resolution(); + } + return 1; +} + +static void set_spline_resolution(SplinePtr &spline, const int resolution) +{ + if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) { + bezier_spline->set_resolution(std::max(resolution, 1)); + } + if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) { + nurb_spline->set_resolution(std::max(resolution, 1)); + } +} + +static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve) +{ + return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>( + curve.splines.as_span()); +} + +static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve) +{ + return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr, + int, + get_spline_resolution, + set_spline_resolution>>( + curve.splines.as_mutable_span()); +} + +static bool get_cyclic_value(const SplinePtr &spline) +{ + return spline->is_cyclic(); +} + +static void set_cyclic_value(SplinePtr &spline, const bool value) +{ + if (spline->is_cyclic() != value) { + spline->set_cyclic(value); + spline->mark_cache_invalid(); + } +} + +static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve) +{ + return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>( + curve.splines.as_span()); +} + +static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) +{ + return std::make_unique< + fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>( + curve.splines.as_mutable_span()); +} + +/** + * In this function all the attribute providers for a curve component are created. Most data + * in this function is statically allocated, because it does not change over time. + */ +static ComponentAttributeProviders create_attribute_providers_for_curve() +{ + static BuiltinSplineAttributeProvider resolution("resolution", + CD_PROP_INT32, + BuiltinAttributeProvider::Writable, + make_resolution_read_attribute, + make_resolution_write_attribute); + + static BuiltinSplineAttributeProvider cyclic("cyclic", + CD_PROP_BOOL, + BuiltinAttributeProvider::Writable, + make_cyclic_read_attribute, + make_cyclic_write_attribute); + + return ComponentAttributeProviders({&resolution, &cyclic}, {}); +} + +} // namespace blender::bke + +const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +{ + static blender::bke::ComponentAttributeProviders providers = + blender::bke::create_attribute_providers_for_curve(); + return &providers; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index feb30e8575a..55025ed5ac9 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -42,72 +42,86 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_ GeometryComponent *InstancesComponent::copy() const { InstancesComponent *new_component = new InstancesComponent(); - new_component->transforms_ = transforms_; - new_component->instanced_data_ = instanced_data_; - new_component->ids_ = ids_; + new_component->instance_reference_handles_ = instance_reference_handles_; + new_component->instance_transforms_ = instance_transforms_; + new_component->instance_ids_ = instance_ids_; + new_component->references_ = references_; return new_component; } -void InstancesComponent::clear() +void InstancesComponent::reserve(int min_capacity) { - instanced_data_.clear(); - transforms_.clear(); - ids_.clear(); + instance_reference_handles_.reserve(min_capacity); + instance_transforms_.reserve(min_capacity); + instance_ids_.reserve(min_capacity); } -void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id) +void InstancesComponent::clear() { - InstancedData data; - data.type = INSTANCE_DATA_TYPE_OBJECT; - data.data.object = object; - this->add_instance(data, transform, id); + instance_reference_handles_.clear(); + instance_transforms_.clear(); + instance_ids_.clear(); + + references_.clear(); } -void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id) +void InstancesComponent::add_instance(const int instance_handle, + const float4x4 &transform, + const int id) { - InstancedData data; - data.type = INSTANCE_DATA_TYPE_COLLECTION; - data.data.collection = collection; - this->add_instance(data, transform, id); + BLI_assert(instance_handle >= 0); + BLI_assert(instance_handle < references_.size()); + instance_reference_handles_.append(instance_handle); + instance_transforms_.append(transform); + instance_ids_.append(id); } -void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id) +blender::Span<int> InstancesComponent::instance_reference_handles() const { - instanced_data_.append(data); - transforms_.append(transform); - ids_.append(id); + return instance_reference_handles_; } -Span<InstancedData> InstancesComponent::instanced_data() const +blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms() +{ + return instance_transforms_; +} +blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const { - return instanced_data_; + return instance_transforms_; } -Span<float4x4> InstancesComponent::transforms() const +blender::MutableSpan<int> InstancesComponent::instance_ids() +{ + return instance_ids_; +} +blender::Span<int> InstancesComponent::instance_ids() const { - return transforms_; + return instance_ids_; } -Span<int> InstancesComponent::ids() const +/** + * Returns a handle for the given reference. + * If the reference exists already, the handle of the existing reference is returned. + * Otherwise a new handle is added. + */ +int InstancesComponent::add_reference(InstanceReference reference) { - return ids_; + return references_.index_of_or_add_as(reference); } -MutableSpan<float4x4> InstancesComponent::transforms() +blender::Span<InstanceReference> InstancesComponent::references() const { - return transforms_; + return references_; } int InstancesComponent::instances_amount() const { - const int size = instanced_data_.size(); - BLI_assert(transforms_.size() == size); - return size; + return instance_transforms_.size(); } bool InstancesComponent::is_empty() const { - return transforms_.size() == 0; + return this->instance_reference_handles_.size() == 0; } bool InstancesComponent::owns_direct_data() const @@ -178,8 +192,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) blender::Span<int> InstancesComponent::almost_unique_ids() const { std::lock_guard lock(almost_unique_ids_mutex_); - if (almost_unique_ids_.size() != ids_.size()) { - almost_unique_ids_ = generate_unique_instance_ids(ids_); + if (almost_unique_ids_.size() != instance_ids_.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); } return almost_unique_ids_; } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 5697fb2ccde..2ecd0e6bd85 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -32,7 +32,7 @@ /* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */ extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id); -using blender::bke::ReadAttributePtr; +using blender::fn::GVArray; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -201,14 +201,14 @@ namespace blender::bke { template<typename T> static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, - const TypedReadAttribute<T> &attribute, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); attribute_math::DefaultMixer<T> mixer(r_values); for (const int loop_index : IndexRange(mesh.totloop)) { - const T value = attribute[loop_index]; + const T value = old_values[loop_index]; const MLoop &loop = mesh.mloop[loop_index]; const int point_index = loop.v; mixer.mix_in(point_index, value); @@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { /* We compute all interpolated values at once, because for this interpolation, one has to * iterate over all loops anyway. */ Array<T> values(mesh.totvert); - adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, - const TypedReadAttribute<T> &attribute, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); for (const int loop_index : IndexRange(mesh.totloop)) { const int vertex_index = mesh.mloop[loop_index].v; - r_values[loop_index] = attribute[vertex_index]; + r_values[loop_index] = old_values[vertex_index]; } } -static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); /* It is not strictly necessary to compute the value for all corners here. Instead one could @@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, * when an algorithm only accesses very few of the corner values. However, for the algorithms * we currently have, precomputing the array is fine. Also, it is easier to implement. */ Array<T> values(mesh.totloop); - adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER, - std::move(values)); + adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); }); - return new_attribute; + return new_varray; } /** @@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); @@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totvert); - adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); @@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, } } -static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totloop); - adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); @@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totloop); - adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); @@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totvert); - adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } } // namespace blender::bke -ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute, - const AttributeDomain new_domain) const +blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain( + blender::fn::GVArrayPtr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { - if (!attribute) { + if (!varray) { return {}; } - if (attribute->size() == 0) { + if (varray->size() == 0) { return {}; } - const AttributeDomain old_domain = attribute->domain(); - if (old_domain == new_domain) { - return attribute; + if (from_domain == to_domain) { + return varray; } - switch (old_domain) { + switch (from_domain) { case ATTR_DOMAIN_CORNER: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_POINT: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_FACE: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_EDGE: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray)); default: break; } @@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - AttributeDomain Domain> -static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size) +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size) { - return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>( - Domain, Span<StructT>((const StructT *)data, domain_size)); + return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( + Span<StructT>((const StructT *)data, domain_size)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, const ElemT &), - AttributeDomain Domain> -static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size) + void (*SetFunc)(StructT &, ElemT)> +static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size) { - return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>( - Domain, MutableSpan<StructT>((StructT *)data, domain_size)); + return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>( + MutableSpan<StructT>((StructT *)data, domain_size)); } static float3 get_vertex_position(const MVert &vert) @@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert) return float3(vert.co); } -static void set_vertex_position(MVert &vert, const float3 &position) +static void set_vertex_position(MVert &vert, float3 position) { copy_v3_v3(vert.co, position); } @@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly) return static_cast<int>(mpoly.mat_nr); } -static void set_material_index(MPoly &mpoly, const int &index) +static void set_material_index(MPoly &mpoly, int index) { mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX)); } @@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly) return mpoly.flag & ME_SMOOTH; } -static void set_shade_smooth(MPoly &mpoly, const bool &value) +static void set_shade_smooth(MPoly &mpoly, bool value) { SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH); } @@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv) return float2(uv.uv); } -static void set_loop_uv(MLoopUV &uv, const float2 &co) +static void set_loop_uv(MLoopUV &uv, float2 co) { copy_v2_v2(uv.uv, co); } @@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col) return linear_color; } -static void set_loop_color(MLoopCol &col, const Color4f &linear_color) +static void set_loop_color(MLoopCol &col, Color4f linear_color) { linearrgb_to_srgb_uchar4(&col.r, linear_color); } @@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge) return edge.crease / 255.0f; } -static void set_crease(MEdge &edge, const float &value) +static void set_crease(MEdge &edge, float value) { edge.crease = round_fl_to_uchar_clamp(value * 255.0f); } -class VertexWeightWriteAttribute final : public WriteAttribute { +class VMutableArray_For_VertexWeights final : public VMutableArray<float> { private: MDeformVert *dverts_; const int dvert_index_; public: - VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index) - : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), - dverts_(dverts), - dvert_index_(dvert_index) + VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index) + : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index) { } - void get_internal(const int64_t index, void *r_value) const override + float get_impl(const int64_t index) const override { - get_internal(dverts_, dvert_index_, index, r_value); + return get_internal(dverts_, dvert_index_, index); } - void set_internal(const int64_t index, const void *value) override + void set_impl(const int64_t index, const float value) override { MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_); - weight->weight = *reinterpret_cast<const float *>(value); + weight->weight = value; } - static void get_internal(const MDeformVert *dverts, - const int dvert_index, - const int64_t index, - void *r_value) + static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index) { if (dverts == nullptr) { - *(float *)r_value = 0.0f; - return; + return 0.0f; } const MDeformVert &dvert = dverts[index]; for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) { if (weight.def_nr == dvert_index) { - *(float *)r_value = weight.weight; - return; + return weight.weight; } } - *(float *)r_value = 0.0f; + return 0.0f; } }; -class VertexWeightReadAttribute final : public ReadAttribute { +class VArray_For_VertexWeights final : public VArray<float> { private: const MDeformVert *dverts_; const int dvert_index_; public: - VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index) - : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), - dverts_(dverts), - dvert_index_(dvert_index) + VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index) + : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index) { } - void get_internal(const int64_t index, void *r_value) const override + float get_impl(const int64_t index) const override { - VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value); + return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index); } }; @@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute { */ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); @@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } if (mesh == nullptr || mesh->dvert == nullptr) { static const float default_value = 0.0f; - return std::make_unique<ConstantReadAttribute>( - ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value); + return {std::make_unique<fn::GVArray_For_SingleValueRef>( + CPPType::get<float>(), mesh->totvert, &default_value), + ATTR_DOMAIN_POINT}; } - return std::make_unique<VertexWeightReadAttribute>( - mesh->dvert, mesh->totvert, vertex_group_index); + return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>( + mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index), + ATTR_DOMAIN_POINT}; } - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); MeshComponent &mesh_component = static_cast<MeshComponent &>(component); @@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer( &mesh->vdata, CD_MDEFORMVERT, mesh->totvert); } - return std::make_unique<blender::bke::VertexWeightWriteAttribute>( - mesh->dvert, mesh->totvert, vertex_group_index); + return { + std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>( + mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index), + ATTR_DOMAIN_POINT}; } bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final @@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final { const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); const Mesh *mesh = mesh_component.get_for_read(); @@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL); - return std::make_unique<ArrayReadAttribute<float3>>( - ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly)); + return std::make_unique<fn::GVArray_For_Span<float3>>( + Span<float3>((const float3 *)data, mesh->totpoly)); } Array<float3> normals(mesh->totpoly); @@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]); } - return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals)); + return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); } - WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final + GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final { return {}; } @@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { return false; } - bool try_create(GeometryComponent &UNUSED(component)) const final + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final { return false; } @@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, point_access, - make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>, - make_derived_write_attribute<MVert, - float3, - get_vertex_position, - set_vertex_position, - ATTR_DOMAIN_POINT>, + make_derived_read_attribute<MVert, float3, get_vertex_position>, + make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>, tag_normals_dirty_when_writing_position); static NormalAttributeProvider normal; @@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, - make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>, - make_derived_write_attribute<MPoly, - int, - get_material_index, - set_material_index, - ATTR_DOMAIN_FACE>, + make_derived_read_attribute<MPoly, int, get_material_index>, + make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>, nullptr); static BuiltinCustomDataLayerProvider shade_smooth( @@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, - make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>, - make_derived_write_attribute<MPoly, - bool, - get_shade_smooth, - set_shade_smooth, - ATTR_DOMAIN_FACE>, + make_derived_read_attribute<MPoly, bool, get_shade_smooth>, + make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>, nullptr); static BuiltinCustomDataLayerProvider crease( @@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, edge_access, - make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>, - make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>, + make_derived_read_attribute<MEdge, float, get_crease>, + make_derived_write_attribute<MEdge, float, get_crease, set_crease>, nullptr); static NamedLegacyCustomDataProvider uvs( @@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_FLOAT2, CD_MLOOPUV, corner_access, - make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>, - make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>); + make_derived_read_attribute<MLoopUV, float2, get_loop_uv>, + make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>); static NamedLegacyCustomDataProvider vertex_colors( ATTR_DOMAIN_CORNER, CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>, - make_derived_write_attribute<MLoopCol, - Color4f, - get_loop_color, - set_loop_color, - ATTR_DOMAIN_CORNER>); + make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>, + make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 135de14b4f7..6c4af7a6d23 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con namespace blender::bke { -template<typename T, AttributeDomain Domain> -static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size) +template<typename T> +static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size) { - return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size)); + return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size)); } -template<typename T, AttributeDomain Domain> -static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size) +template<typename T> +static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size) { - return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size)); + return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>( + MutableSpan<T>((T *)data, domain_size)); } /** @@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() }, update_custom_data_pointers}; - static BuiltinCustomDataLayerProvider position( - "position", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT3, - CD_PROP_FLOAT3, - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::NonDeletable, - point_access, - make_array_read_attribute<float3, ATTR_DOMAIN_POINT>, - make_array_write_attribute<float3, ATTR_DOMAIN_POINT>, - nullptr); - static BuiltinCustomDataLayerProvider radius( - "radius", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT, - CD_PROP_FLOAT, - BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::Deletable, - point_access, - make_array_read_attribute<float, ATTR_DOMAIN_POINT>, - make_array_write_attribute<float, ATTR_DOMAIN_POINT>, - nullptr); + static BuiltinCustomDataLayerProvider position("position", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonDeletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + nullptr); + static BuiltinCustomDataLayerProvider radius("radius", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + nullptr); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); return ComponentAttributeProviders({&position, &radius}, {&point_custom_data}); } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 3e457e48076..3d85118deee 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -24,6 +24,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "BKE_volume.h" #include "DNA_collection_types.h" @@ -60,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return new InstancesComponent(); case GEO_COMPONENT_TYPE_VOLUME: return new VolumeComponent(); + case GEO_COMPONENT_TYPE_CURVE: + return new CurveComponent(); } BLI_assert_unreachable(); return nullptr; @@ -182,6 +185,11 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma if (volume != nullptr) { BKE_volume_min_max(volume, *r_min, *r_max); } + const CurveEval *curve = this->get_curve_for_read(); + if (curve != nullptr) { + /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */ + curve->bounds_min_max(*r_min, *r_max, true); + } } std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set) @@ -252,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +/* Returns a read-only curve or null. */ +const CurveEval *GeometrySet::get_curve_for_read() const +{ + const CurveComponent *component = this->get_component_for_read<CurveComponent>(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + /* Returns true when the geometry set has a point cloud component that has a point cloud. */ bool GeometrySet::has_pointcloud() const { @@ -273,6 +288,13 @@ bool GeometrySet::has_volume() const return component != nullptr && component->has_volume(); } +/* Returns true when the geometry set has a curve component that has a curve. */ +bool GeometrySet::has_curve() const +{ + const CurveComponent *component = this->get_component_for_read<CurveComponent>(); + return component != nullptr && component->has_curve(); +} + /* Create a new geometry set that only contains the given mesh. */ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { @@ -292,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, return geometry_set; } +/* Create a new geometry set that only contains the given curve. */ +GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); + component.replace(curve, ownership); + return geometry_set; +} + /* Clear the existing mesh and replace it with the given one. */ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { @@ -299,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) component.replace(mesh, ownership); } +/* Clear the existing curve and replace it with the given one. */ +void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership) +{ + CurveComponent &component = this->get_component_for_write<CurveComponent>(); + component.replace(curve, ownership); +} + /* Clear the existing point cloud and replace with the given one. */ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { @@ -306,6 +344,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy pointcloud_component.replace(pointcloud, ownership); } +/* Clear the existing volume and replace with the given one. */ +void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership) +{ + VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>(); + volume_component.replace(volume, ownership); +} + /* Returns a mutable mesh or null. No ownership is transferred. */ Mesh *GeometrySet::get_mesh_for_write() { @@ -327,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write() return component.get_for_write(); } +/* Returns a mutable curve or null. No ownership is transferred. */ +CurveEval *GeometrySet::get_curve_for_write() +{ + CurveComponent &component = this->get_component_for_write<CurveComponent>(); + return component.get_for_write(); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 07d0e520c93..a792e268d5c 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -19,6 +19,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" +#include "BKE_spline.hh" #include "DNA_collection_types.h" #include "DNA_mesh_types.h" @@ -50,6 +51,16 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS } } +static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set) +{ + BLI_assert(object.type == OB_CURVE); + if (object.data != nullptr) { + std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data); + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + curve_component.replace(curve.release(), GeometryOwnershipType::Owned); + } +} + /** * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. */ @@ -73,6 +84,9 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object) if (object.type == OB_MESH) { add_final_mesh_as_geometry_component(object, geometry_set); } + else if (object.type == OB_CURVE) { + add_curve_data_as_geometry_component(object, geometry_set); + } /* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the * #geometry_set_eval case above. */ @@ -135,21 +149,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set, const InstancesComponent &instances_component = *geometry_set.get_component_for_read<InstancesComponent>(); - Span<float4x4> transforms = instances_component.transforms(); - Span<InstancedData> instances = instances_component.instanced_data(); - for (const int i : instances.index_range()) { - const InstancedData &data = instances[i]; + Span<float4x4> transforms = instances_component.instance_transforms(); + Span<int> handles = instances_component.instance_reference_handles(); + Span<InstanceReference> references = instances_component.references(); + for (const int i : transforms.index_range()) { + const InstanceReference &reference = references[handles[i]]; const float4x4 instance_transform = transform * transforms[i]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - BLI_assert(data.data.object != nullptr); - const Object &object = *data.data.object; - geometry_set_collect_recursive_object(object, instance_transform, r_sets); - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - BLI_assert(data.data.collection != nullptr); - const Collection &collection = *data.data.collection; - geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets); + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); + geometry_set_collect_recursive_object(object, instance_transform, r_sets); + break; + } + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + geometry_set_collect_recursive_collection_instance( + collection, instance_transform, r_sets); + break; + } + case InstanceReference::Type::None: { + break; + } } } } @@ -253,19 +274,24 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se return true; } - for (const InstancedData &data : instances_component->instanced_data()) { - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - BLI_assert(data.data.object != nullptr); - const Object &object = *data.data.object; - if (!object_instance_attribute_foreach(object, callback, limit, count)) { - return false; + for (const InstanceReference &reference : instances_component->references()) { + switch (reference.type()) { + case InstanceReference::Type::Object: { + const Object &object = reference.object(); + if (!object_instance_attribute_foreach(object, callback, limit, count)) { + return false; + } + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - BLI_assert(data.data.collection != nullptr); - const Collection &collection = *data.data.collection; - if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { - return false; + case InstanceReference::Type::Collection: { + const Collection &collection = reference.collection(); + if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { + return false; + } + break; + } + case InstanceReference::Type::None: { + break; } } } @@ -449,13 +475,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); BLI_assert(cpp_type != nullptr); - result.attribute_try_create(entry.key, domain_output, data_type_output); - WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); - if (!write_attribute || &write_attribute->cpp_type() != cpp_type || - write_attribute->domain() != domain_output) { + result.attribute_try_create( + entry.key, domain_output, data_type_output, AttributeInitDefault()); + WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); + if (!write_attribute || &write_attribute.varray->type() != cpp_type || + write_attribute.domain != domain_output) { continue; } - fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + + fn::GVMutableArray_GSpan dst_span{*write_attribute.varray}; int offset = 0; for (const GeometryInstanceGroup &set_group : set_groups) { @@ -467,11 +495,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, if (domain_size == 0) { continue; /* Domain size is 0, so no need to increment the offset. */ } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + GVArrayPtr source_attribute = component.attribute_try_get_for_read( name, domain_output, data_type_output); if (source_attribute) { - fn::GSpan src_span = source_attribute->get_span(); + fn::GVArray_GSpan src_span{*source_attribute}; const void *src_buffer = src_span.data(); for (const int UNUSED(i) : set_group.transforms.index_range()) { void *dst_buffer = dst_span[offset]; @@ -486,10 +514,32 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } - write_attribute->apply_span(); + dst_span.save(); } } +static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result) +{ + CurveEval *new_curve = new CurveEval(); + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (!set.has_curve()) { + continue; + } + + const CurveEval &source_curve = *set.get_curve_for_read(); + for (const SplinePtr &source_spline : source_curve.splines) { + for (const float4x4 &transform : set_group.transforms) { + SplinePtr new_spline = source_spline->copy(); + new_spline->transform(transform); + new_curve->splines.append(std::move(new_spline)); + } + } + } + + result.replace(new_curve); +} + static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, bool convert_points_to_vertices, GeometrySet &result) @@ -556,6 +606,12 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, UNUSED_VARS(set_groups, dst_component); } +static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) +{ + CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); + join_curve_splines(set_groups, dst_component); +} + GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) { if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) { @@ -587,6 +643,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) join_instance_groups_mesh(set_groups, false, new_geometry_set); join_instance_groups_pointcloud(set_groups, new_geometry_set); join_instance_groups_volume(set_groups, new_geometry_set); + join_instance_groups_curve(set_groups, new_geometry_set); return new_geometry_set; } diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index fb6500cfe1b..04403e264a4 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -1050,7 +1050,7 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, normalize_v3(locx); normalize_v3(locy); - /* Calculcate last point first. */ + /* Calculate last point first. */ const bGPDspoint *pt_last = &points[totpoints - 1]; float tmp[3]; sub_v3_v3v3(tmp, &pt_last->x, &pt0->x); diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 54d14e33209..43a76c27b0f 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -878,7 +878,8 @@ bool BKE_lib_override_library_resync(Main *bmain, ID *id_root, Collection *override_resync_residual_storage, const bool do_hierarchy_enforce, - const bool do_post_process) + const bool do_post_process, + ReportList *reports) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); BLI_assert(!ID_IS_LINKED(id_root)); @@ -1057,6 +1058,7 @@ bool BKE_lib_override_library_resync(Main *bmain, /* Delete old override IDs. * Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT. * This improves performances anyway, so everything is fine. */ + int user_edited_overrides_deletion_count = 0; FOREACH_MAIN_ID_BEGIN (bmain, id) { if (id->tag & LIB_TAG_DOIT) { /* Note that this works because linked IDs are always after local ones (including overrides), @@ -1081,6 +1083,7 @@ bool BKE_lib_override_library_resync(Main *bmain, id->tag &= ~LIB_TAG_MISSING; CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name); } +#if 0 else { /* Otherwise, keep them, user needs to decide whether what to do with them. */ BLI_assert((id->tag & LIB_TAG_DOIT) == 0); @@ -1088,6 +1091,17 @@ bool BKE_lib_override_library_resync(Main *bmain, id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER; CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name); } +#else + else { + /* Delete them nevertheless, with fat warning, user needs to decide whether they want to + * save that version of the file (and accept the loss), or not. */ + id->tag |= LIB_TAG_DOIT; + id->tag &= ~LIB_TAG_MISSING; + CLOG_WARN( + &LOG, "Old override %s is being deleted even though it was user-edited", id->name); + user_edited_overrides_deletion_count++; + } +#endif } } FOREACH_MAIN_ID_END; @@ -1098,6 +1112,15 @@ bool BKE_lib_override_library_resync(Main *bmain, */ id_root = id_root_reference->newid; + if (user_edited_overrides_deletion_count > 0) { + BKE_reportf(reports, + RPT_WARNING, + "During resync of data-block %s, %d obsolete overrides were deleted, that had " + "local changes defined by user", + id_root->name + 2, + user_edited_overrides_deletion_count); + } + if (do_post_process) { /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ /* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine @@ -1136,7 +1159,10 @@ bool BKE_lib_override_library_resync(Main *bmain, * Then it will handle the resync of necessary IDs (through calls to * #BKE_lib_override_library_resync). */ -void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer) +void BKE_lib_override_library_main_resync(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + ReportList *reports) { /* We use a specific collection to gather/store all 'orphaned' override collections and objects * generated by re-sync-process. This avoids putting them in scene's master collection. */ @@ -1252,7 +1278,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer * CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name); const bool success = BKE_lib_override_library_resync( - bmain, scene, view_layer, id, override_resync_residual_storage, false, false); + bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports); CLOG_INFO(&LOG, 2, "\tSuccess: %d", success); break; } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index cbbe07f99d8..3281783d81a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -56,7 +56,7 @@ typedef struct LibraryForeachIDData { */ ID *self_id; - /** Flags controlling the bahaviour of the 'foreach id' looping code. */ + /** Flags controlling the behavior of the 'foreach id' looping code. */ int flag; /** Generic flags to be passed to all callback calls for current processed data. */ int cb_flag; diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 729c38ae7cf..098d9c420aa 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object) * * Note that there are extra fields in there like bevel and path, but those are not needed during * conversion, so they are not copied to save unnecessary allocations. */ - if (object->runtime.curve_cache != NULL) { + if (temp_object->runtime.curve_cache == NULL) { temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); + } + + if (object->runtime.curve_cache != NULL) { BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp); } + /* Constructive modifiers will use mesh to store result. */ if (object->runtime.data_eval != NULL) { BKE_id_copy_ex( @@ -1057,17 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object) return temp_object; } +/** + * Populate `object->runtime.curve_cache` which is then used to create the mesh. + */ static void curve_to_mesh_eval_ensure(Object *object) { - if (object->runtime.curve_cache == NULL) { - object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); - } Curve *curve = (Curve *)object->data; Curve remapped_curve = *curve; Object remapped_object = *object; - remapped_object.runtime.bb = NULL; + BKE_object_runtime_reset(&remapped_object); + remapped_object.data = &remapped_curve; + if (object->runtime.curve_cache == NULL) { + object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve"); + } + + /* Temporarily share the curve-cache with the temporary object, owned by `object`. */ + remapped_object.runtime.curve_cache = object->runtime.curve_cache; + /* Clear all modifiers for the bevel object. * * This is because they can not be reliably evaluated for an original object (at least because @@ -1078,8 +1090,8 @@ static void curve_to_mesh_eval_ensure(Object *object) Object bevel_object = {{NULL}}; if (remapped_curve.bevobj != NULL) { bevel_object = *remapped_curve.bevobj; - bevel_object.runtime.bb = NULL; BLI_listbase_clear(&bevel_object.modifiers); + BKE_object_runtime_reset(&bevel_object); remapped_curve.bevobj = &bevel_object; } @@ -1087,8 +1099,8 @@ static void curve_to_mesh_eval_ensure(Object *object) Object taper_object = {{NULL}}; if (remapped_curve.taperobj != NULL) { taper_object = *remapped_curve.taperobj; - taper_object.runtime.bb = NULL; BLI_listbase_clear(&taper_object.modifiers); + BKE_object_runtime_reset(&taper_object); remapped_curve.taperobj = &taper_object; } @@ -1110,12 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object) BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true); } - MEM_SAFE_FREE(remapped_object.runtime.bb); - MEM_SAFE_FREE(taper_object.runtime.bb); - MEM_SAFE_FREE(bevel_object.runtime.bb); + /* Owned by `object` & needed by the caller to create the mesh. */ + remapped_object.runtime.curve_cache = NULL; - BKE_object_free_curve_cache(&bevel_object); - BKE_object_free_curve_cache(&taper_object); + BKE_object_runtime_free_data(&remapped_object); + BKE_object_runtime_free_data(&taper_object); + BKE_object_runtime_free_data(&taper_object); } static Mesh *mesh_new_from_curve_type_object(Object *object) @@ -1542,7 +1554,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, * check whether it is still true with Mesh */ Mesh tmp = *mesh_dst; int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; - int did_shapekeys = 0; + bool did_shapekeys = false; eCDAllocType alloctype = CD_DUPLICATE; if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) { @@ -1597,7 +1609,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, } shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid); - did_shapekeys = 1; + did_shapekeys = true; } /* copy texture space */ @@ -1626,13 +1638,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, totedge); } if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { - /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the - * BKE_mesh_update_customdata_pointers() call below. */ - tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop); - tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly); - - CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop); - CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly); + CustomData_add_layer(&tmp.ldata, + CD_MLOOP, + CD_ASSIGN, + (alloctype == CD_ASSIGN) ? mesh_src->mloop : + MEM_dupallocN(mesh_src->mloop), + tmp.totloop); + CustomData_add_layer(&tmp.pdata, + CD_MPOLY, + CD_ASSIGN, + (alloctype == CD_ASSIGN) ? mesh_src->mpoly : + MEM_dupallocN(mesh_src->mpoly), + tmp.totpoly); } /* object had got displacement layer, should copy this layer to save sculpted data */ @@ -1651,9 +1668,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* yes, must be before _and_ after tessellate */ BKE_mesh_update_customdata_pointers(&tmp, false); - /* since 2.65 caller must do! */ - // BKE_mesh_tessface_calc(&tmp); - CustomData_free(&mesh_dst->vdata, mesh_dst->totvert); CustomData_free(&mesh_dst->edata, mesh_dst->totedge); CustomData_free(&mesh_dst->fdata, mesh_dst->totface); diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc new file mode 100644 index 00000000000..91c9951ae89 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_attribute_math.hh" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +namespace blender::bke::mesh_surface_sample { + +static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) +{ + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); + const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); + return {looptris, looptris_len}; +} + +template<typename T> +BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + const Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + + const T v0 = data_in[v0_index]; + const T v1 = data_in[v1_index]; + const T v2 = data_in[v2_index]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +void sample_point_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_out.size() == bary_coords.size()); + BLI_assert(data_in.size() == mesh.totvert); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_point_attribute<T>( + mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +template<typename T> +BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int loop_index_0 = looptri.tri[0]; + const int loop_index_1 = looptri.tri[1]; + const int loop_index_2 = looptri.tri[2]; + + const T v0 = data_in[loop_index_0]; + const T v1 = data_in[loop_index_1]; + const T v2 = data_in[loop_index_2]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +void sample_corner_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_out.size() == bary_coords.size()); + BLI_assert(data_in.size() == mesh.totloop); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_corner_attribute<T>( + mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +template<typename T> +void sample_face_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : data_out.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const int poly_index = looptri.poly; + data_out[i] = data_in[poly_index]; + } +} + +void sample_face_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_in.size() == mesh.totpoly); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +} // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 9c2cd03dbc2..0f2f56f4f2b 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip) int width, height; MovieClipUser user = {0}; - user.framenr = 1; + user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1); BKE_movieclip_get_size(clip, &user, &width, &height); if (width && height) { diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 97aa6b07ab0..92e21183acb 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain, * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... * flags in BKE_lib_id.h */ -void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag) +void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag) { NlaTrack *nlt, *nlt_d; @@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl } } +/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */ +static void update_active_strip(AnimData *adt_dest, + NlaTrack *track_dest, + const AnimData *adt_source, + NlaTrack *track_source) +{ + BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips)); + + NlaStrip *strip_dest = track_dest->strips.first; + LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) { + if (strip_source == adt_source->actstrip) { + adt_dest->actstrip = strip_dest; + } + + strip_dest = strip_dest->next; + } +} + +/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */ +static void update_active_track(AnimData *adt_dest, const AnimData *adt_source) +{ + BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) == + BLI_listbase_count(&adt_dest->nla_tracks)); + + NlaTrack *track_dest = adt_dest->nla_tracks.first; + LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) { + if (track_source == adt_source->act_track) { + adt_dest->act_track = track_dest; + /* Assumption: the active strip is on the active track. */ + update_active_strip(adt_dest, track_dest, adt_source, track_source); + } + + track_dest = track_dest->next; + } +} + +void BKE_nla_tracks_copy_from_adt(Main *bmain, + AnimData *adt_dest, + const AnimData *adt_source, + const int flag) +{ + adt_dest->act_track = NULL; + adt_dest->actstrip = NULL; + + BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag); + update_active_track(adt_dest, adt_source); +} + /* Adding ------------------------------------------- */ /* Add a NLA Track to the given AnimData diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 02195e0d60f..05eb7e43441 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -501,6 +501,12 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } + else if ((ntree->type == NTREE_GEOMETRY) && (node->type == GEO_NODE_ATTRIBUTE_CURVE_MAP)) { + BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage); + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec); + BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_rgb); + } else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) { NodeShaderScript *nss = (NodeShaderScript *)node->storage; if (nss->bytecode) { @@ -676,6 +682,18 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage); break; } + case GEO_NODE_ATTRIBUTE_CURVE_MAP: { + NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; + BLO_read_data_address(reader, &data->curve_vec); + if (data->curve_vec) { + BKE_curvemapping_blend_read(reader, data->curve_vec); + } + BLO_read_data_address(reader, &data->curve_rgb); + if (data->curve_rgb) { + BKE_curvemapping_blend_read(reader, data->curve_rgb); + } + break; + } case SH_NODE_SCRIPT: { NodeShaderScript *nss = (NodeShaderScript *)node->storage; BLO_read_data_address(reader, &nss->bytecode); @@ -4934,6 +4952,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_combine_xyz(); register_node_type_geo_attribute_compare(); register_node_type_geo_attribute_convert(); + register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); @@ -4941,11 +4960,13 @@ static void registerGeometryNodes() register_node_type_geo_attribute_proximity(); register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_separate_xyz(); + register_node_type_geo_attribute_transfer(); register_node_type_geo_attribute_vector_math(); register_node_type_geo_attribute_remove(); register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); + register_node_type_geo_curve_to_mesh(); register_node_type_geo_edge_split(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); @@ -4968,6 +4989,7 @@ static void registerGeometryNodes() register_node_type_geo_sample_texture(); register_node_type_geo_subdivide(); register_node_type_geo_subdivision_surface(); + register_node_type_geo_switch(); register_node_type_geo_transform(); register_node_type_geo_triangulate(); register_node_type_geo_volume_to_mesh(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 160fb93b835..02be16d1d28 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) if (ob->type == OB_HAIR) { return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); } - if (ob->type == OB_POINTCLOUD) { + if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) { return (mti->modifyGeometrySet != NULL); } - if (ob->type == OB_VOLUME) { - return (mti->modifyVolume != NULL); - } if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { return false; @@ -4346,7 +4343,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph, } /* Speed optimization for animation lookups. */ if (ob->pose != NULL) { - BKE_pose_channels_hash_make(ob->pose); + BKE_pose_channels_hash_ensure(ob->pose); if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(ob->pose); } @@ -5117,6 +5114,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) } /** + * The function frees memory used by the runtime data, but not the runtime field itself. + * + * All runtime data is cleared to ensure it's not used again, + * in keeping with other `_free_data(..)` functions. + */ +void BKE_object_runtime_free_data(Object *object) +{ + /* Currently this is all that's needed. */ + BKE_object_free_derived_caches(object); + + BKE_object_runtime_reset(object); +} + +/** * Find an associated armature object. */ static Object *obrel_armature_find(Object *ob) diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 835b62c06bf..768fa9373c1 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -842,38 +842,38 @@ static void make_duplis_instances_component(const DupliContext *ctx) return; } - Span<float4x4> instance_offset_matrices = component->transforms(); + Span<float4x4> instance_offset_matrices = component->instance_transforms(); + Span<int> instance_reference_handles = component->instance_reference_handles(); Span<int> almost_unique_ids = component->almost_unique_ids(); - Span<InstancedData> instanced_data = component->instanced_data(); + Span<InstanceReference> references = component->references(); - for (int i = 0; i < component->instances_amount(); i++) { - const InstancedData &data = instanced_data[i]; + for (int64_t i : instance_offset_matrices.index_range()) { + const InstanceReference &reference = references[instance_reference_handles[i]]; const int id = almost_unique_ids[i]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - Object *object = data.data.object; - if (object != nullptr) { + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); float matrix[4][4]; mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values); - make_dupli(ctx, object, matrix, id); + make_dupli(ctx, &object, matrix, id); float space_matrix[4][4]; - mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object->imat); + mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat); mul_m4_m4_pre(space_matrix, ctx->object->obmat); - make_recursive_duplis(ctx, object, space_matrix, id); + make_recursive_duplis(ctx, &object, space_matrix, id); + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - Collection *collection = data.data.collection; - if (collection != nullptr) { + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); float collection_matrix[4][4]; unit_m4(collection_matrix); - sub_v3_v3(collection_matrix[3], collection->instance_offset); + sub_v3_v3(collection_matrix[3], collection.instance_offset); mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values); mul_m4_m4_pre(collection_matrix, ctx->object->obmat); eEvaluationMode mode = DEG_get_mode(ctx->depsgraph); - FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) { + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) { if (object == ctx->object) { continue; } @@ -885,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx) make_recursive_duplis(ctx, object, collection_matrix, id); } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; + break; + } + case InstanceReference::Type::None: { + break; } } } diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 63bc8753fc7..948b57578dc 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -187,31 +187,31 @@ int BB_widest_axis(const BB *bb); void pbvh_grow_nodes(PBVH *bvh, int totnode); bool ray_face_intersection_quad(const float ray_start[3], struct IsectRayPrecalc *isect_precalc, - const float *t0, - const float *t1, - const float *t2, - const float *t3, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], float *depth); bool ray_face_intersection_tri(const float ray_start[3], struct IsectRayPrecalc *isect_precalc, - const float *t0, - const float *t1, - const float *t2, + const float t0[3], + const float t1[3], + const float t2[3], float *depth); bool ray_face_nearest_quad(const float ray_start[3], const float ray_normal[3], - const float *t0, - const float *t1, - const float *t2, - const float *t3, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], float *r_depth, float *r_dist_sq); bool ray_face_nearest_tri(const float ray_start[3], const float ray_normal[3], - const float *t0, - const float *t1, - const float *t2, + const float t0[3], + const float t1[3], + const float t2[3], float *r_depth, float *r_dist_sq); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 17434ee8023..be206f8a642 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH } for (i = 0; i < BPHYS_TOT_DATA; i++) { - cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL; + cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL; } return 1; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index d52e4443ac1..3b1230ce3b6 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm) } MEM_freeN(ccdm->mima); MEM_freeN(ccdm); - ccdm = NULL; } } diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc new file mode 100644 index 00000000000..31c2178fa3f --- /dev/null +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -0,0 +1,274 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_span.hh" + +#include "FN_generic_virtual_array.hh" + +#include "BKE_spline.hh" + +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + +Spline::Type Spline::type() const +{ + return type_; +} + +void Spline::translate(const blender::float3 &translation) +{ + for (float3 &position : this->positions()) { + position += translation; + } + this->mark_cache_invalid(); +} + +void Spline::transform(const blender::float4x4 &matrix) +{ + for (float3 &position : this->positions()) { + position = matrix * position; + } + this->mark_cache_invalid(); +} + +int Spline::evaluated_edges_size() const +{ + const int eval_size = this->evaluated_points_size(); + if (eval_size == 1) { + return 0; + } + + return this->is_cyclic_ ? eval_size : eval_size - 1; +} + +float Spline::length() const +{ + return this->evaluated_lengths().last(); +} + +int Spline::segments_size() const +{ + const int points_len = this->size(); + + return is_cyclic_ ? points_len : points_len - 1; +} + +bool Spline::is_cyclic() const +{ + return is_cyclic_; +} + +void Spline::set_cyclic(const bool value) +{ + is_cyclic_ = value; +} + +static void accumulate_lengths(Span<float3> positions, + const bool is_cyclic, + MutableSpan<float> lengths) +{ + float length = 0.0f; + for (const int i : IndexRange(positions.size() - 1)) { + length += float3::distance(positions[i], positions[i + 1]); + lengths[i] = length; + } + if (is_cyclic) { + lengths.last() = length + float3::distance(positions.last(), positions.first()); + } +} + +/** + * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the + * length of the subsequent segment, i.e. the first value is the length of the first segment rather + * than 0. This calculation is rather trivial, and only depends on the evaluated positions. + * However, the results are used often, so it makes sense to cache it. + */ +Span<float> Spline::evaluated_lengths() const +{ + if (!length_cache_dirty_) { + return evaluated_lengths_cache_; + } + + std::lock_guard lock{length_cache_mutex_}; + if (!length_cache_dirty_) { + return evaluated_lengths_cache_; + } + + const int total = evaluated_edges_size(); + evaluated_lengths_cache_.resize(total); + + Span<float3> positions = this->evaluated_positions(); + accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); + + length_cache_dirty_ = false; + return evaluated_lengths_cache_; +} + +static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next) +{ + const float3 dir_prev = (middle - prev).normalized(); + const float3 dir_next = (next - middle).normalized(); + + return (dir_prev + dir_next).normalized(); +} + +static void calculate_tangents(Span<float3> positions, + const bool is_cyclic, + MutableSpan<float3> tangents) +{ + if (positions.size() == 1) { + return; + } + + for (const int i : IndexRange(1, positions.size() - 2)) { + tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]); + } + + if (is_cyclic) { + const float3 &second_to_last = positions[positions.size() - 2]; + const float3 &last = positions.last(); + const float3 &first = positions.first(); + const float3 &second = positions[1]; + tangents.first() = direction_bisect(last, first, second); + tangents.last() = direction_bisect(second_to_last, last, first); + } + else { + tangents.first() = (positions[1] - positions[0]).normalized(); + tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized(); + } +} + +/** + * Return non-owning access to the direction of the curve at each evaluated point. + */ +Span<float3> Spline::evaluated_tangents() const +{ + if (!tangent_cache_dirty_) { + return evaluated_tangents_cache_; + } + + std::lock_guard lock{tangent_cache_mutex_}; + if (!tangent_cache_dirty_) { + return evaluated_tangents_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_tangents_cache_.resize(eval_size); + + Span<float3> positions = this->evaluated_positions(); + + if (eval_size == 1) { + evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f); + } + else { + calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); + this->correct_end_tangents(); + } + + tangent_cache_dirty_ = false; + return evaluated_tangents_cache_; +} + +static float3 rotate_direction_around_axis(const float3 &direction, + const float3 &axis, + const float angle) +{ + BLI_ASSERT_UNIT_V3(direction); + BLI_ASSERT_UNIT_V3(axis); + + const float3 axis_scaled = axis * float3::dot(direction, axis); + const float3 diff = direction - axis_scaled; + const float3 cross = float3::cross(axis, diff); + + return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); +} + +static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals) +{ + for (const int i : normals.index_range()) { + normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized(); + } +} + +/** + * Return non-owning access to the direction vectors perpendicular to the tangents at every + * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode. + */ +Span<float3> Spline::evaluated_normals() const +{ + if (!normal_cache_dirty_) { + return evaluated_normals_cache_; + } + + std::lock_guard lock{normal_cache_mutex_}; + if (!normal_cache_dirty_) { + return evaluated_normals_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_normals_cache_.resize(eval_size); + + Span<float3> tangents = evaluated_tangents(); + MutableSpan<float3> normals = evaluated_normals_cache_; + + /* Only Z up normals are supported at the moment. */ + calculate_normals_z_up(tangents, normals); + + /* Rotate the generated normals with the interpolated tilt data. */ + blender::fn::GVArray_Typed<float> tilts{ + this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span(this->tilts()))}; + for (const int i : normals.index_range()) { + normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]); + } + + normal_cache_dirty_ = false; + return evaluated_normals_cache_; +} + +Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const +{ + return this->lookup_evaluated_length(this->length() * factor); +} + +/** + * \note This does not support extrapolation currently. + */ +Spline::LookupResult Spline::lookup_evaluated_length(const float length) const +{ + BLI_assert(length >= 0.0f && length <= this->length()); + + Span<float> lengths = this->evaluated_lengths(); + + const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); + const int index = offset - lengths.begin(); + const int next_index = (index == this->size() - 1) ? 0 : index + 1; + + const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; + const float factor = (length - previous_length) / (lengths[index] - previous_length); + + return LookupResult{index, next_index, factor}; +} + +void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const +{ + Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions(); + for (const float3 &position : positions) { + minmax_v3v3_v3(min, max, position); + } +} diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc new file mode 100644 index 00000000000..f62718011da --- /dev/null +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -0,0 +1,478 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + +SplinePtr BezierSpline::copy() const +{ + return std::make_unique<BezierSpline>(*this); +} + +int BezierSpline::size() const +{ + const int size = positions_.size(); + BLI_assert(size == handle_types_left_.size()); + BLI_assert(size == handle_positions_left_.size()); + BLI_assert(size == handle_types_right_.size()); + BLI_assert(size == handle_positions_right_.size()); + BLI_assert(size == radii_.size()); + BLI_assert(size == tilts_.size()); + return size; +} + +int BezierSpline::resolution() const +{ + return resolution_; +} + +void BezierSpline::set_resolution(const int value) +{ + BLI_assert(value > 0); + resolution_ = value; + this->mark_cache_invalid(); +} + +void BezierSpline::add_point(const float3 position, + const HandleType handle_type_start, + const float3 handle_position_start, + const HandleType handle_type_end, + const float3 handle_position_end, + const float radius, + const float tilt) +{ + handle_types_left_.append(handle_type_start); + handle_positions_left_.append(handle_position_start); + positions_.append(position); + handle_types_right_.append(handle_type_end); + handle_positions_right_.append(handle_position_end); + radii_.append(radius); + tilts_.append(tilt); + this->mark_cache_invalid(); +} + +MutableSpan<float3> BezierSpline::positions() +{ + return positions_; +} +Span<float3> BezierSpline::positions() const +{ + return positions_; +} +MutableSpan<float> BezierSpline::radii() +{ + return radii_; +} +Span<float> BezierSpline::radii() const +{ + return radii_; +} +MutableSpan<float> BezierSpline::tilts() +{ + return tilts_; +} +Span<float> BezierSpline::tilts() const +{ + return tilts_; +} +Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const +{ + return handle_types_left_; +} +MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left() +{ + return handle_types_left_; +} +Span<float3> BezierSpline::handle_positions_left() const +{ + return handle_positions_left_; +} +MutableSpan<float3> BezierSpline::handle_positions_left() +{ + return handle_positions_left_; +} +Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const +{ + return handle_types_right_; +} +MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right() +{ + return handle_types_right_; +} +Span<float3> BezierSpline::handle_positions_right() const +{ + return handle_positions_right_; +} +MutableSpan<float3> BezierSpline::handle_positions_right() +{ + return handle_positions_right_; +} + +void BezierSpline::translate(const blender::float3 &translation) +{ + for (float3 &position : this->positions()) { + position += translation; + } + for (float3 &handle_position : this->handle_positions_left()) { + handle_position += translation; + } + for (float3 &handle_position : this->handle_positions_right()) { + handle_position += translation; + } + this->mark_cache_invalid(); +} + +void BezierSpline::transform(const blender::float4x4 &matrix) +{ + for (float3 &position : this->positions()) { + position = matrix * position; + } + for (float3 &handle_position : this->handle_positions_left()) { + handle_position = matrix * handle_position; + } + for (float3 &handle_position : this->handle_positions_right()) { + handle_position = matrix * handle_position; + } + this->mark_cache_invalid(); +} + +bool BezierSpline::point_is_sharp(const int index) const +{ + return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) || + ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free); +} + +bool BezierSpline::segment_is_vector(const int index) const +{ + if (index == this->size() - 1) { + BLI_assert(is_cyclic_); + return handle_types_right_.last() == HandleType::Vector && + handle_types_left_.first() == HandleType::Vector; + } + return handle_types_right_[index] == HandleType::Vector && + handle_types_left_[index + 1] == HandleType::Vector; +} + +void BezierSpline::mark_cache_invalid() +{ + offset_cache_dirty_ = true; + position_cache_dirty_ = true; + mapping_cache_dirty_ = true; + tangent_cache_dirty_ = true; + normal_cache_dirty_ = true; + length_cache_dirty_ = true; +} + +int BezierSpline::evaluated_points_size() const +{ + const int points_len = this->size(); + BLI_assert(points_len > 0); + + const int last_offset = this->control_point_offsets().last(); + if (is_cyclic_ && points_len > 1) { + return last_offset + (this->segment_is_vector(points_len - 1) ? 0 : resolution_); + } + + return last_offset + 1; +} + +/** + * If the spline is not cyclic, the direction for the first and last points is just the + * direction formed by the corresponding handles and control points. In the unlikely situation + * that the handles define a zero direction, fallback to using the direction defined by the + * first and last evaluated segments already calculated in #Spline::evaluated_tangents(). + */ +void BezierSpline::correct_end_tangents() const +{ + if (is_cyclic_) { + return; + } + + MutableSpan<float3> tangents(evaluated_tangents_cache_); + + if (handle_positions_left_.first() != positions_.first()) { + tangents.first() = (positions_.first() - handle_positions_left_.first()).normalized(); + } + if (handle_positions_right_.last() != positions_.last()) { + tangents.last() = (handle_positions_right_.last() - positions_.last()).normalized(); + } +} + +static void bezier_forward_difference_3d(const float3 &point_0, + const float3 &point_1, + const float3 &point_2, + const float3 &point_3, + MutableSpan<float3> result) +{ + BLI_assert(result.size() > 0); + const float inv_len = 1.0f / static_cast<float>(result.size()); + const float inv_len_squared = inv_len * inv_len; + const float inv_len_cubed = inv_len_squared * inv_len; + + const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len; + const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared; + const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed; + + float3 q0 = point_0; + float3 q1 = rt1 + rt2 + rt3; + float3 q2 = 2.0f * rt2 + 6.0f * rt3; + float3 q3 = 6.0f * rt3; + for (const int i : result.index_range()) { + result[i] = q0; + q0 += q1; + q1 += q2; + q2 += q3; + } +} + +void BezierSpline::evaluate_bezier_segment(const int index, + const int next_index, + MutableSpan<float3> positions) const +{ + if (this->segment_is_vector(index)) { + BLI_assert(positions.size() == 1); + positions.first() = positions_[index]; + } + else { + bezier_forward_difference_3d(positions_[index], + handle_positions_right_[index], + handle_positions_left_[next_index], + positions_[next_index], + positions); + } +} + +/** + * Returns access to a cache of offsets into the evaluated point array for each control point. + * This is important because while most control point edges generate the number of edges specified + * by the resolution, vector segments only generate one edge. + */ +Span<int> BezierSpline::control_point_offsets() const +{ + if (!offset_cache_dirty_) { + return offset_cache_; + } + + std::lock_guard lock{offset_cache_mutex_}; + if (!offset_cache_dirty_) { + return offset_cache_; + } + + const int points_len = this->size(); + offset_cache_.resize(points_len); + + MutableSpan<int> offsets = offset_cache_; + + int offset = 0; + for (const int i : IndexRange(points_len - 1)) { + offsets[i] = offset; + offset += this->segment_is_vector(i) ? 1 : resolution_; + } + offsets.last() = offset; + + offset_cache_dirty_ = false; + return offsets; +} + +static void calculate_mappings_linear_resolution(Span<int> offsets, + const int size, + const int resolution, + const bool is_cyclic, + MutableSpan<float> r_mappings) +{ + const float first_segment_len_inv = 1.0f / offsets[1]; + for (const int i : IndexRange(0, offsets[1])) { + r_mappings[i] = i * first_segment_len_inv; + } + + const int grain_size = std::max(2048 / resolution, 1); + blender::parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) { + for (const int i_control_point : range) { + const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point]; + const float segment_len_inv = 1.0f / segment_len; + for (const int i : IndexRange(segment_len)) { + r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv; + } + } + }); + + if (is_cyclic) { + const int last_segment_len = offsets[size - 1] - offsets[size - 2]; + const float last_segment_len_inv = 1.0f / last_segment_len; + for (const int i : IndexRange(last_segment_len)) { + r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv; + } + } + else { + r_mappings.last() = size - 1; + } +} + +/** + * Returns non-owning access to an array of values containing the information necessary to + * interpolate values from the original control points to evaluated points. The control point + * index is the integer part of each value, and the factor used for interpolating to the next + * control point is the remaining factional part. + */ +Span<float> BezierSpline::evaluated_mappings() const +{ + if (!mapping_cache_dirty_) { + return evaluated_mapping_cache_; + } + + std::lock_guard lock{mapping_cache_mutex_}; + if (!mapping_cache_dirty_) { + return evaluated_mapping_cache_; + } + + const int size = this->size(); + const int eval_size = this->evaluated_points_size(); + evaluated_mapping_cache_.resize(eval_size); + MutableSpan<float> mappings = evaluated_mapping_cache_; + + if (eval_size == 1) { + mappings.first() = 0.0f; + mapping_cache_dirty_ = false; + return mappings; + } + + Span<int> offsets = this->control_point_offsets(); + + calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings); + + mapping_cache_dirty_ = false; + return mappings; +} + +Span<float3> BezierSpline::evaluated_positions() const +{ + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + std::lock_guard lock{position_cache_mutex_}; + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_position_cache_.resize(eval_size); + + MutableSpan<float3> positions = evaluated_position_cache_; + + Span<int> offsets = this->control_point_offsets(); + BLI_assert(offsets.last() <= eval_size); + + const int grain_size = std::max(512 / resolution_, 1); + blender::parallel_for(IndexRange(this->size() - 1), grain_size, [&](IndexRange range) { + for (const int i : range) { + this->evaluate_bezier_segment( + i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); + } + }); + + const int i_last = this->size() - 1; + if (is_cyclic_) { + this->evaluate_bezier_segment(i_last, 0, positions.slice(offsets.last(), resolution_)); + } + else { + /* Since evaluating the bezier segment doesn't add the final point, + * it must be added manually in the non-cyclic case. */ + positions.last() = positions_.last(); + } + + position_cache_dirty_ = false; + return positions; +} + +/** + * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary + * to interpolate data from control points to evaluated points between them. The next control + * point index result will not overflow the size of the vector. + */ +BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( + const float index_factor) const +{ + const int points_len = this->size(); + + if (is_cyclic_) { + if (index_factor < points_len) { + const int index = std::floor(index_factor); + const int next_index = (index < points_len - 1) ? index + 1 : 0; + return InterpolationData{index, next_index, index_factor - index}; + } + return InterpolationData{points_len - 1, 0, 1.0f}; + } + + if (index_factor < points_len - 1) { + const int index = std::floor(index_factor); + const int next_index = index + 1; + return InterpolationData{index, next_index, index_factor - index}; + } + return InterpolationData{points_len - 2, points_len - 1, 1.0f}; +} + +/* Use a spline argument to avoid adding this to the header. */ +template<typename T> +static void interpolate_to_evaluated_points_impl(const BezierSpline &spline, + const blender::VArray<T> &source_data, + MutableSpan<T> result_data) +{ + Span<float> mappings = spline.evaluated_mappings(); + + for (const int i : result_data.index_range()) { + BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor( + mappings[i]); + + const T &value = source_data[interp.control_point_index]; + const T &next_value = source_data[interp.next_control_point_index]; + + result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value); + } +} + +blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const +{ + BLI_assert(source_data.size() == this->size()); + + const int eval_size = this->evaluated_points_size(); + if (eval_size == 1) { + return source_data.shallow_copy(); + } + + blender::fn::GVArrayPtr new_varray; + blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { + Array<T> values(eval_size); + interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values); + new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>( + std::move(values)); + } + }); + + return new_varray; +} diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc new file mode 100644 index 00000000000..37d1232cfeb --- /dev/null +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -0,0 +1,417 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_span.hh" +#include "BLI_virtual_array.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +using blender::Array; +using blender::float3; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + +SplinePtr NURBSpline::copy() const +{ + return std::make_unique<NURBSpline>(*this); +} + +int NURBSpline::size() const +{ + const int size = positions_.size(); + BLI_assert(size == radii_.size()); + BLI_assert(size == tilts_.size()); + BLI_assert(size == weights_.size()); + return size; +} + +int NURBSpline::resolution() const +{ + return resolution_; +} + +void NURBSpline::set_resolution(const int value) +{ + BLI_assert(value > 0); + resolution_ = value; + this->mark_cache_invalid(); +} + +uint8_t NURBSpline::order() const +{ + return order_; +} + +void NURBSpline::set_order(const uint8_t value) +{ + BLI_assert(value >= 2 && value <= 6); + order_ = value; + this->mark_cache_invalid(); +} + +void NURBSpline::add_point(const float3 position, + const float radius, + const float tilt, + const float weight) +{ + positions_.append(position); + radii_.append(radius); + tilts_.append(tilt); + weights_.append(weight); + knots_dirty_ = true; + this->mark_cache_invalid(); +} + +MutableSpan<float3> NURBSpline::positions() +{ + return positions_; +} +Span<float3> NURBSpline::positions() const +{ + return positions_; +} +MutableSpan<float> NURBSpline::radii() +{ + return radii_; +} +Span<float> NURBSpline::radii() const +{ + return radii_; +} +MutableSpan<float> NURBSpline::tilts() +{ + return tilts_; +} +Span<float> NURBSpline::tilts() const +{ + return tilts_; +} +MutableSpan<float> NURBSpline::weights() +{ + return weights_; +} +Span<float> NURBSpline::weights() const +{ + return weights_; +} + +void NURBSpline::mark_cache_invalid() +{ + basis_cache_dirty_ = true; + position_cache_dirty_ = true; + tangent_cache_dirty_ = true; + normal_cache_dirty_ = true; + length_cache_dirty_ = true; +} + +int NURBSpline::evaluated_points_size() const +{ + if (!this->check_valid_size_and_order()) { + return 0; + } + return resolution_ * this->segments_size(); +} + +void NURBSpline::correct_end_tangents() const +{ +} + +bool NURBSpline::check_valid_size_and_order() const +{ + if (this->size() < order_) { + return false; + } + + if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) { + if (order_ == 4) { + if (this->size() < 5) { + return false; + } + } + else if (order_ != 3) { + return false; + } + } + + return true; +} + +int NURBSpline::knots_size() const +{ + const int size = this->size() + order_; + return is_cyclic_ ? size + order_ - 1 : size; +} + +void NURBSpline::calculate_knots() const +{ + const KnotsMode mode = this->knots_mode; + const int length = this->size(); + const int order = order_; + + knots_.resize(this->knots_size()); + + MutableSpan<float> knots = knots_; + + if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) { + for (const int i : knots.index_range()) { + knots[i] = static_cast<float>(i); + } + } + else if (mode == NURBSpline::KnotsMode::EndPoint) { + float k = 0.0f; + for (const int i : IndexRange(1, knots.size())) { + knots[i - 1] = k; + if (i >= order && i <= length) { + k += 1.0f; + } + } + } + else if (mode == NURBSpline::KnotsMode::Bezier) { + BLI_assert(ELEM(order, 3, 4)); + if (order == 3) { + float k = 0.6f; + for (const int i : knots.index_range()) { + if (i >= order && i <= length) { + k += 0.5f; + } + knots[i] = std::floor(k); + } + } + else { + float k = 0.34f; + for (const int i : knots.index_range()) { + knots[i] = std::floor(k); + k += 1.0f / 3.0f; + } + } + } + + if (is_cyclic_) { + const int b = length + order - 1; + if (order > 2) { + for (const int i : IndexRange(1, order - 2)) { + if (knots[b] != knots[b - i]) { + if (i == order - 1) { + knots[length + order - 2] += 1.0f; + break; + } + } + } + } + + int c = order; + for (int i = b; i < this->knots_size(); i++) { + knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]); + c--; + } + } +} + +Span<float> NURBSpline::knots() const +{ + if (!knots_dirty_) { + BLI_assert(knots_.size() == this->size() + order_); + return knots_; + } + + std::lock_guard lock{knots_mutex_}; + if (!knots_dirty_) { + BLI_assert(knots_.size() == this->size() + order_); + return knots_; + } + + this->calculate_knots(); + + knots_dirty_ = false; + + return knots_; +} + +static void calculate_basis_for_point(const float parameter, + const int points_len, + const int order, + Span<float> knots, + MutableSpan<float> basis_buffer, + NURBSpline::BasisCache &basis_cache) +{ + /* Clamp parameter due to floating point inaccuracy. TODO: Look into using doubles. */ + const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]); + + int start = 0; + int end = 0; + for (const int i : IndexRange(points_len + order - 1)) { + const bool knots_equal = knots[i] == knots[i + 1]; + if (knots_equal || t < knots[i] || t > knots[i + 1]) { + basis_buffer[i] = 0.0f; + continue; + } + + basis_buffer[i] = 1.0f; + start = std::max(i - order - 1, 0); + end = i; + basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f); + break; + } + basis_buffer[points_len + order - 1] = 0.0f; + + for (const int i_order : IndexRange(2, order - 1)) { + if (end + i_order >= points_len + order) { + end = points_len + order - 1 - i_order; + } + for (const int i : IndexRange(start, end - start + 1)) { + float new_basis = 0.0f; + if (basis_buffer[i] != 0.0f) { + new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]); + } + + if (basis_buffer[i + 1] != 0.0f) { + new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) / + (knots[i + i_order] - knots[i + 1]); + } + + basis_buffer[i] = new_basis; + } + } + + /* Shrink the range of calculated values to avoid storing unnecessary zeros. */ + while (basis_buffer[start] == 0.0f && start < end) { + start++; + } + while (basis_buffer[end] == 0.0f && end > start) { + end--; + } + + basis_cache.weights.clear(); + basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1)); + basis_cache.start_index = start; +} + +void NURBSpline::calculate_basis_cache() const +{ + if (!basis_cache_dirty_) { + return; + } + + std::lock_guard lock{basis_cache_mutex_}; + if (!basis_cache_dirty_) { + return; + } + + const int points_len = this->size(); + const int eval_size = this->evaluated_points_size(); + BLI_assert(this->evaluated_edges_size() > 0); + basis_cache_.resize(eval_size); + + const int order = this->order(); + Span<float> control_weights = this->weights(); + Span<float> knots = this->knots(); + + MutableSpan<BasisCache> basis_cache(basis_cache_); + + /* This buffer is reused by each basis calculation to store temporary values. + * Theoretically it could be optimized away in the future. */ + Array<float> basis_buffer(this->knots_size()); + + const float start = knots[order - 1]; + const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len]; + const float step = (end - start) / this->evaluated_edges_size(); + float parameter = start; + for (const int i : IndexRange(eval_size)) { + BasisCache &basis = basis_cache[i]; + calculate_basis_for_point( + parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis); + BLI_assert(basis.weights.size() <= order); + + for (const int j : basis.weights.index_range()) { + const int point_index = (basis.start_index + j) % points_len; + basis.weights[j] *= control_weights[point_index]; + } + + parameter += step; + } + + basis_cache_dirty_ = false; +} + +template<typename T> +void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights, + const blender::VArray<T> &source_data, + MutableSpan<T> result_data) +{ + const int points_len = source_data.size(); + BLI_assert(result_data.size() == weights.size()); + blender::attribute_math::DefaultMixer<T> mixer(result_data); + + for (const int i : result_data.index_range()) { + Span<float> point_weights = weights[i].weights; + const int start_index = weights[i].start_index; + + for (const int j : point_weights.index_range()) { + const int point_index = (start_index + j) % points_len; + mixer.mix_in(i, source_data[point_index], point_weights[j]); + } + } + + mixer.finalize(); +} + +blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const +{ + BLI_assert(source_data.size() == this->size()); + + this->calculate_basis_cache(); + Span<BasisCache> weights(basis_cache_); + + blender::fn::GVArrayPtr new_varray; + blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { + Array<T> values(this->evaluated_points_size()); + interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values); + new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>( + std::move(values)); + } + }); + + return new_varray; +} + +Span<float3> NURBSpline::evaluated_positions() const +{ + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + std::lock_guard lock{position_cache_mutex_}; + if (!position_cache_dirty_) { + return evaluated_position_cache_; + } + + const int eval_size = this->evaluated_points_size(); + evaluated_position_cache_.resize(eval_size); + + blender::fn::GVArray_Typed<float3> evaluated_positions{ + this->interpolate_to_evaluated_points(blender::fn::GVArray_For_Span<float3>(positions_))}; + + evaluated_positions->materialize(evaluated_position_cache_); + + position_cache_dirty_ = false; + return evaluated_position_cache_; +} diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc new file mode 100644 index 00000000000..09c578c0503 --- /dev/null +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -0,0 +1,105 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_span.hh" +#include "BLI_virtual_array.hh" + +#include "BKE_spline.hh" + +using blender::float3; +using blender::MutableSpan; +using blender::Span; + +SplinePtr PolySpline::copy() const +{ + return std::make_unique<PolySpline>(*this); +} + +int PolySpline::size() const +{ + const int size = positions_.size(); + BLI_assert(size == radii_.size()); + BLI_assert(size == tilts_.size()); + return size; +} + +void PolySpline::add_point(const float3 position, const float radius, const float tilt) +{ + positions_.append(position); + radii_.append(radius); + tilts_.append(tilt); + this->mark_cache_invalid(); +} + +MutableSpan<float3> PolySpline::positions() +{ + return positions_; +} +Span<float3> PolySpline::positions() const +{ + return positions_; +} +MutableSpan<float> PolySpline::radii() +{ + return radii_; +} +Span<float> PolySpline::radii() const +{ + return radii_; +} +MutableSpan<float> PolySpline::tilts() +{ + return tilts_; +} +Span<float> PolySpline::tilts() const +{ + return tilts_; +} + +void PolySpline::mark_cache_invalid() +{ + tangent_cache_dirty_ = true; + normal_cache_dirty_ = true; + length_cache_dirty_ = true; +} + +int PolySpline::evaluated_points_size() const +{ + return this->size(); +} + +void PolySpline::correct_end_tangents() const +{ +} + +Span<float3> PolySpline::evaluated_positions() const +{ + return this->positions(); +} + +/** + * Poly spline interpolation from control points to evaluated points is a special case, since + * the result data is the same as the input data. This function returns a GVArray that points to + * the original data. Therefore the lifetime of the returned virtual array must not be longer than + * the source data. + */ +blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points( + const blender::fn::GVArray &source_data) const +{ + BLI_assert(source_data.size() == this->size()); + + return source_data.shallow_copy(); +} diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 7b03839f659..c0ce57818d1 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -39,6 +39,7 @@ #include "BLI_utildefines.h" #include "BKE_anim_data.h" +#include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -1003,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr #endif } -static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - Volume *volume_input) +static void volume_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + GeometrySet &geometry_set) { - Volume *volume = volume_input; - /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; @@ -1030,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } - if (mti->modifyVolume) { - /* Ensure we are not modifying the input. */ - if (volume == volume_input) { - volume = BKE_volume_copy_for_eval(volume, true); - } - - Volume *volume_next = mti->modifyVolume(md, &mectx, volume); - - if (volume_next && volume_next != volume) { - /* If the modifier returned a new volume, release the old one. */ - if (volume != volume_input) { - BKE_id_free(nullptr, volume); - } - volume = volume_next; - } + if (mti->modifyGeometrySet) { + mti->modifyGeometrySet(md, &mectx, &geometry_set); } } - - return volume; } void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) @@ -1072,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) } } +static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set) +{ + if (!geometry_set.has<VolumeComponent>()) { + return nullptr; + } + VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); + Volume *volume = volume_component.release(); + if (volume != nullptr) { + /* Add back, but only as read-only non-owning component. */ + volume_component.replace(volume, GeometryOwnershipType::ReadOnly); + } + else { + /* The component was empty, we can remove it. */ + geometry_set.remove<VolumeComponent>(); + } + return volume; +} + void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) { /* Free any evaluated data and restore original data. */ @@ -1079,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob /* Evaluate modifiers. */ Volume *volume = (Volume *)object->data; - Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume); + GeometrySet geometry_set; + geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly); + volume_evaluate_modifiers(depsgraph, scene, object, geometry_set); + + Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set); + + /* If the geometry set did not contain a volume, we still create an empty one. */ + if (volume_eval == nullptr) { + volume_eval = BKE_volume_new_for_eval(volume); + } /* Assign evaluated object. */ - const bool is_owned = (volume != volume_eval); - BKE_object_eval_assign_data(object, &volume_eval->id, is_owned); + const bool eval_is_owned = (volume != volume_eval); + BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned); + object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); } void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath) |