diff options
-rw-r--r-- | release/scripts/startup/nodeitems_builtins.py | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_geometry_set.hh | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_spline.hh | 9 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/spline_base.cc | 20 | ||||
-rw-r--r-- | source/blender/nodes/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 2 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc | 172 |
8 files changed, 124 insertions, 96 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d617cfab988..783a55c6500 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -504,6 +504,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveSubdivide"), NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), + NodeItem("GeometryNodeCurveSample"), NodeItem("GeometryNodeMeshToCurve"), NodeItem("GeometryNodeCurveToPoints"), NodeItem("GeometryNodeCurveEndpoints"), diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index c79977752c9..77e827bf6f2 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -180,18 +180,6 @@ class GeometryComponent { return blender::fn::GVArray_Typed<T>(std::move(varray)); } - /* Should be used instead of the method above when the requested data type is known at compile - * time for better type safety. */ - template<typename T> - blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name, - const T &default_value) const - { - const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); - const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - std::unique_ptr varray = this->attribute_get_for_read(attribute_name, type, &default_value); - return blender::fn::GVArray_Typed<T>(std::move(varray)); - } - /** * Returns an "output attribute", which is essentially a mutable virtual array with some commonly * used convince features. The returned output attribute might be empty if requested attribute diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index f1c66de7773..176e44b3245 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -56,7 +56,7 @@ 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_length_parameters_to_index_factors does the same, but uses arbitrary parameter + * 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 @@ -172,13 +172,16 @@ class Spline { LookupResult lookup_evaluated_length(const float length) const; blender::Array<float> sample_uniform_index_factors(const int samples_size) const; - void sample_length_parameters_to_index_factors(blender::MutableSpan<float> parameters) 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, @@ -209,8 +212,6 @@ class Spline { return blender::fn::GVArray_Typed<T>(this->interpolate_to_evaluated(blender::fn::GSpan(data))); } - blender::fn::GVArrayPtr get_evaluated_attribute(const blender::StringRef name) const; - protected: virtual void correct_end_tangents() const = 0; virtual void copy_settings(Spline &dst) const = 0; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 715c2fa91d2..cc71151c1b0 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5116,8 +5116,6 @@ static void registerGeometryNodes() 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_to_mesh(); - register_node_type_geo_curve_to_points(); 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 e5e2fd7e87e..87d8825ad90 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -484,14 +484,19 @@ static void assert_sorted_array_in_range(Span<float> data, const float min, cons * * \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. */ -void Spline::sample_length_parameters_to_index_factors(MutableSpan<float> parameters) const +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()); + // 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; @@ -506,8 +511,10 @@ void Spline::sample_length_parameters_to_index_factors(MutableSpan<float> parame } const float factor = (sample_length - prev_length) / (lengths[i_evaluated] - prev_length); - parameters[i_sample] = i_evaluated + factor; + index_factors[i_sample] = i_evaluated + factor; } + + return index_factors; } Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const @@ -544,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/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 09085ac8799..e9ce823967e 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -174,8 +174,6 @@ set(SRC 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_to_mesh.cc - geometry/nodes/node_geo_curve_to_points.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 8a2b77118e5..73e711eb401 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -61,8 +61,6 @@ 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_to_mesh(void); -void register_node_type_geo_curve_to_points(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/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 5e8094169d7..d7f8bdd6bc4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -30,26 +30,29 @@ using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_For_Span; using blender::fn::GVArray_Typed; -static bNodeSocketTemplate geo_node_curve_resample_in[] = { +static bNodeSocketTemplate geo_node_curve_sample_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_STRING, N_("Parameter")}, {SOCK_GEOMETRY, N_("Curve")}, - {SOCK_GEOMETRY, N_("Attribute")}, - {SOCK_GEOMETRY, N_("Result")}, + {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_resample_out[] = { +static bNodeSocketTemplate geo_node_curve_sample_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, {-1, ""}, }; -static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +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_resample_init(bNodeTree *UNUSED(tree), bNode *node) +static void geo_node_curve_sample_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN( sizeof(NodeGeometryCurveSample), __func__); @@ -58,7 +61,7 @@ static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) +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; @@ -92,85 +95,107 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, return ATTR_DOMAIN_POINT; } -Array<float> calculate_spline_accumulated_lengths(Span<SplinePtr> splines) +static CustomDataType get_result_type(const CurveComponent &curve_component, + const StringRef attribute_name) { - Array<float> lengths(splines.size() + 1); - float length = 0.0f; - for (const int i : splines.index_range()) { - length += splines[i]->length(); - lengths[i] = length; - } - lengths.last() = length; - return lengths; + std::optional<AttributeMetaData> curve_meta_data = curve_component.attribute_get_meta_data( + attribute_name); + return curve_meta_data->data_type; } -static void spline_sample_data(const Span<GSpan> evaluated_spans) -{ -} +using SamplePair = std::pair<GSpan, GMutableSpan>; -/** - * 1. Sort input parameters - * 2. For each spline in the curve, sample the values on it. - */ -static void curve_sample_attributes(const Span<SplinePtr> splines, - const Span<float> spline_lengths, - const Span<GSpan> evaluated_spans, - const Span<float> parameters, - GMutableSpan result) +/** 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(parameters.size()); + 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(), [parameters](int a, int b) { - return parameters[a] > parameters[b]; + std::sort(original_indices.begin(), original_indices.end(), [lengths](int a, int b) { + return lengths[a] > lengths[b]; }); - Span<float> remaining_parameters = parameters; - - std::lower_bound + const Array<float> index_factors = spline.sample_lengths_to_index_factors(lengths); - int spline_start_index = 0; - int spline_end_index = 0; - for (const int i : splines.index_range()) { + for (const SamplePair &sample : samples_extrapolated) { + spline.sample_with_index_factors(sample.first, index_factors, sample.second); } - for (const int i : range) { + /* 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, - 0.0f); + GVArray_Typed<float> parameters = component.attribute_get_for_read<float>( + pararameter_name, domain, 0.0f); VArray_Span<float> parameters_span{parameters}; - std::optional<AttributeMetaData> curve_meta_data = curve_component.attribute_get_meta_data( - attribute_name); + /* 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)); + } + } - OutputAttribute result = component.attribute_try_get_for_output_only( - result_name, domain, curve_meta_data->data_type); - GMutableSpan result_span = result.as_span(); + spline_sample_attributes(spline, parameters_span, sample_data_extrapolated, sample_data); - const CurveEval &curve = *curve_component.get_for_read(); - const Array<float> lengths = calculate_spline_accumulated_lengths(curve.splines()); - - threading::parallel_for(IndexRange(result_span.size()), 1024, [&](IndexRange range) { - curve_sample_attributes(curve, - lengths, - attribute_name, - parameters_span.slice(range.start(), range.size()), - result_span.slice(range.start(), range.size())); - }); + for (OutputAttribute &output_attribute : output_attributes) { + output_attribute.save(); + } } -static void geo_node_resample_exec(GeoNodeExecParams params) +static void geo_node_sample_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); @@ -184,21 +209,23 @@ static void geo_node_resample_exec(GeoNodeExecParams params) } 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"); - if (pararameter_name.empty() || attribute_name.empty() || result_name.empty()) { - params.set_output("Geometry", geometry_set); - return; - } const CurveComponent &curve_component = *curve_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - if (!curve_component.attribute_exists(attribute_name)) { + 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 + "\""); - params.set_output("Geometry", geometry_set); - return; } for (const GeometryComponentType type : @@ -207,6 +234,9 @@ static void geo_node_resample_exec(GeoNodeExecParams params) execute_on_component(geometry_set.get_component_for_write(type), curve_component, pararameter_name, + position_name, + tangent_name, + normal_name, attribute_name, result_name); } @@ -217,17 +247,17 @@ static void geo_node_resample_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_resample() +void register_node_type_geo_curve_sample() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out); - ntype.draw_buttons = geo_node_curve_resample_layout; + 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_resample_init); - node_type_update(&ntype, geo_node_curve_resample_update); - ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec; + 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); } |