diff options
-rw-r--r-- | source/blender/blenkernel/BKE_blender_version.h | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_300.cc | 19 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 18 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc | 336 |
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 ¶ms) +{ + 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 ¶ms) { + 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 ¶ms, 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 ¶ms, 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); } |