diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_spline.hh | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/spline_base.cc | 61 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 18 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 36 | ||||
-rw-r--r-- | source/blender/nodes/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_static_types.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 20 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc | 263 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc | 24 |
12 files changed, 403 insertions, 31 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 9b8eca77e6b..39024445808 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1465,6 +1465,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_CURVE_TRIM 1071 +#define GEO_NODE_CURVE_SAMPLE 1072 /** \} */ diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index f85e62768f7..176e44b3245 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -56,6 +56,8 @@ using SplinePtr = std::unique_ptr<Spline>; * along the length of a curve. * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples * along the spline which can be used to interpolate data from method 1. + * 4. #sample_lengths_to_index_factors does the same, but uses arbitrary parameter + * inputs, instead of sampling uniformly. * * Commonly used evaluated data is stored in caches on the spline itself so that operations on * splines don't need to worry about taking ownership of evaluated data when they don't need to. @@ -170,11 +172,16 @@ class Spline { LookupResult lookup_evaluated_length(const float length) const; blender::Array<float> sample_uniform_index_factors(const int samples_size) const; + blender::Array<float> sample_lengths_to_index_factors(blender::Span<float> parameters) const; + LookupResult lookup_data_from_index_factor(const float index_factor) const; void sample_with_index_factors(const blender::fn::GVArray &src, blender::Span<float> index_factors, blender::fn::GMutableSpan dst) const; + void sample_with_index_factors(blender::fn::GSpan src, + blender::Span<float> index_factors, + blender::fn::GMutableSpan dst) const; template<typename T> void sample_with_index_factors(const blender::VArray<T> &src, blender::Span<float> index_factors, diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3d5973b1d5e..dbea83ae0ca 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5115,6 +5115,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_primitive_quadrilateral(); register_node_type_geo_curve_primitive_spiral(); register_node_type_geo_curve_primitive_star(); + register_node_type_geo_curve_sample(); register_node_type_geo_curve_resample(); register_node_type_geo_curve_reverse(); register_node_type_geo_curve_subdivide(); diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index a7caae967f6..9652622d4a1 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -463,6 +463,60 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const return samples; } +#ifdef DEBUG +static void assert_sorted_array_in_range(Span<float> data, const float min, const float max) +{ + BLI_assert(data.first() >= min); + for (const int i : IndexRange(1, data.size() - 1)) { + BLI_assert(data[i] >= data[i - 1]); + } + BLI_assert(data.last() <= max); +} +#endif + +/** + * Transform an array of sorted length parameters into index factors. The result is indices + * and factors to the next index, encoded in floats. The logic for converting from the float + * values to interpolation data is in #lookup_data_from_index_factor. + * + * \param parameters: Lengths along the spline to be transformed into index factors + * (to save another allocation). Must be between zero and the total length of the spline. + * + * \note The implementation is similar to #sample_uniform_index_factors(), though + * the two loops are inverted, and obviously custom parameters are provided. + * + * \note This could have a result data argument instead of returning a span in the future. + */ +Array<float> Spline::sample_lengths_to_index_factors(const Span<float> parameters) const +{ + const Span<float> lengths = this->evaluated_lengths(); + const float total_length = this->length(); +#ifdef DEBUG + // assert_sorted_array_in_range(parameters, 0.0f, this->length()); +#endif + + Array<float> index_factors(parameters.size()); + + /* Store the length at the previous evaluated point in a variable so it can + * start out at zero (the lengths array doesn't contain 0 for the first point). */ + float prev_length = 0.0f; + int i_evaluated = 0; + for (const int i_sample : parameters.index_range()) { + const float sample_length = parameters[i_sample]; + + /* Skip over every evaluated point that fits before this sample. */ + while (lengths[i_evaluated] < sample_length) { + prev_length = lengths[i_evaluated]; + i_evaluated++; + } + + const float factor = (sample_length - prev_length) / (lengths[i_evaluated] - prev_length); + index_factors[i_sample] = i_evaluated + factor; + } + + return index_factors; +} + Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const { const int eval_size = this->evaluated_points_size(); @@ -497,6 +551,13 @@ GVArrayPtr Spline::interpolate_to_evaluated(GSpan data) const return this->interpolate_to_evaluated(GVArray_For_GSpan(data)); } +void Spline::sample_with_index_factors(GSpan src, + Span<float> index_factors, + GMutableSpan dst) const +{ + return this->sample_with_index_factors(GVArray_For_GSpan(src), index_factors, dst); +} + /** * Sample any input data with a value for each evaluated point (already interpolated to evaluated * points) to arbitrary parameters in between the evaluated points. The interpolation is quite diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 2b8e48557b6..e325a22a74d 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1376,10 +1376,15 @@ typedef struct NodeGeometryCurvePrimitiveQuad { } NodeGeometryCurvePrimitiveQuad; typedef struct NodeGeometryCurveResample { - /* GeometryNodeCurveSampleMode. */ + /* GeometryNodeCurveResampleMode. */ uint8_t mode; } NodeGeometryCurveResample; +typedef struct NodeGeometryCurveSample { + /* GeometryNodeCurveSampleMode. */ + uint8_t mode; +} NodeGeometryCurveSample; + typedef struct NodeGeometryCurveSubdivide { /* GeometryNodeAttributeInputMode (integer or attribute). */ uint8_t cuts_type; @@ -1391,7 +1396,7 @@ typedef struct NodeGeometryCurveTrim { } NodeGeometryCurveTrim; typedef struct NodeGeometryCurveToPoints { - /* GeometryNodeCurveSampleMode. */ + /* GeometryNodeCurveResampleMode. */ uint8_t mode; } NodeGeometryCurveToPoints; @@ -1943,10 +1948,15 @@ typedef enum GeometryNodeCurvePrimitiveBezierSegmentMode { GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_OFFSET = 1, } GeometryNodeCurvePrimitiveBezierSegmentMode; +typedef enum GeometryNodeCurveResampleMode { + GEO_NODE_CURVE_RESAMPLE_COUNT = 0, + GEO_NODE_CURVE_RESAMPLE_LENGTH = 1, + GEO_NODE_CURVE_RESAMPLE_EVALUATED = 2, +} GeometryNodeCurveResampleMode; + typedef enum GeometryNodeCurveSampleMode { - GEO_NODE_CURVE_SAMPLE_COUNT = 0, + GEO_NODE_CURVE_SAMPLE_FACTOR = 0, GEO_NODE_CURVE_SAMPLE_LENGTH = 1, - GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, } GeometryNodeCurveSampleMode; typedef enum GeometryNodeCurveInterpolateMode { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 6998a7d0afe..ce2c777b782 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9973,12 +9973,12 @@ static void def_geo_curve_resample(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_SAMPLE_COUNT, + {GEO_NODE_CURVE_RESAMPLE_COUNT, "COUNT", 0, "Count", "Sample the specified number of points along each spline"}, - {GEO_NODE_CURVE_SAMPLE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", @@ -9995,6 +9995,32 @@ static void def_geo_curve_resample(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_sample(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_FACTOR, + "COUNT", + 0, + "Count", + "Choose sample points on the curve based on the portion of the total length"}, + {GEO_NODE_CURVE_SAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Choose sample points on the curve based on the assumulated length at that point"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveResample", "storage"); + + 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", "How to specify the amount of samples"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_subdivide(StructRNA *srna) { PropertyRNA *prop; @@ -10012,18 +10038,18 @@ static void def_geo_curve_to_points(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_SAMPLE_EVALUATED, + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, "EVALUATED", 0, "Evaluated", "Create points from the curve's evaluated points, based on the resolution attribute for " "NURBS and Bezier splines"}, - {GEO_NODE_CURVE_SAMPLE_COUNT, + {GEO_NODE_CURVE_RESAMPLE_COUNT, "COUNT", 0, "Count", "Sample each spline by evenly distributing the specified number of points"}, - {GEO_NODE_CURVE_SAMPLE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 130032ccf71..974cbf95af0 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -173,6 +173,7 @@ set(SRC geometry/nodes/node_geo_curve_primitive_quadrilateral.cc geometry/nodes/node_geo_curve_primitive_spiral.cc geometry/nodes/node_geo_curve_primitive_star.cc + geometry/nodes/node_geo_curve_sample.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc geometry/nodes/node_geo_curve_subdivide.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 29a1d2cc853..b8d8f120e91 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -60,6 +60,7 @@ void register_node_type_geo_curve_primitive_quadratic_bezier(void); void register_node_type_geo_curve_primitive_quadrilateral(void); void register_node_type_geo_curve_primitive_spiral(void); void register_node_type_geo_curve_primitive_star(void); +void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_subdivide(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 42a0454d3e6..dd158cdaf75 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -300,6 +300,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") 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 ad0c453c2af..bc1159cff05 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -52,26 +52,26 @@ static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( sizeof(NodeGeometryCurveResample), __func__); - data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } namespace blender::nodes { struct SampleModeParam { - GeometryNodeCurveSampleMode mode; + GeometryNodeCurveResampleMode mode; std::optional<float> length; std::optional<int> count; }; @@ -161,7 +161,7 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, output_curve->resize(input_splines.size()); MutableSpan<SplinePtr> output_splines = output_curve->splines(); - if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); @@ -169,7 +169,7 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, } }); } - else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { const float length = input_splines[i]->length(); @@ -197,10 +197,10 @@ static void geo_node_resample_exec(GeoNodeExecParams params) const CurveEval &input_curve = *geometry_set.get_curve_for_read(); NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; SampleModeParam mode_param; mode_param.mode = mode; - if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { const int count = params.extract_input<int>("Count"); if (count < 1) { params.set_output("Geometry", GeometrySet()); @@ -208,7 +208,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) } mode_param.count.emplace(count); } - else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { /* Don't allow asymptotic count increase for low resolution values. */ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); mode_param.length.emplace(resolution); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc new file mode 100644 index 00000000000..734d17ac459 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -0,0 +1,263 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_Span; +using blender::fn::GVArray_Typed; + +static bNodeSocketTemplate geo_node_curve_sample_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Parameter")}, + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_STRING, N_("Position")}, + {SOCK_STRING, N_("Tangent")}, + {SOCK_STRING, N_("Normal")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_sample_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_sample_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN( + sizeof(NodeGeometryCurveSample), __func__); + + data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR; + node->storage = data; +} + +static void geo_node_curve_sample_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)node->storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + bNodeSocket *parameter_socket = ((bNodeSocket *)node->inputs.first)->next; + + if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { + node_sock_label(parameter_socket, "Factor"); + } + else { + BLI_assert(mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + node_sock_label(parameter_socket, "Length"); + } +} + +namespace blender::nodes { + +static AttributeDomain get_result_domain(const GeometryComponent &component, + const StringRef parameter_name, + const StringRef result_name) +{ + std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); + if (result_info) { + return result_info->domain; + } + std::optional<AttributeMetaData> parameter_info = component.attribute_get_meta_data( + parameter_name); + if (parameter_info) { + return parameter_info->domain; + } + return ATTR_DOMAIN_POINT; +} + +static CustomDataType get_result_type(const CurveComponent &curve_component, + const StringRef attribute_name) +{ + std::optional<AttributeMetaData> curve_meta_data = curve_component.attribute_get_meta_data( + attribute_name); + return curve_meta_data->data_type; +} + +using SamplePair = std::pair<GSpan, GMutableSpan>; + +/** TODO: Investigate chunkifying the first part, + * since #sample_lengths_to_index_factors is single threaded. */ +static void spline_sample_attributes(const Spline &spline, + Span<float> lengths, + Span<SamplePair> samples_extrapolated, + Span<SamplePair> samples) +{ + Array<int> original_indices(lengths.size()); + for (const int i : original_indices.index_range()) { + original_indices[i] = i; + } + + std::sort(original_indices.begin(), original_indices.end(), [lengths](int a, int b) { + return lengths[a] > lengths[b]; + }); + + const Array<float> index_factors = spline.sample_lengths_to_index_factors(lengths); + + for (const SamplePair &sample : samples_extrapolated) { + spline.sample_with_index_factors(sample.first, index_factors, sample.second); + } + + /* TODO: Clamp index factors. */ + + for (const SamplePair &sample : samples) { + spline.sample_with_index_factors(sample.first, index_factors, sample.second); + } +} + +static void execute_on_component(GeometryComponent &component, + const CurveComponent &curve_component, + const StringRef pararameter_name, + const StringRef position_name, + const StringRef tangent_name, + const StringRef normal_name, + const StringRef attribute_name, + const StringRef result_name) +{ + const CurveEval &curve = *curve_component.get_for_read(); + const Spline &spline = *curve.splines().first(); + + const AttributeDomain domain = get_result_domain(component, pararameter_name, result_name); + + GVArray_Typed<float> parameters = component.attribute_get_for_read<float>( + pararameter_name, domain, 0.0f); + VArray_Span<float> parameters_span{parameters}; + /* TODO: Multiply by length if in factor mode. */ + + Vector<OutputAttribute> output_attributes; + Vector<GVArrayPtr> owned_curve_attributes; + Vector<SamplePair> sample_data; + Vector<SamplePair> sample_data_extrapolated; + + if (!position_name.is_empty()) { + OutputAttribute result = component.attribute_try_get_for_output_only( + position_name, domain, CD_PROP_FLOAT3); + sample_data_extrapolated.append({spline.evaluated_positions(), result.as_span()}); + output_attributes.append(std::move(result)); + } + if (!tangent_name.is_empty()) { + OutputAttribute result = component.attribute_try_get_for_output_only( + tangent_name, domain, CD_PROP_FLOAT3); + sample_data.append({spline.evaluated_tangents(), result.as_span()}); + output_attributes.append(std::move(result)); + } + if (!normal_name.is_empty()) { + OutputAttribute result = component.attribute_try_get_for_output_only( + normal_name, domain, CD_PROP_FLOAT3); + sample_data.append({spline.evaluated_normals(), result.as_span()}); + output_attributes.append(std::move(result)); + } + if (!attribute_name.is_empty() && !result_name.is_empty()) { + OutputAttribute result = component.attribute_try_get_for_output_only( + result_name, domain, get_result_type(curve_component, attribute_name)); + std::optional<GSpan> attribute = spline.attributes.get_for_read(attribute_name); + if (attribute) { + GVArrayPtr attribute_interpolated = spline.interpolate_to_evaluated(*attribute); + sample_data.append({attribute_interpolated->get_internal_span(), result.as_span()}); + output_attributes.append(std::move(result)); + owned_curve_attributes.append(std::move(attribute_interpolated)); + } + } + + spline_sample_attributes(spline, parameters_span, sample_data_extrapolated, sample_data); + + for (OutputAttribute &output_attribute : output_attributes) { + output_attribute.save(); + } +} + +static void geo_node_sample_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + curve_set = bke::geometry_set_realize_instances(curve_set); + + if (!curve_set.has_curve()) { + params.set_output("Geometry", geometry_set); + return; + } + + const std::string pararameter_name = params.extract_input<std::string>("Parameter"); + const std::string position_name = params.extract_input<std::string>("Position"); + const std::string tangent_name = params.extract_input<std::string>("Tangent"); + const std::string normal_name = params.extract_input<std::string>("Normal"); + const std::string attribute_name = params.extract_input<std::string>("Attribute"); + const std::string result_name = params.extract_input<std::string>("Result"); + + const CurveComponent &curve_component = *curve_set.get_component_for_read<CurveComponent>(); + + if (attribute_name.empty()) { + if (position_name.empty() && tangent_name.empty() && normal_name.empty()) { + params.set_output("Geometry", geometry_set); + return; + } + } + else if (!curve_component.attribute_exists(attribute_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + attribute_name + "\""); + } + + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + if (geometry_set.has(type)) { + execute_on_component(geometry_set.get_component_for_write(type), + curve_component, + pararameter_name, + position_name, + tangent_name, + normal_name, + attribute_name, + result_name); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_sample() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, "Sample Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_sample_in, geo_node_curve_sample_out); + ntype.draw_buttons = geo_node_curve_sample_layout; + node_type_storage( + &ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_sample_init); + node_type_update(&ntype, geo_node_curve_sample_update); + ntype.geometry_node_execute = blender::nodes::geo_node_sample_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index bb8f560f92d..4edebf24d6a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -48,20 +48,20 @@ static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( sizeof(NodeGeometryCurveToPoints), __func__); - data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } namespace blender::nodes { @@ -81,13 +81,13 @@ static void evaluate_splines(Span<SplinePtr> splines) } static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveSampleMode mode, + const GeometryNodeCurveResampleMode mode, const CurveEval &curve, const Span<SplinePtr> splines) { const int size = curve.splines().size(); switch (mode) { - case GEO_NODE_CURVE_SAMPLE_COUNT: { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { const int count = params.extract_input<int>("Count"); if (count < 1) { return {0}; @@ -98,7 +98,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } return offsets; } - case GEO_NODE_CURVE_SAMPLE_LENGTH: { + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { /* Don't allow asymptotic count increase for low resolution values. */ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); Array<int> offsets(size + 1); @@ -110,7 +110,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, offsets.last() = offset; return offsets; } - case GEO_NODE_CURVE_SAMPLE_EVALUATED: { + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { return curve.evaluated_point_offsets(); } } @@ -303,7 +303,7 @@ void curve_create_default_rotation_attribute(Span<float3> tangents, static void geo_node_curve_to_points_exec(GeoNodeExecParams params) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); geometry_set = bke::geometry_set_realize_instances(geometry_set); @@ -333,11 +333,11 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, curve); switch (mode) { - case GEO_NODE_CURVE_SAMPLE_COUNT: - case GEO_NODE_CURVE_SAMPLE_LENGTH: + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: copy_uniform_sample_point_attributes(splines, offsets, new_attributes); break; - case GEO_NODE_CURVE_SAMPLE_EVALUATED: + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: copy_evaluated_point_attributes(splines, offsets, new_attributes); break; } |