diff options
author | Hans Goudey <h.goudey@me.com> | 2022-09-18 22:56:15 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-09-18 22:56:24 +0300 |
commit | 7536abbe16bd8672ca38e2b866f9a9585d054713 (patch) | |
tree | 0989cabe13fe970eb8b14cdf84ea943bf6aba746 /source | |
parent | cf56b8be37c071f7d7fa22e3ab84029f4c895863 (diff) |
Curves: Port Curve to Points node to the new data-block
This is the last node to use the `CurveEval` type. Since the curve to
points node is basically the same as the resample node, now it just
reuses the resample code and moves the curve point `CustomData` to a
new point cloud at the end. I had to add support for sampling tangents
and normals to the resampling.
There is one behavior change: If the radius attribute doesn't exist,
the node won't set the radius to 1 for the output point cloud anymore.
Instead, the default radius for point clouds will be used.
That issue was similar to T99814.
Differential Revision: https://developer.blender.org/D16008
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_curves_utils.hh | 20 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curves_utils.cc | 15 | ||||
-rw-r--r-- | source/blender/geometry/GEO_resample_curves.hh | 15 | ||||
-rw-r--r-- | source/blender/geometry/intern/resample_curves.cc | 183 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc | 396 |
5 files changed, 277 insertions, 352 deletions
diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh index 7d81847f4c1..f9155023db7 100644 --- a/source/blender/blenkernel/BKE_curves_utils.hh +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -326,8 +326,8 @@ void copy_point_data(const CurvesGeometry &src_curves, template<typename T> void copy_point_data(const CurvesGeometry &src_curves, const CurvesGeometry &dst_curves, - const IndexMask src_curve_selection, - const Span<T> src, + IndexMask src_curve_selection, + Span<T> src, MutableSpan<T> dst) { copy_point_data(src_curves, dst_curves, src_curve_selection, GSpan(src), GMutableSpan(dst)); @@ -340,13 +340,27 @@ void fill_points(const CurvesGeometry &curves, template<typename T> void fill_points(const CurvesGeometry &curves, - const IndexMask curve_selection, + IndexMask curve_selection, const T &value, MutableSpan<T> dst) { fill_points(curves, curve_selection, &value, dst); } +void fill_points(const CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + GPointer value, + GMutableSpan dst); + +template<typename T> +void fill_points(const CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + const T &value, + MutableSpan<T> dst) +{ + fill_points(curves, curve_ranges, &value, dst); +} + /** * Copy only the information on the point domain, but not the offsets or any point attributes, * meant for operations that change the number of points but not the number of curves. diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc index d98832e796c..f5a69a995a3 100644 --- a/source/blender/blenkernel/intern/curves_utils.cc +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -84,6 +84,21 @@ void fill_points(const CurvesGeometry &curves, }); } +void fill_points(const CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + GPointer value, + GMutableSpan dst) +{ + BLI_assert(*value.type() == dst.type()); + const CPPType &type = dst.type(); + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange points = curves.points_for_curves(range); + type.fill_assign_n(value.get(), dst.slice(points).data(), points.size()); + } + }); +} + bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves) { bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh index 7ecfb5c26ec..35365167eba 100644 --- a/source/blender/geometry/GEO_resample_curves.hh +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -4,12 +4,18 @@ #include "FN_field.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_curves.hh" namespace blender::geometry { using bke::CurvesGeometry; +struct ResampleCurvesOutputAttributeIDs { + bke::AttributeIDRef tangent_id; + bke::AttributeIDRef normal_id; +}; + /** * Create new curves where the selected curves have been resampled with a number of uniform-length * samples defined by the count field. Interpolate attributes to the result, with an accuracy that @@ -19,7 +25,8 @@ using bke::CurvesGeometry; */ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<int> &count_field); + const fn::Field<int> &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); /** * Create new curves resampled to make each segment have the length specified by the @@ -28,12 +35,14 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, */ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<float> &segment_length_field); + const fn::Field<float> &segment_length_field, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); /** * Evaluate each selected curve to its implicit evaluated points. */ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, - const fn::Field<bool> &selection_field); + const fn::Field<bool> &selection_field, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); } // namespace blender::geometry diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index d5560a95a18..a7f6ac16f8d 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -116,14 +116,21 @@ struct AttributesForInterpolation : NonCopyable, NonMovable { Vector<GSpan> src_no_interpolation; Vector<GMutableSpan> dst_no_interpolation; + + Span<float3> src_evaluated_tangents; + Span<float3> src_evaluated_normals; + MutableSpan<float3> dst_tangents; + MutableSpan<float3> dst_normals; }; /** * Gather a set of all generic attribute IDs to copy to the result curves. */ -static void gather_point_attributes_to_interpolate(const CurvesGeometry &src_curves, - CurvesGeometry &dst_curves, - AttributesForInterpolation &result) +static void gather_point_attributes_to_interpolate( + const CurvesGeometry &src_curves, + CurvesGeometry &dst_curves, + AttributesForInterpolation &result, + const ResampleCurvesOutputAttributeIDs &output_ids) { VectorSet<bke::AttributeIDRef> ids; VectorSet<bke::AttributeIDRef> ids_no_interpolation; @@ -159,11 +166,75 @@ static void gather_point_attributes_to_interpolate(const CurvesGeometry &src_cur result.src_no_interpolation, result.dst_no_interpolation, result.dst_attributes); + + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + if (output_ids.tangent_id) { + result.src_evaluated_tangents = src_curves.evaluated_tangents(); + bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span( + output_ids.tangent_id, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + result.dst_tangents = dst_attribute.span.typed<float3>(); + result.dst_attributes.append(std::move(dst_attribute)); + } + if (output_ids.normal_id) { + result.src_evaluated_normals = src_curves.evaluated_normals(); + bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span( + output_ids.normal_id, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + result.dst_normals = dst_attribute.span.typed<float3>(); + result.dst_attributes.append(std::move(dst_attribute)); + } +} + +static void copy_or_defaults_for_unselected_curves(const CurvesGeometry &src_curves, + const Span<IndexRange> unselected_ranges, + const AttributesForInterpolation &attributes, + CurvesGeometry &dst_curves) +{ + bke::curves::copy_point_data(src_curves, + dst_curves, + unselected_ranges, + src_curves.positions(), + dst_curves.positions_for_write()); + + for (const int i : attributes.src.index_range()) { + bke::curves::copy_point_data( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + bke::curves::copy_point_data(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + if (!attributes.dst_tangents.is_empty()) { + bke::curves::fill_points(dst_curves, unselected_ranges, float3(0), attributes.dst_tangents); + } + if (!attributes.dst_normals.is_empty()) { + bke::curves::fill_points(dst_curves, unselected_ranges, float3(0), attributes.dst_normals); + } +} + +static void normalize_span(MutableSpan<float3> data) +{ + for (const int i : data.index_range()) { + data[i] = math::normalize(data[i]); + } +} + +static void normalize_curve_point_data(const CurvesGeometry &curves, + const IndexMask curve_selection, + MutableSpan<float3> data) +{ + for (const int i_curve : curve_selection) { + normalize_span(data.slice(curves.points_for_curve(i_curve))); + } } static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<int> &count_field) + const fn::Field<int> &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { /* Create the new curves without any points and evaluate the final count directly * into the offsets array, in order to be accumulated into offsets later. */ @@ -200,7 +271,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes); + gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids); src_curves.ensure_evaluated_lengths(); @@ -272,14 +343,27 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, }); } + auto interpolate_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) { + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::interpolate(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + }; + /* Interpolate the evaluated positions to the resampled curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::interpolate(evaluated_positions.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst_positions.slice(dst_points)); + interpolate_evaluated_data(evaluated_positions, dst_positions); + + if (!attributes.dst_tangents.is_empty()) { + interpolate_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents); + } + if (!attributes.dst_normals.is_empty()) { + interpolate_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals); } /* Fill the default value for non-interpolating attributes that still must be copied. */ @@ -291,23 +375,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, } }); - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + copy_or_defaults_for_unselected_curves(src_curves, unselected_ranges, attributes, dst_curves); for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) { attribute.finish(); @@ -318,21 +386,25 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<int> &count_field) + const fn::Field<int> &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { - return resample_to_uniform(src_curves, selection_field, get_count_input_max_one(count_field)); + return resample_to_uniform( + src_curves, selection_field, get_count_input_max_one(count_field), output_ids); } CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<float> &segment_length_field) + const fn::Field<float> &segment_length_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { return resample_to_uniform( - src_curves, selection_field, get_count_input_from_length(segment_length_field)); + src_curves, selection_field, get_count_input_from_length(segment_length_field), output_ids); } CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, - const fn::Field<bool> &selection_field) + const fn::Field<bool> &selection_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { src_curves.ensure_evaluated_offsets(); @@ -368,11 +440,11 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); /* Create the correct number of uniform-length samples for every selected curve. */ - Span<float3> evaluated_positions = src_curves.evaluated_positions(); + const Span<float3> evaluated_positions = src_curves.evaluated_positions(); MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes); + gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids); threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { const IndexMask sliced_selection = selection.slice(selection_range); @@ -393,11 +465,24 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, }); } + auto copy_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) { + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }; + /* Copy the evaluated positions to the selected curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); + copy_evaluated_data(evaluated_positions, dst_positions); + + if (!attributes.dst_tangents.is_empty()) { + copy_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents); + } + if (!attributes.dst_normals.is_empty()) { + copy_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals); } /* Fill the default value for non-interpolating attributes that still must be copied. */ @@ -409,23 +494,7 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, } }); - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + copy_or_defaults_for_unselected_curves(src_curves, unselected_ranges, attributes, dst_curves); for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) { attribute.finish(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 0ddf06dc8c7..c32600eccd1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -4,9 +4,13 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" +#include "DNA_pointcloud_types.h" + #include "BKE_pointcloud.h" #include "BKE_spline.hh" +#include "GEO_resample_curves.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -62,9 +66,9 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -static void curve_create_default_rotation_attribute(Span<float3> tangents, - Span<float3> normals, - MutableSpan<float3> rotations) +static void fill_rotation_attribute(const Span<float3> tangents, + const Span<float3> normals, + MutableSpan<float3> rotations) { threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { for (const int i : range) { @@ -74,239 +78,30 @@ static void curve_create_default_rotation_attribute(Span<float3> tangents, }); } -static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveResampleMode mode, - const CurveEval &curve, - const Span<SplinePtr> splines) -{ - const int size = curve.splines().size(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: { - const int count = params.get_input<int>("Count"); - if (count < 1) { - return {0}; - } - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - if (splines[i]->evaluated_points_num() > 0) { - offset += count; - } - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.get_input<float>("Length"), 0.0001f); - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - if (splines[i]->evaluated_points_num() > 0) { - offset += splines[i]->length() / resolution + 1; - } - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { - return curve.evaluated_point_offsets(); - } - } - BLI_assert_unreachable(); - return {0}; -} - -/** - * \note Relies on the fact that all attributes on point clouds are stored contiguously. - */ -static GMutableSpan ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id, - const eCustomDataType data_type) -{ - GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write( - attribute_id, ATTR_DOMAIN_POINT, data_type); - GMutableSpan span = attribute.varray.get_internal_span(); - attribute.finish(); - return span; -} - -template<typename T> -static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id) -{ - AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>( - attribute_id, ATTR_DOMAIN_POINT); - MutableSpan<T> span = attribute.varray.get_internal_span(); - attribute.finish(); - return span; -} - -namespace { -struct AnonymousAttributeIDs { - StrongAnonymousAttributeID tangent_id; - StrongAnonymousAttributeID normal_id; - StrongAnonymousAttributeID rotation_id; -}; - -struct ResultAttributes { - MutableSpan<float3> positions; - MutableSpan<float> radii; - - Map<AttributeIDRef, GMutableSpan> point_attributes; - - MutableSpan<float3> tangents; - MutableSpan<float3> normals; - MutableSpan<float3> rotations; -}; -} // namespace - -static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, - const CurveEval &curve, - const AnonymousAttributeIDs &attributes) +static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves, + const AttributeIDRef &tangent_id, + const AttributeIDRef &normal_id, + const AttributeIDRef &rotation_id) { - ResultAttributes outputs; - - outputs.positions = ensure_point_attribute<float3>(points, "position"); - outputs.radii = ensure_point_attribute<float>(points, "radius"); - - if (attributes.tangent_id) { - outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get()); - } - if (attributes.normal_id) { - outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get()); - } - if (attributes.rotation_id) { - outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get()); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(0); + pointcloud->totpoint = curves.points_num(); + + if (rotation_id) { + MutableAttributeAccessor attributes = curves.attributes_for_write(); + const VArraySpan<float3> tangents = attributes.lookup<float3>(tangent_id, ATTR_DOMAIN_POINT); + const VArraySpan<float3> normals = attributes.lookup<float3>(normal_id, ATTR_DOMAIN_POINT); + SpanAttributeWriter<float3> rotations = attributes.lookup_or_add_for_write_only_span<float3>( + rotation_id, ATTR_DOMAIN_POINT); + fill_rotation_attribute(tangents, normals, rotations.span); + rotations.finish(); } - /* Because of the invariants of the curve component, we use the attributes of the first spline - * as a representative for the attribute meta data all splines. Attributes from the spline domain - * are handled separately. */ - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (id.should_be_kept()) { - outputs.point_attributes.add_new( - id, ensure_point_attribute(points, id, meta_data.data_type)); - } - return true; - }, - ATTR_DOMAIN_POINT); - - return outputs; -} - -/** - * TODO: For non-poly splines, this has double copies that could be avoided as part - * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. - */ -static void copy_evaluated_point_attributes(const Span<SplinePtr> splines, - const Span<int> offsets, - ResultAttributes &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - - data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); - spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size)); + /* Move the curve point custom data to the pointcloud, to avoid any copying. */ + CustomData_free(&pointcloud->pdata, pointcloud->totpoint); + pointcloud->pdata = curves.point_data; + CustomData_reset(&curves.point_data); - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - const GMutableSpan dst = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data()); - } - - if (!data.tangents.is_empty()) { - data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); - } - if (!data.normals.is_empty()) { - data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); - } - } - }); -} - -static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, - const Span<int> offsets, - ResultAttributes &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int num = offsets[i + 1] - offsets[i]; - if (num == 0) { - continue; - } - - const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); - - spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); - spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), - uniform_samples, - data.radii.slice(offset, num)); - - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - const GMutableSpan dst = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.sample_with_index_factors( - spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); - } - - if (!data.tangents.is_empty()) { - Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); - spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); - for (float3 &vector : sampled_tangents) { - vector = math::normalize(vector); - } - } - - if (!data.normals.is_empty()) { - Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); - spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); - for (float3 &vector : sampled_normals) { - vector = math::normalize(vector); - } - } - } - }); -} - -static void copy_spline_domain_attributes(const CurveEval &curve, - const Span<int> offsets, - PointCloudComponent &points) -{ - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id); - const CPPType &type = curve_attribute.type(); - const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type); - - for (const int i : curve.splines().index_range()) { - const int offset = offsets[i]; - const int num = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], num); - } - - return true; - }, - ATTR_DOMAIN_CURVE); + return pointcloud; } static void node_geo_exec(GeoNodeExecParams params) @@ -315,73 +110,96 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - AnonymousAttributeIDs attribute_outputs; - attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent"); - attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); - attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); - GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - geometry_set.remove_geometry_during_modify(); - return; - } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - const Span<SplinePtr> splines = curve->splines(); - curve->assert_valid_point_attributes(); - - const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_num = offsets.last(); - if (total_num == 0) { - geometry_set.remove_geometry_during_modify(); - return; - } + StrongAnonymousAttributeID tangent_anonymous_id; + StrongAnonymousAttributeID normal_anonymous_id; + StrongAnonymousAttributeID rotation_anonymous_id; + const bool rotation_required = params.output_is_required("Rotation"); + if (params.output_is_required("Tangent") || rotation_required) { + tangent_anonymous_id = StrongAnonymousAttributeID("Tangent"); + } + if (params.output_is_required("Normal") || rotation_required) { + normal_anonymous_id = StrongAnonymousAttributeID("Normal"); + } + if (rotation_required) { + rotation_anonymous_id = StrongAnonymousAttributeID("Rotation"); + } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); - PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); - ResultAttributes point_attributes = create_attributes_for_transfer( - points, *curve, attribute_outputs); + geometry::ResampleCurvesOutputAttributeIDs resample_attributes; + resample_attributes.tangent_id = tangent_anonymous_id.get(); + resample_attributes.normal_id = normal_anonymous_id.get(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: - case GEO_NODE_CURVE_RESAMPLE_LENGTH: - copy_uniform_sample_point_attributes(splines, offsets, point_attributes); - break; - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - copy_evaluated_point_attributes(splines, offsets, point_attributes); - break; + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_count( + src_curves, fn::make_constant_field<bool>(true), count, resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; } - - copy_spline_domain_attributes(*curve, offsets, points); - - if (!point_attributes.rotations.is_empty()) { - curve_create_default_rotation_attribute( - point_attributes.tangents, point_attributes.normals, point_attributes.rotations); + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + Field<float> length = params.extract_input<Field<float>>("Length"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_length( + src_curves, fn::make_constant_field<bool>(true), length, resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; } - - geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); - }); + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated( + src_curves, fn::make_constant_field<bool>(true), resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; + } params.set_output("Points", std::move(geometry_set)); - if (attribute_outputs.tangent_id) { - params.set_output( - "Tangent", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id), - params.attribute_producer_name())); + if (tangent_anonymous_id) { + params.set_output("Tangent", + AnonymousAttributeFieldInput::Create<float3>( + std::move(tangent_anonymous_id), params.attribute_producer_name())); } - if (attribute_outputs.normal_id) { - params.set_output( - "Normal", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), - params.attribute_producer_name())); + if (normal_anonymous_id) { + params.set_output("Normal", + AnonymousAttributeFieldInput::Create<float3>( + std::move(normal_anonymous_id), params.attribute_producer_name())); } - if (attribute_outputs.rotation_id) { - params.set_output( - "Rotation", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), - params.attribute_producer_name())); + if (rotation_anonymous_id) { + params.set_output("Rotation", + AnonymousAttributeFieldInput::Create<float3>( + std::move(rotation_anonymous_id), params.attribute_producer_name())); } } |