diff options
Diffstat (limited to 'source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc')
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc | 322 |
1 files changed, 14 insertions, 308 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 4d8745bf79e..bd44adb35a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -1,10 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_task.hh" -#include "BLI_timeit.hh" +#include "BKE_curves.hh" -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" +#include "GEO_subdivide_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -26,302 +24,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static Array<int> get_subdivided_offsets(const Spline &spline, - const VArray<int> &cuts, - const int spline_offset) -{ - Array<int> offsets(spline.segments_num() + 1); - int offset = 0; - for (const int i : IndexRange(spline.segments_num())) { - offsets[i] = offset; - offset = offset + std::max(cuts[spline_offset + i], 0) + 1; - } - offsets.last() = offset; - return offsets; -} - -template<typename T> -static void subdivide_attribute(Span<T> src, - const Span<int> offsets, - const bool is_cyclic, - MutableSpan<T> dst) -{ - const int src_num = src.size(); - threading::parallel_for(IndexRange(src_num - 1), 1024, [&](IndexRange range) { - for (const int i : range) { - const int cuts = offsets[i + 1] - offsets[i]; - dst[offsets[i]] = src[i]; - const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; - for (const int cut : IndexRange(cuts)) { - const float factor = cut * factor_delta; - dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); - } - } - }); - - if (is_cyclic) { - const int i = src_num - 1; - const int cuts = offsets[i + 1] - offsets[i]; - dst[offsets[i]] = src.last(); - const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; - for (const int cut : IndexRange(cuts)) { - const float factor = cut * factor_delta; - dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); - } - } - else { - dst.last() = src.last(); - } -} - -/** - * In order to generate a Bezier spline with the same shape as the input spline, apply the - * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the - * previous result point's right handle and the left handle at the end of the segment. - * - * \note Non-vector segments in the result spline are given free handles. This could possibly be - * improved with another pass that sets handles to aligned where possible, but currently that does - * not provide much benefit for the increased complexity. - */ -static void subdivide_bezier_segment(const BezierSpline &src, - const int index, - const int offset, - const int result_num, - Span<float3> src_positions, - Span<float3> src_handles_left, - Span<float3> src_handles_right, - MutableSpan<float3> dst_positions, - MutableSpan<float3> dst_handles_left, - MutableSpan<float3> dst_handles_right, - MutableSpan<int8_t> dst_type_left, - MutableSpan<int8_t> dst_type_right) -{ - const bool is_last_cyclic_segment = index == (src.size() - 1); - const int next_index = is_last_cyclic_segment ? 0 : index + 1; - - /* The first point in the segment is always copied. */ - dst_positions[offset] = src_positions[index]; - - if (src.segment_is_vector(index)) { - if (is_last_cyclic_segment) { - dst_type_left.first() = BEZIER_HANDLE_VECTOR; - } - dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR); - dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR); - - const float factor_delta = 1.0f / result_num; - for (const int cut : IndexRange(result_num)) { - const float factor = cut * factor_delta; - dst_positions[offset + cut] = attribute_math::mix2( - factor, src_positions[index], src_positions[next_index]); - } - } - else { - if (is_last_cyclic_segment) { - dst_type_left.first() = BEZIER_HANDLE_FREE; - } - dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE); - dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE); - - const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num; - - /* Create a Bezier segment to update iteratively for every subdivision - * and references to the meaningful values for ease of use. */ - BezierSpline temp; - temp.resize(2); - float3 &segment_start = temp.positions().first(); - float3 &segment_end = temp.positions().last(); - float3 &handle_prev = temp.handle_positions_right().first(); - float3 &handle_next = temp.handle_positions_left().last(); - segment_start = src_positions[index]; - segment_end = src_positions[next_index]; - handle_prev = src_handles_right[index]; - handle_next = src_handles_left[next_index]; - - for (const int cut : IndexRange(result_num - 1)) { - const float parameter = 1.0f / (result_num - cut); - const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); - - /* Copy relevant temporary data to the result. */ - dst_handles_right[offset + cut] = insert.handle_prev; - dst_handles_left[offset + cut + 1] = insert.left_handle; - dst_positions[offset + cut + 1] = insert.position; - - /* Update the segment to prepare it for the next subdivision. */ - segment_start = insert.position; - handle_prev = insert.right_handle; - handle_next = insert.handle_next; - } - - /* Copy the handles for the last segment from the temporary spline. */ - dst_handles_right[offset + result_num - 1] = handle_prev; - dst_handles_left[i_segment_last] = handle_next; - } -} - -static void subdivide_bezier_spline(const BezierSpline &src, - const Span<int> offsets, - BezierSpline &dst) -{ - Span<float3> src_positions = src.positions(); - Span<float3> src_handles_left = src.handle_positions_left(); - Span<float3> src_handles_right = src.handle_positions_right(); - MutableSpan<float3> dst_positions = dst.positions(); - MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); - MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); - MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); - - threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { - for (const int i : range) { - subdivide_bezier_segment(src, - i, - offsets[i], - offsets[i + 1] - offsets[i], - src_positions, - src_handles_left, - src_handles_right, - dst_positions, - dst_handles_left, - dst_handles_right, - dst_type_left, - dst_type_right); - } - }); - - if (src.is_cyclic()) { - const int i_last = src.size() - 1; - subdivide_bezier_segment(src, - i_last, - offsets[i_last], - offsets.last() - offsets[i_last], - src_positions, - src_handles_left, - src_handles_right, - dst_positions, - dst_handles_left, - dst_handles_right, - dst_type_left, - dst_type_right); - } - else { - dst_positions.last() = src_positions.last(); - dst_type_left.first() = src.handle_types_left().first(); - dst_type_right.last() = src.handle_types_right().last(); - dst_handles_left.first() = src_handles_left.first(); - dst_handles_right.last() = src_handles_right.last(); - } -} - -static void subdivide_builtin_attributes(const Spline &src_spline, - const Span<int> offsets, - Spline &dst_spline) -{ - const bool is_cyclic = src_spline.is_cyclic(); - subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); - subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); - switch (src_spline.type()) { - case CURVE_TYPE_POLY: { - const PolySpline &src = static_cast<const PolySpline &>(src_spline); - PolySpline &dst = static_cast<PolySpline &>(dst_spline); - subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); - break; - } - case CURVE_TYPE_BEZIER: { - const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); - BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); - subdivide_bezier_spline(src, offsets, dst); - dst.mark_cache_invalid(); - break; - } - case CURVE_TYPE_NURBS: { - const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); - NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); - subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); - subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); - break; - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - break; - } - } -} - -static void subdivide_dynamic_attributes(const Spline &src_spline, - const Span<int> offsets, - Spline &dst_spline) -{ - const bool is_cyclic = src_spline.is_cyclic(); - src_spline.attributes.foreach_attribute( - [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - - if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { - /* Since the source spline of the same type had the attribute, adding it should work. */ - BLI_assert_unreachable(); - } - - std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id); - BLI_assert(dst); - - attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { - using T = decltype(dummy); - subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); -} - -static SplinePtr subdivide_spline(const Spline &spline, - const VArray<int> &cuts, - const int spline_offset) -{ - if (spline.size() <= 1) { - return spline.copy(); - } - - /* Since we expect to access each value many times, it should be worth it to make sure count - * of cuts is a real span (especially considering the note below). Using the offset at each - * point facilitates subdividing in parallel later. */ - Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); - const int result_num = offsets.last() + int(!spline.is_cyclic()); - SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(result_num); - subdivide_builtin_attributes(spline, offsets, *new_spline); - subdivide_dynamic_attributes(spline, offsets, *new_spline); - return new_spline; -} - -/** - * \note Passing the virtual array for the entire spline is possibly quite inefficient here when - * the attribute was on the point domain and stored separately for each spline already, and it - * prevents some other optimizations like skipping splines with a single attribute value of < 1. - * However, it allows the node to access builtin attribute easily, so it the makes most sense this - * way until the attribute API is refactored. - */ -static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve, - const VArray<int> &cuts) -{ - const Array<int> control_point_offsets = input_curve.control_point_offsets(); - const Span<SplinePtr> input_splines = input_curve.splines(); - - std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); - output_curve->resize(input_splines.size()); - output_curve->attributes = input_curve.attributes; - MutableSpan<SplinePtr> output_splines = output_curve->splines(); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]); - } - }); - - return output_curve; -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -333,20 +35,24 @@ static void node_geo_exec(GeoNodeExecParams params) } const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + const Curves &src_curves_id = *component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - fn::FieldEvaluator evaluator{field_context, domain_num}; + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(cuts_field); evaluator.evaluate(); - const VArray<int> &cuts = evaluator.get_evaluated<int>(0); - + const VArray<int> cuts = evaluator.get_evaluated<int>(0); if (cuts.is_single() && cuts.get_internal_single() < 1) { return; } - std::unique_ptr<CurveEval> output_curve = subdivide_curve( - *curves_to_curve_eval(*component.get_for_read()), cuts); - geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); + + bke::CurvesGeometry dst_curves = geometry::subdivide_curves( + src_curves, src_curves.curves_range(), cuts); + + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); }); params.set_output("Curve", geometry_set); } |