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:
authorHans Goudey <h.goudey@me.com>2022-11-02 14:29:01 +0300
committerHans Goudey <h.goudey@me.com>2022-11-02 14:34:22 +0300
commit5f7ca5462d31ff56f16016d5f9e6a5489b30a05e (patch)
treec3c0fa2a326d4bcd972ac586ac0ef9c7f6a09711
parent460c9d3d92e9f74254d58c6bb07d7e4fcb53e8b7 (diff)
Geometry Nodes: Add index and value inputs to sample curve node
As described in T92474 and T91650, this patch adds two features to the sample curve node. First is an index input, to allow choosing the curve to sample for each point. Second is a custom field input, which is evaluated on the control points of the curve and then sampled like the other outputs. There is an "All Curves" option for the old behavior which takes the length of all curves into account. For invalid curve indices, the node outputs zeros (default values). Invalid lengths and factors are clamped. There have been various discussions about splitting the node up more, but this is an intuitive combination of options and will work well enough for current use cases. The node could still be generalized more in the future. Keep in mind that the source field is evaluated on curve control points, not the evaluated points used for sampling. This is necessary so that fields like "Index" work as expected. Differential Revision: https://developer.blender.org/D16147
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenloader/intern/versioning_300.cc19
-rw-r--r--source/blender/makesdna/DNA_node_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc336
5 files changed, 309 insertions, 70 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 806fff2099e..1a642cf4eb3 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -25,7 +25,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 5
+#define BLENDER_FILE_SUBVERSION 6
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc
index a2bd7fd2fd1..65094655cfe 100644
--- a/source/blender/blenloader/intern/versioning_300.cc
+++ b/source/blender/blenloader/intern/versioning_300.cc
@@ -3653,6 +3653,25 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 304, 6)) {
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_SAMPLE_CURVE) {
+ continue;
+ }
+ static_cast<NodeGeometryCurveSample *>(node->storage)->use_all_curves = true;
+ static_cast<NodeGeometryCurveSample *>(node->storage)->data_type = CD_PROP_FLOAT;
+ bNodeSocket *curve_socket = nodeFindSocket(node, SOCK_IN, "Curve");
+ BLI_assert(curve_socket != nullptr);
+ STRNCPY(curve_socket->name, "Curves");
+ STRNCPY(curve_socket->identifier, "Curves");
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 74714cf7e41..0b7c17d44bb 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1497,6 +1497,10 @@ typedef struct NodeGeometryCurveToPoints {
typedef struct NodeGeometryCurveSample {
/* GeometryNodeCurveSampleMode. */
uint8_t mode;
+ int8_t use_all_curves;
+ /* eCustomDataType. */
+ int8_t data_type;
+ char _pad[1];
} NodeGeometryCurveSample;
typedef struct NodeGeometryTransferAttribute {
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 865399df9ef..7457267a83c 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -9442,10 +9442,26 @@ static void def_geo_curve_sample(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSample", "storage");
- PropertyRNA *prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ PropertyRNA *prop;
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "Method for sampling input");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "use_all_curves", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop,
+ "All Curves",
+ "Sample lengths based on the total lengh of all curves, rather than "
+ "using a length inside each selected curve");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(
+ prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_triangulate(StructRNA *srna)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index 27e111822bf..2b732bba889 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_devirtualize_parameters.hh"
+#include "BLI_generic_array.hh"
#include "BLI_length_parameterize.hh"
#include "BKE_curves.hh"
@@ -8,6 +9,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_sample_cc {
@@ -16,9 +19,16 @@ NODE_STORAGE_FUNCS(NodeGeometryCurveSample)
static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>(N_("Curve"))
+ b.add_input<decl::Geometry>(N_("Curves"))
.only_realized_data()
.supported_type(GEO_COMPONENT_TYPE_CURVE);
+
+ b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field();
+
b.add_input<decl::Float>(N_("Factor"))
.min(0.0f)
.max(1.0f)
@@ -30,6 +40,16 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_DISTANCE)
.supports_field()
.make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; });
+ b.add_input<decl::Int>(N_("Curve Index")).supports_field().make_available([](bNode &node) {
+ node_storage(node).use_all_curves = false;
+ });
+
+ b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field();
+ b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field();
+ b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field();
+ b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field();
+ b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field();
+
b.add_output<decl::Vector>(N_("Position")).dependent_field();
b.add_output<decl::Vector>(N_("Tangent")).dependent_field();
b.add_output<decl::Vector>(N_("Normal")).dependent_field();
@@ -37,13 +57,17 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_all_curves", 0, nullptr, ICON_NONE);
}
static void node_type_init(bNodeTree * /*tree*/, bNode *node)
{
NodeGeometryCurveSample *data = MEM_cnew<NodeGeometryCurveSample>(__func__);
- data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
+ data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
+ data->use_all_curves = false;
+ data->data_type = CD_PROP_FLOAT;
node->storage = data;
}
@@ -51,16 +75,62 @@ static void node_update(bNodeTree *ntree, bNode *node)
{
const NodeGeometryCurveSample &storage = node_storage(*node);
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
+ const eCustomDataType data_type = eCustomDataType(storage.data_type);
+
+ bNodeSocket *in_socket_float = static_cast<bNodeSocket *>(node->inputs.first)->next;
+ bNodeSocket *in_socket_int32 = in_socket_float->next;
+ bNodeSocket *in_socket_vector = in_socket_int32->next;
+ bNodeSocket *in_socket_color4f = in_socket_vector->next;
+ bNodeSocket *in_socket_bool = in_socket_color4f->next;
- bNodeSocket *factor = static_cast<bNodeSocket *>(node->inputs.first)->next;
+ bNodeSocket *factor = in_socket_bool->next;
bNodeSocket *length = factor->next;
+ bNodeSocket *curve_index = length->next;
nodeSetSocketAvailability(ntree, factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+ nodeSetSocketAvailability(ntree, curve_index, !storage.use_all_curves);
+
+ nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32);
+
+ bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first);
+ bNodeSocket *out_socket_int32 = out_socket_float->next;
+ bNodeSocket *out_socket_vector = out_socket_int32->next;
+ bNodeSocket *out_socket_color4f = out_socket_vector->next;
+ bNodeSocket *out_socket_bool = out_socket_color4f->next;
+
+ nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, out_socket_bool, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(4));
+ search_link_ops_for_declarations(params, declaration.outputs().take_front(3));
+
+ const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
+ eNodeSocketDatatype(params.other_socket().type));
+ if (type && *type != CD_PROP_STRING) {
+ /* The input and output sockets have the same name. */
+ params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSampleCurve");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
}
static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
const Span<float> sample_lengths,
+ const GeometryNodeCurveSampleMode length_mode,
const IndexMask mask,
MutableSpan<int> r_segment_indices,
MutableSpan<float> r_length_in_segment)
@@ -70,10 +140,13 @@ static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
mask.to_best_mask_type([&](const auto mask) {
for (const int64_t i : mask) {
+ const float sample_length = length_mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
+ sample_lengths[i] * total_length :
+ sample_lengths[i];
int segment_i;
float factor_in_segment;
length_parameterize::sample_at_length(accumulated_lengths,
- std::clamp(sample_lengths[i], 0.0f, total_length),
+ std::clamp(sample_length, 0.0f, total_length),
segment_i,
factor_in_segment,
&hint);
@@ -89,6 +162,7 @@ static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
const Span<float> sample_lengths,
+ const GeometryNodeCurveSampleMode length_mode,
const IndexMask mask,
MutableSpan<int> r_segment_indices,
MutableSpan<float> r_factor_in_segment)
@@ -96,16 +170,32 @@ static void sample_indices_and_factors_to_compressed(const Span<float> accumulat
const float total_length = accumulated_lengths.last();
length_parameterize::SampleSegmentHint hint;
- mask.to_best_mask_type([&](const auto mask) {
- for (const int64_t i : IndexRange(mask.size())) {
- const float length = sample_lengths[mask[i]];
- length_parameterize::sample_at_length(accumulated_lengths,
- std::clamp(length, 0.0f, total_length),
- r_segment_indices[i],
- r_factor_in_segment[i],
- &hint);
- }
- });
+ switch (length_mode) {
+ case GEO_NODE_CURVE_SAMPLE_FACTOR:
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : IndexRange(mask.size())) {
+ const float length = sample_lengths[mask[i]] * total_length;
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(length, 0.0f, total_length),
+ r_segment_indices[i],
+ r_factor_in_segment[i],
+ &hint);
+ }
+ });
+ break;
+ case GEO_NODE_CURVE_SAMPLE_LENGTH:
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : IndexRange(mask.size())) {
+ const float length = sample_lengths[mask[i]];
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(length, 0.0f, total_length),
+ r_segment_indices[i],
+ r_factor_in_segment[i],
+ &hint);
+ }
+ });
+ break;
+ }
}
/**
@@ -115,10 +205,12 @@ static void sample_indices_and_factors_to_compressed(const Span<float> accumulat
class SampleFloatSegmentsFunction : public fn::MultiFunction {
private:
Array<float> accumulated_lengths_;
+ GeometryNodeCurveSampleMode length_mode_;
public:
- SampleFloatSegmentsFunction(Array<float> accumulated_lengths)
- : accumulated_lengths_(std::move(accumulated_lengths))
+ SampleFloatSegmentsFunction(Array<float> accumulated_lengths,
+ const GeometryNodeCurveSampleMode length_mode)
+ : accumulated_lengths_(std::move(accumulated_lengths)), length_mode_(length_mode)
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
@@ -141,7 +233,8 @@ class SampleFloatSegmentsFunction : public fn::MultiFunction {
MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
2, "Length in Curve");
- sample_indices_and_lengths(accumulated_lengths_, lengths, mask, indices, lengths_in_segments);
+ sample_indices_and_lengths(
+ accumulated_lengths_, lengths, length_mode_, mask, indices, lengths_in_segments);
}
};
@@ -153,15 +246,27 @@ class SampleCurveFunction : public fn::MultiFunction {
* that the curve is not freed before the function can execute.
*/
GeometrySet geometry_set_;
+ GField src_field_;
+ GeometryNodeCurveSampleMode length_mode_;
+
+ fn::MFSignature signature_;
+
+ std::optional<bke::CurvesFieldContext> source_context_;
+ std::unique_ptr<FieldEvaluator> source_evaluator_;
+ const GVArray *source_data_;
public:
- SampleCurveFunction(GeometrySet geometry_set) : geometry_set_(std::move(geometry_set))
+ SampleCurveFunction(GeometrySet geometry_set,
+ const GeometryNodeCurveSampleMode length_mode,
+ const GField &src_field)
+ : geometry_set_(std::move(geometry_set)), src_field_(src_field), length_mode_(length_mode)
{
- static fn::MFSignature signature = create_signature();
- this->set_signature(&signature);
+ signature_ = create_signature();
+ this->set_signature(&signature_);
+ this->evaluate_source();
}
- static fn::MFSignature create_signature()
+ fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Sample Curve"};
signature.single_input<int>("Curve Index");
@@ -169,6 +274,7 @@ class SampleCurveFunction : public fn::MultiFunction {
signature.single_output<float3>("Position");
signature.single_output<float3>("Tangent");
signature.single_output<float3>("Normal");
+ signature.single_output("Value", src_field_.cpp_type());
return signature.build();
}
@@ -180,6 +286,7 @@ class SampleCurveFunction : public fn::MultiFunction {
3, "Tangent");
MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
4, "Normal");
+ GMutableSpan sampled_values = params.uninitialized_single_output_if_required(5, "Value");
auto return_default = [&]() {
if (!sampled_positions.is_empty()) {
@@ -218,18 +325,40 @@ class SampleCurveFunction : public fn::MultiFunction {
Array<int> indices;
Array<float> factors;
+ GArray<> src_original_values(source_data_->type());
+ GArray<> src_evaluated_values(source_data_->type());
+
+ auto fill_invalid = [&](const IndexMask mask) {
+ if (!sampled_positions.is_empty()) {
+ sampled_positions.fill_indices(mask, float3(0));
+ }
+ if (!sampled_tangents.is_empty()) {
+ sampled_tangents.fill_indices(mask, float3(0));
+ }
+ if (!sampled_normals.is_empty()) {
+ sampled_normals.fill_indices(mask, float3(0));
+ }
+ if (!sampled_values.is_empty()) {
+ attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ sampled_values.typed<T>().fill_indices(mask, {});
+ });
+ }
+ };
auto sample_curve = [&](const int curve_i, const IndexMask mask) {
+ const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
+ cyclic[curve_i]);
+ if (accumulated_lengths.is_empty()) {
+ fill_invalid(mask);
+ return;
+ }
/* Store the sampled indices and factors in arrays the size of the mask.
* Then, during interpolation, move the results back to the masked indices. */
indices.reinitialize(mask.size());
factors.reinitialize(mask.size());
sample_indices_and_factors_to_compressed(
- curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]),
- lengths,
- mask,
- indices,
- factors);
+ accumulated_lengths, lengths, length_mode_, mask, indices, factors);
const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i);
if (!sampled_positions.is_empty()) {
@@ -254,54 +383,67 @@ class SampleCurveFunction : public fn::MultiFunction {
sampled_normals[i] = math::normalize(sampled_normals[i]);
}
}
+ if (!sampled_values.is_empty()) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ src_original_values.reinitialize(points.size());
+ source_data_->materialize_compressed_to_uninitialized(points, src_original_values.data());
+ src_evaluated_values.reinitialize(curves.evaluated_points_for_curve(curve_i).size());
+ curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
+ attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>();
+ MutableSpan<T> sampled_values_typed = sampled_values.typed<T>();
+ length_parameterize::interpolate_to_masked<T>(
+ src_evaluated_values_typed, indices, factors, mask, sampled_values_typed);
+ });
+ }
};
- if (curve_indices.is_single()) {
- sample_curve(curve_indices.get_internal_single(), mask);
+ if (const std::optional<int> curve_i = curve_indices.get_if_single()) {
+ if (curves.curves_range().contains(*curve_i)) {
+ sample_curve(*curve_i, mask);
+ }
+ else {
+ fill_invalid(mask);
+ }
}
else {
+ Vector<int64_t> invalid_indices;
MultiValueMap<int, int64_t> indices_per_curve;
devirtualize_varray(curve_indices, [&](const auto curve_indices) {
for (const int64_t i : mask) {
- indices_per_curve.add(curve_indices[i], i);
+ const int curve_i = curve_indices[i];
+ if (curves.curves_range().contains(curve_i)) {
+ indices_per_curve.add(curve_i, i);
+ }
+ else {
+ invalid_indices.append(i);
+ }
}
});
for (const int curve_i : indices_per_curve.keys()) {
sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i)));
}
+ fill_invalid(IndexMask(invalid_indices));
}
}
-};
-/**
- * Pre-process the lengths or factors used for the sampling, turning factors into lengths, and
- * clamping between zero and the total length of the curves. Do this as a separate operation in the
- * field tree to make the sampling simpler, and to let the evaluator optimize better.
- *
- * \todo Use a mutable single input instead when they are supported.
- */
-static Field<float> get_length_input_field(GeoNodeExecParams params,
- const GeometryNodeCurveSampleMode mode,
- const float curves_total_length)
-{
- if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- return params.extract_input<Field<float>>("Length");
+ private:
+ void evaluate_source()
+ {
+ const Curves &curves_id = *geometry_set_.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ source_context_.emplace(bke::CurvesFieldContext{curves, ATTR_DOMAIN_POINT});
+ source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num());
+ source_evaluator_->add(src_field_);
+ source_evaluator_->evaluate();
+ source_data_ = &source_evaluator_->get_evaluated(0);
}
-
- /* Convert the factor to a length. */
- Field<float> factor_field = params.get_input<Field<float>>("Factor");
- auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
- __func__,
- [curves_total_length](float factor) { return factor * curves_total_length; },
- fn::CustomMF_presets::AllSpanOrSingle());
-
- return Field<float>(FieldOperation::Create(std::move(clamp_fn), {std::move(factor_field)}), 0);
-}
+};
static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves)
{
- curves.ensure_evaluated_lengths();
Array<float> curve_lengths(curves.curves_num());
const VArray<bool> cyclic = curves.cyclic();
@@ -313,9 +455,56 @@ static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves)
return curve_lengths;
}
+static GField get_input_attribute_field(GeoNodeExecParams &params, const eCustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return params.extract_input<Field<float>>("Value_Float");
+ case CD_PROP_FLOAT3:
+ return params.extract_input<Field<float3>>("Value_Vector");
+ case CD_PROP_COLOR:
+ return params.extract_input<Field<ColorGeometry4f>>("Value_Color");
+ case CD_PROP_BOOL:
+ return params.extract_input<Field<bool>>("Value_Bool");
+ case CD_PROP_INT32:
+ return params.extract_input<Field<int>>("Value_Int");
+ default:
+ BLI_assert_unreachable();
+ }
+ return {};
+}
+
+static void output_attribute_field(GeoNodeExecParams &params, GField field)
+{
+ switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Value_Float", Field<float>(field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Value_Vector", Field<float3>(field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Value_Color", Field<ColorGeometry4f>(field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Value_Bool", Field<bool>(field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Value_Int", Field<int>(field));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curves");
if (!geometry_set.has_curves()) {
params.set_default_remaining_outputs();
return;
@@ -328,18 +517,18 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- Array<float> curve_lengths = curve_accumulated_lengths(curves);
- const float total_length = curve_lengths.last();
- if (total_length == 0.0f) {
- params.set_default_remaining_outputs();
- return;
- }
+ curves.ensure_evaluated_lengths();
const NodeGeometryCurveSample &storage = node_storage(params.node());
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
- Field<float> length_field = get_length_input_field(params, mode, total_length);
+ const eCustomDataType data_type = eCustomDataType(storage.data_type);
- auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set));
+ Field<float> length_field = params.extract_input<Field<float>>(
+ mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? "Factor" : "Length");
+ GField src_values_field = get_input_attribute_field(params, data_type);
+
+ auto sample_fn = std::make_unique<SampleCurveFunction>(
+ std::move(geometry_set), mode, std::move(src_values_field));
std::shared_ptr<FieldOperation> sample_op;
if (curves.curves_num() == 1) {
@@ -347,15 +536,26 @@ static void node_geo_exec(GeoNodeExecParams params)
{fn::make_constant_field<int>(0), std::move(length_field)});
}
else {
- auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(std::move(curve_lengths));
- auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
- sample_op = FieldOperation::Create(std::move(sample_fn),
- {Field<int>(index_op, 0), Field<float>(index_op, 1)});
+ Field<int> curve_index;
+ Field<float> length_in_curve;
+ if (storage.use_all_curves) {
+ auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(
+ curve_accumulated_lengths(curves), mode);
+ auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
+ curve_index = Field<int>(index_op, 0);
+ length_in_curve = Field<float>(index_op, 1);
+ }
+ else {
+ curve_index = params.extract_input<Field<int>>("Curve Index");
+ length_in_curve = std::move(length_field);
+ }
+ sample_op = FieldOperation::Create(std::move(sample_fn), {curve_index, length_in_curve});
}
params.set_output("Position", Field<float3>(sample_op, 0));
params.set_output("Tangent", Field<float3>(sample_op, 1));
params.set_output("Normal", Field<float3>(sample_op, 2));
+ output_attribute_field(params, GField(sample_op, 3));
}
} // namespace blender::nodes::node_geo_curve_sample_cc
@@ -374,6 +574,6 @@ void register_node_type_geo_curve_sample()
node_type_storage(
&ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage);
ntype.draw_buttons = file_ns::node_layout;
-
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}