Commit: 60a6fbf5b59911cba54d30bd1105626fcc577875 Author: Hans Goudey Date: Wed Mar 30 10:37:39 2022 -0500 Branches: master https://developer.blender.org/rB60a6fbf5b59911cba54d30bd1105626fcc577875
Curves: Port resample node to the new data-block This commit re-implements the resample curve node to use the new curves type instead of CurveEval. The largest changes come from the need to keep track of offsets into the point attribute arrays, and the fact that the attributes for all curves are stored in a flat array. Another difference is that a bit more of the logic is handled by building of the field network inputs. The idea is to let the field evaluator handle potential optimizations while making the rest of the code simpler. When resampling 1 million small poly curves,the node is about 6 times faster compared to 3.1 on my hardware (500ms to 80ms). This also adds support for Catmull Rom curve inputs. Differential Revision: https://developer.blender.org/D14435 =================================================================== M source/blender/blenlib/BLI_generic_span.hh M source/blender/nodes/geometry/node_geometry_util.hh M source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc M source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc =================================================================== diff --git a/source/blender/blenlib/BLI_generic_span.hh b/source/blender/blenlib/BLI_generic_span.hh index f4f93735e06..4c0bfc83ba8 100644 --- a/source/blender/blenlib/BLI_generic_span.hh +++ b/source/blender/blenlib/BLI_generic_span.hh @@ -164,6 +164,18 @@ class GMutableSpan { { return this->slice(range.start(), range.size()); } + + /** + * Copy all values from another span into this span. This invokes undefined behavior when the + * destination contains uninitialized data and T is not trivially copy constructible. + * The size of both spans is expected to be the same. + */ + void copy_from(GSpan values) + { + BLI_assert(type_ == &values.type()); + BLI_assert(size_ == values.size()); + type_->copy_assign_n(values.data(), data_, size_); + } }; } // namespace blender diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 5b7211e44b4..7af3159bbf8 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -81,4 +81,14 @@ void separate_geometry(GeometrySet &geometry_set, std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); +class SplineLengthFieldInput final : public GeometryFieldInput { + public: + SplineLengthFieldInput(); + GVArray get_varray_for_context(const GeometryComponent &component, + AttributeDomain domain, + IndexMask mask) const final; + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 5a4c2ad1660..54fa56f7419 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array.hh" +#include "BLI_index_mask_ops.hh" +#include "BLI_length_parameterize.hh" #include "BLI_task.hh" #include "BLI_timeit.hh" #include "BKE_attribute_math.hh" -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -23,7 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(100000).supports_field(); b.add_input<decl::Float>(N_("Length")) .default_value(0.1f) - .min(0.001f) + .min(0.01f) .supports_field() .subtype(PROP_DISTANCE); b.add_output<decl::Geometry>(N_("Curve")); @@ -54,195 +56,549 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -struct SampleModeParam { - GeometryNodeCurveResampleMode mode; - std::optional<Field<float>> length; - std::optional<Field<int>> count; - Field<bool> selection; +/** Returns the number of evaluated points in each curve. Used to deselect curves with none. */ +class EvaluatedCountFieldInput final : public GeometryFieldInput { + public: + EvaluatedCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Evaluated Point Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_CURVE && domain == ATTR_DOMAIN_CURVE && + !component.is_empty()) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const Curves &curves_id = *curve_component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + curves.ensure_evaluated_offsets(); + return VArray<int>::ForFunc(curves.curves_num(), [&](const int64_t index) -> int { + return curves.evaluated_points_for_curve(index).size(); + }); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 234905872379865; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const EvaluatedCountFieldInput *>(&other) != nullptr; + } }; -static SplinePtr resample_spline(const Spline &src, const int count) +/** + * Return true if the attribute should be copied/interpolated to the result curves. + * Don't output attributes that correspond to curve types that have no curves in the result. + */ +static bool interpolate_attribute_to_curves(const AttributeIDRef &attribute_id, + const std::array<int, CURVE_TYPES_NUM> &type_counts) { - std::unique_ptr<PolySpline> dst = std::make_unique<PolySpline>(); - Spline::copy_base_settings(src, *dst); - - if (src.evaluated_edges_size() < 1 || count == 1) { - dst->resize(1); - dst->positions().first() = src.positions().first(); - dst->radii().first() = src.radii().first(); - dst->tilts().first() = src.tilts().first(); - - src.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id); - if (dst->attributes.create(attribute_id, meta_data.data_type)) { - std::optional<GMutableSpan> dst_attribute = dst->attributes.get_for_write( - attribute_id); - if (dst_attribute) { - src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data()); - return true; - } - } - BLI_assert_unreachable(); - return false; - }, - ATTR_DOMAIN_POINT); - return dst; + if (!attribute_id.is_named()) { + return true; } + if (ELEM(attribute_id.name(), + "handle_type_left", + "handle_type_right", + "handle_left", + "handle_right")) { + return type_counts[CURVE_TYPE_BEZIER] != 0; + } + if (ELEM(attribute_id.name(), "nurbs_weight")) { + return type_counts[CURVE_TYPE_NURBS] != 0; + } + return true; +} - dst->resize(count); +/** + * Return true if the attribute should be copied to poly curves. + */ +static bool interpolate_attribute_to_poly_curve(const AttributeIDRef &attribute_id) +{ + static const Set<StringRef> no_interpolation{{ + "handle_type_left", + "handle_type_right", + "handle_position_right", + "handle_position_left", + "nurbs_weight", + }}; + return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); +} - Array<float> uniform_samples = src.sample_uniform_index_factors(count); +/** + * Retrieve spans from source and result attributes. + */ +static void retrieve_attribute_spans(const Span<AttributeIDRef> ids, + const CurveComponent &src_component, + CurveComponent &dst_component, + Vector<GSpan> &src, + Vector<GMutableSpan> &dst, + Vector<OutputAttribute> &dst_attributes) +{ + for (const int i : ids.index_range()) { + GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); + BLI_assert(src_attribute); + src.append(src_attribute.get_internal_span()); + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + ids[i], ATTR_DOMAIN_POINT, data_type); + dst.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } +} - src.sample_with_index_factors<float3>( - src.evaluated_positions(), uniform_samples, dst->positions()); +struct AttributesForInterpolation : NonCopyable, NonMovable { + Vector<GSpan> src; + Vector<GMutableSpan> dst; - src.sample_with_index_factors<float>( - src.interpolate_to_evaluated(src.radii()), uniform_samples, dst->radii()); + Vector<OutputAttribute> dst_attributes; - src.sample_with_index_factors<float>( - src.interpolate_to_evaluated(src.tilts()), uniform_samples, dst->tilts()); + Vector<GSpan> src_no_interpolation; + Vector<GMutableSpan> dst_no_interpolation; +}; - src.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> input_attribute = src.attributes.get_for_read(attribute_id); - if (dst->attributes.create(attribute_id, meta_data.data_type)) { - std::optional<GMutableSpan> output_attribute = dst->attributes.get_for_write( - attribute_id); - if (output_attribute) { - src.sample_with_index_factors(src.interpolate_to_evaluated(*input_attribute), - uniform_samples, - *output_attribute); - return true; - } +/** + * Gather a set of all generic attribute IDs to copy to the result curves. + */ +static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, + CurveComponent &dst_component, + AttributesForInterpolation &result) +{ + const Curves &dst_curves_id = *dst_component.get_for_read(); + const bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id.geometry); + const std::array<int, CURVE_TYPES_NUM> type_counts = dst_curves.count_curve_types(); + + VectorSet<AttributeIDRef> ids; + VectorSet<AttributeIDRef> ids_no_interpolation; + src_component.attribute_foreach( + [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs