diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index 2e7225548c9..d6ac15db8f9 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -11,6 +11,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" @@ -928,6 +929,20 @@ 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, + 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 cad19661be8..bdbe1e0f28d 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1028,6 +1028,58 @@ 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) +{ + 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 049e45224de..7ede22fd6cc 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1176,62 +1176,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_POINT, + 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 6449fd89397..053bf228fb2 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_.size() > 1 ? offsets_.last() : 0; } /** @@ -146,6 +146,10 @@ 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); /** * 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/blenlib/intern/offset_indices.cc b/source/blender/blenlib/intern/offset_indices.cc index 3525e190dee..5a7d4ae3476 100644 --- a/source/blender/blenlib/intern/offset_indices.cc +++ b/source/blender/blenlib/intern/offset_indices.cc @@ -38,6 +38,19 @@ void gather_group_sizes(const OffsetIndices offsets, }); } +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)); + gather_group_sizes(src_offsets, selection, dst_offsets); + accumulate_counts_to_offsets(dst_offsets); + return OffsetIndices(dst_offsets); +} + void build_reverse_map(OffsetIndices offsets, MutableSpan r_map) { threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) { diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 88e3a15fffd..99f1c8b1b58 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -19,6 +19,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 @@ -43,6 +44,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_copy_selection.hh b/source/blender/geometry/GEO_mesh_copy_selection.hh new file mode 100644 index 00000000000..cfa58ab6b14 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_copy_selection.hh @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BKE_attribute.h" + +struct Mesh; +namespace blender { +class IndexMask; +namespace fn { +template class Field; +} +namespace bke { +class AnonymousAttributePropagationInfo; +} +} // namespace blender + +namespace blender::geometry { + +std::optional mesh_copy_selection( + const Mesh &src_mesh, + const fn::Field &selection, + eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +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, + eAttrDomain selection_domain, + const bke::AnonymousAttributePropagationInfo &propagation_info); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_copy_selection.cc b/source/blender/geometry/intern/mesh_copy_selection.cc new file mode 100644 index 00000000000..abbf64272b4 --- /dev/null +++ b/source/blender/geometry/intern/mesh_copy_selection.cc @@ -0,0 +1,494 @@ +/* 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_copy_selection.hh" + +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(4096), [&](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_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_mask, map); + threading::parallel_invoke( + vert_mask.size() > 1024, + [&]() { + 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_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_mask, + const IndexMask &poly_mask, + const Span src_corner_edges, + MutableSpan dst_corner_edges) +{ + Array map(src_edges_num); + 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()) { + 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_mask, + const int verts_num, + IndexMaskMemory &memory) +{ + Array array(verts_num, false); + edge_mask.foreach_index_optimized([&](const int i) { + array[edges[i][0]] = true; + array[edges[i][1]] = true; + }); + return IndexMask::from_bools(array, memory); +} + +static IndexMask mapped_corner_selection_from_poly(const OffsetIndices polys, + const IndexMask &poly_mask, + const Span corner_verts_or_edges, + const int verts_or_edges_num, + IndexMaskMemory &memory) +{ + Array array(verts_or_edges_num, false); + poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) { + array.as_mutable_span().fill_indices(corner_verts_or_edges.slice(polys[i]), 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_mask, + const Span corner_verts, + const int verts_num, + IndexMaskMemory &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_mask, + const Span corner_edges, + const int edges_num, + IndexMaskMemory &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, + 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 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]; + }); + }); +} + +/** 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_mask, + IndexMaskMemory &memory) +{ + 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, + 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(); + 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); + 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); + } + + threading::EnumerableThreadSpecific memory; + IndexMask vert_mask; + IndexMask edge_mask; + IndexMask poly_mask; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: { + const VArraySpan span(selection); + threading::parallel_invoke( + src_mesh.totvert > 1024, + [&]() { vert_mask = IndexMask::from_bools(span, memory.local()); }, + [&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); }, + [&]() { + poly_mask = poly_selection_from_vert( + src_polys, src_corner_verts, span, memory.local()); + }); + break; + } + case ATTR_DOMAIN_EDGE: { + const VArraySpan span(selection); + threading::parallel_invoke( + src_edges.size() > 1024, + [&]() { + edge_mask = IndexMask::from_bools(span, memory.local()); + vert_mask = vert_selection_from_edge( + src_edges, edge_mask, src_mesh.totvert, memory.local()); + }, + [&]() { + poly_mask = poly_selection_from_edge( + src_polys, src_corner_edges, span, memory.local()); + }); + break; + } + case ATTR_DOMAIN_FACE: { + const VArraySpan span(selection); + poly_mask = IndexMask::from_bools(span, memory.local()); + threading::parallel_invoke( + poly_mask.size() > 1024, + [&]() { + vert_mask = vert_selection_from_poly( + src_polys, poly_mask, src_corner_verts, src_mesh.totvert, memory.local()); + }, + [&]() { + edge_mask = edge_selection_from_poly( + src_polys, poly_mask, src_corner_edges, src_mesh.totedge, memory.local()); + }); + break; + } + default: + BLI_assert_unreachable(); + break; + } + + if (vert_mask.is_empty()) { + return nullptr; + } + if (vert_mask.size() == src_mesh.totvert) { + return std::nullopt; + } + + 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()); + 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_mask.size() > 1024, + [&]() { + remap_verts(src_polys, + dst_polys, + src_mesh.totvert, + vert_mask, + edge_mask, + poly_mask, + src_edges, + src_corner_verts, + dst_edges, + dst_corner_verts); + }, + [&]() { + remap_edges(src_polys, + dst_polys, + src_edges.size(), + edge_mask, + poly_mask, + src_corner_edges, + 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); + }); + + return dst_mesh; +} + +std::optional mesh_copy_selection_keep_verts( + 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(); + 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(); + + 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 (selection.is_empty()) { + return std::nullopt; + } + + threading::EnumerableThreadSpecific memory; + IndexMask edge_mask; + IndexMask poly_mask; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: { + const VArraySpan span(selection); + threading::parallel_invoke( + src_edges.size() > 1024, + [&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); }, + [&]() { + poly_mask = poly_selection_from_vert( + src_polys, src_corner_verts, span, memory.local()); + }); + break; + } + case ATTR_DOMAIN_EDGE: { + const VArraySpan span(selection); + threading::parallel_invoke( + src_edges.size() > 1024, + [&]() { edge_mask = IndexMask::from_bools(span, memory.local()); }, + [&]() { + poly_mask = poly_selection_from_edge( + src_polys, src_corner_edges, span, memory.local()); + }); + break; + } + case ATTR_DOMAIN_FACE: { + const VArraySpan span(selection); + 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: + BLI_assert_unreachable(); + break; + } + + 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( + 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(); + + 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; + return dst_mesh; +} + +std::optional mesh_copy_selection_keep_edges( + const Mesh &src_mesh, + 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); + evaluator.evaluate(); + const VArray selection = evaluator.get_evaluated(0); + if (selection.is_empty()) { + return std::nullopt; + } + + IndexMaskMemory memory; + IndexMask poly_mask; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: + poly_mask = poly_selection_from_vert( + src_polys, src_mesh.corner_verts(), VArraySpan(selection), memory); + break; + case ATTR_DOMAIN_EDGE: + poly_mask = poly_selection_from_edge( + src_polys, src_mesh.corner_edges(), VArraySpan(selection), memory); + break; + case ATTR_DOMAIN_FACE: + poly_mask = IndexMask::from_bools(selection, memory); + break; + default: + BLI_assert_unreachable(); + break; + } + + 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); + 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, + {}, + 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; + return dst_mesh; +} + +} // namespace blender::geometry 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 64605517af8..813dba4798b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -19,193 +19,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, @@ -291,729 +110,24 @@ 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) -{ - 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( +static std::optional separate_mesh_selection( 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) + const Field &selection, + const eAttrDomain selection_domain, + const GeometryNodeDeleteGeometryMode mode, + const AnonymousAttributePropagationInfo &propagation_info) { - 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 @@ -1040,9 +154,13 @@ 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); + 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; } }