From 5224bdb9445623f6dab07c9015cd6304efe9017e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 30 May 2023 14:02:45 -0400 Subject: [PATCH 01/20] WIP: Rewrite delete geometry node mesh handling --- source/blender/blenkernel/BKE_attribute.hh | 10 + .../blenkernel/intern/attribute_access.cc | 53 ++ .../blenkernel/intern/curves_geometry.cc | 58 +-- source/blender/blenlib/BLI_offset_indices.hh | 3 + .../intern/mesh_separate_selection.cc | 452 ++++++++++++++++++ 5 files changed, 530 insertions(+), 46 deletions(-) create mode 100644 source/blender/geometry/intern/mesh_separate_selection.cc diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index ac4bd4b57ea..95a9f707168 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -9,6 +9,7 @@ #include "BLI_generic_span.hh" #include "BLI_generic_virtual_array.hh" #include "BLI_math_vector_types.hh" +#include "BLI_offset_indices.hh" #include "BLI_set.hh" #include "BKE_anonymous_attribute_id.hh" @@ -926,6 +927,15 @@ void gather_attributes(AttributeAccessor src_attributes, const IndexMask &selection, MutableAttributeAccessor dst_attributes); +void gather_attributes_group_to_group(AttributeAccessor src_attributes, + eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info, + const Set &skip, + OffsetIndices src_offsets, + OffsetIndices dst_offsets, + const IndexMask &selection, + MutableAttributeAccessor dst_attributes); + void copy_attributes(const AttributeAccessor src_attributes, const eAttrDomain domain, const AnonymousAttributePropagationInfo &propagation_info, diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 3d52b6421b4..0e5ef45a933 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1026,6 +1026,59 @@ void gather_attributes(const AttributeAccessor src_attributes, }); } +template +static void gather_group_to_group(const OffsetIndices src_offsets, + const OffsetIndices dst_offsets, + const IndexMask &selection, + const Span src, + MutableSpan dst) +{ + selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { + dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i])); + }); +} + +static void gather_group_to_group(const OffsetIndices src_offsets, + const OffsetIndices dst_offsets, + const IndexMask &selection, + const GSpan src, + GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + gather_group_to_group(src_offsets, dst_offsets, selection, src.typed(), dst.typed()); + }); +} + +void gather_attributes_group_to_group(const AttributeAccessor src_attributes, + const eAttrDomain domain, + const AnonymousAttributePropagationInfo &propagation_info, + const Set &skip, + const OffsetIndices src_offsets, + const OffsetIndices dst_offsets, + const IndexMask &selection, + MutableAttributeAccessor dst_attributes) +{ + const int src_size = src_attributes.domain_size(domain); + src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != domain) { + return true; + } + if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { + return true; + } + if (skip.contains(id.name())) { + return true; + } + const GVArraySpan src = *src_attributes.lookup(id, domain); + bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, domain, meta_data.data_type); + gather_group_to_group(src_offsets, dst_offsets, selection, src, dst.span); + dst.finish(); + return true; + }); +} + void copy_attributes(const AttributeAccessor src_attributes, const eAttrDomain domain, const AnonymousAttributePropagationInfo &propagation_info, diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index e12e0d4d8d8..93145efb0a7 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1174,62 +1174,28 @@ void CurvesGeometry::remove_points(const IndexMask &points_to_delete, *this = curves_copy_point_selection(*this, points_to_copy, propagation_info); } -template -static void gather_group_to_group(const OffsetIndices src_offsets, - const OffsetIndices dst_offsets, - const IndexMask &selection, - const Span src, - MutableSpan dst) -{ - selection.foreach_index(GrainSize(256), [&](const int64_t src_i, const int64_t dst_i) { - dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i])); - }); -} - -static void gather_group_to_group(const OffsetIndices src_offsets, - const OffsetIndices dst_offsets, - const IndexMask &selection, - const GSpan src, - GMutableSpan dst) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - gather_group_to_group(src_offsets, dst_offsets, selection, src.typed(), dst.typed()); - }); -} - CurvesGeometry curves_copy_curve_selection( const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AnonymousAttributePropagationInfo &propagation_info) { + const OffsetIndices points_by_curve = curves.points_by_curve(); CurvesGeometry dst_curves(0, curves_to_copy.size()); - MutableSpan new_curve_offsets = dst_curves.offsets_for_write(); - offset_indices::gather_group_sizes( - curves.points_by_curve(), curves_to_copy, new_curve_offsets.drop_back(1)); - offset_indices::accumulate_counts_to_offsets(new_curve_offsets); - dst_curves.resize(new_curve_offsets.last(), dst_curves.curves_num()); - - const OffsetIndices src_points_by_curve = curves.points_by_curve(); - const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve(); + const OffsetIndices dst_points_by_curve = offset_indices::gather_selected_offsets( + points_by_curve, curves_to_copy, dst_curves.offsets_for_write()); + dst_curves.resize(dst_points_by_curve.total_size(), dst_curves.curves_num()); const AttributeAccessor src_attributes = curves.attributes(); MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - return true; - } - if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { - return true; - } - const GVArraySpan src = *src_attributes.lookup(id); - GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( - id, meta_data.domain, meta_data.data_type); - gather_group_to_group(src_points_by_curve, dst_points_by_curve, curves_to_copy, src, dst.span); - dst.finish(); - return true; - }); + gather_attributes_group_to_group(src_attributes, + ATTR_DOMAIN_CURVE, + propagation_info, + {}, + points_by_curve, + dst_points_by_curve, + curves_to_copy, + dst_attributes); gather_attributes( src_attributes, ATTR_DOMAIN_CURVE, propagation_info, {}, curves_to_copy, dst_attributes); diff --git a/source/blender/blenlib/BLI_offset_indices.hh b/source/blender/blenlib/BLI_offset_indices.hh index f7dc80e2f21..c6f5ed4dc57 100644 --- a/source/blender/blenlib/BLI_offset_indices.hh +++ b/source/blender/blenlib/BLI_offset_indices.hh @@ -144,6 +144,9 @@ void copy_group_sizes(OffsetIndices offsets, const IndexMask &mask, Mutable /** Gather the number of indices in each indexed group to sizes. */ void gather_group_sizes(OffsetIndices offsets, const IndexMask &mask, MutableSpan sizes); +OffsetIndices gather_selected_offsets(OffsetIndices src_offsets, + const IndexMask &selection, + MutableSpan dst_offsets); /** * Create a map from indexed elements to the source indices, in other words from the larger array * to the smaller array. diff --git a/source/blender/geometry/intern/mesh_separate_selection.cc b/source/blender/geometry/intern/mesh_separate_selection.cc new file mode 100644 index 00000000000..7c17e49b373 --- /dev/null +++ b/source/blender/geometry/intern/mesh_separate_selection.cc @@ -0,0 +1,452 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_array_utils.hh" +#include "BLI_index_mask.hh" +#include "BLI_multi_value_map.hh" + +#include "BKE_attribute.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_fields.hh" +#include "BKE_mesh.hh" +#include "BKE_mesh_mapping.h" + +#include "GEO_mesh_separate.hh" + +namespace blender::geometry { + +static void create_reverse_map(const IndexMask &mask, MutableSpan r_map) +{ + mask.foreach_index_optimized( + GrainSize(4049), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; }); +} + +static void remap_verts(const OffsetIndices src_polys, + const OffsetIndices dst_polys, + const int src_verts_num, + const IndexMask &vert_selection, + const IndexMask &edge_selection, + const IndexMask &poly_selection, + const Span src_edges, + const Span src_corner_verts, + MutableSpan dst_edges, + MutableSpan dst_corner_verts) +{ + Array map(src_verts_num); + create_reverse_map(vert_selection, map); + threading::parallel_invoke( + vert_selection.size() > 1024, + [&]() { + poly_selection.foreach_index( + GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { + const IndexRange src_poly = src_polys[src_i]; + const IndexRange dst_poly = dst_polys[dst_i]; + for (const int i : src_poly.index_range()) { + dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]]; + } + }); + }, + [&]() { + edge_selection.foreach_index(GrainSize(512), + [&](const int64_t src_i, const int64_t dst_i) { + dst_edges[dst_i][0] = map[src_edges[src_i][0]]; + dst_edges[dst_i][1] = map[src_edges[src_i][1]]; + }); + }); +} + +static void remap_edges(const OffsetIndices src_polys, + const OffsetIndices dst_polys, + const int src_edges_num, + const IndexMask &edge_selection, + const IndexMask &poly_selection, + const Span src_corner_edges, + MutableSpan dst_corner_edges) +{ + Array map(src_edges_num); + create_reverse_map(edge_selection, map); + poly_selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { + const IndexRange src_poly = src_polys[src_i]; + const IndexRange dst_poly = dst_polys[dst_i]; + for (const int i : src_poly.index_range()) { + dst_corner_edges[dst_poly[i]] = map[src_corner_edges[src_poly[i]]]; + } + }); +} + +/** A vertex is selected if it's used by a selected edge. */ +static IndexMask vert_selection_from_edge(const Span edges, + const IndexMask &edge_selection, + const int verts_num, + IndexMaskMemory &memory) +{ + Array span(verts_num, false); + edge_selection.foreach_index_optimized([&](const int i) { + span[edges[i][0]] = true; + span[edges[i][1]] = true; + }); + return IndexMask::from_bools(span, memory); +} + +static IndexMask poly_selection_from_mapped_corner(const OffsetIndices polys, + const Span corner_verts_or_edges, + const IndexMask &vert_or_edge_selection, + IndexMaskMemory &memory) +{ + // TODO: To bits first? + return IndexMask::from_predicate( + polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) { + const Span poly = corner_verts_or_edges.slice(polys[i]); + return std::all_of(poly.begin(), poly.end(), [&](const int edge) { + return vert_or_edge_selection.contains(edge); + }); + }); +} + +/** A face is selected if all of its vertices are selected. */ +static IndexMask poly_selection_from_vert(const OffsetIndices polys, + const Span corner_verts, + const IndexMask &vert_selection, + IndexMaskMemory &memory) +{ + return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory); +} + +/** A face is selected if all of its edges are selected. */ +static IndexMask poly_selection_from_edge(const OffsetIndices polys, + const Span corner_edges, + const IndexMask &edge_selection, + IndexMaskMemory &memory) +{ + return poly_selection_from_mapped_corner(polys, corner_edges, edge_selection, memory); +} + +static IndexMask mapped_corner_selection_from_poly(const OffsetIndices polys, + const IndexMask &poly_selection, + const Span corner_verts_or_edges, + const int verts_or_edges_num, + IndexMaskMemory &memory) +{ + Array array(verts_or_edges_num); + poly_selection.foreach_index(GrainSize(512), [&](const int64_t i) { + const Span poly_edges = corner_verts_or_edges.slice(polys[i]); + array.as_mutable_span().fill_indices(poly_edges, true); + }); + return IndexMask::from_bools(array, memory); +} + +/** A vertex is selected if it is used by a selected face. */ +static IndexMask vert_selection_from_poly(const OffsetIndices polys, + const IndexMask &poly_selection, + const Span corner_verts, + const int verts_num, + IndexMaskMemory &memory) +{ + return mapped_corner_selection_from_poly(polys, poly_selection, corner_verts, verts_num, memory); +} + +/** An edge is selected if it is used by a selected face. */ +static IndexMask edge_selection_from_poly(const OffsetIndices polys, + const IndexMask &poly_selection, + const Span corner_edges, + const int edges_num, + IndexMaskMemory &memory) +{ + return mapped_corner_selection_from_poly(polys, poly_selection, corner_edges, edges_num, memory); +} + +Mesh *mesh_copy_selection(const Mesh &src_mesh, + const fn::Field &selection, + const eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info) +{ + const Span src_edges = src_mesh.edges(); + const OffsetIndices src_polys = src_mesh.polys(); + const Span src_corner_verts = src_mesh.corner_verts(); + const Span src_corner_edges = src_mesh.corner_edges(); + + // TODO: Local + IndexMaskMemory memory; + IndexMask vert_selection; + IndexMask edge_selection; + IndexMask poly_selection; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); + fn::FieldEvaluator evaluator(context, src_mesh.totvert); + evaluator.add(selection); + evaluator.evaluate(); + vert_selection = evaluator.get_evaluated_as_mask(0); + BitVector<> selected_verts(src_mesh.totvert); + vert_selection.to_bits(selected_verts); + threading::parallel_invoke( + vert_selection.size() > 1024, + [&]() { + edge_selection = IndexMask::from_predicate( + src_edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { + const int2 edge = src_edges[i]; + return selected_verts[edge[0]] && selected_verts[edge[1]]; + }); + }, + [&]() { + poly_selection = poly_selection_from_vert( + src_polys, src_corner_verts, vert_selection, memory); + }); + break; + } + case ATTR_DOMAIN_EDGE: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE); + fn::FieldEvaluator evaluator(context, src_mesh.totedge); + evaluator.add(selection); + evaluator.evaluate(); + edge_selection = evaluator.get_evaluated_as_mask(0); + threading::parallel_invoke( + edge_selection.size() > 1024, + [&]() { + vert_selection = vert_selection_from_edge( + src_edges, edge_selection, src_mesh.totvert, memory); + }, + [&]() { + poly_selection = poly_selection_from_edge( + src_polys, src_corner_edges, edge_selection, memory); + }); + break; + } + case ATTR_DOMAIN_FACE: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); + fn::FieldEvaluator evaluator(context, src_mesh.totpoly); + evaluator.add(selection); + evaluator.evaluate(); + poly_selection = evaluator.get_evaluated_as_mask(0); + threading::parallel_invoke( + poly_selection.size() > 1024, + [&]() { + vert_selection = vert_selection_from_poly( + src_polys, poly_selection, src_corner_verts, src_mesh.totvert, memory); + }, + [&]() { + edge_selection = edge_selection_from_poly( + src_polys, poly_selection, src_corner_edges, src_mesh.totedge, memory); + }); + break; + } + default: + BLI_assert_unreachable(); + break; + } + + Mesh *dst_mesh = BKE_mesh_new_nomain( + vert_selection.size(), edge_selection.size(), poly_selection.size(), 0); + BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); + + const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( + src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); + dst_mesh->totloop = dst_polys.total_size(); + + const bke::AttributeAccessor src_attributes = src_mesh.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); + + dst_attributes.add(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); + dst_attributes.add(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); + + threading::parallel_invoke( + vert_selection.size() > 1024, + [&]() { + remap_verts(src_polys, + dst_polys, + src_mesh.totvert, + vert_selection, + edge_selection, + poly_selection, + src_edges, + src_corner_verts, + dst_mesh->edges_for_write(), + dst_mesh->corner_verts_for_write()); + }, + [&]() { + remap_edges(src_polys, + dst_polys, + src_edges.size(), + edge_selection, + poly_selection, + src_corner_edges, + dst_mesh->corner_edges_for_write()); + }); + + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_selection, dst_attributes); + bke::gather_attributes(src_attributes, + ATTR_DOMAIN_EDGE, + propagation_info, + {".edge_verts"}, + edge_selection, + dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes); + bke::gather_attributes_group_to_group(src_attributes, + ATTR_DOMAIN_CORNER, + propagation_info, + {".corner_edge", ".corner_vert"}, + src_polys, + dst_polys, + poly_selection, + dst_attributes); + + return dst_mesh; +} + +Mesh *mesh_copy_selection_keep_verts( + const Mesh &src_mesh, + const fn::Field &selection, + const eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info) +{ + const Span src_edges = src_mesh.edges(); + const OffsetIndices src_polys = src_mesh.polys(); + const Span src_corner_verts = src_mesh.corner_verts(); + const Span src_corner_edges = src_mesh.corner_edges(); + + IndexMaskMemory memory; + IndexMask edge_selection; + IndexMask poly_selection; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); + fn::FieldEvaluator evaluator(context, src_mesh.totvert); + evaluator.add(selection); + evaluator.evaluate(); + const VArraySpan vert_selection = evaluator.get_evaluated(0); + + // TODO: Deduplicate + edge_selection = IndexMask::from_predicate( + src_edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { + const int2 edge = src_edges[i]; + return vert_selection[edge[0]] && vert_selection[edge[1]]; + }); + poly_selection = IndexMask::from_predicate( + src_polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) { + const Span poly_verts = src_corner_verts.slice(src_polys[i]); + return std::all_of(poly_verts.begin(), poly_verts.end(), [&](const int vert) { + return vert_selection[vert]; + }); + }); + break; + } + case ATTR_DOMAIN_EDGE: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE); + fn::FieldEvaluator evaluator(context, src_mesh.totedge); + evaluator.add(selection); + evaluator.evaluate(); + edge_selection = evaluator.get_evaluated_as_mask(0); + poly_selection = poly_selection_from_edge( + src_polys, src_corner_edges, edge_selection, memory); + break; + } + case ATTR_DOMAIN_FACE: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_FACE); + fn::FieldEvaluator evaluator(context, src_polys.size()); + evaluator.add(selection); + evaluator.evaluate(); + poly_selection = evaluator.get_evaluated_as_mask(0); + edge_selection = edge_selection_from_poly( + src_polys, poly_selection, src_corner_edges, src_edges.size(), memory); + break; + } + default: + BLI_assert_unreachable(); + break; + } + + Mesh *dst_mesh = BKE_mesh_new_nomain( + src_mesh.totvert, edge_selection.size(), poly_selection.size(), 0); + BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); + CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); + dst_mesh->totvert = src_mesh.totvert; + + const bke::AttributeAccessor src_attributes = src_mesh.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); + + const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( + src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); + dst_mesh->totloop = dst_polys.total_size(); + + dst_attributes.add(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); + + remap_edges(src_polys, + dst_polys, + src_edges.size(), + edge_selection, + poly_selection, + src_corner_edges, + dst_mesh->corner_edges_for_write()); + + bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_selection, dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes); + bke::gather_attributes_group_to_group(src_attributes, + ATTR_DOMAIN_CORNER, + propagation_info, + {".corner_edge"}, + src_polys, + dst_polys, + poly_selection, + dst_attributes); + + /* Positions are not changed by the operation, so the bounds are the same. */ + dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache; + return dst_mesh; +} + +Mesh *mesh_copy_selection_keep_verts_edges( + const Mesh &mesh, + const fn::Field &selection, + const bke::AnonymousAttributePropagationInfo &propagation_info) +{ + const bke::MeshFieldContext context(mesh, ATTR_DOMAIN_FACE); + fn::FieldEvaluator evaluator(context, mesh.totpoly); + evaluator.set_selection(selection); + evaluator.evaluate(); + const IndexMask poly_selection = evaluator.get_evaluated_selection_as_mask(); + if (poly_selection.is_empty()) { + return nullptr; + } + if (poly_selection.size() == mesh.totpoly) { + return BKE_mesh_copy_for_eval(&mesh); + } + + Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_selection.size(), 0); + CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); + CustomData_free_layer_named(&dst_mesh->edata, ".edge_verts", 0); + dst_mesh->totvert = mesh.totvert; + dst_mesh->totedge = mesh.totedge; + BKE_mesh_copy_parameters_for_eval(dst_mesh, &mesh); + + const OffsetIndices src_polys = mesh.polys(); + + const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( + src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); + dst_mesh->totloop = dst_polys.total_size(); + + const bke::AttributeAccessor src_attributes = mesh.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); + + bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); + bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes); + bke::gather_attributes_group_to_group(src_attributes, + ATTR_DOMAIN_CORNER, + propagation_info, + {}, + src_polys, + dst_polys, + poly_selection, + dst_attributes); + + /* Positions are not changed by the operation, so the bounds are the same. */ + dst_mesh->runtime->bounds_cache = mesh.runtime->bounds_cache; + return dst_mesh; +} + +} // namespace blender::geometry -- 2.30.2 From b886fcac4c604e831001b3008c6b64448d40e702 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 30 May 2023 14:13:24 -0400 Subject: [PATCH 02/20] Missing change --- source/blender/blenlib/intern/offset_indices.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenlib/intern/offset_indices.cc b/source/blender/blenlib/intern/offset_indices.cc index 76f0279a58d..818ab707616 100644 --- a/source/blender/blenlib/intern/offset_indices.cc +++ b/source/blender/blenlib/intern/offset_indices.cc @@ -36,6 +36,15 @@ void gather_group_sizes(const OffsetIndices offsets, }); } +OffsetIndices gather_selected_offsets(const OffsetIndices src_offsets, + const IndexMask &selection, + MutableSpan dst_offsets) +{ + BLI_assert(selection.size() == (dst_offsets.size() - 1)); + offset_indices::gather_group_sizes(src_offsets, selection, dst_offsets); + offset_indices::accumulate_counts_to_offsets(dst_offsets); +} + void build_reverse_map(OffsetIndices offsets, MutableSpan r_map) { threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) { -- 2.30.2 From e2a3e022bf050bcb3c5cea711a96015aeb81b949 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 30 May 2023 21:20:43 -0400 Subject: [PATCH 03/20] Progress --- source/blender/geometry/GEO_mesh_separate.hh | 37 +++ .../intern/mesh_separate_selection.cc | 225 +++++++++++------- 2 files changed, 176 insertions(+), 86 deletions(-) create mode 100644 source/blender/geometry/GEO_mesh_separate.hh diff --git a/source/blender/geometry/GEO_mesh_separate.hh b/source/blender/geometry/GEO_mesh_separate.hh new file mode 100644 index 00000000000..14a4d17c2b8 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_separate.hh @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_attribute.h" + +struct Mesh; +namespace blender { +class IndexMask; +namespace fn { +template class Field; +} +namespace bke { +class AnonymousAttributePropagationInfo; +} +} // namespace blender + +namespace blender::geometry { + +Mesh *mesh_copy_selection(const Mesh &src_mesh, + const fn::Field &selection, + eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +Mesh *mesh_copy_selection_keep_verts( + const Mesh &src_mesh, + const fn::Field &selection, + eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +Mesh *mesh_copy_selection_keep_edges( + const Mesh &mesh, + const fn::Field &selection, + const eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_separate_selection.cc b/source/blender/geometry/intern/mesh_separate_selection.cc index 7c17e49b373..6b1ef78ff99 100644 --- a/source/blender/geometry/intern/mesh_separate_selection.cc +++ b/source/blender/geometry/intern/mesh_separate_selection.cc @@ -1,14 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_array_utils.hh" #include "BLI_index_mask.hh" -#include "BLI_multi_value_map.hh" #include "BKE_attribute.hh" -#include "BKE_attribute_math.hh" #include "BKE_geometry_fields.hh" #include "BKE_mesh.hh" -#include "BKE_mesh_mapping.h" #include "GEO_mesh_separate.hh" @@ -87,39 +83,6 @@ static IndexMask vert_selection_from_edge(const Span edges, return IndexMask::from_bools(span, memory); } -static IndexMask poly_selection_from_mapped_corner(const OffsetIndices polys, - const Span corner_verts_or_edges, - const IndexMask &vert_or_edge_selection, - IndexMaskMemory &memory) -{ - // TODO: To bits first? - return IndexMask::from_predicate( - polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) { - const Span poly = corner_verts_or_edges.slice(polys[i]); - return std::all_of(poly.begin(), poly.end(), [&](const int edge) { - return vert_or_edge_selection.contains(edge); - }); - }); -} - -/** A face is selected if all of its vertices are selected. */ -static IndexMask poly_selection_from_vert(const OffsetIndices polys, - const Span corner_verts, - const IndexMask &vert_selection, - IndexMaskMemory &memory) -{ - return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory); -} - -/** A face is selected if all of its edges are selected. */ -static IndexMask poly_selection_from_edge(const OffsetIndices polys, - const Span corner_edges, - const IndexMask &edge_selection, - IndexMaskMemory &memory) -{ - return poly_selection_from_mapped_corner(polys, corner_edges, edge_selection, memory); -} - static IndexMask mapped_corner_selection_from_poly(const OffsetIndices polys, const IndexMask &poly_selection, const Span corner_verts_or_edges, @@ -154,6 +117,49 @@ static IndexMask edge_selection_from_poly(const OffsetIndices polys, return mapped_corner_selection_from_poly(polys, poly_selection, corner_edges, edges_num, memory); } +/** An edge is selected if both of its vertices are selected. */ +static IndexMask edge_selection_from_vert(const Span edges, + const Span vert_selection, + IndexMaskMemory &memory) +{ + return IndexMask::from_predicate( + edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { + const int2 edge = edges[i]; + return vert_selection[edge[0]] && vert_selection[edge[1]]; + }); +} + +static IndexMask poly_selection_from_mapped_corner(const OffsetIndices polys, + const Span corner_verts_or_edges, + const Span vert_or_edge_selection, + IndexMaskMemory &memory) +{ + return IndexMask::from_predicate( + polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) { + const Span poly = corner_verts_or_edges.slice(polys[i]); + return std::all_of( + poly.begin(), poly.end(), [&](const int i) { return vert_or_edge_selection[i]; }); + }); +} + +/** A face is selected if all of its vertices are selected. */ +static IndexMask poly_selection_from_vert(const OffsetIndices polys, + const Span corner_verts, + const Span vert_selection, + IndexMaskMemory &memory) +{ + return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory); +} + +/** A face is selected if all of its edges are selected. */ +static IndexMask poly_selection_from_edge(const OffsetIndices polys, + const Span corner_edges, + const Span edge_selection, + IndexMaskMemory &memory) +{ + return poly_selection_from_mapped_corner(polys, corner_edges, edge_selection, memory); +} + Mesh *mesh_copy_selection(const Mesh &src_mesh, const fn::Field &selection, const eAttrDomain selection_domain, @@ -175,21 +181,14 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, fn::FieldEvaluator evaluator(context, src_mesh.totvert); evaluator.add(selection); evaluator.evaluate(); - vert_selection = evaluator.get_evaluated_as_mask(0); - BitVector<> selected_verts(src_mesh.totvert); - vert_selection.to_bits(selected_verts); + const VArraySpan selection = evaluator.get_evaluated(0); threading::parallel_invoke( vert_selection.size() > 1024, - [&]() { - edge_selection = IndexMask::from_predicate( - src_edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { - const int2 edge = src_edges[i]; - return selected_verts[edge[0]] && selected_verts[edge[1]]; - }); - }, + [&]() { vert_selection = IndexMask::from_bools(selection, memory); }, + [&]() { edge_selection = edge_selection_from_vert(src_edges, selection, memory); }, [&]() { poly_selection = poly_selection_from_vert( - src_polys, src_corner_verts, vert_selection, memory); + src_polys, src_corner_verts, selection, memory); }); break; } @@ -198,16 +197,17 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, fn::FieldEvaluator evaluator(context, src_mesh.totedge); evaluator.add(selection); evaluator.evaluate(); - edge_selection = evaluator.get_evaluated_as_mask(0); + const VArraySpan selection = evaluator.get_evaluated(0); threading::parallel_invoke( edge_selection.size() > 1024, [&]() { + edge_selection = IndexMask::from_bools(selection, memory); vert_selection = vert_selection_from_edge( src_edges, edge_selection, src_mesh.totvert, memory); }, [&]() { poly_selection = poly_selection_from_edge( - src_polys, src_corner_edges, edge_selection, memory); + src_polys, src_corner_edges, selection, memory); }); break; } @@ -216,7 +216,13 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, fn::FieldEvaluator evaluator(context, src_mesh.totpoly); evaluator.add(selection); evaluator.evaluate(); - poly_selection = evaluator.get_evaluated_as_mask(0); + poly_selection = IndexMask::from_bools(evaluator.get_evaluated(0), memory); + if (poly_selection.is_empty()) { + return nullptr; + } + if (poly_selection.size() == src_mesh.totpoly) { + return BKE_mesh_copy_for_eval(&src_mesh); + } threading::parallel_invoke( poly_selection.size() > 1024, [&]() { @@ -314,20 +320,13 @@ Mesh *mesh_copy_selection_keep_verts( fn::FieldEvaluator evaluator(context, src_mesh.totvert); evaluator.add(selection); evaluator.evaluate(); - const VArraySpan vert_selection = evaluator.get_evaluated(0); - - // TODO: Deduplicate - edge_selection = IndexMask::from_predicate( - src_edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { - const int2 edge = src_edges[i]; - return vert_selection[edge[0]] && vert_selection[edge[1]]; - }); - poly_selection = IndexMask::from_predicate( - src_polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) { - const Span poly_verts = src_corner_verts.slice(src_polys[i]); - return std::all_of(poly_verts.begin(), poly_verts.end(), [&](const int vert) { - return vert_selection[vert]; - }); + const VArraySpan selection = evaluator.get_evaluated(0); + threading::parallel_invoke( + poly_selection.size() > 1024, + [&]() { edge_selection = edge_selection_from_vert(src_edges, selection, memory); }, + [&]() { + poly_selection = poly_selection_from_vert( + src_polys, src_corner_verts, selection, memory); }); break; } @@ -336,9 +335,20 @@ Mesh *mesh_copy_selection_keep_verts( fn::FieldEvaluator evaluator(context, src_mesh.totedge); evaluator.add(selection); evaluator.evaluate(); - edge_selection = evaluator.get_evaluated_as_mask(0); - poly_selection = poly_selection_from_edge( - src_polys, src_corner_edges, edge_selection, memory); + const VArraySpan selection = evaluator.get_evaluated(0); + threading::parallel_invoke( + poly_selection.size() > 1024, + [&]() { edge_selection = IndexMask::from_bools(selection, memory); }, + [&]() { + poly_selection = poly_selection_from_edge( + src_polys, src_corner_edges, selection, memory); + }); + if (edge_selection.is_empty()) { + return nullptr; + } + if (edge_selection.size() == src_mesh.totedge) { + return BKE_mesh_copy_for_eval(&src_mesh); + } break; } case ATTR_DOMAIN_FACE: { @@ -346,9 +356,20 @@ Mesh *mesh_copy_selection_keep_verts( fn::FieldEvaluator evaluator(context, src_polys.size()); evaluator.add(selection); evaluator.evaluate(); - poly_selection = evaluator.get_evaluated_as_mask(0); - edge_selection = edge_selection_from_poly( - src_polys, poly_selection, src_corner_edges, src_edges.size(), memory); + const VArraySpan selection = evaluator.get_evaluated(0); + threading::parallel_invoke( + poly_selection.size() > 1024, + [&]() { + edge_selection = edge_selection_from_poly( + src_polys, poly_selection, src_corner_edges, src_edges.size(), memory); + }, + [&]() { poly_selection = IndexMask::from_bools(selection, memory); }); + if (poly_selection.is_empty()) { + return nullptr; + } + if (poly_selection.size() == src_mesh.totpoly) { + return BKE_mesh_copy_for_eval(&src_mesh); + } break; } default: @@ -398,37 +419,69 @@ Mesh *mesh_copy_selection_keep_verts( return dst_mesh; } -Mesh *mesh_copy_selection_keep_verts_edges( - const Mesh &mesh, +Mesh *mesh_copy_selection_keep_edges( + const Mesh &src_mesh, const fn::Field &selection, + const eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info) { - const bke::MeshFieldContext context(mesh, ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator(context, mesh.totpoly); - evaluator.set_selection(selection); - evaluator.evaluate(); - const IndexMask poly_selection = evaluator.get_evaluated_selection_as_mask(); + IndexMaskMemory memory; + IndexMask poly_selection; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); + fn::FieldEvaluator evaluator(context, src_mesh.totvert); + evaluator.add(selection); + evaluator.evaluate(); + const VArraySpan vert_selection = evaluator.get_evaluated(0); + poly_selection = poly_selection_from_vert( + src_mesh.polys(), src_mesh.corner_verts(), vert_selection, memory); + break; + } + case ATTR_DOMAIN_EDGE: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE); + fn::FieldEvaluator evaluator(context, src_mesh.totedge); + evaluator.add(selection); + evaluator.evaluate(); + const VArraySpan edge_selection = evaluator.get_evaluated(0); + poly_selection = poly_selection_from_edge( + src_mesh.polys(), src_mesh.corner_edges(), edge_selection, memory); + break; + } + case ATTR_DOMAIN_FACE: { + const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_FACE); + fn::FieldEvaluator evaluator(context, src_mesh.totpoly); + evaluator.add(selection); + evaluator.evaluate(); + poly_selection = IndexMask::from_bools(evaluator.get_evaluated(0), memory); + break; + } + default: + BLI_assert_unreachable(); + break; + } + if (poly_selection.is_empty()) { return nullptr; } - if (poly_selection.size() == mesh.totpoly) { - return BKE_mesh_copy_for_eval(&mesh); + if (poly_selection.size() == src_mesh.totpoly) { + return BKE_mesh_copy_for_eval(&src_mesh); } Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_selection.size(), 0); CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); CustomData_free_layer_named(&dst_mesh->edata, ".edge_verts", 0); - dst_mesh->totvert = mesh.totvert; - dst_mesh->totedge = mesh.totedge; - BKE_mesh_copy_parameters_for_eval(dst_mesh, &mesh); + dst_mesh->totvert = src_mesh.totvert; + dst_mesh->totedge = src_mesh.totedge; + BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); - const OffsetIndices src_polys = mesh.polys(); + const OffsetIndices src_polys = src_mesh.polys(); const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); dst_mesh->totloop = dst_polys.total_size(); - const bke::AttributeAccessor src_attributes = mesh.attributes(); + const bke::AttributeAccessor src_attributes = src_mesh.attributes(); bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); @@ -445,7 +498,7 @@ Mesh *mesh_copy_selection_keep_verts_edges( dst_attributes); /* Positions are not changed by the operation, so the bounds are the same. */ - dst_mesh->runtime->bounds_cache = mesh.runtime->bounds_cache; + dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache; return dst_mesh; } -- 2.30.2 From 153e416677fae4109356aaec44f7d6703eba20eb Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 30 May 2023 22:31:03 -0400 Subject: [PATCH 04/20] Progress --- .../blenkernel/intern/attribute_access.cc | 1 - .../blender/blenlib/intern/offset_indices.cc | 1 + source/blender/geometry/CMakeLists.txt | 2 + ...separate.hh => GEO_mesh_copy_selection.hh} | 0 ...te_selection.cc => mesh_copy_selection.cc} | 439 ++++----- .../nodes/node_geo_delete_geometry.cc | 923 +----------------- 6 files changed, 234 insertions(+), 1132 deletions(-) rename source/blender/geometry/{GEO_mesh_separate.hh => GEO_mesh_copy_selection.hh} (100%) rename source/blender/geometry/intern/{mesh_separate_selection.cc => mesh_copy_selection.cc} (50%) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 0e5ef45a933..6cf67a9f69a 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1059,7 +1059,6 @@ void gather_attributes_group_to_group(const AttributeAccessor src_attributes, const IndexMask &selection, MutableAttributeAccessor dst_attributes) { - const int src_size = src_attributes.domain_size(domain); src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (meta_data.domain != domain) { return true; diff --git a/source/blender/blenlib/intern/offset_indices.cc b/source/blender/blenlib/intern/offset_indices.cc index 818ab707616..dccbce38a4b 100644 --- a/source/blender/blenlib/intern/offset_indices.cc +++ b/source/blender/blenlib/intern/offset_indices.cc @@ -43,6 +43,7 @@ OffsetIndices gather_selected_offsets(const OffsetIndices src_offsets, BLI_assert(selection.size() == (dst_offsets.size() - 1)); offset_indices::gather_group_sizes(src_offsets, selection, dst_offsets); offset_indices::accumulate_counts_to_offsets(dst_offsets); + return OffsetIndices(dst_offsets); } void build_reverse_map(OffsetIndices offsets, MutableSpan r_map) diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 0239c095358..e3cf708e8ff 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC intern/add_curves_on_mesh.cc intern/curve_constraints.cc intern/fillet_curves.cc + intern/mesh_copy_selection.cc intern/mesh_flip_faces.cc intern/mesh_merge_by_distance.cc intern/mesh_primitive_cuboid.cc @@ -42,6 +43,7 @@ set(SRC GEO_mesh_merge_by_distance.hh GEO_mesh_primitive_cuboid.hh GEO_mesh_split_edges.hh + GEO_mesh_copy_selection.hh GEO_mesh_to_curve.hh GEO_mesh_to_volume.hh GEO_point_merge_by_distance.hh diff --git a/source/blender/geometry/GEO_mesh_separate.hh b/source/blender/geometry/GEO_mesh_copy_selection.hh similarity index 100% rename from source/blender/geometry/GEO_mesh_separate.hh rename to source/blender/geometry/GEO_mesh_copy_selection.hh diff --git a/source/blender/geometry/intern/mesh_separate_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc similarity index 50% rename from source/blender/geometry/intern/mesh_separate_selection.cc rename to source/blender/geometry/intern/mesh_copy_selection.cc index 6b1ef78ff99..2450ce98ca0 100644 --- a/source/blender/geometry/intern/mesh_separate_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_enumerable_thread_specific.hh" #include "BLI_index_mask.hh" #include "BKE_attribute.hh" #include "BKE_geometry_fields.hh" #include "BKE_mesh.hh" -#include "GEO_mesh_separate.hh" +#include "GEO_mesh_copy_selection.hh" namespace blender::geometry { @@ -19,48 +20,46 @@ static void create_reverse_map(const IndexMask &mask, MutableSpan r_map) static void remap_verts(const OffsetIndices src_polys, const OffsetIndices dst_polys, const int src_verts_num, - const IndexMask &vert_selection, - const IndexMask &edge_selection, - const IndexMask &poly_selection, + const IndexMask &vert_mask, + const IndexMask &edge_mask, + const IndexMask &poly_mask, const Span src_edges, const Span src_corner_verts, MutableSpan dst_edges, MutableSpan dst_corner_verts) { Array map(src_verts_num); - create_reverse_map(vert_selection, map); + create_reverse_map(vert_mask, map); threading::parallel_invoke( - vert_selection.size() > 1024, + vert_mask.size() > 1024, [&]() { - poly_selection.foreach_index( - GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { - const IndexRange src_poly = src_polys[src_i]; - const IndexRange dst_poly = dst_polys[dst_i]; - for (const int i : src_poly.index_range()) { - dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]]; - } - }); + poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { + const IndexRange src_poly = src_polys[src_i]; + const IndexRange dst_poly = dst_polys[dst_i]; + for (const int i : src_poly.index_range()) { + dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]]; + } + }); }, [&]() { - edge_selection.foreach_index(GrainSize(512), - [&](const int64_t src_i, const int64_t dst_i) { - dst_edges[dst_i][0] = map[src_edges[src_i][0]]; - dst_edges[dst_i][1] = map[src_edges[src_i][1]]; - }); + edge_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { + dst_edges[dst_i][0] = map[src_edges[src_i][0]]; + dst_edges[dst_i][1] = map[src_edges[src_i][1]]; + }); }); } static void remap_edges(const OffsetIndices src_polys, const OffsetIndices dst_polys, const int src_edges_num, - const IndexMask &edge_selection, - const IndexMask &poly_selection, + const IndexMask &edge_mask, + const IndexMask &poly_mask, const Span src_corner_edges, MutableSpan dst_corner_edges) { Array map(src_edges_num); - create_reverse_map(edge_selection, map); - poly_selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { + create_reverse_map(edge_mask, map); + poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) { const IndexRange src_poly = src_polys[src_i]; const IndexRange dst_poly = dst_polys[dst_i]; for (const int i : src_poly.index_range()) { @@ -71,12 +70,12 @@ static void remap_edges(const OffsetIndices src_polys, /** A vertex is selected if it's used by a selected edge. */ static IndexMask vert_selection_from_edge(const Span edges, - const IndexMask &edge_selection, + const IndexMask &edge_mask, const int verts_num, IndexMaskMemory &memory) { Array span(verts_num, false); - edge_selection.foreach_index_optimized([&](const int i) { + edge_mask.foreach_index_optimized([&](const int i) { span[edges[i][0]] = true; span[edges[i][1]] = true; }); @@ -84,13 +83,13 @@ static IndexMask vert_selection_from_edge(const Span edges, } static IndexMask mapped_corner_selection_from_poly(const OffsetIndices polys, - const IndexMask &poly_selection, + const IndexMask &poly_mask, const Span corner_verts_or_edges, const int verts_or_edges_num, IndexMaskMemory &memory) { Array array(verts_or_edges_num); - poly_selection.foreach_index(GrainSize(512), [&](const int64_t i) { + poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) { const Span poly_edges = corner_verts_or_edges.slice(polys[i]); array.as_mutable_span().fill_indices(poly_edges, true); }); @@ -99,33 +98,33 @@ static IndexMask mapped_corner_selection_from_poly(const OffsetIndices poly /** A vertex is selected if it is used by a selected face. */ static IndexMask vert_selection_from_poly(const OffsetIndices polys, - const IndexMask &poly_selection, + const IndexMask &poly_mask, const Span corner_verts, const int verts_num, IndexMaskMemory &memory) { - return mapped_corner_selection_from_poly(polys, poly_selection, corner_verts, verts_num, memory); + return mapped_corner_selection_from_poly(polys, poly_mask, corner_verts, verts_num, memory); } /** An edge is selected if it is used by a selected face. */ static IndexMask edge_selection_from_poly(const OffsetIndices polys, - const IndexMask &poly_selection, + const IndexMask &poly_mask, const Span corner_edges, const int edges_num, IndexMaskMemory &memory) { - return mapped_corner_selection_from_poly(polys, poly_selection, corner_edges, edges_num, memory); + return mapped_corner_selection_from_poly(polys, poly_mask, corner_edges, edges_num, memory); } /** An edge is selected if both of its vertices are selected. */ static IndexMask edge_selection_from_vert(const Span edges, - const Span vert_selection, + const Span vert_mask, IndexMaskMemory &memory) { return IndexMask::from_predicate( edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { const int2 edge = edges[i]; - return vert_selection[edge[0]] && vert_selection[edge[1]]; + return vert_mask[edge[0]] && vert_mask[edge[1]]; }); } @@ -145,23 +144,23 @@ static IndexMask poly_selection_from_mapped_corner(const OffsetIndices poly /** A face is selected if all of its vertices are selected. */ static IndexMask poly_selection_from_vert(const OffsetIndices polys, const Span corner_verts, - const Span vert_selection, + const Span vert_mask, IndexMaskMemory &memory) { - return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory); + return poly_selection_from_mapped_corner(polys, corner_verts, vert_mask, memory); } /** A face is selected if all of its edges are selected. */ static IndexMask poly_selection_from_edge(const OffsetIndices polys, const Span corner_edges, - const Span edge_selection, + const Span edge_mask, IndexMaskMemory &memory) { - return poly_selection_from_mapped_corner(polys, corner_edges, edge_selection, memory); + return poly_selection_from_mapped_corner(polys, corner_edges, edge_mask, memory); } Mesh *mesh_copy_selection(const Mesh &src_mesh, - const fn::Field &selection, + const fn::Field &selection_field, const eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info) { @@ -169,69 +168,61 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, const OffsetIndices src_polys = src_mesh.polys(); const Span src_corner_verts = src_mesh.corner_verts(); const Span src_corner_edges = src_mesh.corner_edges(); + const bke::AttributeAccessor src_attributes = src_mesh.attributes(); - // TODO: Local - IndexMaskMemory memory; - IndexMask vert_selection; - IndexMask edge_selection; - IndexMask poly_selection; + const bke::MeshFieldContext context(src_mesh, selection_domain); + fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain)); + evaluator.add(selection_field); + evaluator.evaluate(); + const VArray selection = evaluator.get_evaluated(0); + if (const std::optional single = selection.get_if_single()) { + return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr; + } + + threading::EnumerableThreadSpecific memory; + IndexMask vert_mask; + IndexMask edge_mask; + IndexMask poly_mask; switch (selection_domain) { case ATTR_DOMAIN_POINT: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator(context, src_mesh.totvert); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan selection = evaluator.get_evaluated(0); + const VArraySpan span(selection); threading::parallel_invoke( - vert_selection.size() > 1024, - [&]() { vert_selection = IndexMask::from_bools(selection, memory); }, - [&]() { edge_selection = edge_selection_from_vert(src_edges, selection, memory); }, + src_mesh.totvert > 1024, + [&]() { vert_mask = IndexMask::from_bools(span, memory.local()); }, + [&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); }, [&]() { - poly_selection = poly_selection_from_vert( - src_polys, src_corner_verts, selection, memory); + poly_mask = poly_selection_from_vert( + src_polys, src_corner_verts, span, memory.local()); }); break; } case ATTR_DOMAIN_EDGE: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE); - fn::FieldEvaluator evaluator(context, src_mesh.totedge); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan selection = evaluator.get_evaluated(0); + const VArraySpan span(selection); threading::parallel_invoke( - edge_selection.size() > 1024, + src_edges.size() > 1024, [&]() { - edge_selection = IndexMask::from_bools(selection, memory); - vert_selection = vert_selection_from_edge( - src_edges, edge_selection, src_mesh.totvert, memory); + edge_mask = IndexMask::from_bools(span, memory.local()); + vert_mask = vert_selection_from_edge( + src_edges, edge_mask, src_mesh.totvert, memory.local()); }, [&]() { - poly_selection = poly_selection_from_edge( - src_polys, src_corner_edges, selection, memory); + poly_mask = poly_selection_from_edge( + src_polys, src_corner_edges, span, memory.local()); }); break; } case ATTR_DOMAIN_FACE: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator(context, src_mesh.totpoly); - evaluator.add(selection); - evaluator.evaluate(); - poly_selection = IndexMask::from_bools(evaluator.get_evaluated(0), memory); - if (poly_selection.is_empty()) { - return nullptr; - } - if (poly_selection.size() == src_mesh.totpoly) { - return BKE_mesh_copy_for_eval(&src_mesh); - } + const VArraySpan span(selection); + poly_mask = IndexMask::from_bools(span, memory.local()); threading::parallel_invoke( - poly_selection.size() > 1024, + poly_mask.size() > 1024, [&]() { - vert_selection = vert_selection_from_poly( - src_polys, poly_selection, src_corner_verts, src_mesh.totvert, memory); + vert_mask = vert_selection_from_poly( + src_polys, poly_mask, src_corner_verts, src_mesh.totvert, memory.local()); }, [&]() { - edge_selection = edge_selection_from_poly( - src_polys, poly_selection, src_corner_edges, src_mesh.totedge, memory); + edge_mask = edge_selection_from_poly( + src_polys, poly_mask, src_corner_edges, src_mesh.totedge, memory.local()); }); break; } @@ -240,69 +231,78 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, break; } - Mesh *dst_mesh = BKE_mesh_new_nomain( - vert_selection.size(), edge_selection.size(), poly_selection.size(), 0); + if (vert_mask.is_empty()) { + return nullptr; + } + if (vert_mask.size() == src_mesh.totvert) { + return BKE_mesh_copy_for_eval(&src_mesh); + } + + Mesh *dst_mesh = BKE_mesh_new_nomain(vert_mask.size(), edge_mask.size(), poly_mask.size(), 0); + CustomData_free_layer_named(&dst_mesh->ldata, ".corner_vert", 0); + CustomData_free_layer_named(&dst_mesh->ldata, ".corner_edge", 0); BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); - - const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( - src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); - dst_mesh->totloop = dst_polys.total_size(); - - const bke::AttributeAccessor src_attributes = src_mesh.attributes(); + MutableSpan dst_edges = dst_mesh->edges_for_write(); bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); + const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( + src_polys, poly_mask, dst_mesh->poly_offsets_for_write()); + dst_mesh->totloop = dst_polys.total_size(); dst_attributes.add(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); dst_attributes.add(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); + MutableSpan dst_corner_verts = dst_mesh->corner_verts_for_write(); + MutableSpan dst_corner_edges = dst_mesh->corner_edges_for_write(); threading::parallel_invoke( - vert_selection.size() > 1024, + vert_mask.size() > 1024, [&]() { remap_verts(src_polys, dst_polys, src_mesh.totvert, - vert_selection, - edge_selection, - poly_selection, + vert_mask, + edge_mask, + poly_mask, src_edges, src_corner_verts, - dst_mesh->edges_for_write(), - dst_mesh->corner_verts_for_write()); + dst_edges, + dst_corner_verts); }, [&]() { remap_edges(src_polys, dst_polys, src_edges.size(), - edge_selection, - poly_selection, + edge_mask, + poly_mask, src_corner_edges, - dst_mesh->corner_edges_for_write()); + dst_corner_edges); + }, + [&]() { + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_mask, dst_attributes); + bke::gather_attributes(src_attributes, + ATTR_DOMAIN_EDGE, + propagation_info, + {".edge_verts"}, + edge_mask, + dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes); + bke::gather_attributes_group_to_group(src_attributes, + ATTR_DOMAIN_CORNER, + propagation_info, + {".corner_edge", ".corner_vert"}, + src_polys, + dst_polys, + poly_mask, + dst_attributes); }); - bke::gather_attributes( - src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_selection, dst_attributes); - bke::gather_attributes(src_attributes, - ATTR_DOMAIN_EDGE, - propagation_info, - {".edge_verts"}, - edge_selection, - dst_attributes); - bke::gather_attributes( - src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes); - bke::gather_attributes_group_to_group(src_attributes, - ATTR_DOMAIN_CORNER, - propagation_info, - {".corner_edge", ".corner_vert"}, - src_polys, - dst_polys, - poly_selection, - dst_attributes); - return dst_mesh; } Mesh *mesh_copy_selection_keep_verts( const Mesh &src_mesh, - const fn::Field &selection, + const fn::Field &selection_field, const eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info) { @@ -310,66 +310,52 @@ Mesh *mesh_copy_selection_keep_verts( const OffsetIndices src_polys = src_mesh.polys(); const Span src_corner_verts = src_mesh.corner_verts(); const Span src_corner_edges = src_mesh.corner_edges(); + const bke::AttributeAccessor src_attributes = src_mesh.attributes(); - IndexMaskMemory memory; - IndexMask edge_selection; - IndexMask poly_selection; + const bke::MeshFieldContext context(src_mesh, selection_domain); + fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain)); + evaluator.add(selection_field); + evaluator.evaluate(); + const VArray selection = evaluator.get_evaluated(0); + if (const std::optional single = selection.get_if_single()) { + return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr; + } + + threading::EnumerableThreadSpecific memory; + IndexMask edge_mask; + IndexMask poly_mask; switch (selection_domain) { case ATTR_DOMAIN_POINT: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator(context, src_mesh.totvert); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan selection = evaluator.get_evaluated(0); + const VArraySpan span(selection); threading::parallel_invoke( - poly_selection.size() > 1024, - [&]() { edge_selection = edge_selection_from_vert(src_edges, selection, memory); }, + src_edges.size() > 1024, + [&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); }, [&]() { - poly_selection = poly_selection_from_vert( - src_polys, src_corner_verts, selection, memory); + poly_mask = poly_selection_from_vert( + src_polys, src_corner_verts, span, memory.local()); }); break; } case ATTR_DOMAIN_EDGE: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE); - fn::FieldEvaluator evaluator(context, src_mesh.totedge); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan selection = evaluator.get_evaluated(0); + const VArraySpan span(selection); threading::parallel_invoke( - poly_selection.size() > 1024, - [&]() { edge_selection = IndexMask::from_bools(selection, memory); }, + src_edges.size() > 1024, + [&]() { edge_mask = IndexMask::from_bools(span, memory.local()); }, [&]() { - poly_selection = poly_selection_from_edge( - src_polys, src_corner_edges, selection, memory); + poly_mask = poly_selection_from_edge( + src_polys, src_corner_edges, span, memory.local()); }); - if (edge_selection.is_empty()) { - return nullptr; - } - if (edge_selection.size() == src_mesh.totedge) { - return BKE_mesh_copy_for_eval(&src_mesh); - } break; } case ATTR_DOMAIN_FACE: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator(context, src_polys.size()); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan selection = evaluator.get_evaluated(0); + const VArraySpan span(selection); threading::parallel_invoke( - poly_selection.size() > 1024, + src_edges.size() > 1024, [&]() { - edge_selection = edge_selection_from_poly( - src_polys, poly_selection, src_corner_edges, src_edges.size(), memory); + edge_mask = edge_selection_from_poly( + src_polys, poly_mask, src_corner_edges, src_edges.size(), memory.local()); }, - [&]() { poly_selection = IndexMask::from_bools(selection, memory); }); - if (poly_selection.is_empty()) { - return nullptr; - } - if (poly_selection.size() == src_mesh.totpoly) { - return BKE_mesh_copy_for_eval(&src_mesh); - } + [&]() { poly_mask = IndexMask::from_bools(span, memory.local()); }); break; } default: @@ -377,42 +363,53 @@ Mesh *mesh_copy_selection_keep_verts( break; } - Mesh *dst_mesh = BKE_mesh_new_nomain( - src_mesh.totvert, edge_selection.size(), poly_selection.size(), 0); - BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); - CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); - dst_mesh->totvert = src_mesh.totvert; + if (edge_mask.is_empty()) { + return nullptr; + } + if (edge_mask.size() == src_mesh.totedge) { + return BKE_mesh_copy_for_eval(&src_mesh); + } - const bke::AttributeAccessor src_attributes = src_mesh.attributes(); + Mesh *dst_mesh = BKE_mesh_new_nomain(0, edge_mask.size(), poly_mask.size(), 0); + CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); + CustomData_free_layer_named(&dst_mesh->ldata, ".corner_vert", 0); + CustomData_free_layer_named(&dst_mesh->ldata, ".corner_edge", 0); + BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); + dst_mesh->totvert = src_mesh.totvert; bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( - src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); + src_polys, poly_mask, dst_mesh->poly_offsets_for_write()); dst_mesh->totloop = dst_polys.total_size(); - dst_attributes.add(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); + MutableSpan dst_corner_edges = dst_mesh->corner_edges_for_write(); - remap_edges(src_polys, - dst_polys, - src_edges.size(), - edge_selection, - poly_selection, - src_corner_edges, - dst_mesh->corner_edges_for_write()); - - bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); - bke::gather_attributes( - src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_selection, dst_attributes); - bke::gather_attributes( - src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes); - bke::gather_attributes_group_to_group(src_attributes, - ATTR_DOMAIN_CORNER, - propagation_info, - {".corner_edge"}, - src_polys, - dst_polys, - poly_selection, - dst_attributes); + threading::parallel_invoke( + [&]() { + remap_edges(src_polys, + dst_polys, + src_edges.size(), + edge_mask, + poly_mask, + src_corner_edges, + dst_corner_edges); + }, + [&]() { + bke::copy_attributes( + src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_mask, dst_attributes); + bke::gather_attributes( + src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes); + bke::gather_attributes_group_to_group(src_attributes, + ATTR_DOMAIN_CORNER, + propagation_info, + {".corner_edge"}, + src_polys, + dst_polys, + poly_mask, + dst_attributes); + }); /* Positions are not changed by the operation, so the bounds are the same. */ dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache; @@ -421,80 +418,70 @@ Mesh *mesh_copy_selection_keep_verts( Mesh *mesh_copy_selection_keep_edges( const Mesh &src_mesh, - const fn::Field &selection, + const fn::Field &selection_field, const eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info) { + const OffsetIndices src_polys = src_mesh.polys(); + const bke::AttributeAccessor src_attributes = src_mesh.attributes(); + + const bke::MeshFieldContext context(src_mesh, selection_domain); + fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain)); + evaluator.add(selection_field); + const VArray selection = evaluator.get_evaluated(0); + if (const std::optional single = selection.get_if_single()) { + return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr; + } + IndexMaskMemory memory; - IndexMask poly_selection; + IndexMask poly_mask; switch (selection_domain) { - case ATTR_DOMAIN_POINT: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator(context, src_mesh.totvert); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan vert_selection = evaluator.get_evaluated(0); - poly_selection = poly_selection_from_vert( - src_mesh.polys(), src_mesh.corner_verts(), vert_selection, memory); + case ATTR_DOMAIN_POINT: + poly_mask = poly_selection_from_vert( + src_polys, src_mesh.corner_verts(), VArraySpan(selection), memory); break; - } - case ATTR_DOMAIN_EDGE: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE); - fn::FieldEvaluator evaluator(context, src_mesh.totedge); - evaluator.add(selection); - evaluator.evaluate(); - const VArraySpan edge_selection = evaluator.get_evaluated(0); - poly_selection = poly_selection_from_edge( - src_mesh.polys(), src_mesh.corner_edges(), edge_selection, memory); + case ATTR_DOMAIN_EDGE: + poly_mask = poly_selection_from_edge( + src_polys, src_mesh.corner_edges(), VArraySpan(selection), memory); break; - } - case ATTR_DOMAIN_FACE: { - const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator(context, src_mesh.totpoly); - evaluator.add(selection); - evaluator.evaluate(); - poly_selection = IndexMask::from_bools(evaluator.get_evaluated(0), memory); + case ATTR_DOMAIN_FACE: + poly_mask = IndexMask::from_bools(evaluator.get_evaluated(0), memory); break; - } default: BLI_assert_unreachable(); break; } - if (poly_selection.is_empty()) { + if (poly_mask.is_empty()) { return nullptr; } - if (poly_selection.size() == src_mesh.totpoly) { + if (poly_mask.size() == src_mesh.totpoly) { return BKE_mesh_copy_for_eval(&src_mesh); } - Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_selection.size(), 0); + Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_mask.size(), 0); CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); CustomData_free_layer_named(&dst_mesh->edata, ".edge_verts", 0); dst_mesh->totvert = src_mesh.totvert; dst_mesh->totedge = src_mesh.totedge; BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); - - const OffsetIndices src_polys = src_mesh.polys(); + bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( - src_polys, poly_selection, dst_mesh->poly_offsets_for_write()); + src_polys, poly_mask, dst_mesh->poly_offsets_for_write()); dst_mesh->totloop = dst_polys.total_size(); - const bke::AttributeAccessor src_attributes = src_mesh.attributes(); - bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); - bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes); bke::gather_attributes( - src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes); + src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes); bke::gather_attributes_group_to_group(src_attributes, ATTR_DOMAIN_CORNER, propagation_info, {}, src_polys, dst_polys, - poly_selection, + poly_mask, dst_attributes); /* Positions are not changed by the operation, so the bounds are the same. */ 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 44c385c251c..4385f5701fd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -17,193 +17,12 @@ #include "BKE_mesh.hh" #include "BKE_pointcloud.h" +#include "GEO_mesh_copy_selection.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_delete_geometry_cc { -template -static void copy_data_based_on_map(const Span src, - const Span index_map, - MutableSpan dst) -{ - threading::parallel_for(index_map.index_range(), 1024, [&](const IndexRange range) { - for (const int i_src : range) { - const int i_dst = index_map[i_src]; - if (i_dst != -1) { - dst[i_dst] = src[i_src]; - } - } - }); -} - -static void copy_attributes_based_on_map(const bke::AttributeAccessor src_attributes, - bke::MutableAttributeAccessor dst_attributes, - const eAttrDomain domain, - const AnonymousAttributePropagationInfo &propagation_info, - const Set &skip, - const Span index_map) -{ - src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (meta_data.domain != domain) { - return true; - } - if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) { - return true; - } - if (skip.contains(id.name())) { - return true; - } - const GVArraySpan src = *src_attributes.lookup(id); - GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( - id, meta_data.domain, meta_data.data_type); - - bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - copy_data_based_on_map(src.typed(), index_map, dst.span.typed()); - }); - dst.finish(); - return true; - }); -} - -static void copy_face_corner_attributes(const bke::AttributeAccessor src_attributes, - bke::MutableAttributeAccessor dst_attributes, - const AnonymousAttributePropagationInfo &propagation_info, - const Set &skip, - const int selected_loops_num, - const Span selected_poly_indices, - const Mesh &mesh_in) -{ - const OffsetIndices polys = mesh_in.polys(); - Vector indices; - indices.reserve(selected_loops_num); - for (const int src_poly_index : selected_poly_indices) { - for (const int corner : polys[src_poly_index]) { - indices.append_unchecked(corner); - } - } - IndexMaskMemory memory; - bke::gather_attributes(src_attributes, - ATTR_DOMAIN_CORNER, - propagation_info, - skip, - IndexMask::from_indices(indices, memory), - dst_attributes); -} - -static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span edge_map) -{ - BLI_assert(src_mesh.totedge == edge_map.size()); - const Span src_edges = src_mesh.edges(); - MutableSpan dst_edges = dst_mesh.edges_for_write(); - - threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange range) { - for (const int i_src : range) { - const int i_dst = edge_map[i_src]; - if (ELEM(i_dst, -1, -2)) { - continue; - } - dst_edges[i_dst] = src_edges[i_src]; - } - }); -} - -static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span vertex_map, - Span edge_map) -{ - BLI_assert(src_mesh.totvert == vertex_map.size()); - BLI_assert(src_mesh.totedge == edge_map.size()); - const Span src_edges = src_mesh.edges(); - MutableSpan dst_edges = dst_mesh.edges_for_write(); - - threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange range) { - for (const int i_src : range) { - const int i_dst = edge_map[i_src]; - if (i_dst == -1) { - continue; - } - - dst_edges[i_dst][0] = vertex_map[src_edges[i_src][0]]; - dst_edges[i_dst][1] = vertex_map[src_edges[i_src][1]]; - } - }); -} - -/* Faces and edges changed but vertices are the same. */ -static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span edge_map, - Span masked_poly_indices, - Span new_loop_starts) -{ - const OffsetIndices src_polys = src_mesh.polys(); - const Span src_corner_verts = src_mesh.corner_verts(); - const Span src_corner_edges = src_mesh.corner_edges(); - MutableSpan dst_poly_offsets = dst_mesh.poly_offsets_for_write(); - MutableSpan dst_corner_verts = dst_mesh.corner_verts_for_write(); - MutableSpan dst_corner_edges = dst_mesh.corner_edges_for_write(); - - threading::parallel_for(masked_poly_indices.index_range(), 512, [&](const IndexRange range) { - for (const int i_dst : range) { - const int i_src = masked_poly_indices[i_dst]; - const IndexRange poly_src = src_polys[i_src]; - const Span src_poly_verts = src_corner_verts.slice(poly_src); - const Span src_poly_edges = src_corner_edges.slice(poly_src); - - dst_poly_offsets[i_dst] = new_loop_starts[i_dst]; - MutableSpan dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst], - poly_src.size()); - MutableSpan dst_poly_edges = dst_corner_edges.slice(dst_poly_offsets[i_dst], - poly_src.size()); - - dst_poly_verts.copy_from(src_poly_verts); - - for (const int i : IndexRange(poly_src.size())) { - dst_poly_edges[i] = edge_map[src_poly_edges[i]]; - } - } - }); -} - -static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span vertex_map, - Span edge_map, - Span masked_poly_indices, - Span new_loop_starts) -{ - const OffsetIndices src_polys = src_mesh.polys(); - const Span src_corner_verts = src_mesh.corner_verts(); - const Span src_corner_edges = src_mesh.corner_edges(); - MutableSpan dst_poly_offsets = dst_mesh.poly_offsets_for_write(); - MutableSpan dst_corner_verts = dst_mesh.corner_verts_for_write(); - MutableSpan dst_corner_edges = dst_mesh.corner_edges_for_write(); - - threading::parallel_for(masked_poly_indices.index_range(), 512, [&](const IndexRange range) { - for (const int i_dst : range) { - const int i_src = masked_poly_indices[i_dst]; - const IndexRange poly_src = src_polys[i_src]; - const Span src_poly_verts = src_corner_verts.slice(poly_src); - const Span src_poly_edges = src_corner_edges.slice(poly_src); - - dst_poly_offsets[i_dst] = new_loop_starts[i_dst]; - MutableSpan dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst], - poly_src.size()); - MutableSpan dst_poly_edges = dst_corner_edges.slice(dst_poly_offsets[i_dst], - poly_src.size()); - - for (const int i : IndexRange(poly_src.size())) { - dst_poly_verts[i] = vertex_map[src_poly_verts[i]]; - } - for (const int i : IndexRange(poly_src.size())) { - dst_poly_edges[i] = edge_map[src_poly_edges[i]]; - } - } - }); -} - /** \return std::nullopt if the geometry should remain unchanged. */ static std::optional separate_curves_selection( const Curves &src_curves_id, @@ -289,729 +108,23 @@ static void delete_selected_instances(GeometrySet &geometry_set, instances.remove(selection, propagation_info); } -static void compute_selected_verts_from_vertex_selection(const Span vertex_selection, - MutableSpan r_vertex_map, - int *r_selected_verts_num) +static Mesh *separate_mesh_selection(const Mesh &mesh, + const Field &selection, + const eAttrDomain selection_domain, + const GeometryNodeDeleteGeometryMode mode, + const AnonymousAttributePropagationInfo &propagation_info) { - BLI_assert(vertex_selection.size() == r_vertex_map.size()); - - int selected_verts_num = 0; - for (const int i : r_vertex_map.index_range()) { - if (vertex_selection[i]) { - r_vertex_map[i] = selected_verts_num; - selected_verts_num++; - } - else { - r_vertex_map[i] = -1; - } - } - - *r_selected_verts_num = selected_verts_num; -} - -static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, - const Span vertex_selection, - MutableSpan r_edge_map, - int *r_selected_edges_num) -{ - BLI_assert(mesh.totedge == r_edge_map.size()); - const Span edges = mesh.edges(); - - int selected_edges_num = 0; - for (const int i : IndexRange(mesh.totedge)) { - const int2 &edge = edges[i]; - - /* Only add the edge if both vertices will be in the new mesh. */ - if (vertex_selection[edge[0]] && vertex_selection[edge[1]]) { - r_edge_map[i] = selected_edges_num; - selected_edges_num++; - } - else { - r_edge_map[i] = -1; - } - } - - *r_selected_edges_num = selected_edges_num; -} - -static void compute_selected_polys_from_vertex_selection(const Mesh &mesh, - const Span vertex_selection, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - BLI_assert(mesh.totvert == vertex_selection.size()); - const OffsetIndices polys = mesh.polys(); - const Span corner_verts = mesh.corner_verts(); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - int selected_loops_num = 0; - for (const int i : polys.index_range()) { - const IndexRange poly_src = polys[i]; - - bool all_verts_in_selection = true; - for (const int vert : corner_verts.slice(poly_src)) { - if (!vertex_selection[vert]) { - all_verts_in_selection = false; - break; - } - } - - if (all_verts_in_selection) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(selected_loops_num); - selected_loops_num += poly_src.size(); - } - } - - *r_selected_polys_num = r_selected_poly_indices.size(); - *r_selected_loops_num = selected_loops_num; -} - -/** - * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the - * edge are kept along with the edge. - */ -static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh, - const Span edge_selection, - MutableSpan r_vertex_map, - MutableSpan r_edge_map, - int *r_selected_verts_num, - int *r_selected_edges_num) -{ - BLI_assert(mesh.totedge == edge_selection.size()); - const Span edges = mesh.edges(); - - int selected_edges_num = 0; - int selected_verts_num = 0; - for (const int i : IndexRange(mesh.totedge)) { - const int2 &edge = edges[i]; - if (edge_selection[i]) { - r_edge_map[i] = selected_edges_num; - selected_edges_num++; - if (r_vertex_map[edge[0]] == -1) { - r_vertex_map[edge[0]] = selected_verts_num; - selected_verts_num++; - } - if (r_vertex_map[edge[1]] == -1) { - r_vertex_map[edge[1]] = selected_verts_num; - selected_verts_num++; - } - } - else { - r_edge_map[i] = -1; - } - } - - *r_selected_verts_num = selected_verts_num; - *r_selected_edges_num = selected_edges_num; -} - -/** - * Checks for every edge if it is in `edge_selection`. - */ -static void compute_selected_edges_from_edge_selection(const Mesh &mesh, - const Span edge_selection, - MutableSpan r_edge_map, - int *r_selected_edges_num) -{ - BLI_assert(mesh.totedge == edge_selection.size()); - - int selected_edges_num = 0; - for (const int i : IndexRange(mesh.totedge)) { - if (edge_selection[i]) { - r_edge_map[i] = selected_edges_num; - selected_edges_num++; - } - else { - r_edge_map[i] = -1; - } - } - - *r_selected_edges_num = selected_edges_num; -} - -/** - * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that - * polygon is kept. - */ -static void compute_selected_polys_from_edge_selection(const Mesh &mesh, - const Span edge_selection, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - const OffsetIndices polys = mesh.polys(); - const Span corner_edges = mesh.corner_edges(); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - int selected_loops_num = 0; - for (const int i : polys.index_range()) { - const IndexRange poly_src = polys[i]; - - bool all_edges_in_selection = true; - for (const int edge : corner_edges.slice(poly_src)) { - if (!edge_selection[edge]) { - all_edges_in_selection = false; - break; - } - } - - if (all_edges_in_selection) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(selected_loops_num); - selected_loops_num += poly_src.size(); - } - } - - *r_selected_polys_num = r_selected_poly_indices.size(); - *r_selected_loops_num = selected_loops_num; -} - -/** - * Checks for every edge and polygon if all its vertices are in `vertex_selection`. - */ -static void compute_selected_mesh_data_from_vertex_selection_edge_face( - const Mesh &mesh, - const Span vertex_selection, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_edges_num, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - threading::parallel_invoke( - mesh.totedge > 1000, - [&]() { - compute_selected_edges_from_vertex_selection( - mesh, vertex_selection, r_edge_map, r_selected_edges_num); - }, - [&]() { - compute_selected_polys_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); - }); -} - -/** - * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all - * vertices of that polygon or edge are in the selection. - */ -static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, - const Span vertex_selection, - MutableSpan r_vertex_map, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_verts_num, - int *r_selected_edges_num, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - threading::parallel_invoke( - mesh.totedge > 1000, - [&]() { - compute_selected_verts_from_vertex_selection( - vertex_selection, r_vertex_map, r_selected_verts_num); - }, - [&]() { - compute_selected_edges_from_vertex_selection( - mesh, vertex_selection, r_edge_map, r_selected_edges_num); - }, - [&]() { - compute_selected_polys_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); - }); -} - -/** - * Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in - * the selection. - */ -static void compute_selected_mesh_data_from_edge_selection_edge_face( - const Mesh &mesh, - const Span edge_selection, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_edges_num, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - threading::parallel_invoke( - mesh.totedge > 1000, - [&]() { - compute_selected_edges_from_edge_selection( - mesh, edge_selection, r_edge_map, r_selected_edges_num); - }, - [&]() { - compute_selected_polys_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); - }); -} - -/** - * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to - * that edge are kept as well. The polys are kept if all edges are in the selection. - */ -static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, - const Span edge_selection, - MutableSpan r_vertex_map, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_verts_num, - int *r_selected_edges_num, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - threading::parallel_invoke( - mesh.totedge > 1000, - [&]() { - r_vertex_map.fill(-1); - compute_selected_verts_and_edges_from_edge_selection(mesh, - edge_selection, - r_vertex_map, - r_edge_map, - r_selected_verts_num, - r_selected_edges_num); - }, - [&]() { - compute_selected_polys_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); - }); -} - -/** - * Checks for every polygon if it is in `poly_selection`. - */ -static void compute_selected_polys_from_poly_selection(const Mesh &mesh, - const Span poly_selection, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - BLI_assert(mesh.totpoly == poly_selection.size()); - const OffsetIndices polys = mesh.polys(); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - int selected_loops_num = 0; - for (const int i : polys.index_range()) { - const IndexRange poly_src = polys[i]; - /* We keep this one. */ - if (poly_selection[i]) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(selected_loops_num); - selected_loops_num += poly_src.size(); - } - } - *r_selected_polys_num = r_selected_poly_indices.size(); - *r_selected_loops_num = selected_loops_num; -} -/** - * Checks for every polygon if it is in `poly_selection`. If it is, the edges - * belonging to that polygon are kept as well. - */ -static void compute_selected_mesh_data_from_poly_selection_edge_face( - const Mesh &mesh, - const Span poly_selection, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_edges_num, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - BLI_assert(mesh.totpoly == poly_selection.size()); - BLI_assert(mesh.totedge == r_edge_map.size()); - const OffsetIndices polys = mesh.polys(); - const Span corner_edges = mesh.corner_edges(); - - r_edge_map.fill(-1); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - int selected_loops_num = 0; - int selected_edges_num = 0; - for (const int i : polys.index_range()) { - const IndexRange poly_src = polys[i]; - /* We keep this one. */ - if (poly_selection[i]) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(selected_loops_num); - selected_loops_num += poly_src.size(); - - /* Add the vertices and the edges. */ - for (const int edge : corner_edges.slice(poly_src)) { - /* Check first if it has not yet been added. */ - if (r_edge_map[edge] == -1) { - r_edge_map[edge] = selected_edges_num; - selected_edges_num++; - } - } - } - } - *r_selected_edges_num = selected_edges_num; - *r_selected_polys_num = r_selected_poly_indices.size(); - *r_selected_loops_num = selected_loops_num; -} - -/** - * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices - * belonging to that polygon are kept as well. - */ -static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, - const Span poly_selection, - MutableSpan r_vertex_map, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - int *r_selected_verts_num, - int *r_selected_edges_num, - int *r_selected_polys_num, - int *r_selected_loops_num) -{ - BLI_assert(mesh.totpoly == poly_selection.size()); - BLI_assert(mesh.totedge == r_edge_map.size()); - const OffsetIndices polys = mesh.polys(); - const Span corner_verts = mesh.corner_verts(); - const Span corner_edges = mesh.corner_edges(); - - r_vertex_map.fill(-1); - r_edge_map.fill(-1); - - r_selected_poly_indices.reserve(mesh.totpoly); - r_loop_starts.reserve(mesh.totloop); - - int selected_loops_num = 0; - int selected_verts_num = 0; - int selected_edges_num = 0; - for (const int i : polys.index_range()) { - const IndexRange poly_src = polys[i]; - /* We keep this one. */ - if (poly_selection[i]) { - r_selected_poly_indices.append_unchecked(i); - r_loop_starts.append_unchecked(selected_loops_num); - selected_loops_num += poly_src.size(); - - /* Add the vertices and the edges. */ - for (const int corner : poly_src) { - const int vert = corner_verts[corner]; - const int edge = corner_edges[corner]; - /* Check first if it has not yet been added. */ - if (r_vertex_map[vert] == -1) { - r_vertex_map[vert] = selected_verts_num; - selected_verts_num++; - } - if (r_edge_map[edge] == -1) { - r_edge_map[edge] = selected_edges_num; - selected_edges_num++; - } - } - } - } - *r_selected_verts_num = selected_verts_num; - *r_selected_edges_num = selected_edges_num; - *r_selected_polys_num = r_selected_poly_indices.size(); - *r_selected_loops_num = selected_loops_num; -} - -/** - * Keep the parts of the mesh that are in the selection. - */ -static void do_mesh_separation(GeometrySet &geometry_set, - const Mesh &mesh_in, - const Span selection, - const eAttrDomain domain, - const GeometryNodeDeleteGeometryMode mode, - const AnonymousAttributePropagationInfo &propagation_info) -{ - /* Needed in all cases. */ - Vector selected_poly_indices; - Vector new_loop_starts; - int selected_polys_num = 0; - int selected_loops_num = 0; - - IndexMaskMemory memory; - - Mesh *mesh_out; - switch (mode) { - case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { - Array vertex_map(mesh_in.totvert); - int selected_verts_num = 0; - - Array edge_map(mesh_in.totedge); - int selected_edges_num = 0; - - switch (domain) { - case ATTR_DOMAIN_POINT: - compute_selected_mesh_data_from_vertex_selection(mesh_in, - selection, - vertex_map, - edge_map, - selected_poly_indices, - new_loop_starts, - &selected_verts_num, - &selected_edges_num, - &selected_polys_num, - &selected_loops_num); - break; - case ATTR_DOMAIN_EDGE: - compute_selected_mesh_data_from_edge_selection(mesh_in, - selection, - vertex_map, - edge_map, - selected_poly_indices, - new_loop_starts, - &selected_verts_num, - &selected_edges_num, - &selected_polys_num, - &selected_loops_num); - break; - case ATTR_DOMAIN_FACE: - compute_selected_mesh_data_from_poly_selection(mesh_in, - selection, - vertex_map, - edge_map, - selected_poly_indices, - new_loop_starts, - &selected_verts_num, - &selected_edges_num, - &selected_polys_num, - &selected_loops_num); - break; - default: - BLI_assert_unreachable(); - break; - } - mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, - selected_verts_num, - selected_edges_num, - selected_polys_num, - selected_loops_num); - - copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); - copy_masked_polys_to_new_mesh( - mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); - - copy_attributes_based_on_map(mesh_in.attributes(), - mesh_out->attributes_for_write(), - ATTR_DOMAIN_POINT, - propagation_info, - {}, - vertex_map); - copy_attributes_based_on_map(mesh_in.attributes(), - mesh_out->attributes_for_write(), - ATTR_DOMAIN_EDGE, - propagation_info, - {".edge_verts"}, - edge_map); - bke::gather_attributes(mesh_in.attributes(), - ATTR_DOMAIN_FACE, - propagation_info, - {}, - IndexMask::from_indices(selected_poly_indices.as_span(), memory), - mesh_out->attributes_for_write()); - copy_face_corner_attributes(mesh_in.attributes(), - mesh_out->attributes_for_write(), - propagation_info, - {".corner_vert", ".corner_edge"}, - selected_loops_num, - selected_poly_indices, - mesh_in); - break; - } - case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { - Array edge_map(mesh_in.totedge); - int selected_edges_num = 0; - - switch (domain) { - case ATTR_DOMAIN_POINT: - compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in, - selection, - edge_map, - selected_poly_indices, - new_loop_starts, - &selected_edges_num, - &selected_polys_num, - &selected_loops_num); - break; - case ATTR_DOMAIN_EDGE: - compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in, - selection, - edge_map, - selected_poly_indices, - new_loop_starts, - &selected_edges_num, - &selected_polys_num, - &selected_loops_num); - break; - case ATTR_DOMAIN_FACE: - compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in, - selection, - edge_map, - selected_poly_indices, - new_loop_starts, - &selected_edges_num, - &selected_polys_num, - &selected_loops_num); - break; - default: - BLI_assert_unreachable(); - break; - } - mesh_out = BKE_mesh_new_nomain_from_template( - &mesh_in, mesh_in.totvert, selected_edges_num, selected_polys_num, selected_loops_num); - - copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); - copy_masked_polys_to_new_mesh( - mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); - - bke::copy_attributes(mesh_in.attributes(), - ATTR_DOMAIN_POINT, - propagation_info, - {}, - mesh_out->attributes_for_write()); - copy_attributes_based_on_map(mesh_in.attributes(), - mesh_out->attributes_for_write(), - ATTR_DOMAIN_EDGE, - propagation_info, - {".edge_verts"}, - edge_map); - bke::gather_attributes(mesh_in.attributes(), - ATTR_DOMAIN_FACE, - propagation_info, - {}, - IndexMask::from_indices(selected_poly_indices.as_span(), memory), - mesh_out->attributes_for_write()); - copy_face_corner_attributes(mesh_in.attributes(), - mesh_out->attributes_for_write(), - propagation_info, - {".corner_vert", ".corner_edge"}, - selected_loops_num, - selected_poly_indices, - mesh_in); - - /* Positions are not changed by the operation, so the bounds are the same. */ - mesh_out->runtime->bounds_cache = mesh_in.runtime->bounds_cache; - break; - } - case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: { - switch (domain) { - case ATTR_DOMAIN_POINT: - compute_selected_polys_from_vertex_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); - break; - case ATTR_DOMAIN_EDGE: - compute_selected_polys_from_edge_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); - break; - case ATTR_DOMAIN_FACE: - compute_selected_polys_from_poly_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); - break; - default: - BLI_assert_unreachable(); - break; - } - mesh_out = BKE_mesh_new_nomain_from_template( - &mesh_in, mesh_in.totvert, mesh_in.totedge, selected_polys_num, selected_loops_num); - - mesh_out->poly_offsets_for_write().drop_back(1).copy_from(new_loop_starts); - - bke::copy_attributes(mesh_in.attributes(), - ATTR_DOMAIN_POINT, - propagation_info, - {}, - mesh_out->attributes_for_write()); - bke::copy_attributes(mesh_in.attributes(), - ATTR_DOMAIN_EDGE, - propagation_info, - {}, - mesh_out->attributes_for_write()); - bke::gather_attributes(mesh_in.attributes(), - ATTR_DOMAIN_FACE, - propagation_info, - {}, - IndexMask::from_indices(selected_poly_indices.as_span(), memory), - mesh_out->attributes_for_write()); - copy_face_corner_attributes(mesh_in.attributes(), - mesh_out->attributes_for_write(), - propagation_info, - {}, - selected_loops_num, - selected_poly_indices, - mesh_in); - - /* Positions are not changed by the operation, so the bounds are the same. */ - mesh_out->runtime->bounds_cache = mesh_in.runtime->bounds_cache; - break; - } + case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: + return geometry::mesh_copy_selection(mesh, selection, selection_domain, propagation_info); + case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: + return geometry::mesh_copy_selection_keep_verts( + mesh, selection, selection_domain, propagation_info); + case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: + return geometry::mesh_copy_selection_keep_edges( + mesh, selection, selection_domain, propagation_info); } - - geometry_set.replace_mesh(mesh_out); -} - -static void separate_mesh_selection(GeometrySet &geometry_set, - const Field &selection_field, - const eAttrDomain selection_domain, - const GeometryNodeDeleteGeometryMode mode, - const AnonymousAttributePropagationInfo &propagation_info) -{ - const Mesh &src_mesh = *geometry_set.get_mesh_for_read(); - const bke::MeshFieldContext field_context{src_mesh, selection_domain}; - fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)}; - evaluator.add(selection_field); - evaluator.evaluate(); - const VArray selection = evaluator.get_evaluated(0); - /* Check if there is anything to delete. */ - if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) { - return; - } - - const VArraySpan selection_span{selection}; - - do_mesh_separation( - geometry_set, src_mesh, selection_span, selection_domain, mode, propagation_info); + return nullptr; } } // namespace blender::nodes::node_geo_delete_geometry_cc @@ -1038,9 +151,9 @@ void separate_geometry(GeometrySet &geometry_set, some_valid_domain = true; } } - if (geometry_set.has_mesh()) { + if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) { - file_ns::separate_mesh_selection(geometry_set, selection, domain, mode, propagation_info); + file_ns::separate_mesh_selection(*mesh, selection, domain, mode, propagation_info); some_valid_domain = true; } } -- 2.30.2 From eaecd3303b716997af12d8cddb390e59e1c87d34 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 30 May 2023 22:42:56 -0400 Subject: [PATCH 05/20] Return optional for case where nothing is removed --- .../geometry/GEO_mesh_copy_selection.hh | 8 +++--- .../geometry/intern/mesh_copy_selection.cc | 26 ++++++++++--------- .../nodes/node_geo_delete_geometry.cc | 17 +++++++----- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/source/blender/geometry/GEO_mesh_copy_selection.hh b/source/blender/geometry/GEO_mesh_copy_selection.hh index 14a4d17c2b8..3f19be25920 100644 --- a/source/blender/geometry/GEO_mesh_copy_selection.hh +++ b/source/blender/geometry/GEO_mesh_copy_selection.hh @@ -2,6 +2,8 @@ #pragma once +#include + #include "BKE_attribute.h" struct Mesh; @@ -17,18 +19,18 @@ class AnonymousAttributePropagationInfo; namespace blender::geometry { -Mesh *mesh_copy_selection(const Mesh &src_mesh, +std::optionalmesh_copy_selection(const Mesh &src_mesh, const fn::Field &selection, eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info); -Mesh *mesh_copy_selection_keep_verts( +std::optionalmesh_copy_selection_keep_verts( const Mesh &src_mesh, const fn::Field &selection, eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info); -Mesh *mesh_copy_selection_keep_edges( +std::optionalmesh_copy_selection_keep_edges( const Mesh &mesh, const fn::Field &selection, const eAttrDomain selection_domain, diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index 2450ce98ca0..562ca503a5c 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -159,10 +159,11 @@ static IndexMask poly_selection_from_edge(const OffsetIndices polys, return poly_selection_from_mapped_corner(polys, corner_edges, edge_mask, memory); } -Mesh *mesh_copy_selection(const Mesh &src_mesh, - const fn::Field &selection_field, - const eAttrDomain selection_domain, - const bke::AnonymousAttributePropagationInfo &propagation_info) +std::optional mesh_copy_selection( + const Mesh &src_mesh, + const fn::Field &selection_field, + const eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info) { const Span src_edges = src_mesh.edges(); const OffsetIndices src_polys = src_mesh.polys(); @@ -176,7 +177,7 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); if (const std::optional single = selection.get_if_single()) { - return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr; + return *single ? std::nullopt : std::make_optional(nullptr); } threading::EnumerableThreadSpecific memory; @@ -235,7 +236,7 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, return nullptr; } if (vert_mask.size() == src_mesh.totvert) { - return BKE_mesh_copy_for_eval(&src_mesh); + return std::nullopt; } Mesh *dst_mesh = BKE_mesh_new_nomain(vert_mask.size(), edge_mask.size(), poly_mask.size(), 0); @@ -300,7 +301,7 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh, return dst_mesh; } -Mesh *mesh_copy_selection_keep_verts( +std::optional mesh_copy_selection_keep_verts( const Mesh &src_mesh, const fn::Field &selection_field, const eAttrDomain selection_domain, @@ -318,7 +319,7 @@ Mesh *mesh_copy_selection_keep_verts( evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); if (const std::optional single = selection.get_if_single()) { - return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr; + return *single ? std::nullopt : std::make_optional(nullptr); } threading::EnumerableThreadSpecific memory; @@ -367,7 +368,7 @@ Mesh *mesh_copy_selection_keep_verts( return nullptr; } if (edge_mask.size() == src_mesh.totedge) { - return BKE_mesh_copy_for_eval(&src_mesh); + return std::nullopt; } Mesh *dst_mesh = BKE_mesh_new_nomain(0, edge_mask.size(), poly_mask.size(), 0); @@ -416,7 +417,7 @@ Mesh *mesh_copy_selection_keep_verts( return dst_mesh; } -Mesh *mesh_copy_selection_keep_edges( +std::optional mesh_copy_selection_keep_edges( const Mesh &src_mesh, const fn::Field &selection_field, const eAttrDomain selection_domain, @@ -428,9 +429,10 @@ Mesh *mesh_copy_selection_keep_edges( const bke::MeshFieldContext context(src_mesh, selection_domain); fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain)); evaluator.add(selection_field); + evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); if (const std::optional single = selection.get_if_single()) { - return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr; + return *single ? std::nullopt : std::make_optional(nullptr); } IndexMaskMemory memory; @@ -456,7 +458,7 @@ Mesh *mesh_copy_selection_keep_edges( return nullptr; } if (poly_mask.size() == src_mesh.totpoly) { - return BKE_mesh_copy_for_eval(&src_mesh); + return std::nullopt; } Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_mask.size(), 0); 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 4385f5701fd..537f272e1fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -108,11 +108,12 @@ static void delete_selected_instances(GeometrySet &geometry_set, instances.remove(selection, propagation_info); } -static Mesh *separate_mesh_selection(const Mesh &mesh, - const Field &selection, - const eAttrDomain selection_domain, - const GeometryNodeDeleteGeometryMode mode, - const AnonymousAttributePropagationInfo &propagation_info) +static std::optional separate_mesh_selection( + const Mesh &mesh, + const Field &selection, + const eAttrDomain selection_domain, + const GeometryNodeDeleteGeometryMode mode, + const AnonymousAttributePropagationInfo &propagation_info) { switch (mode) { case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: @@ -153,7 +154,11 @@ void separate_geometry(GeometrySet &geometry_set, } if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) { - file_ns::separate_mesh_selection(*mesh, selection, domain, mode, propagation_info); + std::optional dst_mesh = file_ns::separate_mesh_selection( + *mesh, selection, domain, mode, propagation_info); + if (dst_mesh) { + geometry_set.replace_mesh(*dst_mesh); + } some_valid_domain = true; } } -- 2.30.2 From a80172cee4d193e8d052c5809b5f4b2086207124 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 11:50:14 -0400 Subject: [PATCH 06/20] Rename variable --- source/blender/geometry/intern/mesh_copy_selection.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index 562ca503a5c..bff6cc4af18 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -118,13 +118,13 @@ static IndexMask edge_selection_from_poly(const OffsetIndices polys, /** An edge is selected if both of its vertices are selected. */ static IndexMask edge_selection_from_vert(const Span edges, - const Span vert_mask, + const Span vert_selection, IndexMaskMemory &memory) { return IndexMask::from_predicate( edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) { const int2 edge = edges[i]; - return vert_mask[edge[0]] && vert_mask[edge[1]]; + return vert_selection[edge[0]] && vert_selection[edge[1]]; }); } -- 2.30.2 From 7ddda7eb07f80dfd55c54cdbdd49f17df70cbd4b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 12:11:35 -0400 Subject: [PATCH 07/20] Rename variable --- source/blender/geometry/intern/mesh_copy_selection.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index bff6cc4af18..48a7ec049b0 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -144,10 +144,10 @@ static IndexMask poly_selection_from_mapped_corner(const OffsetIndices poly /** A face is selected if all of its vertices are selected. */ static IndexMask poly_selection_from_vert(const OffsetIndices polys, const Span corner_verts, - const Span vert_mask, + const Span vert_selection, IndexMaskMemory &memory) { - return poly_selection_from_mapped_corner(polys, corner_verts, vert_mask, memory); + return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory); } /** A face is selected if all of its edges are selected. */ -- 2.30.2 From 8c78b933d30344c7ff0aede9bfe23e7142c29b5d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 12:11:47 -0400 Subject: [PATCH 08/20] Fix uninitialized array --- source/blender/geometry/intern/mesh_copy_selection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index 48a7ec049b0..7f6b1bf3afc 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -88,7 +88,7 @@ static IndexMask mapped_corner_selection_from_poly(const OffsetIndices poly const int verts_or_edges_num, IndexMaskMemory &memory) { - Array array(verts_or_edges_num); + Array array(verts_or_edges_num, false); poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) { const Span poly_edges = corner_verts_or_edges.slice(polys[i]); array.as_mutable_span().fill_indices(poly_edges, true); -- 2.30.2 From 772b849b7e88b996ef0575a9d88a8f29d9eb386b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 12:15:49 -0400 Subject: [PATCH 09/20] Rename more variables --- .../blender/geometry/intern/mesh_copy_selection.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index 7f6b1bf3afc..495bd0950ed 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -74,12 +74,12 @@ static IndexMask vert_selection_from_edge(const Span edges, const int verts_num, IndexMaskMemory &memory) { - Array span(verts_num, false); + Array array(verts_num, false); edge_mask.foreach_index_optimized([&](const int i) { - span[edges[i][0]] = true; - span[edges[i][1]] = true; + array[edges[i][0]] = true; + array[edges[i][1]] = true; }); - return IndexMask::from_bools(span, memory); + return IndexMask::from_bools(array, memory); } static IndexMask mapped_corner_selection_from_poly(const OffsetIndices polys, @@ -90,8 +90,8 @@ static IndexMask mapped_corner_selection_from_poly(const OffsetIndices poly { Array array(verts_or_edges_num, false); poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) { - const Span poly_edges = corner_verts_or_edges.slice(polys[i]); - array.as_mutable_span().fill_indices(poly_edges, true); + const Span poly = corner_verts_or_edges.slice(polys[i]); + array.as_mutable_span().fill_indices(poly, true); }); return IndexMask::from_bools(array, memory); } -- 2.30.2 From c42af2d982915ef3f5c9c730e682442303ac4f18 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 13:38:24 -0400 Subject: [PATCH 10/20] Fix OffsetIndices::total_size() --- source/blender/blenlib/BLI_offset_indices.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_offset_indices.hh b/source/blender/blenlib/BLI_offset_indices.hh index 9cc64c178a0..0979e0758c4 100644 --- a/source/blender/blenlib/BLI_offset_indices.hh +++ b/source/blender/blenlib/BLI_offset_indices.hh @@ -38,7 +38,7 @@ template class OffsetIndices { /** Return the total number of elements in the referenced arrays. */ T total_size() const { - return offsets_.size() == 1 ? 0 : offsets_.last(); + return offsets_.is_empty() ? 0 : offsets_.last(); } /** -- 2.30.2 From 7f724964f258ea39129e01159b22896892bfac4e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 13:38:30 -0400 Subject: [PATCH 11/20] Fix gather_selected_offsets --- source/blender/blenlib/intern/offset_indices.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenlib/intern/offset_indices.cc b/source/blender/blenlib/intern/offset_indices.cc index 5dec4b1c74c..2b1d946bd69 100644 --- a/source/blender/blenlib/intern/offset_indices.cc +++ b/source/blender/blenlib/intern/offset_indices.cc @@ -42,6 +42,9 @@ OffsetIndices gather_selected_offsets(const OffsetIndices src_offsets, const IndexMask &selection, MutableSpan dst_offsets) { + if (selection.is_empty()) { + return {}; + } BLI_assert(selection.size() == (dst_offsets.size() - 1)); offset_indices::gather_group_sizes(src_offsets, selection, dst_offsets); offset_indices::accumulate_counts_to_offsets(dst_offsets); -- 2.30.2 From e3a4e64144af560b9323635324cd87320d4d75ec Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 13:39:37 -0400 Subject: [PATCH 12/20] Cleanups and fixes in new code --- .../geometry/intern/mesh_copy_selection.cc | 89 +++++++++---------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index 495bd0950ed..a189e4f4ee2 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -13,6 +13,9 @@ namespace blender::geometry { static void create_reverse_map(const IndexMask &mask, MutableSpan r_map) { +#ifdef DEBUG + r_map.fill(-1); +#endif mask.foreach_index_optimized( GrainSize(4049), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; }); } @@ -90,8 +93,7 @@ static IndexMask mapped_corner_selection_from_poly(const OffsetIndices poly { Array array(verts_or_edges_num, false); poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) { - const Span poly = corner_verts_or_edges.slice(polys[i]); - array.as_mutable_span().fill_indices(poly, true); + array.as_mutable_span().fill_indices(corner_verts_or_edges.slice(polys[i]), true); }); return IndexMask::from_bools(array, memory); } @@ -135,9 +137,10 @@ static IndexMask poly_selection_from_mapped_corner(const OffsetIndices poly { return IndexMask::from_predicate( polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) { - const Span poly = corner_verts_or_edges.slice(polys[i]); - return std::all_of( - poly.begin(), poly.end(), [&](const int i) { return vert_or_edge_selection[i]; }); + const Span indices = corner_verts_or_edges.slice(polys[i]); + return std::all_of(indices.begin(), indices.end(), [&](const int i) { + return vert_or_edge_selection[i]; + }); }); } @@ -159,6 +162,25 @@ static IndexMask poly_selection_from_edge(const OffsetIndices polys, return poly_selection_from_mapped_corner(polys, corner_edges, edge_mask, memory); } +/** Create a mesh with no built-in attributes. */ +static Mesh *create_mesh_no_attributes(const Mesh ¶ms_mesh, + const int verts_num, + const int edges_num, + const int polys_num, + const int corners_num) +{ + Mesh *mesh = BKE_mesh_new_nomain(0, 0, polys_num, 0); + mesh->totvert = verts_num; + mesh->totedge = edges_num; + mesh->totloop = corners_num; + CustomData_free_layer_named(&mesh->vdata, "position", 0); + CustomData_free_layer_named(&mesh->edata, ".edge_verts", 0); + CustomData_free_layer_named(&mesh->ldata, ".corner_vert", 0); + CustomData_free_layer_named(&mesh->ldata, ".corner_edge", 0); + BKE_mesh_copy_parameters_for_eval(mesh, ¶ms_mesh); + return mesh; +} + std::optional mesh_copy_selection( const Mesh &src_mesh, const fn::Field &selection_field, @@ -239,12 +261,11 @@ std::optional mesh_copy_selection( return std::nullopt; } - Mesh *dst_mesh = BKE_mesh_new_nomain(vert_mask.size(), edge_mask.size(), poly_mask.size(), 0); - CustomData_free_layer_named(&dst_mesh->ldata, ".corner_vert", 0); - CustomData_free_layer_named(&dst_mesh->ldata, ".corner_edge", 0); - BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); - MutableSpan dst_edges = dst_mesh->edges_for_write(); + Mesh *dst_mesh = create_mesh_no_attributes( + src_mesh, vert_mask.size(), edge_mask.size(), poly_mask.size(), 0); bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); + dst_attributes.add(".edge_verts", ATTR_DOMAIN_EDGE, bke::AttributeInitConstruct()); + MutableSpan dst_edges = dst_mesh->edges_for_write(); const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( src_polys, poly_mask, dst_mesh->poly_offsets_for_write()); @@ -318,9 +339,6 @@ std::optional mesh_copy_selection_keep_verts( evaluator.add(selection_field); evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); - if (const std::optional single = selection.get_if_single()) { - return *single ? std::nullopt : std::make_optional(nullptr); - } threading::EnumerableThreadSpecific memory; IndexMask edge_mask; @@ -350,13 +368,9 @@ std::optional mesh_copy_selection_keep_verts( } case ATTR_DOMAIN_FACE: { const VArraySpan span(selection); - threading::parallel_invoke( - src_edges.size() > 1024, - [&]() { - edge_mask = edge_selection_from_poly( - src_polys, poly_mask, src_corner_edges, src_edges.size(), memory.local()); - }, - [&]() { poly_mask = IndexMask::from_bools(span, memory.local()); }); + poly_mask = IndexMask::from_bools(span, memory.local()); + edge_mask = edge_selection_from_poly( + src_polys, poly_mask, src_corner_edges, src_edges.size(), memory.local()); break; } default: @@ -364,19 +378,8 @@ std::optional mesh_copy_selection_keep_verts( break; } - if (edge_mask.is_empty()) { - return nullptr; - } - if (edge_mask.size() == src_mesh.totedge) { - return std::nullopt; - } - - Mesh *dst_mesh = BKE_mesh_new_nomain(0, edge_mask.size(), poly_mask.size(), 0); - CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); - CustomData_free_layer_named(&dst_mesh->ldata, ".corner_vert", 0); - CustomData_free_layer_named(&dst_mesh->ldata, ".corner_edge", 0); - BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); - dst_mesh->totvert = src_mesh.totvert; + Mesh *dst_mesh = create_mesh_no_attributes( + src_mesh, src_mesh.totvert, edge_mask.size(), poly_mask.size(), 0); bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( @@ -431,9 +434,6 @@ std::optional mesh_copy_selection_keep_edges( evaluator.add(selection_field); evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); - if (const std::optional single = selection.get_if_single()) { - return *single ? std::nullopt : std::make_optional(nullptr); - } IndexMaskMemory memory; IndexMask poly_mask; @@ -454,24 +454,15 @@ std::optional mesh_copy_selection_keep_edges( break; } - if (poly_mask.is_empty()) { - return nullptr; - } - if (poly_mask.size() == src_mesh.totpoly) { - return std::nullopt; - } - - Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_mask.size(), 0); - CustomData_free_layer_named(&dst_mesh->vdata, "position", 0); - CustomData_free_layer_named(&dst_mesh->edata, ".edge_verts", 0); - dst_mesh->totvert = src_mesh.totvert; - dst_mesh->totedge = src_mesh.totedge; - BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh); + Mesh *dst_mesh = create_mesh_no_attributes( + src_mesh, src_mesh.totvert, src_mesh.totedge, poly_mask.size(), 0); bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write(); const OffsetIndices dst_polys = offset_indices::gather_selected_offsets( src_polys, poly_mask, dst_mesh->poly_offsets_for_write()); dst_mesh->totloop = dst_polys.total_size(); + dst_attributes.add(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); + dst_attributes.add(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct()); bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes); bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes); -- 2.30.2 From 4aae63f8809e87de90fff71deb64c151b658ce45 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 14:25:49 -0400 Subject: [PATCH 13/20] Fix --- source/blender/blenkernel/intern/curves_geometry.cc | 2 +- source/blender/blenlib/BLI_offset_indices.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index f19e9854575..7ede22fd6cc 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1191,7 +1191,7 @@ CurvesGeometry curves_copy_curve_selection( MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); gather_attributes_group_to_group(src_attributes, - ATTR_DOMAIN_CURVE, + ATTR_DOMAIN_POINT, propagation_info, {}, points_by_curve, diff --git a/source/blender/blenlib/BLI_offset_indices.hh b/source/blender/blenlib/BLI_offset_indices.hh index 0979e0758c4..f7aa18224ca 100644 --- a/source/blender/blenlib/BLI_offset_indices.hh +++ b/source/blender/blenlib/BLI_offset_indices.hh @@ -38,7 +38,7 @@ template class OffsetIndices { /** Return the total number of elements in the referenced arrays. */ T total_size() const { - return offsets_.is_empty() ? 0 : offsets_.last(); + return offsets_.size() > 1 ? offsets_.last() : 0; } /** -- 2.30.2 From 06de933bbe522d099641c4d4ce7fd3ae243616c6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 31 May 2023 14:28:16 -0400 Subject: [PATCH 14/20] Format --- .../blender/geometry/GEO_mesh_copy_selection.hh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/geometry/GEO_mesh_copy_selection.hh b/source/blender/geometry/GEO_mesh_copy_selection.hh index 3f19be25920..7ccbbb17ccb 100644 --- a/source/blender/geometry/GEO_mesh_copy_selection.hh +++ b/source/blender/geometry/GEO_mesh_copy_selection.hh @@ -19,18 +19,19 @@ class AnonymousAttributePropagationInfo; namespace blender::geometry { -std::optionalmesh_copy_selection(const Mesh &src_mesh, - const fn::Field &selection, - eAttrDomain selection_domain, - const bke::AnonymousAttributePropagationInfo &propagation_info); - -std::optionalmesh_copy_selection_keep_verts( +std::optional mesh_copy_selection( const Mesh &src_mesh, const fn::Field &selection, eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info); -std::optionalmesh_copy_selection_keep_edges( +std::optional mesh_copy_selection_keep_verts( + const Mesh &src_mesh, + const fn::Field &selection, + eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +std::optional mesh_copy_selection_keep_edges( const Mesh &mesh, const fn::Field &selection, const eAttrDomain selection_domain, -- 2.30.2 From c953dcb5ba4bdd884bda6d9694a2f05bb4560b47 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 07:45:07 -0400 Subject: [PATCH 15/20] Skip when selection domain is empty --- source/blender/geometry/intern/mesh_copy_selection.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index a189e4f4ee2..d9b9beae109 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -198,6 +198,9 @@ std::optional mesh_copy_selection( evaluator.add(selection_field); evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); + if (selection.is_empty()) { + return std::nullopt; + } if (const std::optional single = selection.get_if_single()) { return *single ? std::nullopt : std::make_optional(nullptr); } @@ -339,6 +342,9 @@ std::optional mesh_copy_selection_keep_verts( evaluator.add(selection_field); evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); + if (selection.is_empty()) { + return std::nullopt; + } threading::EnumerableThreadSpecific memory; IndexMask edge_mask; @@ -434,6 +440,9 @@ std::optional mesh_copy_selection_keep_edges( evaluator.add(selection_field); evaluator.evaluate(); const VArray selection = evaluator.get_evaluated(0); + if (selection.is_empty()) { + return std::nullopt; + } IndexMaskMemory memory; IndexMask poly_mask; -- 2.30.2 From 7042f745385b511f3dea1110919e8b32ff88227e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 07:53:05 -0400 Subject: [PATCH 16/20] Cleanup --- source/blender/geometry/intern/mesh_copy_selection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index d9b9beae109..d4f8e6251b3 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -456,7 +456,7 @@ std::optional mesh_copy_selection_keep_edges( src_polys, src_mesh.corner_edges(), VArraySpan(selection), memory); break; case ATTR_DOMAIN_FACE: - poly_mask = IndexMask::from_bools(evaluator.get_evaluated(0), memory); + poly_mask = IndexMask::from_bools(selection, memory); break; default: BLI_assert_unreachable(); -- 2.30.2 From f25396b4279df7675bc441865ee24d181b057113 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 08:49:45 -0400 Subject: [PATCH 17/20] Add comments in headers --- source/blender/blenkernel/BKE_attribute.hh | 5 +++++ source/blender/blenlib/BLI_offset_indices.hh | 1 + 2 files changed, 6 insertions(+) diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index f5a3179b863..d6ac15db8f9 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -929,6 +929,11 @@ void gather_attributes(AttributeAccessor src_attributes, const IndexMask &selection, MutableAttributeAccessor dst_attributes); +/** + * Copy attribute values from groups groups defined by \a src_offsets to groups defined by \a + * dst_offsets. The group indices are gathered to the result by \a selection. The size of each + * source and result group must be the same. + */ void gather_attributes_group_to_group(AttributeAccessor src_attributes, eAttrDomain domain, const AnonymousAttributePropagationInfo &propagation_info, diff --git a/source/blender/blenlib/BLI_offset_indices.hh b/source/blender/blenlib/BLI_offset_indices.hh index f7aa18224ca..053bf228fb2 100644 --- a/source/blender/blenlib/BLI_offset_indices.hh +++ b/source/blender/blenlib/BLI_offset_indices.hh @@ -146,6 +146,7 @@ void copy_group_sizes(OffsetIndices offsets, const IndexMask &mask, Mutable /** Gather the number of indices in each indexed group to sizes. */ void gather_group_sizes(OffsetIndices offsets, const IndexMask &mask, MutableSpan sizes); +/** Build new offsets that contains only the groups chosen by \a selection. */ OffsetIndices gather_selected_offsets(OffsetIndices src_offsets, const IndexMask &selection, MutableSpan dst_offsets); -- 2.30.2 From 9fdb0eadf3643b6763a622bcf2ade2bf1c3a4549 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 08:49:56 -0400 Subject: [PATCH 18/20] Remove unnecessary namespace specification --- source/blender/blenlib/intern/offset_indices.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/intern/offset_indices.cc b/source/blender/blenlib/intern/offset_indices.cc index 2b1d946bd69..5a7d4ae3476 100644 --- a/source/blender/blenlib/intern/offset_indices.cc +++ b/source/blender/blenlib/intern/offset_indices.cc @@ -46,8 +46,8 @@ OffsetIndices gather_selected_offsets(const OffsetIndices src_offsets, return {}; } BLI_assert(selection.size() == (dst_offsets.size() - 1)); - offset_indices::gather_group_sizes(src_offsets, selection, dst_offsets); - offset_indices::accumulate_counts_to_offsets(dst_offsets); + gather_group_sizes(src_offsets, selection, dst_offsets); + accumulate_counts_to_offsets(dst_offsets); return OffsetIndices(dst_offsets); } -- 2.30.2 From 1c4468cf68499dc9a8aec40dbb615b1209740462 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 08:50:17 -0400 Subject: [PATCH 19/20] Cleanup --- source/blender/geometry/GEO_mesh_copy_selection.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/geometry/GEO_mesh_copy_selection.hh b/source/blender/geometry/GEO_mesh_copy_selection.hh index 7ccbbb17ccb..cfa58ab6b14 100644 --- a/source/blender/geometry/GEO_mesh_copy_selection.hh +++ b/source/blender/geometry/GEO_mesh_copy_selection.hh @@ -34,7 +34,7 @@ std::optional mesh_copy_selection_keep_verts( std::optional mesh_copy_selection_keep_edges( const Mesh &mesh, const fn::Field &selection, - const eAttrDomain selection_domain, + eAttrDomain selection_domain, const bke::AnonymousAttributePropagationInfo &propagation_info); } // namespace blender::geometry -- 2.30.2 From bbe60eb039979681e4fb0add2bf5cc9ce8c66b62 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 08:50:22 -0400 Subject: [PATCH 20/20] Fix grain size --- source/blender/geometry/intern/mesh_copy_selection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc index d4f8e6251b3..abbf64272b4 100644 --- a/source/blender/geometry/intern/mesh_copy_selection.cc +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -17,7 +17,7 @@ static void create_reverse_map(const IndexMask &mask, MutableSpan r_map) r_map.fill(-1); #endif mask.foreach_index_optimized( - GrainSize(4049), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; }); + GrainSize(4096), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; }); } static void remap_verts(const OffsetIndices src_polys, -- 2.30.2