diff options
Diffstat (limited to 'source/blender/nodes')
20 files changed, 1067 insertions, 653 deletions
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index dc0965f5d71..96a1904abdd 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -6,6 +6,7 @@ #include "FN_multi_function_builder.hh" #include "BKE_attribute_access.hh" +#include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 319fcdeebb7..16332be5179 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -26,6 +26,8 @@ #include "NOD_derived_node_tree.hh" +#include "FN_field.hh" + #include <chrono> struct SpaceNode; @@ -353,6 +355,8 @@ class ModifierLog { static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode); static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, const bNode &node); + static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, + const StringRef node_name); static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode, const bNode &node, const bNodeSocket &socket); diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 162ef07a6dd..67d861aad9f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -323,8 +323,8 @@ bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node) BLI_assert(node->type == CMP_NODE_CRYPTOMATTE_LEGACY); NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage); char sockname[32]; - n->num_inputs++; - BLI_snprintf(sockname, sizeof(sockname), "Crypto %.2d", n->num_inputs - 1); + n->inputs_num++; + BLI_snprintf(sockname, sizeof(sockname), "Crypto %.2d", n->inputs_num - 1); bNodeSocket *sock = nodeAddStaticSocket( ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, sockname); return sock; @@ -334,12 +334,12 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node) { BLI_assert(node->type == CMP_NODE_CRYPTOMATTE_LEGACY); NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage); - if (n->num_inputs < 2) { + if (n->inputs_num < 2) { return 0; } bNodeSocket *sock = static_cast<bNodeSocket *>(node->inputs.last); nodeRemoveSocket(ntree, node, sock); - n->num_inputs--; + n->inputs_num--; return 1; } diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc index c04e4bed660..b4dd0bbacd0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -14,11 +14,15 @@ namespace blender::nodes::node_composite_normal_cc { static void cmp_node_normal_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Vector>(N_("Normal")) - .default_value({1.0f, 1.0f, 1.0f}) + .default_value({0.0f, 0.0f, 1.0f}) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_DIRECTION); + b.add_output<decl::Vector>(N_("Normal")) + .default_value({0.0f, 0.0f, 1.0f}) .min(-1.0f) .max(1.0f) .subtype(PROP_DIRECTION); - b.add_output<decl::Vector>(N_("Normal")); b.add_output<decl::Float>(N_("Dot")); } 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_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 6794671f707..4792fada98b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -30,25 +30,25 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) { plConvexHull hull = plConvexHullCompute((float(*)[3])coords.data(), coords.size()); - const int num_verts = plConvexHullNumVertices(hull); - const int num_faces = num_verts <= 2 ? 0 : plConvexHullNumFaces(hull); - const int num_loops = num_verts <= 2 ? 0 : plConvexHullNumLoops(hull); + const int verts_num = plConvexHullNumVertices(hull); + const int faces_num = verts_num <= 2 ? 0 : plConvexHullNumFaces(hull); + const int loops_num = verts_num <= 2 ? 0 : plConvexHullNumLoops(hull); /* Half as many edges as loops, because the mesh is manifold. */ - const int num_edges = num_verts == 2 ? 1 : num_verts < 2 ? 0 : num_loops / 2; + const int edges_num = verts_num == 2 ? 1 : verts_num < 2 ? 0 : loops_num / 2; /* Create Mesh *result with proper capacity. */ Mesh *result; if (mesh) { result = BKE_mesh_new_nomain_from_template( - mesh, num_verts, num_edges, 0, num_loops, num_faces); + mesh, verts_num, edges_num, 0, loops_num, faces_num); } else { - result = BKE_mesh_new_nomain(num_verts, num_edges, 0, num_loops, num_faces); + result = BKE_mesh_new_nomain(verts_num, edges_num, 0, loops_num, faces_num); BKE_id_material_eval_ensure_default_slot(&result->id); } /* Copy vertices. */ - for (const int i : IndexRange(num_verts)) { + for (const int i : IndexRange(verts_num)) { float co[3]; int original_index; plConvexHullGetVertex(hull, i, co, &original_index); @@ -73,9 +73,9 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) /* NOTE: ConvexHull from Bullet uses a half-edge data structure * for its mesh. To convert that, each half-edge needs to be converted * to a loop and edges need to be created from that. */ - Array<MLoop> mloop_src(num_loops); + Array<MLoop> mloop_src(loops_num); uint edge_index = 0; - for (const int i : IndexRange(num_loops)) { + for (const int i : IndexRange(loops_num)) { int v_from; int v_to; plConvexHullGetLoop(hull, i, &v_from, &v_to); @@ -95,7 +95,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) edge_index++; } } - if (num_edges == 1) { + if (edges_num == 1) { /* In this case there are no loops. */ MEdge &edge = result->medge[0]; edge.v1 = 0; @@ -103,13 +103,13 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE; edge_index++; } - BLI_assert(edge_index == num_edges); + BLI_assert(edge_index == edges_num); /* Copy faces. */ Array<int> loops; int j = 0; MLoop *loop = result->mloop; - for (const int i : IndexRange(num_faces)) { + for (const int i : IndexRange(faces_num)) { const int len = plConvexHullGetFaceSize(hull, i); BLI_assert(len > 2); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 81ca87eec25..95ea978541c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -551,8 +551,8 @@ static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve, Span<SplinePtr> input_splines = input_curve.splines(); std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); - const int num_splines = input_splines.size(); - output_curve->resize(num_splines); + const int splines_num = input_splines.size(); + output_curve->resize(splines_num); MutableSpan<SplinePtr> output_splines = output_curve->splines(); Array<int> spline_offsets = input_curve.control_point_offsets(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index d5769c691c8..11eb472a6e2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_length_cc { @@ -18,11 +19,18 @@ static void node_geo_exec(GeoNodeExecParams params) params.set_default_remaining_outputs(); return; } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_set.get_curves_for_read()); + + const Curves &curves_id = *curve_set.get_curves_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + const VArray<bool> cyclic = curves.cyclic(); + + curves.ensure_evaluated_lengths(); + float length = 0.0f; - for (const SplinePtr &spline : curve->splines()) { - length += spline->length(); + for (const int i : curves.curves_range()) { + length += curves.evaluated_length_total_for_curve(i, cyclic[i]); } + params.set_output("Length", length); } 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..139b17138fa 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) { + return true; + } + if (!interpolate_attribute_to_curves(id, type_counts)) { + return true; + } + if (interpolate_attribute_to_poly_curve(id)) { + ids.add_new(id); + } + else { + ids_no_interpolation.add_new(id); } + return true; + }); + + /* Position is handled differently since it has non-generic interpolation for Bezier + * curves and because the evaluated positions are cached for each evaluated point. */ + ids.remove_contained("position"); + + retrieve_attribute_spans( + ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); + + /* Attributes that aren't interpolated like Bezier handles still have to be be copied + * to the result when there are any unselected curves of the corresponding type. */ + retrieve_attribute_spans(ids_no_interpolation, + src_component, + dst_component, + result.src_no_interpolation, + result.dst_no_interpolation, + result.dst_attributes); +} - BLI_assert_unreachable(); - return false; - }, - ATTR_DOMAIN_POINT); +/** + * Copy the provided point attribute values between all curves in the #curve_ranges index + * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. + */ +template<typename T> +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> curve_ranges, + const Span<T> src, + const MutableSpan<T> dst) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange src_points = src_curves.points_for_curves(range); + const IndexRange dst_points = dst_curves.points_for_curves(range); + /* The arrays might be large, so a threaded copy might make sense here too. */ + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }); +} +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> unselected_ranges, + const GSpan src, + const GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); + }); +} - return dst; +/** + * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. + */ +static void fill_curve_counts(const bke::CurvesGeometry &src_curves, + const Span<IndexRange> curve_ranges, + MutableSpan<int> counts) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { + for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { + for (const int i : curves_range) { + counts[i] = src_curves.points_for_curve(i).size(); + } + } + }); } -static SplinePtr resample_spline_evaluated(const Spline &src) +/** + * Turn an array of sizes into the offset at each index including all previous sizes. + */ +static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets) { - std::unique_ptr<PolySpline> dst = std::make_unique<PolySpline>(); - Spline::copy_base_settings(src, *dst); - dst->resize(src.evaluated_points_size()); - - dst->positions().copy_from(src.evaluated_positions()); - dst->positions().copy_from(src.evaluated_positions()); - src.interpolate_to_evaluated(src.radii()).materialize(dst->radii()); - src.interpolate_to_evaluated(src.tilts()).materialize(dst->tilts()); - - 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.interpolate_to_evaluated(*src_attribute).materialize(dst_attribute->data()); - return true; + int total = 0; + for (const int i : counts_to_offsets.index_range().drop_back(1)) { + const int count = counts_to_offsets[i]; + BLI_assert(count > 0); + counts_to_offsets[i] = total; + total += count; + } + counts_to_offsets.last() = total; +} + +/** + * 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 + * depends on the curve's resolution parameter. + * + * \warning The values provided by the #count_field must be 1 or greater. + * \warning Curves with no evaluated points must not be selected. + */ +static Curves *resample_to_uniform_count(const CurveComponent &src_component, + const Field<bool> &selection_field, + const Field<int> &count_field) +{ + const Curves &src_curves_id = *src_component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + + /* 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. */ + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + MutableSpan<int> dst_offsets = dst_curves.offsets(); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(count_field, dst_offsets); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* All resampled curves are poly curves. */ + dst_curves.curve_types().fill_indices(selection, CURVE_TYPE_POLY); + + VArray<bool> curves_cyclic = src_curves.cyclic(); + VArray<int8_t> curve_types = src_curves.curve_types(); + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions(); + + AttributesForInterpolation attributes; + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + src_curves.ensure_evaluated_lengths(); + + /* Sampling arbitrary attributes works by first interpolating them to the curve's standard + * "evaluated points" and then interpolating that result with the uniform samples. This is + * potentially wasteful when down-sampling a curve to many fewer points. There are two possible + * solutions: only sample the necessary points for interpolation, or first sample curve + * parameter/segment indices and evaluate the curve directly. */ + Array<int> sample_indices(dst_curves.points_num()); + Array<float> sample_factors(dst_curves.points_num()); + + /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on + * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a + * time or one curve at a time. */ + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + Vector<std::byte> evaluated_buffer; + + /* Gather uniform samples based on the accumulated lengths of the original curve. */ + for (const int i_curve : sliced_selection) { + const bool cyclic = curves_cyclic[i_curve]; + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::create_uniform_samples( + src_curves.evaluated_lengths_for_curve(i_curve, cyclic), + curves_cyclic[i_curve], + sample_indices.as_mutable_span().slice(dst_points), + sample_factors.as_mutable_span().slice(dst_points)); + } + + /* For every attribute, evaluate attributes from every curve in the range in the original + * curve's "evaluated points", then use linear interpolation to sample to the result. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + + if (curve_types[i_curve] == CURVE_TYPE_POLY) { + length_parameterize::linear_interpolation(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + else { + const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); + evaluated_buffer.clear(); + evaluated_buffer.resize(sizeof(T) * evaluated_size); + MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); + src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); + + length_parameterize::linear_interpolation(evaluated.as_span(), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); } } + }); + } - BLI_assert_unreachable(); - return true; - }, - ATTR_DOMAIN_POINT); + /* 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::linear_interpolation(evaluated_positions.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst_positions.slice(dst_points)); + } - return dst; + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(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(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; } -static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component, - const SampleModeParam &mode_param) +/** + * Evaluate each selected curve to its implicit evaluated points. + * + * \warning Curves with no evaluated points must not be selected. + */ +static Curves *resample_to_evaluated(const CurveComponent &src_component, + const Field<bool> &selection_field) { - const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component->get_for_read()); - GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; - const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); - - Span<SplinePtr> input_splines = input_curve->splines(); - - std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); - output_curve->resize(input_splines.size()); - MutableSpan<SplinePtr> output_splines = output_curve->splines(); - - if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add(*mode_param.count); - evaluator.add(mode_param.selection); - evaluator.evaluate(); - const VArray<int> &cuts = evaluator.get_evaluated<int>(0); - const VArray<bool> &selections = evaluator.get_evaluated<bool>(1); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - BLI_assert(mode_param.count); - if (selections[i] && input_splines[i]->evaluated_points_size() > 0) { - output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); - } - else { - output_splines[i] = input_splines[i]->copy(); + const Curves &src_curves_id = *src_component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + /* All resampled curves are poly curves. */ + dst_curves.curve_types().fill_indices(selection, CURVE_TYPE_POLY); + MutableSpan<int> dst_offsets = dst_curves.offsets(); + + src_curves.ensure_evaluated_offsets(); + threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); + } + }); + fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + accumulate_counts_to_offsets(dst_offsets); + + 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(); + MutableSpan<float3> dst_positions = dst_curves.positions(); + + AttributesForInterpolation attributes; + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + /* Evaluate generic point attributes directly to the result attributes. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + src_curves.interpolate_to_evaluated( + i_curve, src.slice(src_points), dst.slice(dst_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)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); } - }); + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); } - else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add(*mode_param.length); - evaluator.add(mode_param.selection); - evaluator.evaluate(); - const VArray<float> &lengths = evaluator.get_evaluated<float>(0); - const VArray<bool> &selections = evaluator.get_evaluated<bool>(1); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - if (selections[i] && input_splines[i]->evaluated_points_size() > 0) { - /* Don't allow asymptotic count increase for low resolution values. */ - const float divide_length = std::max(lengths[i], 0.0001f); - const float spline_length = input_splines[i]->length(); - const int count = std::max(int(spline_length / divide_length) + 1, 1); - output_splines[i] = resample_spline(*input_splines[i], count); - } - else { - output_splines[i] = input_splines[i]->copy(); - } - } - }); + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); } - else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) { - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add(mode_param.selection); - evaluator.evaluate(); - const VArray<bool> &selections = evaluator.get_evaluated<bool>(0); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - if (selections[i] && input_splines[i]->evaluated_points_size() > 0) { - output_splines[i] = resample_spline_evaluated(*input_splines[i]); - } - else { - output_splines[i] = input_splines[i]->copy(); - } - } - }); + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); } - output_curve->attributes = input_curve->attributes; - return output_curve; + + return dst_curves_id; } -static void geometry_set_curve_resample(GeometrySet &geometry_set, - const SampleModeParam &mode_param) +/** + * Create a resampled curve point count field for both "uniform" options. + * The complexity is handled here in order to make the actual resampling functions simpler. + */ +static Field<int> get_curve_count_field(GeoNodeExecParams params, + const GeometryNodeCurveResampleMode mode) { - if (!geometry_set.has_curves()) { - return; + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { + static fn::CustomMF_SI_SO<int, int> max_one_fn("Clamp Above One", + [](int value) { return std::max(1, value); }); + auto clamp_op = std::make_shared<FieldOperation>( + FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))})); + + return Field<int>(std::move(clamp_op)); } - std::unique_ptr<CurveEval> output_curve = resample_curve( - geometry_set.get_component_for_read<CurveComponent>(), mode_param); + if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { + static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( + "Length Input to Count", [](const float curve_length, const float sample_length) { + /* Find the number of sampled segments by dividing the total length by + * the sample length. Then there is one more sampled point than segment. */ + const int count = int(curve_length / sample_length) + 1; + return std::max(1, count); + }); + + auto get_count_op = std::make_shared<FieldOperation>( + FieldOperation(get_count_fn, + {Field<float>(std::make_shared<SplineLengthFieldInput>()), + params.extract_input<Field<float>>("Length")})); + + return Field<int>(std::move(get_count_op)); + } + + BLI_assert_unreachable(); + return {}; +} + +/** + * Create a selection field that removes curves without any evaluated points (invalid NURBS curves) + * from the original selection provided to the node. This is here to simplify the sampling actual + * resampling code. + */ +static Field<bool> get_selection_field(GeoNodeExecParams params) +{ + static fn::CustomMF_SI_SI_SO<bool, int, bool> get_selection_fn( + "Create Curve Selection", [](const bool orig_selection, const int evaluated_points_num) { + return orig_selection && evaluated_points_num > 1; + }); + + auto selection_op = std::make_shared<FieldOperation>( + FieldOperation(get_selection_fn, + {params.extract_input<Field<bool>>("Selection"), + Field<int>(std::make_shared<EvaluatedCountFieldInput>())})); - geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); + return Field<bool>(std::move(selection_op)); } static void node_geo_exec(GeoNodeExecParams params) @@ -252,25 +608,38 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurveResample &storage = node_storage(params.node()); const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - SampleModeParam mode_param; - mode_param.mode = mode; - mode_param.selection = params.extract_input<Field<bool>>("Selection"); + const Field<bool> selection = get_selection_field(params); - if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - Field<int> count = params.extract_input<Field<int>>("Count"); - if (count < 1) { - params.set_default_remaining_outputs(); - return; + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + Field<int> count = get_curve_count_field(params, mode); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curves()) { + return; + } + + Curves *result = resample_to_uniform_count( + *geometry_set.get_component_for_read<CurveComponent>(), selection, count); + + geometry_set.replace_curves(result); + }); + break; } - mode_param.count.emplace(count); - } - else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - Field<float> resolution = params.extract_input<Field<float>>("Length"); - mode_param.length.emplace(resolution); - } + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curves()) { + return; + } - geometry_set.modify_geometry_sets( - [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); }); + Curves *result = resample_to_evaluated( + *geometry_set.get_component_for_read<CurveComponent>(), selection); + + geometry_set.replace_curves(result); + }); + break; + } params.set_output("Curve", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc index e8ba78816a5..169f808c473 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include <atomic> + +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -49,6 +51,33 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) return BEZIER_HANDLE_AUTO; } +static void set_type_in_component(CurveComponent &component, + const GeometryNodeCurveHandleMode mode, + const HandleType new_handle_type, + const Field<bool> &selection_field) +{ + Curves &curves_id = *component.get_for_write(); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + curves.handle_types_left().fill_indices(selection, new_handle_type); + } + if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { + curves.handle_types_right().fill_indices(selection, new_handle_type); + } + + /* Eagerly calculate automatically derived handle positions if necessary. */ + if (ELEM(new_handle_type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_ALIGN)) { + curves.calculate_bezier_auto_handles(); + } +} + static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSetHandles &storage = node_storage(params.node()); @@ -58,62 +87,33 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); - bool has_bezier_spline = false; + const HandleType new_handle_type = handle_type_from_input_type(type); + + std::atomic<bool> has_curves = false; + std::atomic<bool> has_bezier = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_curves()) { return; } - - /* Retrieve data for write access so we can avoid new allocations for the handles data. */ - CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - MutableSpan<SplinePtr> splines = curve->splines(); - - GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; - const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); - - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); - - const HandleType new_handle_type = handle_type_from_input_type(type); - int point_index = 0; - - for (SplinePtr &spline : splines) { - if (spline->type() != CURVE_TYPE_BEZIER) { - point_index += spline->positions().size(); - continue; - } - - has_bezier_spline = true; - BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { - /* In this case the automatically calculated handle types need to be "baked", because - * they're possibly changing from a type that is calculated automatically to a type that - * is positioned manually. */ - bezier_spline.ensure_auto_handles(); - } - - for (int i_point : IndexRange(bezier_spline.size())) { - if (selection[point_index]) { - if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - bezier_spline.handle_types_left()[i_point] = new_handle_type; - } - if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { - bezier_spline.handle_types_right()[i_point] = new_handle_type; - } - } - point_index++; - } - bezier_spline.mark_cache_invalid(); + has_curves = true; + const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); + if (!component.attribute_exists("handle_type_left") || + !component.attribute_exists("handle_type_right")) { + return; } + has_bezier = true; - curve_component.replace(curve_eval_to_curves(*curve)); + set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(), + mode, + new_handle_type, + selection_field); }); - if (!has_bezier_spline) { - params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); + + if (has_curves && !has_bezier) { + params.error_message_add(NodeWarningType::Info, TIP_("Input curves do not have Bezier type")); } + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes::node_geo_curve_set_handle_type_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index 3edaccba506..62fae8b8eca 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -2,7 +2,7 @@ #include "BLI_task.hh" -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -26,168 +26,160 @@ static void node_declare(NodeDeclarationBuilder &b) } /** - * A basic interpolation from the point domain to the spline domain would be useless, since the - * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for - * each spline is the portion of the total length at the start of the spline. + * For lengths on the curve domain, a basic interpolation from the point domain would be useless, + * since the average parameter for each curve would just be 0.5, or close to it. Instead, the + * value for each curve is defined as the portion of the total length of all curves at its start. */ -static Array<float> curve_length_spline_domain(const CurveEval &curve, - const IndexMask UNUSED(mask)) +static Array<float> accumulated_lengths_curve_domain(const bke::CurvesGeometry &curves) { - Span<SplinePtr> splines = curve.splines(); + curves.ensure_evaluated_lengths(); + + Array<float> lengths(curves.curves_num()); + VArray<bool> cyclic = curves.cyclic(); float length = 0.0f; - Array<float> lengths(splines.size()); - for (const int i : splines.index_range()) { + for (const int i : curves.curves_range()) { lengths[i] = length; - length += splines[i]->length(); - } - return lengths; -} - -/** - * The parameter at each control point is the factor at the corresponding evaluated point. - */ -static void calculate_bezier_lengths(const BezierSpline &spline, MutableSpan<float> lengths) -{ - Span<int> offsets = spline.control_point_offsets(); - Span<float> lengths_eval = spline.evaluated_lengths(); - for (const int i : IndexRange(1, spline.size() - 1)) { - lengths[i] = lengths_eval[offsets[i] - 1]; + length += curves.evaluated_length_total_for_curve(i, cyclic[i]); } -} -/** - * The parameter for poly splines is simply the evaluated lengths divided by the total length. - */ -static void calculate_poly_length(const PolySpline &spline, MutableSpan<float> lengths) -{ - Span<float> lengths_eval = spline.evaluated_lengths(); - if (spline.is_cyclic()) { - lengths.drop_front(1).copy_from(lengths_eval.drop_back(1)); - } - else { - lengths.drop_front(1).copy_from(lengths_eval); - } + return lengths; } /** - * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and - * each control point doesn't correspond well to a specific evaluated point, the parameter at - * each point is not well defined. So instead, treat the control points as if they were a poly - * spline. + * Return the length of each control point along each curve, starting at zero for the first point. + * Importantly, this is different than the length at each evaluated point. The implementation is + * different for every curve type: + * - Catmull Rom Curves: Use the resolution to find the evaluated point for each control point. + * - Poly Curves: Copy the evaluated lengths, but we need to add a zero to the front of the array. + * - Bezier Curves: Use the evaluated offsets to find the evaluated point for each control point. + * - NURBS Curves: Treat the control points as if they were a poly curve, because there + * is no obvious mapping from each control point to a specific evaluated point. */ -static void calculate_nurbs_lengths(const NURBSpline &spline, MutableSpan<float> lengths) -{ - Span<float3> positions = spline.positions(); - Array<float> control_point_lengths(spline.size()); - float length = 0.0f; - for (const int i : IndexRange(positions.size() - 1)) { - lengths[i] = length; - length += math::distance(positions[i], positions[i + 1]); - } - lengths.last() = length; -} - -static Array<float> curve_length_point_domain(const CurveEval &curve) +static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves) { - Span<SplinePtr> splines = curve.splines(); - Array<int> offsets = curve.control_point_offsets(); - const int total_size = offsets.last(); - Array<float> lengths(total_size); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())}; - spline_factors.first() = 0.0f; - switch (splines[i]->type()) { - case CURVE_TYPE_BEZIER: { - calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors); + curves.ensure_evaluated_lengths(); + const VArray<int8_t> types = curves.curve_types(); + const VArray<int> resolution = curves.resolution(); + const VArray<bool> cyclic = curves.cyclic(); + + Array<float> result(curves.points_num()); + VArray<int> resolutions = curves.resolution(); + + threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) { + for (const int i_curve : range) { + const IndexRange points = curves.points_for_curve(i_curve); + const Span<float> evaluated_lengths = curves.evaluated_lengths_for_curve(i_curve, + cyclic[i_curve]); + MutableSpan<float> lengths = result.as_mutable_span().slice(points); + lengths.first() = 0.0f; + switch (types[i_curve]) { + case CURVE_TYPE_CATMULL_ROM: { + const int resolution = resolutions[i_curve]; + for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { + lengths[i] = evaluated_lengths[resolution * i - 1]; + } break; } - case CURVE_TYPE_POLY: { - calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors); + case CURVE_TYPE_POLY: + lengths.drop_front(1).copy_from(evaluated_lengths.take_front(lengths.size() - 1)); break; - } - case CURVE_TYPE_NURBS: { - calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors); + case CURVE_TYPE_BEZIER: { + const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve); + for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { + lengths[i] = evaluated_lengths[offsets[i] - 1]; + } break; } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); + case CURVE_TYPE_NURBS: { + const Span<float3> positions = curves.positions().slice(points); + float length = 0.0f; + for (const int i : positions.index_range().drop_back(1)) { + lengths[i] = length; + length += math::distance(positions[i], positions[i + 1]); + } + lengths.last() = length; break; } } } }); - return lengths; + return result; } -static VArray<float> construct_curve_parameter_varray(const CurveEval &curve, - const IndexMask mask, +static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry &curves, + const IndexMask UNUSED(mask), const AttributeDomain domain) { + VArray<bool> cyclic = curves.cyclic(); + if (domain == ATTR_DOMAIN_POINT) { - Span<SplinePtr> splines = curve.splines(); - Array<float> values = curve_length_point_domain(curve); - - const Array<int> offsets = curve.control_point_offsets(); - for (const int i_spline : curve.splines().index_range()) { - const Spline &spline = *splines[i_spline]; - const float spline_length = spline.length(); - const float spline_length_inv = spline_length == 0.0f ? 0.0f : 1.0f / spline_length; - for (const int i : IndexRange(spline.size())) { - values[offsets[i_spline] + i] *= spline_length_inv; + Array<float> result = curve_length_point_domain(curves); + MutableSpan<float> lengths = result.as_mutable_span(); + + threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) { + for (const int i_curve : range) { + const float total_length = curves.evaluated_length_total_for_curve(i_curve, + cyclic[i_curve]); + const float factor = total_length == 0.0f ? 0.0f : 1.0f / total_length; + MutableSpan<float> curve_lengths = lengths.slice(curves.points_for_curve(i_curve)); + for (float &value : curve_lengths) { + value *= factor; + } } - } - return VArray<float>::ForContainer(std::move(values)); + }); + return VArray<float>::ForContainer(std::move(result)); } if (domain == ATTR_DOMAIN_CURVE) { - Array<float> values = curve.accumulated_spline_lengths(); - const float total_length_inv = values.last() == 0.0f ? 0.0f : 1.0f / values.last(); - for (const int i : mask) { - values[i] *= total_length_inv; + Array<float> lengths = accumulated_lengths_curve_domain(curves); + + const int last_index = curves.curves_num() - 1; + const int total_length = lengths.last() + curves.evaluated_length_total_for_curve( + last_index, cyclic[last_index]); + const float factor = total_length == 0.0f ? 0.0f : 1.0f / total_length; + for (float &value : lengths) { + value *= factor; } - return VArray<float>::ForContainer(std::move(values)); + return VArray<float>::ForContainer(std::move(lengths)); } return {}; } -static VArray<float> construct_curve_length_varray(const CurveEval &curve, - const IndexMask mask, +static VArray<float> construct_curve_length_varray(const bke::CurvesGeometry &curves, + const IndexMask UNUSED(mask), const AttributeDomain domain) { + curves.ensure_evaluated_lengths(); + if (domain == ATTR_DOMAIN_POINT) { - Array<float> lengths = curve_length_point_domain(curve); + Array<float> lengths = curve_length_point_domain(curves); return VArray<float>::ForContainer(std::move(lengths)); } if (domain == ATTR_DOMAIN_CURVE) { - if (curve.splines().size() == 1) { - Array<float> lengths(1, 0.0f); - return VArray<float>::ForContainer(std::move(lengths)); - } - - Array<float> lengths = curve_length_spline_domain(curve, mask); + Array<float> lengths = accumulated_lengths_curve_domain(curves); return VArray<float>::ForContainer(std::move(lengths)); } return {}; } -static VArray<int> construct_index_on_spline_varray(const CurveEval &curve, +static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &curves, const IndexMask UNUSED(mask), const AttributeDomain domain) { if (domain == ATTR_DOMAIN_POINT) { - Array<int> output(curve.total_control_point_size()); - int output_index = 0; - for (int spline_index : curve.splines().index_range()) { - for (int point_index : IndexRange(curve.splines()[spline_index]->size())) { - output[output_index++] = point_index; + Array<int> result(curves.points_num()); + MutableSpan<int> span = result.as_mutable_span(); + threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) { + for (const int i_curve : range) { + MutableSpan<int> indices = span.slice(curves.points_for_curve(i_curve)); + for (const int i : indices.index_range()) { + indices[i] = i; + } } - } - return VArray<int>::ForContainer(std::move(output)); + }); + return VArray<int>::ForContainer(std::move(result)); } return {}; } @@ -206,9 +198,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); if (curve_component.has_curves()) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *curve_component.get_for_read()); - return construct_curve_parameter_varray(*curve, mask, domain); + const Curves &curves_id = *curve_component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + return construct_curve_parameter_varray(curves, mask, domain); } } return {}; @@ -240,8 +232,9 @@ class CurveLengthFieldInput final : public GeometryFieldInput { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); if (curve_component.has_curves()) { - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); - return construct_curve_length_varray(*curve, mask, domain); + const Curves &curves_id = *curve_component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + return construct_curve_length_varray(curves, mask, domain); } } return {}; @@ -273,9 +266,9 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); if (curve_component.has_curves()) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *curve_component.get_for_read()); - return construct_index_on_spline_varray(*curve, mask, domain); + const Curves &curves_id = *curve_component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + return construct_index_on_spline_varray(curves, mask, domain); } } return {}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index cf6837817c2..c3b1a141f4a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -168,12 +168,12 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> &attributes, const GeometryComponent &in_component, GeometryComponent &out_component, - const int num_selected_loops, + const int selected_loops_num, const Span<int> selected_poly_indices, const Mesh &mesh_in) { Vector<int64_t> indices; - indices.reserve(num_selected_loops); + indices.reserve(selected_loops_num); for (const int src_poly_index : selected_poly_indices) { const MPoly &src_poly = mesh_in.mpoly[src_poly_index]; const int src_loop_start = src_poly.loopstart; @@ -546,47 +546,47 @@ static void separate_instance_selection(GeometrySet &geometry_set, static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection, const bool invert, MutableSpan<int> r_vertex_map, - int *r_num_selected_vertices) + int *r_selected_vertices_num) { BLI_assert(vertex_selection.size() == r_vertex_map.size()); - int num_selected_vertices = 0; + int selected_verts_num = 0; for (const int i : r_vertex_map.index_range()) { if (vertex_selection[i] != invert) { - r_vertex_map[i] = num_selected_vertices; - num_selected_vertices++; + r_vertex_map[i] = selected_verts_num; + selected_verts_num++; } else { r_vertex_map[i] = -1; } } - *r_num_selected_vertices = num_selected_vertices; + *r_selected_vertices_num = selected_verts_num; } static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, const Span<bool> vertex_selection, const bool invert, MutableSpan<int> r_edge_map, - int *r_num_selected_edges) + int *r_selected_edges_num) { BLI_assert(mesh.totedge == r_edge_map.size()); - int num_selected_edges = 0; + int selected_edges_num = 0; for (const int i : IndexRange(mesh.totedge)) { const MEdge &edge = mesh.medge[i]; /* Only add the edge if both vertices will be in the new mesh. */ if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { - r_edge_map[i] = num_selected_edges; - num_selected_edges++; + r_edge_map[i] = selected_edges_num; + selected_edges_num++; } else { r_edge_map[i] = -1; } } - *r_num_selected_edges = num_selected_edges; + *r_selected_edges_num = selected_edges_num; } static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, @@ -594,15 +594,15 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, const bool invert, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totvert == vertex_selection.size()); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - int num_selected_loops = 0; + int selected_loops_num = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; @@ -617,13 +617,13 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, if (all_verts_in_selection) { r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; + r_loop_starts.append_unchecked(selected_loops_num); + selected_loops_num += poly_src.totloop; } } - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; + *r_selected_polys_num = r_selected_poly_indices.size(); + *r_selected_loops_num = selected_loops_num; } /** @@ -636,25 +636,25 @@ static void compute_selected_vertices_and_edges_from_edge_selection( const bool invert, MutableSpan<int> r_vertex_map, MutableSpan<int> r_edge_map, - int *r_num_selected_vertices, - int *r_num_selected_edges) + int *r_selected_vertices_num, + int *r_selected_edges_num) { BLI_assert(mesh.totedge == edge_selection.size()); - int num_selected_edges = 0; - int num_selected_vertices = 0; + int selected_edges_num = 0; + int selected_verts_num = 0; for (const int i : IndexRange(mesh.totedge)) { const MEdge &edge = mesh.medge[i]; if (edge_selection[i] != invert) { - r_edge_map[i] = num_selected_edges; - num_selected_edges++; + r_edge_map[i] = selected_edges_num; + selected_edges_num++; if (r_vertex_map[edge.v1] == -1) { - r_vertex_map[edge.v1] = num_selected_vertices; - num_selected_vertices++; + r_vertex_map[edge.v1] = selected_verts_num; + selected_verts_num++; } if (r_vertex_map[edge.v2] == -1) { - r_vertex_map[edge.v2] = num_selected_vertices; - num_selected_vertices++; + r_vertex_map[edge.v2] = selected_verts_num; + selected_verts_num++; } } else { @@ -662,8 +662,8 @@ static void compute_selected_vertices_and_edges_from_edge_selection( } } - *r_num_selected_vertices = num_selected_vertices; - *r_num_selected_edges = num_selected_edges; + *r_selected_vertices_num = selected_verts_num; + *r_selected_edges_num = selected_edges_num; } /** @@ -673,22 +673,22 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh, const Span<bool> edge_selection, const bool invert, MutableSpan<int> r_edge_map, - int *r_num_selected_edges) + int *r_selected_edges_num) { BLI_assert(mesh.totedge == edge_selection.size()); - int num_selected_edges = 0; + int selected_edges_num = 0; for (const int i : IndexRange(mesh.totedge)) { if (edge_selection[i] != invert) { - r_edge_map[i] = num_selected_edges; - num_selected_edges++; + r_edge_map[i] = selected_edges_num; + selected_edges_num++; } else { r_edge_map[i] = -1; } } - *r_num_selected_edges = num_selected_edges; + *r_selected_edges_num = selected_edges_num; } /** @@ -700,13 +700,13 @@ static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, const bool invert, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_polys_num, + int *r_selected_loops_num) { r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - int num_selected_loops = 0; + int selected_loops_num = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; @@ -721,13 +721,13 @@ static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, if (all_edges_in_selection) { r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; + r_loop_starts.append_unchecked(selected_loops_num); + selected_loops_num += poly_src.totloop; } } - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; + *r_selected_polys_num = r_selected_poly_indices.size(); + *r_selected_loops_num = selected_loops_num; } /** @@ -740,21 +740,21 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face( MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_edges, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_edges_num, + int *r_selected_polys_num, + int *r_selected_loops_num) { compute_selected_edges_from_vertex_selection( - mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + mesh, vertex_selection, invert, r_edge_map, r_selected_edges_num); compute_selected_polygons_from_vertex_selection(mesh, vertex_selection, invert, r_selected_poly_indices, r_loop_starts, - r_num_selected_polys, - r_num_selected_loops); + r_selected_polys_num, + r_selected_loops_num); } /** @@ -768,24 +768,24 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_vertices, - int *r_num_selected_edges, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_vertices_num, + int *r_selected_edges_num, + int *r_selected_polys_num, + int *r_selected_loops_num) { compute_selected_vertices_from_vertex_selection( - vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + vertex_selection, invert, r_vertex_map, r_selected_vertices_num); compute_selected_edges_from_vertex_selection( - mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + mesh, vertex_selection, invert, r_edge_map, r_selected_edges_num); compute_selected_polygons_from_vertex_selection(mesh, vertex_selection, invert, r_selected_poly_indices, r_loop_starts, - r_num_selected_polys, - r_num_selected_loops); + r_selected_polys_num, + r_selected_loops_num); } /** @@ -799,19 +799,19 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face( MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_edges, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_edges_num, + int *r_selected_polys_num, + int *r_selected_loops_num) { compute_selected_edges_from_edge_selection( - mesh, edge_selection, invert, r_edge_map, r_num_selected_edges); + mesh, edge_selection, invert, r_edge_map, r_selected_edges_num); compute_selected_polygons_from_edge_selection(mesh, edge_selection, invert, r_selected_poly_indices, r_loop_starts, - r_num_selected_polys, - r_num_selected_loops); + r_selected_polys_num, + r_selected_loops_num); } /** @@ -825,10 +825,10 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_vertices, - int *r_num_selected_edges, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_vertices_num, + int *r_selected_edges_num, + int *r_selected_polys_num, + int *r_selected_loops_num) { r_vertex_map.fill(-1); compute_selected_vertices_and_edges_from_edge_selection(mesh, @@ -836,15 +836,15 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, invert, r_vertex_map, r_edge_map, - r_num_selected_vertices, - r_num_selected_edges); + r_selected_vertices_num, + r_selected_edges_num); compute_selected_polygons_from_edge_selection(mesh, edge_selection, invert, r_selected_poly_indices, r_loop_starts, - r_num_selected_polys, - r_num_selected_loops); + r_selected_polys_num, + r_selected_loops_num); } /** @@ -855,26 +855,26 @@ static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, const bool invert, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - int num_selected_loops = 0; + int selected_loops_num = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ if (poly_selection[i] != invert) { r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; + r_loop_starts.append_unchecked(selected_loops_num); + selected_loops_num += poly_src.totloop; } } - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; + *r_selected_polys_num = r_selected_poly_indices.size(); + *r_selected_loops_num = selected_loops_num; } /** * Checks for every polygon if it is in `poly_selection`. If it is, the edges @@ -887,9 +887,9 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_edges, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_edges_num, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); @@ -898,30 +898,30 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - int num_selected_loops = 0; - int num_selected_edges = 0; + int selected_loops_num = 0; + int selected_edges_num = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ if (poly_selection[i] != invert) { r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; + r_loop_starts.append_unchecked(selected_loops_num); + selected_loops_num += poly_src.totloop; /* Add the vertices and the edges. */ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); for (const MLoop &loop : loops_src) { /* Check first if it has not yet been added. */ if (r_edge_map[loop.e] == -1) { - r_edge_map[loop.e] = num_selected_edges; - num_selected_edges++; + r_edge_map[loop.e] = selected_edges_num; + selected_edges_num++; } } } } - *r_num_selected_edges = num_selected_edges; - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; + *r_selected_edges_num = selected_edges_num; + *r_selected_polys_num = r_selected_poly_indices.size(); + *r_selected_loops_num = selected_loops_num; } /** @@ -935,10 +935,10 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_num_selected_vertices, - int *r_num_selected_edges, - int *r_num_selected_polys, - int *r_num_selected_loops) + int *r_selected_vertices_num, + int *r_selected_edges_num, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); @@ -948,36 +948,36 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - int num_selected_loops = 0; - int num_selected_vertices = 0; - int num_selected_edges = 0; + int selected_loops_num = 0; + int selected_verts_num = 0; + int selected_edges_num = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ if (poly_selection[i] != invert) { r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(num_selected_loops); - num_selected_loops += poly_src.totloop; + r_loop_starts.append_unchecked(selected_loops_num); + selected_loops_num += poly_src.totloop; /* Add the vertices and the edges. */ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); for (const MLoop &loop : loops_src) { /* Check first if it has not yet been added. */ if (r_vertex_map[loop.v] == -1) { - r_vertex_map[loop.v] = num_selected_vertices; - num_selected_vertices++; + r_vertex_map[loop.v] = selected_verts_num; + selected_verts_num++; } if (r_edge_map[loop.e] == -1) { - r_edge_map[loop.e] = num_selected_edges; - num_selected_edges++; + r_edge_map[loop.e] = selected_edges_num; + selected_edges_num++; } } } } - *r_num_selected_vertices = num_selected_vertices; - *r_num_selected_edges = num_selected_edges; - *r_num_selected_polys = r_selected_poly_indices.size(); - *r_num_selected_loops = num_selected_loops; + *r_selected_vertices_num = selected_verts_num; + *r_selected_edges_num = selected_edges_num; + *r_selected_polys_num = r_selected_poly_indices.size(); + *r_selected_loops_num = selected_loops_num; } /** @@ -993,8 +993,8 @@ static void do_mesh_separation(GeometrySet &geometry_set, /* Needed in all cases. */ Vector<int> selected_poly_indices; Vector<int> new_loop_starts; - int num_selected_polys = 0; - int num_selected_loops = 0; + int selected_polys_num = 0; + int selected_loops_num = 0; const Mesh &mesh_in = *in_component.get_for_read(); Mesh *mesh_out; @@ -1007,10 +1007,10 @@ static void do_mesh_separation(GeometrySet &geometry_set, switch (mode) { case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { Array<int> vertex_map(mesh_in.totvert); - int num_selected_vertices = 0; + int selected_verts_num = 0; Array<int> edge_map(mesh_in.totedge); - int num_selected_edges = 0; + int selected_edges_num = 0; /* Fill all the maps based on the selection. */ switch (domain) { @@ -1022,10 +1022,10 @@ static void do_mesh_separation(GeometrySet &geometry_set, edge_map, selected_poly_indices, new_loop_starts, - &num_selected_vertices, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + &selected_verts_num, + &selected_edges_num, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_EDGE: compute_selected_mesh_data_from_edge_selection(mesh_in, @@ -1035,10 +1035,10 @@ static void do_mesh_separation(GeometrySet &geometry_set, edge_map, selected_poly_indices, new_loop_starts, - &num_selected_vertices, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + &selected_verts_num, + &selected_edges_num, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_FACE: compute_selected_mesh_data_from_poly_selection(mesh_in, @@ -1048,21 +1048,21 @@ static void do_mesh_separation(GeometrySet &geometry_set, edge_map, selected_poly_indices, new_loop_starts, - &num_selected_vertices, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + &selected_verts_num, + &selected_edges_num, + &selected_polys_num, + &selected_loops_num); break; default: BLI_assert_unreachable(); break; } mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, - num_selected_vertices, - num_selected_edges, + selected_verts_num, + selected_edges_num, 0, - num_selected_loops, - num_selected_polys); + selected_loops_num, + selected_polys_num); out_component.replace(mesh_out, GeometryOwnershipType::Editable); /* Copy the selected parts of the mesh over to the new mesh. */ @@ -1084,14 +1084,14 @@ static void do_mesh_separation(GeometrySet &geometry_set, copy_face_corner_attributes(attributes, in_component, out_component, - num_selected_loops, + selected_loops_num, selected_poly_indices, mesh_in); break; } case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { Array<int> edge_map(mesh_in.totedge); - int num_selected_edges = 0; + int selected_edges_num = 0; /* Fill all the maps based on the selection. */ switch (domain) { @@ -1102,9 +1102,9 @@ static void do_mesh_separation(GeometrySet &geometry_set, edge_map, selected_poly_indices, new_loop_starts, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + &selected_edges_num, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_EDGE: compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in, @@ -1113,9 +1113,9 @@ static void do_mesh_separation(GeometrySet &geometry_set, edge_map, selected_poly_indices, new_loop_starts, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + &selected_edges_num, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_FACE: compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in, @@ -1124,9 +1124,9 @@ static void do_mesh_separation(GeometrySet &geometry_set, edge_map, selected_poly_indices, new_loop_starts, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + &selected_edges_num, + &selected_polys_num, + &selected_loops_num); break; default: BLI_assert_unreachable(); @@ -1134,10 +1134,10 @@ static void do_mesh_separation(GeometrySet &geometry_set, } mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, mesh_in.totvert, - num_selected_edges, + selected_edges_num, 0, - num_selected_loops, - num_selected_polys); + selected_loops_num, + selected_polys_num); out_component.replace(mesh_out, GeometryOwnershipType::Editable); /* Copy the selected parts of the mesh over to the new mesh. */ @@ -1158,7 +1158,7 @@ static void do_mesh_separation(GeometrySet &geometry_set, copy_face_corner_attributes(attributes, in_component, out_component, - num_selected_loops, + selected_loops_num, selected_poly_indices, mesh_in); break; @@ -1172,8 +1172,8 @@ static void do_mesh_separation(GeometrySet &geometry_set, invert, selected_poly_indices, new_loop_starts, - &num_selected_polys, - &num_selected_loops); + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_EDGE: compute_selected_polygons_from_edge_selection(mesh_in, @@ -1181,8 +1181,8 @@ static void do_mesh_separation(GeometrySet &geometry_set, invert, selected_poly_indices, new_loop_starts, - &num_selected_polys, - &num_selected_loops); + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_FACE: compute_selected_polygons_from_poly_selection(mesh_in, @@ -1190,15 +1190,15 @@ static void do_mesh_separation(GeometrySet &geometry_set, invert, selected_poly_indices, new_loop_starts, - &num_selected_polys, - &num_selected_loops); + &selected_polys_num, + &selected_loops_num); break; default: BLI_assert_unreachable(); break; } mesh_out = BKE_mesh_new_nomain_from_template( - &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, num_selected_loops, num_selected_polys); + &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num); out_component.replace(mesh_out, GeometryOwnershipType::Editable); /* Copy the selected parts of the mesh over to the new mesh. */ @@ -1217,7 +1217,7 @@ static void do_mesh_separation(GeometrySet &geometry_set, copy_face_corner_attributes(attributes, in_component, out_component, - num_selected_loops, + selected_loops_num, selected_poly_indices, mesh_in); break; diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 82584f6f413..0072fbcde93 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -25,7 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); b.add_input<decl::Vector>(N_("Offset")).subtype(PROP_TRANSLATION).implicit_field().hide_value(); - b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).min(0.0f).supports_field(); + b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).supports_field(); b.add_input<decl::Bool>(N_("Individual")).default_value(true); b.add_output<decl::Geometry>("Mesh"); b.add_output<decl::Bool>(N_("Top")).field_source(); @@ -523,7 +523,7 @@ static void extrude_mesh_edges(MeshComponent &component, } case ATTR_DOMAIN_FACE: { /* Attribute values for new faces are a mix of the values of faces connected to the its - * original edge. */ + * original edge. */ copy_with_mixing(data.slice(new_poly_range), data.as_span(), [&](const int i) { return edge_to_poly_map[edge_selection[i]].as_span(); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index ab6f6b40d5e..6c24f86b63b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -3,15 +3,8 @@ #include "node_geometry_util.hh" #include "BKE_curves.hh" -#include "BKE_spline.hh" -namespace blender::nodes::node_geo_input_spline_length_cc { - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_output<decl::Float>(N_("Length")).field_source(); - b.add_output<decl::Int>(N_("Point Count")).field_source(); -} +namespace blender::nodes { /* -------------------------------------------------------------------- * Spline Length @@ -23,55 +16,66 @@ static VArray<float> construct_spline_length_gvarray(const CurveComponent &compo if (!component.has_curves()) { return {}; } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - Span<SplinePtr> splines = curve->splines(); - Array<float> spline_lenghts(splines.size()); - for (const int i : splines.index_range()) { - spline_lenghts[i] = splines[i]->length(); - } + curves.ensure_evaluated_lengths(); + + VArray<bool> cyclic = curves.cyclic(); + VArray<float> lengths = VArray<float>::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 VArray<float>::ForContainer(std::move(spline_lenghts)); + return lengths; } + if (domain == ATTR_DOMAIN_POINT) { - VArray<float> length = VArray<float>::ForContainer(std::move(spline_lenghts)); return component.attribute_try_adapt_domain<float>( - std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } return {}; } -class SplineLengthFieldInput final : public GeometryFieldInput { - public: - SplineLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") - { - category_ = Category::Generated; - } +SplineLengthFieldInput::SplineLengthFieldInput() + : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") +{ + 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) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_length_gvarray(curve_component, domain); - } - return {}; +GVArray SplineLengthFieldInput::get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const +{ + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_spline_length_gvarray(curve_component, domain); } + return {}; +} - uint64_t hash() const override - { - /* Some random constant hash. */ - return 3549623580; - } +uint64_t SplineLengthFieldInput::hash() const +{ + /* Some random constant hash. */ + return 3549623580; +} - bool is_equal_to(const fn::FieldNode &other) const override - { - return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr; - } -}; +bool SplineLengthFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr; +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_input_spline_length_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Length")).field_source(); + b.add_output<decl::Int>(N_("Point Count")).field_source(); +} /* -------------------------------------------------------------------- * Spline Count diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 231ef547a8b..368954447c9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -298,7 +298,7 @@ class RaycastFunction : public fn::MultiFunction { GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute"); if (!result.is_empty()) { MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices); - result.type().fill_assign_indices(result.type().default_value(), result.data(), mask); + result.type().value_initialize_indices(result.data(), mask); interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 271dd824d27..31b9f1765a5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include <atomic> + +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -34,8 +36,40 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static void set_position_in_component(const GeometryNodeCurveHandleMode mode, - CurveComponent &component, +static void update_handle_types_for_movement(int8_t &type, int8_t &other) +{ + switch (type) { + case BEZIER_HANDLE_FREE: + break; + case BEZIER_HANDLE_AUTO: + /* Converting auto handles to aligned handled instead of free handles is + * arbitrary, but expected and "standard" based on behavior in edit mode. */ + if (other == BEZIER_HANDLE_AUTO) { + /* Convert pairs of auto handles to aligned handles when moving one side. */ + type = BEZIER_HANDLE_ALIGN; + other = BEZIER_HANDLE_ALIGN; + } + else { + /* If the other handle isn't automatic, just make the handle free. */ + type = BEZIER_HANDLE_FREE; + } + break; + case BEZIER_HANDLE_VECTOR: + type = BEZIER_HANDLE_FREE; + break; + case BEZIER_HANDLE_ALIGN: + /* The handle can stay aligned if the other handle is also aligned (in which case the other + * handle should be updated to be consistent). But otherwise the handle must be made free to + * avoid conflicting with its "aligned" type. */ + if (other != BEZIER_HANDLE_ALIGN) { + type = BEZIER_HANDLE_FREE; + } + break; + } +} + +static void set_position_in_component(CurveComponent &component, + const GeometryNodeCurveHandleMode mode, const Field<bool> &selection_field, const Field<float3> &position_field, const Field<float3> &offset_field) @@ -52,83 +86,44 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, evaluator.add(offset_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); - - int current_point = 0; - int current_mask = 0; - for (const SplinePtr &spline : curve->splines()) { - if (spline->type() == CURVE_TYPE_BEZIER) { - BezierSpline &bezier = static_cast<BezierSpline &>(*spline); - - bezier.ensure_auto_handles(); - for (const int i : bezier.positions().index_range()) { - if (current_mask < selection.size() && selection[current_mask] == current_point) { - if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - if (bezier.handle_types_left()[i] == BEZIER_HANDLE_VECTOR) { - bezier.handle_types_left()[i] = BEZIER_HANDLE_FREE; - } - else if (bezier.handle_types_left()[i] == BEZIER_HANDLE_AUTO) { - bezier.handle_types_left()[i] = BEZIER_HANDLE_ALIGN; - } - } - else { - if (bezier.handle_types_right()[i] == BEZIER_HANDLE_VECTOR) { - bezier.handle_types_right()[i] = BEZIER_HANDLE_FREE; - } - else if (bezier.handle_types_right()[i] == BEZIER_HANDLE_AUTO) { - bezier.handle_types_right()[i] = BEZIER_HANDLE_ALIGN; - } - } - current_mask++; - } - current_point++; - } + const VArray<float3> &new_positions = evaluator.get_evaluated<float3>(0); + const VArray<float3> &new_offsets = evaluator.get_evaluated<float3>(1); + + Curves &curves_id = *component.get_for_write(); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + + Span<float3> positions = curves.positions(); + + const bool use_left = mode == GEO_NODE_CURVE_HANDLE_LEFT; + MutableSpan<int8_t> handle_types = use_left ? curves.handle_types_left() : + curves.handle_types_right(); + MutableSpan<int8_t> handle_types_other = use_left ? curves.handle_types_right() : + curves.handle_types_left(); + MutableSpan<float3> handle_positions = use_left ? curves.handle_positions_left() : + curves.handle_positions_right(); + MutableSpan<float3> handle_positions_other = use_left ? curves.handle_positions_right() : + curves.handle_positions_left(); + + threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + update_handle_types_for_movement(handle_types[i], handle_types_other[i]); } - else { - for ([[maybe_unused]] int i : spline->positions().index_range()) { - if (current_mask < selection.size() && selection[current_mask] == current_point) { - current_mask++; - } - current_point++; - } - } - } + }); - const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0); - const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1); - - current_point = 0; - current_mask = 0; - for (const SplinePtr &spline : curve->splines()) { - if (spline->type() == CURVE_TYPE_BEZIER) { - BezierSpline &bezier = static_cast<BezierSpline &>(*spline); - for (const int i : bezier.positions().index_range()) { - if (current_mask < selection.size() && selection[current_mask] == current_point) { - if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - bezier.set_handle_position_left( - i, positions_input[current_point] + offsets_input[current_point]); - } - else { - bezier.set_handle_position_right( - i, positions_input[current_point] + offsets_input[current_point]); - } - current_mask++; - } - current_point++; - } - } - else { - for ([[maybe_unused]] int i : spline->positions().index_range()) { - if (current_mask < selection.size() && selection[current_mask] == current_point) { - current_mask++; - } - current_point++; - } + threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + bke::curves::bezier::set_handle_position(positions[i], + HandleType(handle_types[i]), + HandleType(handle_types_other[i]), + new_positions[i] + new_offsets[i], + handle_positions[i], + handle_positions_other[i]); } - } + }); - component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned); + curves.calculate_bezier_auto_handles(); + + curves.tag_positions_changed(); } static void node_geo_exec(GeoNodeExecParams params) @@ -141,24 +136,32 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float3> position_field = params.extract_input<Field<float3>>("Position"); Field<float3> offset_field = params.extract_input<Field<float3>>("Offset"); - bool has_bezier = false; + std::atomic<bool> has_curves = false; + std::atomic<bool> has_bezier = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER); - - set_position_in_component(mode, - geometry_set.get_component_for_write<CurveComponent>(), - selection_field, - position_field, - offset_field); + if (!geometry_set.has_curves()) { + return; + } + has_curves = true; + const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); + if (!component.attribute_exists("handle_left") || + !component.attribute_exists("handle_right")) { + return; } + has_bezier = true; + + set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(), + mode, + selection_field, + position_field, + offset_field); }); - if (!has_bezier) { - params.error_message_add(NodeWarningType::Info, - TIP_("The input geometry does not contain a Bezier spline")); + + if (has_curves && !has_bezier) { + params.error_message_add(NodeWarningType::Info, TIP_("Input curves do not have Bezier type")); } + params.set_output("Curve", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index eb035aa9b6b..d2ff9753897 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -7,6 +7,8 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_position_cc { @@ -62,6 +64,9 @@ static void set_computed_position_and_offset(GeometryComponent &component, break; } case GEO_COMPONENT_TYPE_CURVE: { + CurveComponent &curve_component = static_cast<CurveComponent &>(component); + Curves &curves_id = *curve_component.get_for_write(); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); if (component.attribute_exists("handle_right") && component.attribute_exists("handle_left")) { OutputAttribute_Typed<float3> handle_right_attribute = @@ -90,6 +95,9 @@ static void set_computed_position_and_offset(GeometryComponent &component, handle_right_attribute.save(); handle_left_attribute.save(); + + /* Automatic Bezier handles must be recalculated based on the new positions. */ + curves.calculate_bezier_auto_handles(); break; } else { diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 7f0ba950490..12e306ba480 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -493,7 +493,7 @@ class NearestTransferFunction : public fn::MultiFunction { GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); if (!use_mesh_ && !use_points_) { - dst.type().fill_construct_indices(dst.type().default_value(), dst.data(), mask); + dst.type().value_initialize_indices(dst.data(), mask); return; } @@ -673,7 +673,7 @@ class IndexTransferFunction : public fn::MultiFunction { const CPPType &type = dst.type(); if (src_data_ == nullptr) { - type.fill_construct_indices(type.default_value(), dst.data(), mask); + type.value_initialize_indices(dst.data(), mask); return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index a04544e2814..cc115ee3b3f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -103,8 +103,8 @@ static void transform_volume(Volume &volume, const float4x4 &transform, const De memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4])); openvdb::Mat4d vdb_matrix_d{vdb_matrix}; - const int num_grids = BKE_volume_num_grids(&volume); - for (const int i : IndexRange(num_grids)) { + const int grids_num = BKE_volume_num_grids(&volume); + for (const int i : IndexRange(grids_num)) { VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i); openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false); diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 5c8f4c52f75..13f38c3352e 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -337,6 +337,16 @@ const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &sn return tree_log->lookup_node_log(node); } +const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, + const StringRef node_name) +{ + const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); + if (tree_log == nullptr) { + return nullptr; + } + return tree_log->lookup_node_log(node_name); +} + const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode, const bNode &node, const bNodeSocket &socket) |