Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/geometry_component_curve.cc
| Show First 20 Lines • Show All 617 Lines • ▼ Show 20 Lines | for (const int dst_index : mask) { | ||||
| } | } | ||||
| else { | else { | ||||
| new (dst + dst_index) T(src[index_in_spline]); | new (dst + dst_index) T(src[index_in_spline]); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static GVArrayPtr varray_from_initializer(const AttributeInit &initializer, | |||||
| const CustomDataType data_type, | |||||
| const Span<SplinePtr> splines) | |||||
| { | |||||
| switch (initializer.type) { | |||||
| case AttributeInit::Type::Default: | |||||
| /* This function shouldn't be called in this case, since there | |||||
| * is no need to copy anything to the new custom data array. */ | |||||
| BLI_assert_unreachable(); | |||||
| return {}; | |||||
| case AttributeInit::Type::VArray: | |||||
| return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy(); | |||||
| case AttributeInit::Type::MoveArray: | |||||
| int total_size = 0; | |||||
| for (const SplinePtr &spline : splines) { | |||||
| total_size += spline->size(); | |||||
| } | |||||
| return std::make_unique<fn::GVArray_For_GSpan>( | |||||
| GSpan(*bke::custom_data_type_to_cpp_type(data_type), | |||||
| static_cast<const AttributeInitMove &>(initializer).data, | |||||
| total_size)); | |||||
| } | |||||
| BLI_assert_unreachable(); | |||||
| return {}; | |||||
| } | |||||
| static bool create_point_attribute(GeometryComponent &component, | |||||
| const AttributeIDRef &attribute_id, | |||||
| const AttributeInit &initializer, | |||||
| const CustomDataType data_type) | |||||
| { | |||||
| CurveEval *curve = get_curve_from_component_for_write(component); | |||||
| if (curve == nullptr || curve->splines().size() == 0) { | |||||
| return false; | |||||
| } | |||||
| MutableSpan<SplinePtr> splines = curve->splines(); | |||||
| /* First check the one case that allows us to avoid copying the input data. */ | |||||
| if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { | |||||
| void *source_data = static_cast<const AttributeInitMove &>(initializer).data; | |||||
| if (!splines.first()->attributes.create_by_move(attribute_id, data_type, source_data)) { | |||||
| MEM_freeN(source_data); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* Otherwise just create a custom data layer on each of the splines. */ | |||||
| for (const int i : splines.index_range()) { | |||||
| if (!splines[i]->attributes.create(attribute_id, data_type)) { | |||||
| /* If attribute creation fails on one of the splines, we cannot leave the custom data | |||||
| * layers in the previous splines around, so delete them before returning. However, | |||||
| * this is not an expected case. */ | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /* With a default initializer type, we can keep the values at their initial values. */ | |||||
| if (initializer.type == AttributeInit::Type::Default) { | |||||
| return true; | |||||
| } | |||||
| WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); | |||||
| /* We just created the attribute, it should exist. */ | |||||
| BLI_assert(write_attribute); | |||||
| GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, splines); | |||||
| /* TODO: When we can call a variant of #set_all with a virtual array argument, | |||||
| * this theoretically unnecessary materialize step could be removed. */ | |||||
| GVArray_GSpan source_varray_span{*source_varray}; | |||||
| write_attribute.varray->set_all(source_varray_span.data()); | |||||
| if (initializer.type == AttributeInit::Type::MoveArray) { | |||||
| MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool remove_point_attribute(GeometryComponent &component, | |||||
| const AttributeIDRef &attribute_id) | |||||
| { | |||||
| CurveEval *curve = get_curve_from_component_for_write(component); | |||||
| if (curve == nullptr) { | |||||
| return false; | |||||
| } | |||||
| /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ | |||||
| bool layer_freed = false; | |||||
| for (SplinePtr &spline : curve->splines()) { | |||||
| layer_freed = spline->attributes.remove(attribute_id); | |||||
| } | |||||
| return layer_freed; | |||||
| } | |||||
| /** | /** | ||||
| * Virtual array for any control point data accessed with spans and an offset array. | * Virtual array for any control point data accessed with spans and an offset array. | ||||
| */ | */ | ||||
| template<typename T> class VArray_For_SplinePoints : public VArray<T> { | template<typename T> class VArray_For_SplinePoints : public VArray<T> { | ||||
| private: | private: | ||||
| const Array<Span<T>> data_; | const Array<Span<T>> data_; | ||||
| Array<int> offsets_; | Array<int> offsets_; | ||||
| ▲ Show 20 Lines • Show All 341 Lines • ▼ Show 20 Lines | protected: | ||||
| using GetMutableSpan = MutableSpan<T> (*)(Spline &spline); | using GetMutableSpan = MutableSpan<T> (*)(Spline &spline); | ||||
| using UpdateOnWrite = void (*)(Spline &spline); | using UpdateOnWrite = void (*)(Spline &spline); | ||||
| const GetSpan get_span_; | const GetSpan get_span_; | ||||
| const GetMutableSpan get_mutable_span_; | const GetMutableSpan get_mutable_span_; | ||||
| const UpdateOnWrite update_on_write_; | const UpdateOnWrite update_on_write_; | ||||
| public: | public: | ||||
| BuiltinPointAttributeProvider(std::string attribute_name, | BuiltinPointAttributeProvider(std::string attribute_name, | ||||
| const CreatableEnum creatable, | |||||
| const DeletableEnum deletable, | |||||
| const GetSpan get_span, | const GetSpan get_span, | ||||
| const GetMutableSpan get_mutable_span, | const GetMutableSpan get_mutable_span, | ||||
| const UpdateOnWrite update_on_write) | const UpdateOnWrite update_on_write) | ||||
| : BuiltinAttributeProvider(std::move(attribute_name), | : BuiltinAttributeProvider(std::move(attribute_name), | ||||
| ATTR_DOMAIN_POINT, | ATTR_DOMAIN_POINT, | ||||
| bke::cpp_type_to_custom_data_type(CPPType::get<T>()), | bke::cpp_type_to_custom_data_type(CPPType::get<T>()), | ||||
| CreatableEnum::NonCreatable, | creatable, | ||||
| WritableEnum::Writable, | WritableEnum::Writable, | ||||
| DeletableEnum::NonDeletable), | deletable), | ||||
| get_span_(get_span), | get_span_(get_span), | ||||
| get_mutable_span_(get_mutable_span), | get_mutable_span_(get_mutable_span), | ||||
| update_on_write_(update_on_write) | update_on_write_(update_on_write) | ||||
| { | { | ||||
| } | } | ||||
| GVArrayPtr try_get_for_read(const GeometryComponent &component) const override | GVArrayPtr try_get_for_read(const GeometryComponent &component) const override | ||||
| { | { | ||||
| Show All 36 Lines | for (const int i : splines.index_range()) { | ||||
| if (update_on_write_) { | if (update_on_write_) { | ||||
| update_on_write_(*splines[i]); | update_on_write_(*splines[i]); | ||||
| } | } | ||||
| } | } | ||||
| return point_data_gvarray(spans, offsets); | return point_data_gvarray(spans, offsets); | ||||
| } | } | ||||
| bool try_delete(GeometryComponent &UNUSED(component)) const final | bool try_delete(GeometryComponent &component) const final | ||||
| { | { | ||||
| if (deletable_ == DeletableEnum::NonDeletable) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| return remove_point_attribute(component, name_); | |||||
| } | |||||
| bool try_create(GeometryComponent &UNUSED(component), | bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final | ||||
| const AttributeInit &UNUSED(initializer)) const final | |||||
| { | { | ||||
| if (createable_ == CreatableEnum::NonCreatable) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| return create_point_attribute(component, name_, initializer, CD_PROP_INT32); | |||||
| } | |||||
| bool exists(const GeometryComponent &component) const final | bool exists(const GeometryComponent &component) const final | ||||
| { | { | ||||
| const CurveEval *curve = get_curve_from_component_for_read(component); | const CurveEval *curve = get_curve_from_component_for_read(component); | ||||
| if (curve == nullptr) { | if (curve == nullptr) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| Span<SplinePtr> splines = curve->splines(); | Span<SplinePtr> splines = curve->splines(); | ||||
| if (splines.size() == 0) { | if (splines.size() == 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (!curve->splines().first()->attributes.get_for_read(name_)) { | |||||
| return false; | |||||
| } | |||||
| bool has_point = false; | bool has_point = false; | ||||
| for (const SplinePtr &spline : curve->splines()) { | for (const SplinePtr &spline : curve->splines()) { | ||||
| if (spline->size() != 0) { | if (spline->size() != 0) { | ||||
| has_point = true; | has_point = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| Show All 10 Lines | |||||
| * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the | * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the | ||||
| * positions is more clear. | * positions is more clear. | ||||
| */ | */ | ||||
| class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> { | class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> { | ||||
| public: | public: | ||||
| PositionAttributeProvider() | PositionAttributeProvider() | ||||
| : BuiltinPointAttributeProvider( | : BuiltinPointAttributeProvider( | ||||
| "position", | "position", | ||||
| BuiltinAttributeProvider::NonCreatable, | |||||
| BuiltinAttributeProvider::NonDeletable, | |||||
| [](const Spline &spline) { return spline.positions(); }, | [](const Spline &spline) { return spline.positions(); }, | ||||
| [](Spline &spline) { return spline.positions(); }, | [](Spline &spline) { return spline.positions(); }, | ||||
| [](Spline &spline) { spline.mark_cache_invalid(); }) | [](Spline &spline) { spline.mark_cache_invalid(); }) | ||||
| { | { | ||||
| } | } | ||||
| GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final | GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { | ||||
| } | } | ||||
| attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT}; | attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT}; | ||||
| }); | }); | ||||
| return attribute; | return attribute; | ||||
| } | } | ||||
| bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final | bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final | ||||
| { | { | ||||
| CurveEval *curve = get_curve_from_component_for_write(component); | return remove_point_attribute(component, attribute_id); | ||||
| if (curve == nullptr) { | |||||
| return false; | |||||
| } | |||||
| /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ | |||||
| bool layer_freed = false; | |||||
| for (SplinePtr &spline : curve->splines()) { | |||||
| layer_freed = spline->attributes.remove(attribute_id); | |||||
| } | |||||
| return layer_freed; | |||||
| } | |||||
| static GVArrayPtr varray_from_initializer(const AttributeInit &initializer, | |||||
| const CustomDataType data_type, | |||||
| const int total_size) | |||||
| { | |||||
| switch (initializer.type) { | |||||
| case AttributeInit::Type::Default: | |||||
| /* This function shouldn't be called in this case, since there | |||||
| * is no need to copy anything to the new custom data array. */ | |||||
| BLI_assert_unreachable(); | |||||
| return {}; | |||||
| case AttributeInit::Type::VArray: | |||||
| return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy(); | |||||
| case AttributeInit::Type::MoveArray: | |||||
| return std::make_unique<fn::GVArray_For_GSpan>( | |||||
| GSpan(*bke::custom_data_type_to_cpp_type(data_type), | |||||
| static_cast<const AttributeInitMove &>(initializer).data, | |||||
| total_size)); | |||||
| } | |||||
| BLI_assert_unreachable(); | |||||
| return {}; | |||||
| } | } | ||||
| bool try_create(GeometryComponent &component, | bool try_create(GeometryComponent &component, | ||||
| const AttributeIDRef &attribute_id, | const AttributeIDRef &attribute_id, | ||||
| const AttributeDomain domain, | const AttributeDomain domain, | ||||
| const CustomDataType data_type, | const CustomDataType data_type, | ||||
| const AttributeInit &initializer) const final | const AttributeInit &initializer) const final | ||||
| { | { | ||||
| BLI_assert(this->type_is_supported(data_type)); | BLI_assert(this->type_is_supported(data_type)); | ||||
| if (domain != ATTR_DOMAIN_POINT) { | if (domain != ATTR_DOMAIN_POINT) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| CurveEval *curve = get_curve_from_component_for_write(component); | return create_point_attribute(component, attribute_id, initializer, data_type); | ||||
| if (curve == nullptr || curve->splines().size() == 0) { | |||||
| return false; | |||||
| } | |||||
| MutableSpan<SplinePtr> splines = curve->splines(); | |||||
| /* First check the one case that allows us to avoid copying the input data. */ | |||||
| if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { | |||||
| void *source_data = static_cast<const AttributeInitMove &>(initializer).data; | |||||
| if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) { | |||||
| MEM_freeN(source_data); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* Otherwise just create a custom data layer on each of the splines. */ | |||||
| for (const int i : splines.index_range()) { | |||||
| if (!splines[i]->attributes.create(attribute_id, data_type)) { | |||||
| /* If attribute creation fails on one of the splines, we cannot leave the custom data | |||||
| * layers in the previous splines around, so delete them before returning. However, | |||||
| * this is not an expected case. */ | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /* With a default initializer type, we can keep the values at their initial values. */ | |||||
| if (initializer.type == AttributeInit::Type::Default) { | |||||
| return true; | |||||
| } | |||||
| WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id); | |||||
| /* We just created the attribute, it should exist. */ | |||||
| BLI_assert(write_attribute); | |||||
| const int total_size = curve->control_point_offsets().last(); | |||||
| GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size); | |||||
| /* TODO: When we can call a variant of #set_all with a virtual array argument, | |||||
| * this theoretically unnecessary materialize step could be removed. */ | |||||
| GVArray_GSpan source_varray_span{*source_varray}; | |||||
| write_attribute.varray->set_all(source_varray_span.data()); | |||||
| if (initializer.type == AttributeInit::Type::MoveArray) { | |||||
| MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data); | |||||
| } | |||||
| return true; | |||||
| } | } | ||||
| bool foreach_attribute(const GeometryComponent &component, | bool foreach_attribute(const GeometryComponent &component, | ||||
| const AttributeForeachCallback callback) const final | const AttributeForeachCallback callback) const final | ||||
| { | { | ||||
| const CurveEval *curve = get_curve_from_component_for_read(component); | const CurveEval *curve = get_curve_from_component_for_read(component); | ||||
| if (curve == nullptr || curve->splines().size() == 0) { | if (curve == nullptr || curve->splines().size() == 0) { | ||||
| return false; | return false; | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | static ComponentAttributeProviders create_attribute_providers_for_curve() | ||||
| static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE, | static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE, | ||||
| spline_custom_data_access); | spline_custom_data_access); | ||||
| static PositionAttributeProvider position; | static PositionAttributeProvider position; | ||||
| static BezierHandleAttributeProvider handles_start(false); | static BezierHandleAttributeProvider handles_start(false); | ||||
| static BezierHandleAttributeProvider handles_end(true); | static BezierHandleAttributeProvider handles_end(true); | ||||
| static BuiltinPointAttributeProvider<int> id( | |||||
| "id", | |||||
| BuiltinAttributeProvider::Creatable, | |||||
| BuiltinAttributeProvider::Deletable, | |||||
| [](const Spline &spline) { | |||||
| std::optional<GSpan> span = spline.attributes.get_for_read("id"); | |||||
| return span ? span->typed<int>() : Span<int>(); | |||||
| }, | |||||
| [](Spline &spline) { | |||||
| std::optional<GMutableSpan> span = spline.attributes.get_for_write("id"); | |||||
| return span ? span->typed<int>() : MutableSpan<int>(); | |||||
| }, | |||||
| {}); | |||||
| static BuiltinPointAttributeProvider<float> radius( | static BuiltinPointAttributeProvider<float> radius( | ||||
| "radius", | "radius", | ||||
| BuiltinAttributeProvider::NonCreatable, | |||||
| BuiltinAttributeProvider::NonDeletable, | |||||
| [](const Spline &spline) { return spline.radii(); }, | [](const Spline &spline) { return spline.radii(); }, | ||||
| [](Spline &spline) { return spline.radii(); }, | [](Spline &spline) { return spline.radii(); }, | ||||
| nullptr); | nullptr); | ||||
| static BuiltinPointAttributeProvider<float> tilt( | static BuiltinPointAttributeProvider<float> tilt( | ||||
| "tilt", | "tilt", | ||||
| BuiltinAttributeProvider::NonCreatable, | |||||
| BuiltinAttributeProvider::NonDeletable, | |||||
| [](const Spline &spline) { return spline.tilts(); }, | [](const Spline &spline) { return spline.tilts(); }, | ||||
| [](Spline &spline) { return spline.tilts(); }, | [](Spline &spline) { return spline.tilts(); }, | ||||
| [](Spline &spline) { spline.mark_cache_invalid(); }); | [](Spline &spline) { spline.mark_cache_invalid(); }); | ||||
| static DynamicPointAttributeProvider point_custom_data; | static DynamicPointAttributeProvider point_custom_data; | ||||
| return ComponentAttributeProviders( | return ComponentAttributeProviders( | ||||
| {&position, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, | {&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, | ||||
| {&spline_custom_data, &point_custom_data}); | {&spline_custom_data, &point_custom_data}); | ||||
| } | } | ||||
| } // namespace blender::bke | } // namespace blender::bke | ||||
| const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const | const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const | ||||
| { | { | ||||
| static blender::bke::ComponentAttributeProviders providers = | static blender::bke::ComponentAttributeProviders providers = | ||||
| blender::bke::create_attribute_providers_for_curve(); | blender::bke::create_attribute_providers_for_curve(); | ||||
| return &providers; | return &providers; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||