From 50bfe1dfe39c8932645eeec4f066643dbbc10f5b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 1 Jun 2023 14:55:21 +0200 Subject: [PATCH] Geometry Nodes: Rewrite mesh delete geometry node Replace the implementation of the separate and delete geometry nodes for meshes. The new code makes more use of the `IndexMask` class, which was recently optimized. The main goal is to make more of the work scale with the size of the result mesh rather than the input. For example, instead of keeping a map from input to output elements, the maps used to copy attributes go from output to input elements. The new implementation is generally 2-4x faster, depending on the mode and the number of elements selected. The new code is also able to skip more work when nothing is removed. This also allows using more existing attribute interpolation code, allowing the overall removal of over 300 lines. Some of the attribute utilities from a similar change for curves (f63cfd8e289da0078266) are reused directly. The indices of the result changes, so the test file needs to be updated. Pull Request: https://projects.blender.org/blender/blender/pulls/108435 --- source/blender/blenkernel/BKE_attribute.hh | 15 + .../blenkernel/intern/attribute_access.cc | 52 + .../blenkernel/intern/curves_geometry.cc | 58 +- source/blender/blenlib/BLI_offset_indices.hh | 6 +- .../blender/blenlib/intern/offset_indices.cc | 13 + source/blender/geometry/CMakeLists.txt | 2 + .../geometry/GEO_mesh_copy_selection.hh | 40 + .../geometry/intern/mesh_copy_selection.cc | 494 ++++++++++ .../nodes/node_geo_delete_geometry.cc | 926 +----------------- 9 files changed, 655 insertions(+), 951 deletions(-) create mode 100644 source/blender/geometry/GEO_mesh_copy_selection.hh create mode 100644 source/blender/geometry/intern/mesh_copy_selection.cc 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; } }