diff options
Diffstat (limited to 'source/blender/nodes/geometry')
58 files changed, 1878 insertions, 579 deletions
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index f38562a8926..48a83dc825b 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -98,6 +98,7 @@ set(SRC nodes/node_geo_curve_to_points.cc nodes/node_geo_curve_trim.cc nodes/node_geo_delete_geometry.cc + nodes/node_geo_duplicate_elements.cc nodes/node_geo_distribute_points_on_faces.cc nodes/node_geo_dual_mesh.cc nodes/node_geo_edge_split.cc @@ -116,6 +117,7 @@ set(SRC nodes/node_geo_input_mesh_edge_neighbors.cc nodes/node_geo_input_mesh_edge_vertices.cc nodes/node_geo_input_mesh_face_area.cc + nodes/node_geo_input_mesh_face_is_planar.cc nodes/node_geo_input_mesh_face_neighbors.cc nodes/node_geo_input_mesh_island.cc nodes/node_geo_input_mesh_vertex_neighbors.cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc index b83aa8b69a9..0980c2d6e72 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc @@ -137,15 +137,15 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_default_remaining_outputs(); return; } const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); evaluate_splines(splines); @@ -167,9 +167,9 @@ static void node_geo_exec(GeoNodeExecParams params) end_result.get_component_for_write<PointCloudComponent>(); CurveToPointsResults start_attributes = curve_to_points_create_result_attributes( - start_point_component, curve); + start_point_component, *curve); CurveToPointsResults end_attributes = curve_to_points_create_result_attributes( - end_point_component, curve); + end_point_component, *curve); copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes); copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc index 6deaf5b554a..2fe06a17adf 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc @@ -19,15 +19,15 @@ static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component.attribute_get_for_read( @@ -41,6 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) } }); + geometry_set.replace_curve(curve_eval_to_curves(*curve)); + params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc index f0a201c5adf..729ccca5f04 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc @@ -33,24 +33,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void select_curve_by_handle_type(const CurveEval &curve, - const BezierSpline::HandleType type, + const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { @@ -59,10 +59,10 @@ static void select_curve_by_handle_type(const CurveEval &curve, threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i_spline : range) { const Spline &spline = *splines[i_spline]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); - Span<BezierSpline::HandleType> types_left = bezier_spline.handle_types_left(); - Span<BezierSpline::HandleType> types_right = bezier_spline.handle_types_right(); + Span<int8_t> types_left = bezier_spline.handle_types_left(); + Span<int8_t> types_right = bezier_spline.handle_types_right(); for (const int i_point : IndexRange(bezier_spline.size())) { r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && types_left[i_point] == type) || @@ -81,7 +81,7 @@ static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles *storage = (const NodeGeometryCurveSelectHandles *)params.node().storage; - const BezierSpline::HandleType handle_type = handle_type_from_input_type( + const HandleType handle_type = handle_type_from_input_type( (GeometryNodeCurveHandleType)storage->handle_type); const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; @@ -89,9 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - const CurveEval *curve = curve_component.get_for_read(); - - if (curve != nullptr) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); const std::string selection_name = params.extract_input<std::string>("Selection"); OutputAttribute_Typed<bool> selection = curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc index b574b2e3cff..537c7c42610 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc @@ -31,20 +31,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void node_geo_exec(GeoNodeExecParams params) @@ -56,31 +56,31 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } /* Retrieve data for write access so we can avoid new allocations for the handles data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component.attribute_get_for_read( selection_name, ATTR_DOMAIN_POINT, true); - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + const HandleType new_handle_type = handle_type_from_input_type(type); int point_index = 0; bool has_bezier_spline = false; for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { point_index += spline->positions().size(); continue; } BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { /* In this case the automatically calculated handle types need to be "baked", because * they're possibly changing from a type that is calculated automatically to a type that * is positioned manually. */ @@ -101,6 +101,8 @@ static void node_geo_exec(GeoNodeExecParams params) bezier_spline.mark_cache_invalid(); } + geometry_set.replace_curve(curve_eval_to_curves(*curve)); + if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc index b8696e1eb52..4e3b0839da7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc @@ -148,8 +148,8 @@ static SplinePtr poly_to_bezier(const Spline &input) output->positions().copy_from(input.positions()); output->radii().copy_from(input.radii()); output->tilts().copy_from(input.tilts()); - output->handle_types_left().fill(BezierSpline::HandleType::Vector); - output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->handle_types_left().fill(BEZIER_HANDLE_VECTOR); + output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); output->set_resolution(12); Spline::copy_base_settings(input, *output); output->attributes = input.attributes; @@ -166,8 +166,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input) scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right()); scale_input_assign<float>(input.radii(), 3, 2, output->radii()); scale_input_assign<float>(input.tilts(), 3, 2, output->tilts()); - output->handle_types_left().fill(BezierSpline::HandleType::Align); - output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); output->set_resolution(nurbs_spline.resolution()); Spline::copy_base_settings(input, *output); output->attributes.reallocate(output->size()); @@ -183,11 +183,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input) static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) { switch (input.type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return input.copy(); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_bezier(input); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: if (input.size() < 6) { params.error_message_add( NodeWarningType::Info, @@ -202,6 +202,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params } return nurbs_to_bezier(input); } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + return {}; + } } BLI_assert_unreachable(); return {}; @@ -210,12 +214,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params static SplinePtr convert_to_nurbs(const Spline &input) { switch (input.type()) { - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return input.copy(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return bezier_to_nurbs(input); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_nurbs(input); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; @@ -229,40 +236,40 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component->get_for_read()); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component->attribute_get_for_read( selection_name, ATTR_DOMAIN_CURVE, true); std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - for (const int i : curve.splines().index_range()) { + for (const int i : curve->splines().index_range()) { if (selection[i]) { switch (output_type) { case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + new_curve->add_spline(convert_to_poly_spline(*curve->splines()[i])); break; case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + new_curve->add_spline(convert_to_bezier(*curve->splines()[i], params)); break; case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + new_curve->add_spline(convert_to_nurbs(*curve->splines()[i])); break; } } else { - new_curve->add_spline(curve.splines()[i]->copy()); + new_curve->add_spline(curve->splines()[i]->copy()); } } - new_curve->attributes = curve.attributes; - params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); + new_curve->attributes = curve->attributes; + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*new_curve))); } } // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc index 8ae9df78936..03f7aec8838 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc @@ -111,8 +111,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, MutableSpan<float3> dst_positions, MutableSpan<float3> dst_handles_left, MutableSpan<float3> dst_handles_right, - MutableSpan<BezierSpline::HandleType> dst_type_left, - MutableSpan<BezierSpline::HandleType> dst_type_right) + MutableSpan<int8_t> dst_type_left, + MutableSpan<int8_t> dst_type_right) { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; @@ -122,10 +122,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Vector; + dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { @@ -136,10 +136,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, } else { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Free; + dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; @@ -187,8 +187,8 @@ static void subdivide_bezier_spline(const BezierSpline &src, MutableSpan<float3> dst_positions = dst.positions(); MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); - MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); + MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { @@ -235,26 +235,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline, subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); switch (src_spline.type()) { - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { const PolySpline &src = static_cast<const PolySpline &>(src_spline); PolySpline &dst = static_cast<PolySpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); break; } - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); subdivide_bezier_spline(src, offsets, dst); dst.mark_cache_invalid(); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -338,7 +342,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Geometry", geometry_set); return; } @@ -350,9 +354,11 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); + std::unique_ptr<CurveEval> output_curve = subdivide_curve( + *curves_to_curve_eval(*component.get_for_read()), cuts); - params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); + params.set_output("Geometry", + GeometrySet::create_with_curves(curve_eval_to_curves(*output_curve))); } } // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc index 64627e61910..f8fcc3cc363 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc @@ -283,19 +283,19 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Geometry", GeometrySet()); return; } const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); evaluate_splines(splines); - const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_size = offsets.last(); if (total_size == 0) { params.set_output("Geometry", GeometrySet()); @@ -306,7 +306,7 @@ static void node_geo_exec(GeoNodeExecParams params) PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>(); CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, - curve); + *curve); switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: case GEO_NODE_CURVE_RESAMPLE_LENGTH: diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc index a0b862546bc..ca98d83c137 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc @@ -111,9 +111,9 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data(spline.radii(), r_spline.radii(), mask); copy_data(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: break; - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); BezierSpline &dst = static_cast<BezierSpline &>(r_spline); copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); @@ -122,12 +122,16 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data(src.handle_types_right(), dst.handle_types_right(), mask); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); NURBSpline &dst = static_cast<NURBSpline &>(r_spline); copy_data(src.weights(), dst.weights(), mask); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -231,9 +235,9 @@ static void delete_curve_selection(const CurveComponent &in_component, const bool invert) { std::unique_ptr<CurveEval> r_curve = curve_delete( - *in_component.get_for_read(), selection_name, invert); + *curves_to_curve_eval(*in_component.get_for_read()), selection_name, invert); if (r_curve) { - r_component.replace(r_curve.release()); + r_component.replace(curve_eval_to_curves(*r_curve)); } else { r_component.clear(); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc index ff86a92f2c7..8991261a21a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc @@ -48,7 +48,7 @@ static void node_geo_exec(GeoNodeExecParams params) std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert( component, IndexMask(selected_edge_indices)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); } } // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index b6d677154d0..b3fe9d160b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_CURVE: { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 6424fccbe04..cb7132d5ea2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -33,25 +33,18 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); - if (geometry_set.has<MeshComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names); - } - if (geometry_set.has<PointCloudComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); - } - if (geometry_set.has<CurveComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); - } - if (geometry_set.has<InstancesComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names); + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}) { + if (geometry_set.has(type)) { + remove_attribute(geometry_set.get_component_for_write(type), params, attribute_names); + } } params.set_output("Geometry", geometry_set); } + } // namespace blender::nodes::node_geo_attribute_remove_cc void register_node_type_geo_attribute_remove() diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 59147e9b23f..412f35d62fd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -161,9 +161,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) positions_span = varray.get_internal_span(); } - if (geometry_set.has_curve()) { - const CurveEval &curve = *geometry_set.get_curve_for_read(); - for (const SplinePtr &spline : curve.splines()) { + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { positions_span = spline->evaluated_positions(); total_size += positions_span.size(); count++; @@ -201,9 +202,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) offset += varray.size(); } - if (geometry_set.has_curve()) { - const CurveEval &curve = *geometry_set.get_curve_for_read(); - for (const SplinePtr &spline : curve.splines()) { + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { Span<float3> array = spline->evaluated_positions(); positions.as_mutable_span().slice(offset, array.size()).copy_from(array); offset += array.size(); @@ -272,8 +274,8 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) if (set.has_mesh()) { read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords); } - if (set.has_curve()) { - read_curve_positions(*set.get_curve_for_read(), transforms, &coords); + if (set.has_curves()) { + read_curve_positions(*curves_to_curve_eval(*set.get_curves_for_read()), transforms, &coords); } } return hull_from_bullet(nullptr, coords); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index 65aad0fcbf1..ce3058c7d42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -59,10 +59,13 @@ class EndpointFieldInput final : public GeometryFieldInput { } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); + if (!curve_component.has_curves()) { + return nullptr; + } - Array<int> control_point_offsets = curve->control_point_offsets(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + Array<int> control_point_offsets = curve->control_point_offsets(); if (curve == nullptr || control_point_offsets.last() == 0) { return nullptr; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 9824b2b2ece..6702ee6c0aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -113,12 +113,13 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &resul static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } - const CurveEval &curve = *geometry_set.get_curve_for_read(); - if (curve.splines().is_empty()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + if (curve->splines().is_empty()) { geometry_set.replace_curve(nullptr); return; } @@ -127,7 +128,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : CDT_INSIDE_WITH_HOLES; - const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type); + const blender::meshintersect::CDT_result<double> results = do_cdt(*curve, output_type); Mesh *mesh = cdt_to_mesh(results); geometry_set.replace_mesh(mesh); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 94425ab48f4..24d72ad553b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -394,9 +394,9 @@ static void update_bezier_positions(const FilletData &fd, dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - handle_length * next_dir; dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = - BezierSpline::HandleType::Align; + BEZIER_HANDLE_ALIGN; dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = - BezierSpline::HandleType::Vector; + BEZIER_HANDLE_VECTOR; dst_spline.mark_cache_invalid(); /* Calculate the center of the radius to be formed. */ @@ -406,8 +406,8 @@ static void update_bezier_positions(const FilletData &fd, float radius; radius_vec = math::normalize_and_get_length(radius_vec, radius); - dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align); - dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); + dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); /* For each of the vertices in between the end points. */ for (const int j : IndexRange(1, count - 2)) { @@ -512,12 +512,12 @@ static SplinePtr fillet_spline(const Spline &spline, copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); switch (spline.type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline); BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector); - dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector); + dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR); + dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR); update_poly_positions(fd, dst_spline, src_spline, point_counts); } else { @@ -525,17 +525,21 @@ static SplinePtr fillet_spline(const Spline &spline, } break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline); NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr); copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); update_poly_positions(fd, dst_spline, src_spline, point_counts); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } return dst_spline_ptr; @@ -568,7 +572,7 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, const std::optional<Field<int>> &count_field, const bool limit_radius) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -599,10 +603,10 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, fillet_param.limit_radius = limit_radius; - const CurveEval &input_curve = *geometry_set.get_curve_for_read(); - std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param); + const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read()); + std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param); - geometry_set.replace_curve(output_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 26a8ad2d988..ccd3a587e63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -31,30 +31,30 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void select_by_handle_type(const CurveEval &curve, - const BezierSpline::HandleType type, + const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { int offset = 0; for (const SplinePtr &spline : curve.splines()) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { r_selection.slice(offset, spline->size()).fill(false); offset += spline->size(); } @@ -71,11 +71,11 @@ static void select_by_handle_type(const CurveEval &curve, } class HandleTypeFieldInput final : public GeometryFieldInput { - BezierSpline::HandleType type_; + HandleType type_; GeometryNodeCurveHandleMode mode_; public: - HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) + HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode) : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode) @@ -92,14 +92,14 @@ class HandleTypeFieldInput final : public GeometryFieldInput { } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); + const Curves *curve = curve_component.get_for_read(); if (curve == nullptr) { return {}; } if (domain == ATTR_DOMAIN_POINT) { Array<bool> selection(mask.min_array_size()); - select_by_handle_type(*curve, type_, mode_, selection); + select_by_handle_type(*curves_to_curve_eval(*curve), type_, mode_, selection); return VArray<bool>::ForContainer(std::move(selection)); } return {}; @@ -124,7 +124,7 @@ class HandleTypeFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles &storage = node_storage(params.node()); - const BezierSpline::HandleType handle_type = handle_type_from_input_type( + const HandleType handle_type = handle_type_from_input_type( (GeometryNodeCurveHandleType)storage.handle_type); const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index 82621189964..d5769c691c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -14,13 +14,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); - if (!curve_set.has_curve()) { + if (!curve_set.has_curves()) { params.set_default_remaining_outputs(); return; } - const CurveEval &curve = *curve_set.get_curve_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_set.get_curves_for_read()); float length = 0.0f; - for (const SplinePtr &spline : curve.splines()) { + for (const SplinePtr &spline : curve->splines()) { length += spline->length(); } params.set_output("Length", length); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc index 9919e24473e..6c7d7ed375b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include <numeric> + #include "BLI_math_base_safe.h" + +#include "BKE_curves.hh" + #include "UI_interface.h" #include "UI_resources.h" + #include "node_geometry_util.hh" -#include <numeric> namespace blender::nodes::node_geo_curve_primitive_arc_cc { @@ -139,32 +143,24 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) return (ELEM(a, b, b * -1.0f)); } -static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution, - const float3 a, - const float3 b, - const float3 c, - float angle_offset, - const bool connect_center, - const bool invert_arc, - float3 &r_center, - float3 &r_normal, - float &r_radius) +static Curves *create_arc_curve_from_points(const int resolution, + const float3 a, + const float3 b, + const float3 c, + float angle_offset, + const bool connect_center, + const bool invert_arc, + float3 &r_center, + float3 &r_normal, + float &r_radius) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - if (connect_center) { - spline->resize(resolution + 1); - } - else { - spline->resize(resolution); - } + const int size = connect_center ? resolution + 1 : resolution; + Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); const int stepcount = resolution - 1; const int centerpoint = resolution; - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const bool is_colinear = colinear_f3_f3_f3(a, b, c); @@ -254,7 +250,7 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut } if (connect_center) { - spline->set_cyclic(true); + curves.cyclic().first() = true; positions[centerpoint] = center; } @@ -263,36 +259,26 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut normal = -normal; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); r_center = center; r_radius = radius; r_normal = normal; - return curve; + return curves_id; } -static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution, - const float radius, - const float start_angle, - const float sweep_angle, - const bool connect_center, - const bool invert_arc) +static Curves *create_arc_curve_from_radius(const int resolution, + const float radius, + const float start_angle, + const float sweep_angle, + const bool connect_center, + const bool invert_arc) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - if (connect_center) { - spline->resize(resolution + 1); - } - else { - spline->resize(resolution); - } + const int size = connect_center ? resolution + 1 : resolution; + Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); const int stepcount = resolution - 1; const int centerpoint = resolution; - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle; @@ -305,13 +291,11 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolut } if (connect_center) { - spline->set_cyclic(true); + curves.cyclic().first() = true; positions[centerpoint] = float3(0.0f, 0.0f, 0.0f); } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -322,35 +306,35 @@ static void node_geo_exec(GeoNodeExecParams params) switch (mode) { case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: { - std::unique_ptr<CurveEval> curve; float3 r_center, r_normal; float r_radius; - curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2), - params.extract_input<float3>("Start"), - params.extract_input<float3>("Middle"), - params.extract_input<float3>("End"), - params.extract_input<float>("Offset Angle"), - params.extract_input<bool>("Connect Center"), - params.extract_input<bool>("Invert Arc"), - r_center, - r_normal, - r_radius); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_arc_curve_from_points( + std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float3>("Start"), + params.extract_input<float3>("Middle"), + params.extract_input<float3>("End"), + params.extract_input<float>("Offset Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc"), + r_center, + r_normal, + r_radius); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); params.set_output("Center", r_center); params.set_output("Normal", r_normal); params.set_output("Radius", r_radius); break; } case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: { - std::unique_ptr<CurveEval> curve; - curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2), - params.extract_input<float>("Radius"), - params.extract_input<float>("Start Angle"), - params.extract_input<float>("Sweep Angle"), - params.extract_input<bool>("Connect Center"), - params.extract_input<bool>("Invert Arc")); - - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_arc_curve_from_radius( + std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float>("Radius"), + params.extract_input<float>("Start Angle"), + params.extract_input<float>("Sweep Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc")); + + params.set_output("Curve", GeometrySet::create_with_curves(curves)); break; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index c6b9018f0db..78e1613b630 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -69,21 +69,30 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( spline->resize(2); MutableSpan<float3> positions = spline->positions(); - spline->handle_types_left().fill(BezierSpline::HandleType::Align); - spline->handle_types_right().fill(BezierSpline::HandleType::Align); + spline->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + spline->handle_types_right().fill(BEZIER_HANDLE_ALIGN); spline->radii().fill(1.0f); spline->tilts().fill(0.0f); positions.first() = start; positions.last() = end; + MutableSpan<float3> handles_right = spline->handle_positions_right(); + MutableSpan<float3> handles_left = spline->handle_positions_left(); + if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) { - spline->set_handle_position_right(0, start_handle_right); - spline->set_handle_position_left(1, end_handle_left); + handles_left.first() = 2.0f * start - start_handle_right; + handles_right.first() = start_handle_right; + + handles_left.last() = end_handle_left; + handles_right.last() = 2.0f * end - end_handle_left; } else { - spline->set_handle_position_right(0, start + start_handle_right); - spline->set_handle_position_left(1, end + end_handle_left); + handles_left.first() = start - start_handle_right; + handles_right.first() = start + start_handle_right; + + handles_left.last() = end + end_handle_left; + handles_right.last() = end - end_handle_left; } curve->add_spline(std::move(spline)); @@ -104,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<float3>("End Handle"), std::max(params.extract_input<int>("Resolution"), 1), mode); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); } } // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 44505b61a27..874e29dda86 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -92,7 +92,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) return (ELEM(a, b, b * -1.0f)); } -static std::unique_ptr<CurveEval> create_point_circle_curve( +static Curves *create_point_circle_curve( const float3 p1, const float3 p2, const float3 p3, const int resolution, float3 &r_center) { if (colinear_f3_f3_f3(p1, p2, p3)) { @@ -100,11 +100,11 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( return nullptr; } - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(resolution); - MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> positions = curves.positions(); float3 center; /* Midpoints of `P1->P2` and `P2->P3`. */ @@ -147,24 +147,17 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4; } - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - r_center = center; - return curve; + return curves_id; } -static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolution, - const float radius) +static Curves *create_radius_circle_curve(const int resolution, const float radius) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(resolution); - MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> positions = curves.positions(); const float theta_step = (2.0f * M_PI) / float(resolution); for (int i : IndexRange(resolution)) { @@ -173,12 +166,8 @@ static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolutio const float y = radius * sin(theta); positions[i] = float3(x, y, 0.0f); } - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -187,23 +176,23 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) storage.mode; - std::unique_ptr<CurveEval> curve; + Curves *curves; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { float3 center_point; - curve = create_point_circle_curve(params.extract_input<float3>("Point 1"), - params.extract_input<float3>("Point 2"), - params.extract_input<float3>("Point 3"), - std::max(params.extract_input<int>("Resolution"), 3), - center_point); + curves = create_point_circle_curve(params.extract_input<float3>("Point 1"), + params.extract_input<float3>("Point 2"), + params.extract_input<float3>("Point 3"), + std::max(params.extract_input<int>("Resolution"), 3), + center_point); params.set_output("Center", center_point); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS) { - curve = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), - params.extract_input<float>("Radius")); + curves = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), + params.extract_input<float>("Radius")); } - if (curve) { - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + if (curves) { + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index d11af3b1cc0..2e2f4254752 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -60,39 +60,28 @@ static void node_update(bNodeTree *ntree, bNode *node) ntree, length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); } -static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end) +static Curves *create_point_line_curve(const float3 start, const float3 end) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - positions[0] = start; - positions[1] = end; - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + curves.positions().first() = start; + curves.positions().last() = end; + + return curves_id; } -static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start, - const float3 direction, - const float length) +static Curves *create_direction_line_curve(const float3 start, + const float3 direction, + const float length) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - positions[0] = start; - positions[1] = math::normalize(direction) * length + start; - - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + curves.positions().first() = start; + curves.positions().last() = math::normalize(direction) * length + start; + + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -100,18 +89,18 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurvePrimitiveLine &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode; - std::unique_ptr<CurveEval> curve; + Curves *curves = nullptr; if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) { - curve = create_point_line_curve(params.extract_input<float3>("Start"), - params.extract_input<float3>("End")); + curves = create_point_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("End")); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION) { - curve = create_direction_line_curve(params.extract_input<float3>("Start"), - params.extract_input<float3>("Direction"), - params.extract_input<float>("Length")); + curves = create_direction_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("Direction"), + params.extract_input<float>("Length")); } - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_line_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index 456f6e55c1e..37810ccaff5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc { @@ -28,18 +28,15 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, - const float3 p2, - const float3 p3, - const int resolution) +static Curves *create_quadratic_bezier_curve(const float3 p1, + const float3 p2, + const float3 p3, + const int resolution) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution + 1, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); - spline->resize(resolution + 1); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float step = 1.0f / resolution; for (const int i : IndexRange(resolution + 1)) { @@ -49,19 +46,17 @@ static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, positions[i] = math::interpolate(q1, q2, factor); } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) { - std::unique_ptr<CurveEval> curve = create_quadratic_bezier_curve( + Curves *curves = create_quadratic_bezier_curve( params.extract_input<float3>("Start"), params.extract_input<float3>("Middle"), params.extract_input<float3>("End"), std::max(params.extract_input<int>("Resolution"), 3)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index b6a847eebf4..ad3123a6a4a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -216,13 +216,11 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurvePrimitiveQuad &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveQuadMode mode = (GeometryNodeCurvePrimitiveQuadMode)storage.mode; - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->resize(4); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - MutableSpan<float3> positions = spline->positions(); + Curves *curves_id = bke::curves_new_nomain_single(4, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; + + MutableSpan<float3> positions = curves.positions(); switch (mode) { case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE: @@ -262,9 +260,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves_id)); } } // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index f448ddabd2b..22619577d04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -35,26 +35,23 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, - const int resolution, - const float start_radius, - const float end_radius, - const float height, - const bool direction) +static Curves *create_spiral_curve(const float rotations, + const int resolution, + const float start_radius, + const float end_radius, + const float height, + const bool direction) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; const float delta_height = height / (float)totalpoints; const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * (direction ? 1.0f : -1.0f); - spline->resize(totalpoints + 1); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + Curves *curves_id = bke::curves_new_nomain_single(totalpoints + 1, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + MutableSpan<float3> positions = curves.positions(); for (const int i : IndexRange(totalpoints + 1)) { const float theta = i * delta_theta; @@ -66,9 +63,7 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, positions[i] = {x, y, z}; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -79,14 +74,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr<CurveEval> curve = create_spiral_curve( - rotations, - std::max(params.extract_input<int>("Resolution"), 1), - params.extract_input<float>("Start Radius"), - params.extract_input<float>("End Radius"), - params.extract_input<float>("Height"), - params.extract_input<bool>("Reverse")); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_spiral_curve(rotations, + std::max(params.extract_input<int>("Resolution"), 1), + params.extract_input<float>("Start Radius"), + params.extract_input<float>("End Radius"), + params.extract_input<float>("Height"), + params.extract_input<bool>("Reverse")); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_spiral_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 5969af43bc1..e7e899881cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -33,19 +33,16 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("An attribute field with a selection of the outer points")); } -static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, - const float outer_radius, - const float twist, - const int points) +static Curves *create_star_curve(const float inner_radius, + const float outer_radius, + const float twist, + const int points) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->set_cyclic(true); + Curves *curves_id = bke::curves_new_nomain_single(points * 2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(points * 2); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float theta_step = (2.0f * M_PI) / float(points); for (const int i : IndexRange(points)) { @@ -58,10 +55,7 @@ static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, positions[i * 2 + 1] = {inner_x, inner_y, 0.0f}; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - - return curve; + return curves_id; } static void create_selection_output(CurveComponent &component, @@ -78,12 +72,11 @@ static void create_selection_output(CurveComponent &component, static void node_geo_exec(GeoNodeExecParams params) { - std::unique_ptr<CurveEval> curve = create_star_curve( - std::max(params.extract_input<float>("Inner Radius"), 0.0f), - std::max(params.extract_input<float>("Outer Radius"), 0.0f), - params.extract_input<float>("Twist"), - std::max(params.extract_input<int>("Points"), 3)); - GeometrySet output = GeometrySet::create_with_curve(curve.release()); + Curves *curves = create_star_curve(std::max(params.extract_input<float>("Inner Radius"), 0.0f), + std::max(params.extract_input<float>("Outer Radius"), 0.0f), + params.extract_input<float>("Twist"), + std::max(params.extract_input<int>("Points"), 3)); + GeometrySet output = GeometrySet::create_with_curves(curves); if (params.output_is_required("Outer Points")) { StrongAnonymousAttributeID attribute_output("Outer Points"); 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 d2afeaa7094..c5814a9a1dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -158,7 +158,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src) static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component, const SampleModeParam &mode_param) { - const CurveEval *input_curve = component->get_for_read(); + const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component->get_for_read()); GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); @@ -235,14 +235,14 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component static void geometry_set_curve_resample(GeometrySet &geometry_set, const SampleModeParam &mode_param) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } std::unique_ptr<CurveEval> output_curve = resample_curve( geometry_set.get_component_for_read<CurveComponent>(), mode_param); - geometry_set.replace_curve(output_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 0ef3230937b..8393f9615aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -20,7 +20,7 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -34,13 +34,15 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - CurveEval &curve = *component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write()); + MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) { for (const int i : range) { splines[selection[i]]->reverse(); } }); + + component.replace(curve_eval_to_curves(*curve)); }); params.set_output("Curve", std::move(geometry_set)); 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 152828b284c..6661d03a851 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -122,12 +122,13 @@ class SampleCurveFunction : public fn::MultiFunction { } }; - if (!geometry_set_.has_curve()) { + if (!geometry_set_.has_curves()) { return return_default(); } const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>(); - const CurveEval *curve = curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component->get_for_read()); Span<SplinePtr> splines = curve->splines(); if (splines.is_empty()) { return return_default(); @@ -234,12 +235,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const CurveEval *curve = component->get_for_read(); - if (curve == nullptr) { + if (!component->has_curves()) { params.set_default_remaining_outputs(); return; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read()); + if (curve->splines().is_empty()) { params.set_default_remaining_outputs(); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 3d7b2fddf72..e8da4154586 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -33,20 +33,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void node_geo_exec(GeoNodeExecParams params) @@ -60,14 +60,14 @@ static void node_geo_exec(GeoNodeExecParams params) bool has_bezier_spline = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } /* Retrieve data for write access so we can avoid new allocations for the handles data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -77,18 +77,18 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + const HandleType new_handle_type = handle_type_from_input_type(type); int point_index = 0; for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { point_index += spline->positions().size(); continue; } has_bezier_spline = true; BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { /* In this case the automatically calculated handle types need to be "baked", because * they're possibly changing from a type that is calculated automatically to a type that * is positioned manually. */ @@ -108,6 +108,8 @@ static void node_geo_exec(GeoNodeExecParams params) } bezier_spline.mark_cache_invalid(); } + + curve_component.replace(curve_eval_to_curves(*curve)); }); if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index a303be99242..3edaccba506 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -100,18 +100,22 @@ static Array<float> curve_length_point_domain(const CurveEval &curve) MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())}; spline_factors.first() = 0.0f; switch (splines[i]->type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors); break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } }); @@ -201,8 +205,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component.get_for_read()); return construct_curve_parameter_varray(*curve, mask, domain); } } @@ -234,8 +239,8 @@ class CurveLengthFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); return construct_curve_length_varray(*curve, mask, domain); } } @@ -267,8 +272,9 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component.get_for_read()); return construct_index_on_spline_varray(*curve, mask, domain); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 4e4cabd3c33..55610ec86ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -259,8 +259,8 @@ static SplinePtr poly_to_bezier(const Spline &input) output->positions().copy_from(input.positions()); output->radii().copy_from(input.radii()); output->tilts().copy_from(input.tilts()); - output->handle_types_left().fill(BezierSpline::HandleType::Vector); - output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->handle_types_left().fill(BEZIER_HANDLE_VECTOR); + output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); output->set_resolution(12); Spline::copy_base_settings(input, *output); output->attributes = input.attributes; @@ -298,8 +298,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input) nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode); scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left()); scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right()); - output->handle_types_left().fill(BezierSpline::HandleType::Align); - output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); output->set_resolution(nurbs_spline.resolution()); Spline::copy_base_settings(nurbs_spline, *output); output->attributes.reallocate(output->size()); @@ -315,11 +315,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input) static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) { switch (input.type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return input.copy(); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_bezier(input); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: if (input.size() < 4) { params.error_message_add( NodeWarningType::Info, @@ -327,6 +327,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params return input.copy(); } return nurbs_to_bezier(input); + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + return {}; + } } BLI_assert_unreachable(); return {}; @@ -335,12 +339,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params static SplinePtr convert_to_nurbs(const Spline &input) { switch (input.type()) { - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return input.copy(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return bezier_to_nurbs(input); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_nurbs(input); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; @@ -355,45 +362,48 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component->get_for_read()); GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + Span<SplinePtr> src_splines = curve->splines(); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - new_curve->resize(curve.splines().size()); + new_curve->resize(src_splines.size()); - threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) { + threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) { for (const int i : range) { if (selection[i]) { switch (output_type) { case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]); + new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]); break; case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params); + new_curve->splines()[i] = convert_to_bezier(*src_splines[i], params); break; case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]); + new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]); break; } } else { - new_curve->splines()[i] = curve.splines()[i]->copy(); + new_curve->splines()[i] = src_splines[i]->copy(); } } }); - new_curve->attributes = curve.attributes; - geometry_set.replace_curve(new_curve.release()); + new_curve->attributes = curve->attributes; + geometry_set.replace_curve(curve_eval_to_curves(*new_curve)); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 9daf3ff0594..bbe57b2b3fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -93,8 +93,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, MutableSpan<float3> dst_positions, MutableSpan<float3> dst_handles_left, MutableSpan<float3> dst_handles_right, - MutableSpan<BezierSpline::HandleType> dst_type_left, - MutableSpan<BezierSpline::HandleType> dst_type_right) + MutableSpan<int8_t> dst_type_left, + MutableSpan<int8_t> dst_type_right) { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; @@ -104,10 +104,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Vector; + dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { @@ -118,10 +118,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, } else { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Free; + dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; @@ -169,8 +169,8 @@ static void subdivide_bezier_spline(const BezierSpline &src, MutableSpan<float3> dst_positions = dst.positions(); MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); - MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); + MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { @@ -217,26 +217,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline, subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); switch (src_spline.type()) { - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { const PolySpline &src = static_cast<const PolySpline &>(src_spline); PolySpline &dst = static_cast<PolySpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); break; } - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); subdivide_bezier_spline(src, offsets, dst); dst.mark_cache_invalid(); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -320,7 +324,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<int> cuts_field = params.extract_input<Field<int>>("Cuts"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -336,9 +340,9 @@ static void node_geo_exec(GeoNodeExecParams params) if (cuts.is_single() && cuts.get_internal_single() < 1) { return; } - - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); - geometry_set.replace_curve(output_curve.release()); + std::unique_ptr<CurveEval> output_curve = subdivide_curve( + *curves_to_curve_eval(*component.get_for_read()), cuts); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); }); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index ed497b6fbe0..e7a8c61290b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -27,14 +27,16 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set, const bool fill_caps) { - const CurveEval *curve = geometry_set.get_curve_for_read(); - const CurveEval *profile_curve = profile_set.get_curve_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const Curves *profile_curves = profile_set.get_curves_for_read(); - if (profile_curve == nullptr) { + if (profile_curves == nullptr) { Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { + const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves); Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } @@ -46,10 +48,10 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); const bool fill_caps = params.extract_input<bool>("Fill Caps"); - bool has_curve = false; + bool has_curves = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { - has_curve = true; + if (geometry_set.has_curves()) { + has_curves = true; geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); 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 7481f7248a1..1eb18b2f910 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 @@ -321,15 +321,16 @@ static void node_geo_exec(GeoNodeExecParams params) attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - const CurveEval &curve = *geometry_set.get_curve_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); - const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_size = offsets.last(); if (total_size == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); @@ -339,7 +340,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( - points, curve, attribute_outputs); + points, *curve, attribute_outputs); switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: @@ -351,7 +352,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } - copy_spline_domain_attributes(curve, offsets, points); + copy_spline_domain_attributes(*curve, offsets, points); if (!point_attributes.rotations.is_empty()) { curve_create_default_rotation_attribute( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index abc5b1649c7..a3dab1b50fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -367,15 +367,18 @@ static void trim_spline(SplinePtr &spline, const Spline::LookupResult end) { switch (spline->type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: trim_bezier_spline(*spline, start, end); break; - case Spline::Type::Poly: + case CURVE_TYPE_POLY: trim_poly_spline(*spline, start, end); break; - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + spline = {}; } spline->mark_cache_invalid(); } @@ -400,8 +403,8 @@ static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &l const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( trim.left_index, trim.right_index, trim.factor); bezier.positions().first() = new_point.position; - bezier.handle_types_left().first() = BezierSpline::HandleType::Free; - bezier.handle_types_right().first() = BezierSpline::HandleType::Free; + bezier.handle_types_left().first() = BEZIER_HANDLE_FREE; + bezier.handle_types_right().first() = BEZIER_HANDLE_FREE; bezier.handle_positions_left().first() = new_point.left_handle; bezier.handle_positions_right().first() = new_point.right_handle; @@ -477,15 +480,18 @@ static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::Look static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) { switch (spline->type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: to_single_point_bezier(*spline, lookup); break; - case Spline::Type::Poly: + case CURVE_TYPE_POLY: to_single_point_poly(*spline, lookup); break; - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + spline = {}; } } @@ -494,7 +500,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, Field<float> &start_field, Field<float> &end_field) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -509,8 +515,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0); const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1); - CurveEval &curve = *geometry_set.get_curve_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { @@ -559,6 +565,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } } }); + + geometry_set.replace_curve(curve_eval_to_curves(*curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 2eeb957c123..3baee8a25bb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -332,9 +332,9 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask); copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: break; - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); BezierSpline &dst = static_cast<BezierSpline &>(r_spline); copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask); @@ -343,12 +343,16 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); NURBSpline &dst = static_cast<NURBSpline &>(r_spline); copy_data_based_on_mask(src.weights(), dst.weights(), mask); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -471,9 +475,9 @@ static void separate_curve_selection(GeometrySet &geometry_set, selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); std::unique_ptr<CurveEval> r_curve = curve_separate( - *src_component.get_for_read(), selection, selection_domain, invert); + *curves_to_curve_eval(*src_component.get_for_read()), selection, selection_domain, invert); if (r_curve) { - geometry_set.replace_curve(r_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*r_curve)); } else { geometry_set.replace_curve(nullptr); @@ -1282,7 +1286,7 @@ void separate_geometry(GeometrySet &geometry_set, some_valid_domain = true; } } - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert); some_valid_domain = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 51932d341bc..bdd4d74fe4b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -152,6 +152,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( } KDTree_3d *kdtree = build_kdtree(positions); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); for (const int i : positions.index_range()) { if (elimination_mask[i]) { @@ -176,8 +177,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( }, &callback_data); } - - BLI_kdtree_3d_free(kdtree); } BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index a526f4f9e65..5a2c32a6c8e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -272,7 +272,7 @@ static void create_vertex_poly_map(const Mesh &mesh, * boundary vertex, the first and last polygon have a boundary edge connected to the vertex. The * `r_shared_edges` array at index i is set to the index of the shared edge between the i-th and * `(i+1)-th` sorted polygon. Similarly the `r_sorted_corners` array at index i is set to the - * corner in the i-th sorted polygon. + * corner in the i-th sorted polygon. If the polygons couldn't be sorted, `false` is returned. * * How the faces are sorted (see diagrams below): * (For this explanation we'll assume all faces are oriented clockwise) @@ -321,7 +321,7 @@ static void create_vertex_poly_map(const Mesh &mesh, * - Finally if we are in the normal case we also need to add the last "shared edge" to close the * loop. */ -static void sort_vertex_polys(const Mesh &mesh, +static bool sort_vertex_polys(const Mesh &mesh, const int vertex_index, const bool boundary_vertex, const Span<EdgeType> edge_types, @@ -330,7 +330,7 @@ static void sort_vertex_polys(const Mesh &mesh, MutableSpan<int> r_sorted_corners) { if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { - return; + return true; } /* For each polygon store the two corners whose edge contains the vertex. */ @@ -434,8 +434,11 @@ static void sort_vertex_polys(const Mesh &mesh, break; } } - - BLI_assert(j != connected_polygons.size()); + if (j == connected_polygons.size()) { + /* The vertex is not manifold because the polygons around the vertex don't form a loop, and + * hence can't be sorted. */ + return false; + } std::swap(connected_polygons[i + 1], connected_polygons[j]); std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); @@ -445,6 +448,7 @@ static void sort_vertex_polys(const Mesh &mesh, /* Shared edge between first and last polygon. */ r_shared_edges.last() = shared_edge_i; } + return true; } /** @@ -637,19 +641,26 @@ static void calc_dual_mesh(GeometrySet &geometry_set, } MutableSpan<int> loop_indices = vertex_poly_indices[i]; Array<int> sorted_corners(loop_indices.size()); + bool vertex_ok = true; if (vertex_types[i] == VertexType::Normal) { Array<int> shared_edges(loop_indices.size()); - sort_vertex_polys( + vertex_ok = sort_vertex_polys( mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners); - vertex_shared_edges[i] = shared_edges; + vertex_shared_edges[i] = std::move(shared_edges); } else { Array<int> shared_edges(loop_indices.size() - 1); - sort_vertex_polys( + vertex_ok = sort_vertex_polys( mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners); - vertex_shared_edges[i] = shared_edges; + vertex_shared_edges[i] = std::move(shared_edges); + } + if (!vertex_ok) { + /* The sorting failed which means that the vertex is non-manifold and should be ignored + * further on. */ + vertex_types[i] = VertexType::NonManifold; + continue; } - vertex_corners[i] = sorted_corners; + vertex_corners[i] = std::move(sorted_corners); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc new file mode 100644 index 00000000000..1ceab18c01b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -0,0 +1,1119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_map.hh" +#include "BLI_noise.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_duplicate_elements_cc { + +NODE_STORAGE_FUNCS(NodeGeometryDuplicateElements); + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).hide_value().default_value(true).supports_field(); + b.add_input<decl::Int>(N_("Amount")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The number of duplicates to create for each element")); + + b.add_output<decl::Geometry>(N_("Geometry")) + .description( + N_("The duplicated geometry only. The output does not contain the original geometry")); + b.add_output<decl::Int>(N_("Duplicate Index")) + .field_source() + .description(N_("The indices of the duplicates for each element")); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__); + data->domain = ATTR_DOMAIN_POINT; + node->storage = data; +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +struct IndexAttributes { + StrongAnonymousAttributeID duplicate_index; +}; + +/* -------------------------------------------------------------------- */ +/** \name Attribute Copy/Creation Functions + * \{ */ + +static void gather_attributes_without_id(const GeometrySet &geometry_set, + const GeometryComponentType component_type, + const Span<std::string> skip_attributes, + const bool include_instances, + Map<AttributeIDRef, AttributeKind> &r_gathered_attributes) +{ + geometry_set.gather_attributes_for_propagation( + {component_type}, component_type, include_instances, r_gathered_attributes); + for (const std::string &attribute : skip_attributes) { + r_gathered_attributes.remove(attribute); + } + r_gathered_attributes.remove("id"); +}; + +static IndexRange range_for_offsets_index(const Span<int> offsets, const int index) +{ + return {offsets[index], offsets[index + 1] - offsets[index]}; +} + +static Array<int> accumulate_counts_to_offsets(const IndexMask selection, + const VArray<int> &counts) +{ + Array<int> offsets(selection.size() + 1); + int dst_points_size = 0; + for (const int i_point : selection.index_range()) { + offsets[i_point] = dst_points_size; + dst_points_size += std::max(counts[selection[i_point]], 0); + } + offsets.last() = dst_points_size; + return offsets; +} + +/* Utility functions for threaded copying of attribute data where possible. */ +template<typename T> +static void threaded_slice_fill(Span<int> offsets, Span<T> src, MutableSpan<T> dst) +{ + BLI_assert(offsets.last() == dst.size()); + threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } + }); +} + +template<typename T> +static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst) +{ + threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + dst[i] = src[mapping[i]]; + } + }); +} + +static void threaded_id_offset_copy(const Span<int> offsets, + const Span<int> src, + MutableSpan<int> dst) +{ + BLI_assert(offsets.last() == dst.size()); + threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + dst[offsets[i]] = src[i]; + const int count = offsets[i + 1] - offsets[i]; + for (const int i_duplicate : IndexRange(1, count - 1)) { + dst[offsets[i] + i_duplicate] = noise::hash(src[i], i_duplicate); + } + } + }); +} + +/** Create the copy indices for the duplication domain. */ +static void create_duplicate_index_attribute(GeometryComponent &component, + const AttributeDomain output_domain, + const IndexMask selection, + const IndexAttributes &attributes, + const Span<int> offsets) +{ + OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>( + attributes.duplicate_index.get(), output_domain); + MutableSpan<int> duplicate_indices = copy_attribute.as_span(); + for (const int i : IndexRange(selection.size())) { + const IndexRange range = range_for_offsets_index(offsets, i); + MutableSpan<int> indices = duplicate_indices.slice(range); + for (const int i : indices.index_range()) { + indices[i] = i; + } + } + copy_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for the point domain elements. + */ +static void copy_stable_id_point(const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threaded_id_offset_copy(offsets, src, dst); + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the edge domain. + */ +static void copy_stable_id_edges(const Mesh &mesh, + const IndexMask selection, + const Span<int> edge_offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Span<MEdge> edges(mesh.medge, mesh.totedge); + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + if (edge_range.size() == 0) { + continue; + } + const MEdge &edge = edges[i_edge]; + const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2}; + + dst[vert_range[0]] = src[edge.v1]; + dst[vert_range[1]] = src[edge.v2]; + for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) { + dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate); + dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate); + } + } + }); + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the face domain. + * + * This function could be threaded in the future, but since it is only 1 attribute and the + * `face->edge->vert` mapping would mean creating a 1/1 mapping to allow for it, is it worth it? + */ +static void copy_stable_id_faces(const Mesh &mesh, + const IndexMask selection, + const Span<int> poly_offsets, + const Span<int> vert_mapping, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + int loop_index = 0; + for (const int i_poly : selection.index_range()) { + const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); + if (range.size() == 0) { + continue; + } + const MPoly &source = polys[i_poly]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) { + for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) { + if (i_duplicate == 0) { + dst[loop_index] = src[vert_mapping[loop_index]]; + } + else { + dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate); + } + loop_index++; + } + } + } + + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. In the spline case, copy the entire spline's points to the + * destination, + * then loop over the remaining ones point by point, hashing their ids to the new ids. + */ +static void copy_stable_id_splines(const CurveEval &curve, + const IndexMask selection, + const Span<int> curve_offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Array<int> control_point_offsets = curve.control_point_offsets(); + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + + Array<int> curve_point_offsets(selection.size() + 1); + int dst_point_size = 0; + for (const int i_curve : selection.index_range()) { + const int spline_size = curve.splines()[i_curve]->size(); + const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); + + curve_point_offsets[i_curve] = dst_point_size; + dst_point_size += curve_range.size() * spline_size; + } + curve_point_offsets.last() = dst_point_size; + + threading::parallel_for(IndexRange(curve_point_offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i_curve : range) { + const int spline_size = curve.splines()[i_curve]->size(); + const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); + + dst.slice(curve_point_offsets[i_curve], spline_size) + .copy_from(src.slice(control_point_offsets[i_curve], spline_size)); + for (const int i_duplicate : IndexRange(1, curve_range.size() - 1)) { + for (const int i_point : IndexRange(spline_size)) { + dst[curve_point_offsets[i_curve] + i_duplicate * spline_size + i_point] = noise::hash( + src[control_point_offsets[i_curve] + i_point], i_duplicate); + } + } + } + }); + dst_attribute.save(); +} + +/* The attributes for the point (also instance) duplicated elements are stored sequentially + * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if + * elements are ordered differently a custom function is called to copy the attributes. + */ + +static void copy_point_attributes_without_id(GeometrySet &geometry_set, + const GeometryComponentType component_type, + const bool include_instances, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, component_type, {}, include_instances, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute || src_attribute.domain != ATTR_DOMAIN_POINT) { + continue; + } + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src = src_attribute.varray.typed<T>(); + MutableSpan<T> dst = dst_attribute.as_span<T>(); + threaded_slice_fill<T>(offsets, src, dst); + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for spline duplicates. If copying the spline domain, the attributes are + * copied with an offset fill, otherwise a mapping is used. + */ +static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const Span<std::string> attributes_to_ignore, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, attributes_to_ignore, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_CURVE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are + * copied with an offset fill, for point domain a mapping is used. + */ +static void copy_edge_attributes_without_id(GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + const AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_EDGE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for face duplicates. If copying the face domain, the attributes are + * copied with an offset fill, otherwise a mapping is used. + */ +static void copy_face_attributes_without_id(GeometrySet &geometry_set, + const Span<int> edge_mapping, + const Span<int> vert_mapping, + const Span<int> loop_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_FACE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_EDGE: + threaded_mapped_copy<T>(edge_mapping, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(vert_mapping, src, dst); + break; + case ATTR_DOMAIN_CORNER: + threaded_mapped_copy<T>(loop_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplication Functions + * \{ */ + +static void duplicate_splines(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_curves()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + + const GeometryComponent &src_component = *geometry_set.get_component_for_read( + GEO_COMPONENT_TYPE_CURVE); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> curve_offsets(selection.size() + 1); + + int dst_splines_size = 0; + int dst_points_size = 0; + for (const int i_spline : selection.index_range()) { + int count = std::max(counts[selection[i_spline]], 0); + curve_offsets[i_spline] = dst_splines_size; + dst_splines_size += count; + dst_points_size += count * curve->splines()[selection[i_spline]]->size(); + } + curve_offsets.last() = dst_splines_size; + + Array<int> control_point_offsets = curve->control_point_offsets(); + Array<int> point_mapping(dst_points_size); + + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + int point_index = 0; + for (const int i_spline : selection.index_range()) { + const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline); + for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) { + SplinePtr spline = curve->splines()[selection[i_spline]]->copy(); + for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) { + point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point; + } + new_curve->add_spline(std::move(spline)); + } + } + new_curve->attributes.reallocate(new_curve->splines().size()); + + CurveComponent dst_component; + dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); + + Vector<std::string> skip( + {"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"}); + + copy_spline_attributes_without_id( + geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component); + + copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets); + } + + geometry_set.replace_curve(dst_component.get_for_write()); +} + +static void duplicate_faces(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + + GeometryComponent &component = geometry_set.get_component_for_write(GEO_COMPONENT_TYPE_MESH); + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator(field_context, domain_size); + + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + + MeshComponent &mesh_component = static_cast<MeshComponent &>(component); + const Mesh &mesh = *mesh_component.get_for_read(); + Span<MVert> verts(mesh.mvert, mesh.totvert); + Span<MEdge> edges(mesh.medge, mesh.totedge); + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + Span<MLoop> loops(mesh.mloop, mesh.totloop); + + int total_polys = 0; + int total_loops = 0; + Array<int> offsets(selection.size() + 1); + for (const int i_selection : selection.index_range()) { + const int count = std::max(counts[selection[i_selection]], 0); + offsets[i_selection] = total_polys; + total_polys += count; + total_loops += count * polys[selection[i_selection]].totloop; + } + offsets[selection.size()] = total_polys; + + Array<int> vert_mapping(total_loops); + Array<int> edge_mapping(total_loops); + Array<int> loop_mapping(total_loops); + + Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys); + + MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop); + MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly); + + int poly_index = 0; + int loop_index = 0; + for (const int i_selection : selection.index_range()) { + const IndexRange poly_range = range_for_offsets_index(offsets, i_selection); + + const MPoly &source = polys[selection[i_selection]]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) { + new_poly[poly_index] = source; + new_poly[poly_index].loopstart = loop_index; + for (const int i_loops : IndexRange(source.totloop)) { + const MLoop ¤t_loop = loops[source.loopstart + i_loops]; + loop_mapping[loop_index] = source.loopstart + i_loops; + new_verts[loop_index] = verts[current_loop.v]; + vert_mapping[loop_index] = current_loop.v; + new_edges[loop_index] = edges[current_loop.e]; + edge_mapping[loop_index] = current_loop.e; + new_edges[loop_index].v1 = loop_index; + if (i_loops + 1 != source.totloop) { + new_edges[loop_index].v2 = loop_index + 1; + } + else { + new_edges[loop_index].v2 = new_poly[poly_index].loopstart; + } + new_loops[loop_index].v = loop_index; + new_loops[loop_index].e = loop_index; + loop_index++; + } + poly_index++; + } + } + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + + copy_face_attributes_without_id(geometry_set, + edge_mapping, + vert_mapping, + loop_mapping, + offsets, + mesh_component, + dst_component); + + copy_stable_id_faces(mesh, selection, offsets, vert_mapping, mesh_component, dst_component); + mesh_component.replace(dst_component.get_for_write()); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_FACE, selection, attributes, offsets); + } +} + +static void duplicate_edges(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + }; + const GeometryComponent &src_component = *geometry_set.get_component_for_read( + GEO_COMPONENT_TYPE_MESH); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts); + + const Mesh *mesh = geometry_set.get_mesh_for_read(); + Span<MVert> verts(mesh->mvert, mesh->totvert); + Span<MEdge> edges(mesh->medge, mesh->totedge); + + Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0); + MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + + Array<int> vert_orig_indices(edge_offsets.last() * 2); + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const MEdge &edge = edges[i_edge]; + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2); + + for (const int i_duplicate : IndexRange(edge_range.size())) { + vert_orig_indices[vert_range[i_duplicate * 2]] = edge.v1; + vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge.v2; + } + } + }); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2); + for (const int i_duplicate : IndexRange(edge_range.size())) { + MEdge &new_edge = new_edges[edge_range[i_duplicate]]; + new_edge.v1 = vert_range[i_duplicate * 2]; + new_edge.v2 = vert_range[i_duplicate * 2] + 1; + } + } + }); + + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + + copy_edge_attributes_without_id( + geometry_set, vert_orig_indices, edge_offsets, src_component, dst_component); + + copy_stable_id_edges(*mesh, selection, edge_offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_EDGE, selection, attributes, edge_offsets); + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_curve(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + Array<int> control_point_offsets = curve->control_point_offsets(); + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + + Array<int> parent(domain_size); + int spline = 0; + for (const int i_spline : IndexRange(domain_size)) { + if (i_spline == control_point_offsets[spline + 1]) { + spline++; + } + parent[i_spline] = spline; + } + + for (const int i_point : selection) { + const IndexRange point_range = range_for_offsets_index(offsets, i_point); + for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) { + const SplinePtr &parent_spline = curve->splines()[parent[i_point]]; + switch (parent_spline->type()) { + case CurveType::CURVE_TYPE_BEZIER: { + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->resize(1); + spline->set_resolution(2); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_NURBS: { + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->resize(1); + spline->set_resolution(2); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_POLY: { + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->resize(1); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_CATMULL_ROM: { + /* Catmull Rom curves are not supported yet. */ + break; + } + } + } + } + new_curve->attributes.reallocate(new_curve->splines().size()); + CurveComponent dst_component; + dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + } + + curve_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_mesh(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + const Mesh *mesh = geometry_set.get_mesh_for_read(); + Span<MVert> src_verts(mesh->mvert, mesh->totvert); + + Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0); + MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert); + + threaded_slice_fill<MVert>(offsets.as_span(), src_verts, dst_verts); + + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_pointcloud(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last()); + PointCloudComponent dst_component; + dst_component.replace(pointcloud, GeometryOwnershipType::Editable); + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_POINT_CLOUD, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets); + } + geometry_set.replace_pointcloud(pointcloud); +} + +static void duplicate_points(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh() && !geometry_set.has_curves() && !geometry_set.has_pointcloud()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true); + Vector<GeometryComponentType> types_to_keep; + for (const GeometryComponentType component_type : component_types) { + switch (component_type) { + case GEO_COMPONENT_TYPE_POINT_CLOUD: + types_to_keep.append(component_type); + duplicate_points_pointcloud( + component_type, count_field, selection_field, geometry_set, attributes); + break; + case GEO_COMPONENT_TYPE_MESH: + types_to_keep.append(component_type); + duplicate_points_mesh( + component_type, count_field, selection_field, geometry_set, attributes); + break; + case GEO_COMPONENT_TYPE_CURVE: + types_to_keep.append(component_type); + duplicate_points_curve( + component_type, count_field, selection_field, geometry_set, attributes); + break; + default: + break; + } + } + types_to_keep.append(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.keep_only(types_to_keep); +} + +static void duplicate_instances(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_instances()) { + geometry_set.clear(); + return; + } + + const InstancesComponent &src_instances = + *geometry_set.get_component_for_read<InstancesComponent>(); + + const int domain_size = src_instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + if (offsets.last() == 0) { + geometry_set.clear(); + return; + } + + GeometrySet instances_geometry; + InstancesComponent &dst_instances = + instances_geometry.get_component_for_write<InstancesComponent>(); + dst_instances.resize(offsets.last()); + for (const int i_selection : selection.index_range()) { + const int count = offsets[i_selection + 1] - offsets[i_selection]; + if (count == 0) { + continue; + } + const int old_handle = src_instances.instance_reference_handles()[i_selection]; + const InstanceReference reference = src_instances.references()[old_handle]; + const int new_handle = dst_instances.add_reference(reference); + const float4x4 transform = src_instances.instance_transforms()[i_selection]; + dst_instances.instance_transforms().slice(offsets[i_selection], count).fill(transform); + dst_instances.instance_reference_handles().slice(offsets[i_selection], count).fill(new_handle); + } + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_INSTANCES, true, offsets, src_instances, dst_instances); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_instances, ATTR_DOMAIN_INSTANCE, selection, attributes, offsets); + } + + geometry_set.remove(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.add(dst_instances); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const NodeGeometryDuplicateElements &storage = node_storage(params.node()); + const AttributeDomain duplicate_domain = AttributeDomain(storage.domain); + + Field<int> count_field = params.extract_input<Field<int>>("Amount"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + IndexAttributes attributes; + if (params.output_is_required("Duplicate Index")) { + attributes.duplicate_index = StrongAnonymousAttributeID("duplicate_index"); + } + + if (duplicate_domain == ATTR_DOMAIN_INSTANCE) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + duplicate_instances(geometry_set, count_field, selection_field, attributes); + } + else { + if (geometry_set.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + switch (duplicate_domain) { + case ATTR_DOMAIN_CURVE: + duplicate_splines(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_FACE: + duplicate_faces(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_EDGE: + duplicate_edges(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_POINT: + duplicate_points(geometry_set, count_field, selection_field, attributes); + break; + default: + BLI_assert_unreachable(); + break; + } + }); + } + + if (geometry_set.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + + if (attributes.duplicate_index) { + params.set_output( + "Duplicate Index", + AnonymousAttributeFieldInput::Create<int>(std::move(attributes.duplicate_index), + params.attribute_producer_name())); + } + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes::node_geo_duplicate_elements_cc + +void register_node_type_geo_duplicate_elements() +{ + namespace file_ns = blender::nodes::node_geo_duplicate_elements_cc; + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_DUPLICATE_ELEMENTS, "Duplicate Elements", NODE_CLASS_GEOMETRY); + + node_type_storage(&ntype, + "NodeGeometryDuplicateElements", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, file_ns::node_init); + ntype.draw_buttons = file_ns::node_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} + +/** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 4e053dbc1a3..c03a340a0c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -25,6 +25,9 @@ static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params); BMeshFromMeshParams bmesh_from_mesh_params{}; + bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_ORIGINDEX; + bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX; + bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX; BM_mesh_bm_from_me(bm, &mesh, &bmesh_from_mesh_params); BM_mesh_elem_table_ensure(bm, BM_EDGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 5aeeb72961d..82584f6f413 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -302,7 +302,6 @@ static void extrude_mesh_vertices(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh) @@ -626,7 +625,6 @@ static void extrude_mesh_edges(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } /** @@ -995,7 +993,6 @@ static void extrude_mesh_face_regions(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } /* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */ @@ -1263,7 +1260,6 @@ static void extrude_individual_mesh_faces(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc new file mode 100644 index 00000000000..62af0476057 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Threshold") + .field_source() + .default_value(0.01f) + .subtype(PROP_DISTANCE) + .supports_field() + .description(N_("The distance a point can be from the surface before the face is no longer " + "considered planar")) + .min(0.0f); + b.add_output<decl::Bool>("Planar").field_source(); +} + +class PlanarFieldInput final : public GeometryFieldInput { + private: + Field<float> threshold_; + + public: + PlanarFieldInput(Field<float> threshold) + : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + [[maybe_unused]] IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{context, mesh->totpoly}; + evaluator.add(threshold_); + evaluator.evaluate(); + const VArray<float> &thresholds = evaluator.get_evaluated<float>(0); + + Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}; + + auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool { + if (mesh->mpoly[i_poly].totloop <= 3) { + return true; + } + const int loopstart = mesh->mpoly[i_poly].loopstart; + const int loops = mesh->mpoly[i_poly].totloop; + Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops); + float3 reference_normal = poly_normals[i_poly]; + + float min = FLT_MAX; + float max = FLT_MIN; + + for (const int i_loop : poly_loops.index_range()) { + const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; + float dot = math::dot(reference_normal, vert); + if (dot > max) { + max = dot; + } + if (dot < min) { + min = dot; + } + } + return max - min < thresholds[i_poly] / 2.0f; + }; + + return component.attribute_try_adapt_domain<bool>( + VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 2356235652; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const PlanarFieldInput *>(&other) != nullptr; + } +}; + +static void geo_node_exec(GeoNodeExecParams params) +{ + Field<float> threshold = params.extract_input<Field<float>>("Threshold"); + Field<bool> planar_field{std::make_shared<PlanarFieldInput>(threshold)}; + params.set_output("Planar", std::move(planar_field)); +} + +} // namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc + +void register_node_type_geo_input_mesh_face_is_planar() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_face_is_planar_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, "Face is Planar", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::geo_node_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 4537721d173..f952e15fbbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -19,10 +19,10 @@ static void node_declare(NodeDeclarationBuilder &b) static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { + if (!component.has_curves()) { return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); Span<SplinePtr> splines = curve->splines(); auto length_fn = [splines](int i) { return splines[i]->length(); }; @@ -76,10 +76,10 @@ class SplineLengthFieldInput final : public GeometryFieldInput { static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { + if (!component.has_curves()) { return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); Span<SplinePtr> splines = curve->splines(); auto count_fn = [splines](int i) { return splines[i]->size(); }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index 9ae99b4d83e..435dd969c03 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -52,18 +52,22 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) const Spline &spline = *splines[i]; MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())}; switch (splines[i]->type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { calculate_bezier_tangents(static_cast<const BezierSpline &>(spline), spline_tangents); break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { calculate_poly_tangents(static_cast<const PolySpline &>(spline), spline_tangents); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { calculate_nurbs_tangents(static_cast<const NURBSpline &>(spline), spline_tangents); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } }); @@ -73,21 +77,12 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { - return nullptr; + if (!component.has_curves()) { + return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); if (domain == ATTR_DOMAIN_POINT) { - const Span<SplinePtr> splines = curve->splines(); - - /* Use a reference to evaluated tangents if possible to avoid an allocation and a copy. - * This is only possible when there is only one poly spline. */ - if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { - const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return VArray<float3>::ForSpan(spline.evaluated_tangents()); - } - Array<float3> tangents = curve_tangent_point_domain(*curve); return VArray<float3>::ForContainer(std::move(tangents)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index df6d10991fb..61f719ade4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -195,35 +195,24 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + const Array<GeometryComponentType> types{ + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + Map<AttributeIDRef, AttributeKind> attributes_to_propagate; geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}, - GEO_COMPONENT_TYPE_INSTANCES, - false, - attributes_to_propagate); + types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate); attributes_to_propagate.remove("position"); - if (geometry_set.has<MeshComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<MeshComponent>(), - instance, - params, - attributes_to_propagate); - } - if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<PointCloudComponent>(), - instance, - params, - attributes_to_propagate); - } - if (geometry_set.has<CurveComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<CurveComponent>(), - instance, - params, - attributes_to_propagate); + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + add_instances_from_component(instances, + *geometry_set.get_component_for_read(type), + instance, + params, + attributes_to_propagate); + } } + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 3284378a2cb..91cde52f9eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -35,7 +35,7 @@ static void node_geo_exec(GeoNodeExecParams params) } std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection); - geometry_set.replace_curve(curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*curve)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 1731ba64b97..c99b51ffd4c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -198,17 +198,12 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, Vector<float3> positions; Vector<float> radii; - if (r_geometry_set.has<MeshComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<MeshComponent>(), positions, radii); - } - if (r_geometry_set.has<PointCloudComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<PointCloudComponent>(), positions, radii); - } - if (r_geometry_set.has<CurveComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<CurveComponent>(), positions, radii); + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + if (r_geometry_set.has(type)) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read(type), positions, radii); + } } const float max_radius = *std::max_element(radii.begin(), radii.end()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 1797364ad72..231ef547a8b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -141,10 +141,13 @@ static void raycast_to_mesh(IndexMask mask, { BVHTreeFromMesh tree_data; BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&tree_data); }); + if (tree_data.tree == nullptr) { - free_bvhtree_from_mesh(&tree_data); return; } + /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ + BLI_assert(tree_data.cached); for (const int i : mask) { const float ray_length = ray_lengths[i]; @@ -197,10 +200,6 @@ static void raycast_to_mesh(IndexMask mask, } } } - - /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ - BLI_assert(tree_data.cached); - free_bvhtree_from_mesh(&tree_data); } class RaycastFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index fb648baad08..301410f5126 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -35,7 +35,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) } static void set_position_in_component(const GeometryNodeCurveHandleMode mode, - GeometryComponent &component, + CurveComponent &component, const Field<bool> &selection_field, const Field<float3> &position_field, const Field<float3> &offset_field) @@ -53,37 +53,31 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - CurveComponent *curve_component = static_cast<CurveComponent *>(&component); - CurveEval *curve = curve_component->get_for_write(); - - StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); int current_point = 0; int current_mask = 0; - for (const SplinePtr &spline : curve->splines()) { - if (spline->type() == Spline::Type::Bezier) { + if (spline->type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier = static_cast<BezierSpline &>(*spline); - for (int i : bezier.positions().index_range()) { + + bezier.ensure_auto_handles(); + for (const int i : bezier.positions().index_range()) { if (current_mask < selection.size() && selection[current_mask] == current_point) { if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { - bezier.ensure_auto_handles(); - bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; + if (bezier.handle_types_left()[i] == BEZIER_HANDLE_VECTOR) { + bezier.handle_types_left()[i] = BEZIER_HANDLE_FREE; } - else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { - bezier.ensure_auto_handles(); - bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; + else if (bezier.handle_types_left()[i] == BEZIER_HANDLE_AUTO) { + bezier.handle_types_left()[i] = BEZIER_HANDLE_ALIGN; } } else { - if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { - bezier.ensure_auto_handles(); - bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; + if (bezier.handle_types_right()[i] == BEZIER_HANDLE_VECTOR) { + bezier.handle_types_right()[i] = BEZIER_HANDLE_FREE; } - else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { - bezier.ensure_auto_handles(); - bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; + else if (bezier.handle_types_right()[i] == BEZIER_HANDLE_AUTO) { + bezier.handle_types_right()[i] = BEZIER_HANDLE_ALIGN; } } current_mask++; @@ -104,15 +98,35 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0); const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1); - OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( - side, ATTR_DOMAIN_POINT, {0, 0, 0}); - MutableSpan<float3> position_mutable = positions.as_span(); - - for (int i : selection) { - position_mutable[i] = positions_input[i] + offsets_input[i]; + current_point = 0; + current_mask = 0; + for (const SplinePtr &spline : curve->splines()) { + if (spline->type() == CURVE_TYPE_BEZIER) { + BezierSpline &bezier = static_cast<BezierSpline &>(*spline); + for (const int i : bezier.positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier.set_handle_position_left(i, positions_input[i] + offsets_input[i]); + } + else { + bezier.set_handle_position_right(i, positions_input[i] + offsets_input[i]); + } + current_mask++; + } + current_point++; + } + } + else { + for ([[maybe_unused]] int i : spline->positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + current_mask++; + } + current_point++; + } + } } - positions.save(); + component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned); } static void node_geo_exec(GeoNodeExecParams params) @@ -127,9 +141,11 @@ static void node_geo_exec(GeoNodeExecParams params) bool has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve() && - geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) { - has_bezier = true; + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER); + set_position_in_component(mode, geometry_set.get_component_for_write<CurveComponent>(), selection_field, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 2f59d008df0..a23a6c09551 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -44,7 +44,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_radius_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index b94782b8c4c..1155c97dc38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_tilt_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 361a75ee0a8..eb035aa9b6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -61,6 +61,41 @@ static void set_computed_position_and_offset(GeometryComponent &component, } break; } + case GEO_COMPONENT_TYPE_CURVE: { + if (component.attribute_exists("handle_right") && + component.attribute_exists("handle_left")) { + OutputAttribute_Typed<float3> handle_right_attribute = + component.attribute_try_get_for_output<float3>( + "handle_right", ATTR_DOMAIN_POINT, {0, 0, 0}); + OutputAttribute_Typed<float3> handle_left_attribute = + component.attribute_try_get_for_output<float3>( + "handle_left", ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan<float3> handle_right = handle_right_attribute.as_span(); + MutableSpan<float3> handle_left = handle_left_attribute.as_span(); + + MutableSpan<float3> out_positions_span = positions.as_span(); + devirtualize_varray2( + in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + const float3 new_position = in_positions[i] + in_offsets[i]; + const float3 delta = new_position - out_positions_span[i]; + handle_right[i] += delta; + handle_left[i] += delta; + out_positions_span[i] = new_position; + } + }); + }); + + handle_right_attribute.save(); + handle_left_attribute.save(); + break; + } + else { + ATTR_FALLTHROUGH; + } + } default: { MutableSpan<float3> out_positions_span = positions.as_span(); if (in_positions.is_same(positions.varray())) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index 70e363064cd..dc7f3b1343a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_cyclic_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index e4702035eec..da8d7bcf255 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -43,10 +43,12 @@ static void node_geo_exec(GeoNodeExecParams params) bool only_poly = true; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { if (only_poly) { - for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { - if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { + if (ELEM(spline->type(), CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS)) { only_poly = false; break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index ddc0bb2bc11..bc34a1a6f2c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -298,7 +298,8 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, layout.pivot_points.add_new(layout.char_codes[i], pivot_point); } - GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release()); + GeometrySet geometry_set_curve = GeometrySet::create_with_curves( + curve_eval_to_curves(*curve_eval)); handles.add_new(layout.char_codes[i], instance_component.add_reference(std::move(geometry_set_curve))); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 789478873f6..2d5b0e58367 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -778,7 +778,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { - if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { params.error_message_add(NodeWarningType::Error, TIP_("The source geometry must contain a mesh or a point cloud")); return return_default(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 5950a2a16d2..95cec8eab11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -10,6 +10,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" +#include "BKE_curves.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_spline.hh" @@ -125,8 +126,10 @@ static void translate_geometry_set(GeometrySet &geometry, const float3 translation, const Depsgraph &depsgraph) { - if (CurveEval *curve = geometry.get_curve_for_write()) { + if (Curves *curves = geometry.get_curves_for_write()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); curve->translate(translation); + geometry.replace_curve(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { translate_mesh(*mesh, translation); @@ -146,8 +149,10 @@ void transform_geometry_set(GeometrySet &geometry, const float4x4 &transform, const Depsgraph &depsgraph) { - if (CurveEval *curve = geometry.get_curve_for_write()) { + if (Curves *curves = geometry.get_curves_for_write()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); curve->transform(transform); + geometry.replace_curve(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { transform_mesh(*mesh, transform); |