From 901a03725ed58166e5b2401dfadd7c1cb7e490a2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 7 Mar 2022 19:06:40 -0600 Subject: Curves: Port mesh to curve node to new data-block The main improvement is a code simplification, because attributes don't have to be transferred separately for each curve, and all attributes can be handled generically. Performance improves significantly when the output contains many curves. Basic testing with a 2 million curve output shows an approximate 10x performance improvement. --- source/blender/geometry/GEO_mesh_to_curve.hh | 6 +- .../geometry/intern/mesh_to_curve_convert.cc | 152 +++++++-------------- .../nodes/legacy/node_geo_legacy_mesh_to_curve.cc | 6 +- .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 3 +- 4 files changed, 56 insertions(+), 111 deletions(-) (limited to 'source/blender') diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh index a99ee9c7fee..c480e4178cf 100644 --- a/source/blender/geometry/GEO_mesh_to_curve.hh +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -2,11 +2,10 @@ #include "BLI_index_mask.hh" -#include "BKE_spline.hh" - #pragma once struct Mesh; +struct Curves; class MeshComponent; /** \file @@ -20,7 +19,6 @@ namespace blender::geometry { * intersections of more than three edges will become breaks in splines. Attributes that * are not built-in on meshes and not curves are transferred to the result curve. */ -std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, - const IndexMask selection); +Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection); } // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 121a1b5de39..0c2377fde6d 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -2,7 +2,6 @@ #include "BLI_array.hh" #include "BLI_set.hh" -#include "BLI_string_ref.hh" #include "BLI_task.hh" #include "DNA_mesh_types.h" @@ -10,85 +9,48 @@ #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set.hh" -#include "BKE_spline.hh" #include "GEO_mesh_to_curve.hh" namespace blender::geometry { template -static void copy_attribute_to_points(const VArray &source_data, - Span map, - MutableSpan dest_data) +static void copy_with_map(const VArray &src, Span map, MutableSpan dst) { - for (const int point_index : map.index_range()) { - const int vert_index = map[point_index]; - dest_data[point_index] = source_data[vert_index]; - } + devirtualize_varray(src, [&](const auto &src) { + threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + const int vert_index = map[i]; + dst[i] = src[vert_index]; + } + }); + }); } -static std::unique_ptr create_curve_from_vert_indices( - const MeshComponent &mesh_component, Span> vert_indices, IndexRange cyclic_splines) +static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_component, + const Span vert_indices, + const Span curve_offsets, + const IndexRange cyclic_splines) { - std::unique_ptr curve = std::make_unique(); - curve->resize(vert_indices.size()); - - MutableSpan splines = curve->splines(); + Curves *curves_id = bke::curves_new_nomain(vert_indices.size(), curve_offsets.size()); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.offsets().drop_back(1).copy_from(curve_offsets); + curves.offsets().last() = vert_indices.size(); + curves.curve_types().fill(CURVE_TYPE_POLY); - for (const int i : vert_indices.index_range()) { - splines[i] = std::make_unique(); - splines[i]->resize(vert_indices[i].size()); - } - for (const int i : cyclic_splines) { - splines[i]->set_cyclic(true); - } + curves.cyclic().fill(false); + curves.cyclic().slice(cyclic_splines).fill(true); Set source_attribute_ids = mesh_component.attribute_ids(); - /* Copy builtin control point attributes. */ - if (source_attribute_ids.contains("tilt")) { - const VArray tilt_attribute = mesh_component.attribute_get_for_read( - "tilt", ATTR_DOMAIN_POINT, 0.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points(tilt_attribute, vert_indices[i], splines[i]->tilts()); - } - }); - source_attribute_ids.remove_contained("tilt"); - } - else { - for (SplinePtr &spline : splines) { - spline->tilts().fill(0.0f); - } - } - - if (source_attribute_ids.contains("radius")) { - const VArray radius_attribute = mesh_component.attribute_get_for_read( - "radius", ATTR_DOMAIN_POINT, 1.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points(radius_attribute, vert_indices[i], splines[i]->radii()); - } - }); - source_attribute_ids.remove_contained("radius"); - } - else { - for (SplinePtr &spline : splines) { - spline->radii().fill(1.0f); - } - } - - VArray mesh_positions = mesh_component.attribute_get_for_read( - "position", ATTR_DOMAIN_POINT, float3(0)); - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points(mesh_positions, vert_indices[i], splines[i]->positions()); - } - }); + CurveComponent curves_component; + curves_component.replace(curves_id, GeometryOwnershipType::Editable); for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) { - if (mesh_component.attribute_is_builtin(attribute_id)) { + if (mesh_component.attribute_is_builtin(attribute_id) && + !curves_component.attribute_is_builtin(attribute_id)) { /* Don't copy attributes that are built-in on meshes but not on curves. */ continue; } @@ -105,33 +67,24 @@ static std::unique_ptr create_curve_from_vert_indices( continue; } - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute.type()); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - /* Create attribute on the spline points. */ - splines[i]->attributes.create(attribute_id, data_type); - std::optional spline_attribute = splines[i]->attributes.get_for_write( - attribute_id); - BLI_assert(spline_attribute); - - /* Copy attribute based on the map for this spline. */ - attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points( - mesh_attribute.typed(), vert_indices[i], spline_attribute->typed()); - }); - } + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) { + using T = decltype(dummy); + bke::OutputAttribute_Typed attribute = + curves_component.attribute_try_get_for_output_only(attribute_id, ATTR_DOMAIN_POINT); + copy_with_map(mesh_attribute.typed(), vert_indices, attribute.as_span()); + attribute.save(); }); } - curve->assert_valid_point_attributes(); - return curve; + return curves_id; } struct CurveFromEdgesOutput { /** The indices in the mesh for each control point of each result splines. */ - Vector> vert_indices; + Vector vert_indices; + /** The first index of each curve in the result. */ + Vector curve_offsets; /** A subset of splines that should be set cyclic. */ IndexRange cyclic_splines; }; @@ -139,7 +92,9 @@ struct CurveFromEdgesOutput { static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, Span> edges) { - Vector> vert_indices; + Vector vert_indices; + vert_indices.reserve(edges.size()); + Vector curve_offsets; /* Compute the number of edges connecting to each vertex. */ Array neighbor_count(verts.size(), 0); @@ -191,15 +146,16 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, continue; } - Vector spline_indices; - spline_indices.append(current_vert); + /* Start a new curve in the output. */ + curve_offsets.append(vert_indices.size()); + vert_indices.append(current_vert); /* Follow connected edges until we read a vertex with more than two connected edges. */ while (true) { int last_vert = current_vert; current_vert = next_vert; - spline_indices.append(current_vert); + vert_indices.append(current_vert); unused_edges[current_vert]--; unused_edges[last_vert]--; @@ -212,13 +168,11 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, const int next_b = neighbors[offset + 1]; next_vert = (last_vert == next_a) ? next_b : next_a; } - - vert_indices.append(std::move(spline_indices)); } } /* All splines added after this are cyclic. */ - const int cyclic_start = vert_indices.size(); + const int cyclic_start = curve_offsets.size(); /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ for (const int start_vert : verts.index_range()) { @@ -229,16 +183,15 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, int current_vert = start_vert; int next_vert = neighbors[neighbor_offsets[current_vert]]; - Vector spline_indices; - - spline_indices.append(current_vert); + curve_offsets.append(vert_indices.size()); + vert_indices.append(current_vert); /* Follow connected edges until we loop back to the start vertex. */ while (next_vert != start_vert) { const int last_vert = current_vert; current_vert = next_vert; - spline_indices.append(current_vert); + vert_indices.append(current_vert); unused_edges[current_vert]--; unused_edges[last_vert]--; @@ -247,13 +200,11 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, const int next_b = neighbors[offset + 1]; next_vert = (last_vert == next_a) ? next_b : next_a; } - - vert_indices.append(std::move(spline_indices)); } - const int final_size = vert_indices.size(); + const IndexRange cyclic_curves = curve_offsets.index_range().drop_front(cyclic_start); - return {std::move(vert_indices), IndexRange(cyclic_start, final_size - cyclic_start)}; + return {std::move(vert_indices), std::move(curve_offsets), cyclic_curves}; } /** @@ -270,8 +221,7 @@ static Vector> get_selected_edges(const Mesh &mesh, const In return selected_edges; } -std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, - const IndexMask selection) +Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection) { const Mesh &mesh = *mesh_component.get_for_read(); Vector> selected_edges = get_selected_edges(*mesh_component.get_for_read(), @@ -280,7 +230,7 @@ std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_compo selected_edges); return create_curve_from_vert_indices( - mesh_component, output.vert_indices, output.cyclic_splines); + mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_splines); } } // namespace blender::geometry 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 8991261a21a..72cb540df4a 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 @@ -45,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr curve = geometry::mesh_to_curve_convert( - component, IndexMask(selected_edge_indices)); - - params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); + Curves *curves = geometry::mesh_to_curve_convert(component, IndexMask(selected_edge_indices)); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc 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 d2b824141d7..f6ee3d00dee 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 @@ -34,8 +34,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr curve = geometry::mesh_to_curve_convert(component, selection); - geometry_set.replace_curves(curve_eval_to_curves(*curve)); + geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); -- cgit v1.2.3