Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh1
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc8
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc8
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc715
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc229
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc300
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc88
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc183
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc4
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc10
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)