/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_task.hh" #include "DNA_ID_enums.h" #include "DNA_curve_types.h" #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curve.h" #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "attribute_access_intern.hh" using blender::GVArray; /* -------------------------------------------------------------------- */ /** \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 (curves_ != nullptr) { new_component->curves_ = BKE_curves_copy_for_eval(curves_, false); new_component->ownership_ = GeometryOwnershipType::Owned; } return new_component; } void CurveComponent::clear() { BLI_assert(this->is_mutable()); if (curves_ != nullptr) { if (ownership_ == GeometryOwnershipType::Owned) { BKE_id_free(nullptr, curves_); } if (curve_for_render_ != nullptr) { /* The curve created by this component should not have any edit mode data. */ BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); BKE_id_free(nullptr, curve_for_render_); curve_for_render_ = nullptr; } curves_ = nullptr; } } bool CurveComponent::has_curves() const { return curves_ != nullptr; } void CurveComponent::replace(Curves *curves, GeometryOwnershipType ownership) { BLI_assert(this->is_mutable()); this->clear(); curves_ = curves; ownership_ = ownership; } Curves *CurveComponent::release() { BLI_assert(this->is_mutable()); Curves *curves = curves_; curves_ = nullptr; return curves; } const Curves *CurveComponent::get_for_read() const { return curves_; } Curves *CurveComponent::get_for_write() { BLI_assert(this->is_mutable()); if (ownership_ == GeometryOwnershipType::ReadOnly) { curves_ = BKE_curves_copy_for_eval(curves_, false); ownership_ = GeometryOwnershipType::Owned; } return curves_; } bool CurveComponent::is_empty() const { return curves_ == 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) { curves_ = BKE_curves_copy_for_eval(curves_, false); ownership_ = GeometryOwnershipType::Owned; } } const Curve *CurveComponent::get_curve_for_render() const { if (curves_ == nullptr) { return nullptr; } if (curve_for_render_ != nullptr) { return curve_for_render_; } std::lock_guard lock{curve_for_render_mutex_}; if (curve_for_render_ != nullptr) { return curve_for_render_; } curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU_LEGACY, nullptr); curve_for_render_->curve_eval = curves_; return curve_for_render_; } /** \} */ namespace blender::bke { /* -------------------------------------------------------------------- */ /** \name Curve Normals Access * \{ */ static Array curve_normal_point_domain(const bke::CurvesGeometry &curves) { const VArray types = curves.curve_types(); const VArray resolutions = curves.resolution(); const VArray curves_cyclic = curves.cyclic(); const Span positions = curves.positions(); const VArray normal_modes = curves.normal_mode(); const Span evaluated_normals = curves.evaluated_normals(); Array results(curves.points_num()); threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) { Vector nurbs_tangents; for (const int i_curve : range) { const IndexRange points = curves.points_for_curve(i_curve); const IndexRange evaluated_points = curves.evaluated_points_for_curve(i_curve); MutableSpan curve_normals = results.as_mutable_span().slice(points); switch (types[i_curve]) { case CURVE_TYPE_CATMULL_ROM: { const Span normals = evaluated_normals.slice(evaluated_points); const int resolution = resolutions[i_curve]; for (const int i : IndexRange(points.size())) { curve_normals[i] = normals[resolution * i]; } break; } case CURVE_TYPE_POLY: curve_normals.copy_from(evaluated_normals.slice(evaluated_points)); break; case CURVE_TYPE_BEZIER: { const Span normals = evaluated_normals.slice(evaluated_points); curve_normals.first() = normals.first(); const Span offsets = curves.bezier_evaluated_offsets_for_curve(i_curve); for (const int i : IndexRange(points.size()).drop_front(1)) { curve_normals[i] = normals[offsets[i - 1]]; } break; } case CURVE_TYPE_NURBS: { /* For NURBS curves there is no obvious correspondence between specific evaluated points * and control points, so normals are determined by treating them as poly curves. */ nurbs_tangents.clear(); nurbs_tangents.resize(points.size()); const bool cyclic = curves_cyclic[i_curve]; const Span curve_positions = positions.slice(points); bke::curves::poly::calculate_tangents(curve_positions, cyclic, nurbs_tangents); switch (NormalMode(normal_modes[i_curve])) { case NORMAL_MODE_Z_UP: bke::curves::poly::calculate_normals_z_up(nurbs_tangents, curve_normals); break; case NORMAL_MODE_MINIMUM_TWIST: bke::curves::poly::calculate_normals_minimum(nurbs_tangents, cyclic, curve_normals); break; } break; } } } }); return results; } VArray curve_normals_varray(const CurveComponent &component, const eAttrDomain domain) { if (!component.has_curves()) { return {}; } const Curves &curves_id = *component.get_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); const VArray types = curves.curve_types(); if (curves.is_single_type(CURVE_TYPE_POLY)) { return component.attribute_try_adapt_domain( VArray::ForSpan(curves.evaluated_normals()), ATTR_DOMAIN_POINT, domain); } Array normals = curve_normal_point_domain(curves); if (domain == ATTR_DOMAIN_POINT) { return VArray::ForContainer(std::move(normals)); } if (domain == ATTR_DOMAIN_CURVE) { return component.attribute_try_adapt_domain( VArray::ForContainer(std::move(normals)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); } return nullptr; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Curve Length Field Input * \{ */ static VArray construct_curve_length_gvarray(const CurveComponent &component, const eAttrDomain domain) { if (!component.has_curves()) { return {}; } const Curves &curves_id = *component.get_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); curves.ensure_evaluated_lengths(); VArray cyclic = curves.cyclic(); VArray lengths = VArray::ForFunc( curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { return curves.evaluated_length_total_for_curve(index, cyclic[index]); }); if (domain == ATTR_DOMAIN_CURVE) { return lengths; } if (domain == ATTR_DOMAIN_POINT) { return component.attribute_try_adapt_domain( std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } return {}; } CurveLengthFieldInput::CurveLengthFieldInput() : GeometryFieldInput(CPPType::get(), "Spline Length node") { category_ = Category::Generated; } GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component, const eAttrDomain domain, IndexMask UNUSED(mask)) const { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast(component); return construct_curve_length_gvarray(curve_component, domain); } return {}; } uint64_t CurveLengthFieldInput::hash() const { /* Some random constant hash. */ return 3549623580; } bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const { return dynamic_cast(&other) != nullptr; } /** \} */ } // namespace blender::bke /* -------------------------------------------------------------------- */ /** \name Attribute Access Helper Functions * \{ */ int CurveComponent::attribute_domain_num(const eAttrDomain domain) const { if (curves_ == nullptr) { return 0; } const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_->geometry); if (domain == ATTR_DOMAIN_POINT) { return curves.points_num(); } if (domain == ATTR_DOMAIN_CURVE) { return curves.curves_num(); } return 0; } GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, const eAttrDomain from_domain, const eAttrDomain to_domain) const { return blender::bke::CurvesGeometry::wrap(curves_->geometry) .adapt_domain(varray, from_domain, to_domain); } static Curves *get_curves_from_component_for_write(GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); CurveComponent &curve_component = static_cast(component); return curve_component.get_for_write(); } static const Curves *get_curves_from_component_for_read(const GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); const CurveComponent &curve_component = static_cast(component); return curve_component.get_for_read(); } static void tag_component_topology_changed(GeometryComponent &component) { Curves *curves = get_curves_from_component_for_write(component); if (curves) { blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); } } static void tag_component_curve_types_changed(GeometryComponent &component) { Curves *curves = get_curves_from_component_for_write(component); if (curves) { blender::bke::CurvesGeometry::wrap(curves->geometry).update_curve_types(); blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); } } static void tag_component_positions_changed(GeometryComponent &component) { Curves *curves = get_curves_from_component_for_write(component); if (curves) { blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed(); } } static void tag_component_normals_changed(GeometryComponent &component) { Curves *curves = get_curves_from_component_for_write(component); if (curves) { blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed(); } } /** \} */ namespace blender::bke { /* -------------------------------------------------------------------- */ /** \name Attribute Provider Declaration * \{ */ /** * In this function all the attribute providers for a curves 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 CustomDataAccessInfo curve_access = { [](GeometryComponent &component) -> CustomData * { Curves *curves = get_curves_from_component_for_write(component); return curves ? &curves->geometry.curve_data : nullptr; }, [](const GeometryComponent &component) -> const CustomData * { const Curves *curves = get_curves_from_component_for_read(component); return curves ? &curves->geometry.curve_data : nullptr; }, [](GeometryComponent &component) { Curves *curves = get_curves_from_component_for_write(component); if (curves) { blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); } }}; static CustomDataAccessInfo point_access = { [](GeometryComponent &component) -> CustomData * { Curves *curves = get_curves_from_component_for_write(component); return curves ? &curves->geometry.point_data : nullptr; }, [](const GeometryComponent &component) -> const CustomData * { const Curves *curves = get_curves_from_component_for_read(component); return curves ? &curves->geometry.point_data : nullptr; }, [](GeometryComponent &component) { Curves *curves = get_curves_from_component_for_write(component); if (curves) { blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_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, make_array_write_attribute, tag_component_positions_changed); 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, make_array_write_attribute, nullptr); static BuiltinCustomDataLayerProvider id("id", ATTR_DOMAIN_POINT, CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, nullptr); static BuiltinCustomDataLayerProvider tilt("tilt", ATTR_DOMAIN_POINT, CD_PROP_FLOAT, CD_PROP_FLOAT, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_normals_changed); static BuiltinCustomDataLayerProvider handle_right("handle_right", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_positions_changed); static BuiltinCustomDataLayerProvider handle_left("handle_left", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_positions_changed); static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right", ATTR_DOMAIN_POINT, CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_topology_changed); static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left", ATTR_DOMAIN_POINT, CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_topology_changed); static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight", ATTR_DOMAIN_POINT, CD_PROP_FLOAT, CD_PROP_FLOAT, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_positions_changed); static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order", ATTR_DOMAIN_CURVE, CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, make_array_write_attribute, tag_component_topology_changed); static BuiltinCustomDataLayerProvider normal_mode("normal_mode", ATTR_DOMAIN_CURVE, CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, make_array_write_attribute, tag_component_normals_changed); static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode", ATTR_DOMAIN_CURVE, CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, make_array_write_attribute, tag_component_topology_changed); static BuiltinCustomDataLayerProvider curve_type("curve_type", ATTR_DOMAIN_CURVE, CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, make_array_write_attribute, tag_component_curve_types_changed); static BuiltinCustomDataLayerProvider resolution("resolution", ATTR_DOMAIN_CURVE, CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, make_array_write_attribute, tag_component_topology_changed); static BuiltinCustomDataLayerProvider cyclic("cyclic", ATTR_DOMAIN_CURVE, CD_PROP_BOOL, CD_PROP_BOOL, BuiltinAttributeProvider::Creatable, BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, make_array_write_attribute, tag_component_topology_changed); static CustomDataAttributeProvider curve_custom_data(ATTR_DOMAIN_CURVE, curve_access); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); return ComponentAttributeProviders({&position, &radius, &id, &tilt, &handle_right, &handle_left, &handle_type_right, &handle_type_left, &normal_mode, &nurbs_order, &nurbs_knots_mode, &nurbs_weight, &curve_type, &resolution, &cyclic}, {&curve_custom_data, &point_custom_data}); } /** \} */ } // 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; }