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 (f63cfd8e28) 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
This commit is contained in:
Hans Goudey
2023-06-01 14:55:21 +02:00
committed by Hans Goudey
parent c5694fdf10
commit 50bfe1dfe3
9 changed files with 655 additions and 951 deletions

View File

@@ -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<std::string> &skip,
OffsetIndices<int> src_offsets,
OffsetIndices<int> dst_offsets,
const IndexMask &selection,
MutableAttributeAccessor dst_attributes);
void copy_attributes(const AttributeAccessor src_attributes,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,

View File

@@ -1028,6 +1028,58 @@ void gather_attributes(const AttributeAccessor src_attributes,
});
}
template<typename T>
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const Span<T> src,
MutableSpan<T> 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<int> src_offsets,
const OffsetIndices<int> 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<T>(), dst.typed<T>());
});
}
void gather_attributes_group_to_group(const AttributeAccessor src_attributes,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip,
const OffsetIndices<int> src_offsets,
const OffsetIndices<int> 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,

View File

@@ -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<typename T>
static void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const Span<T> src,
MutableSpan<T> 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<int> src_offsets,
const OffsetIndices<int> 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<T>(), dst.typed<T>());
});
}
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<int> 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);

View File

@@ -38,7 +38,7 @@ template<typename T> 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<int> offsets, const IndexMask &mask, Mutable
/** Gather the number of indices in each indexed group to sizes. */
void gather_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, MutableSpan<int> sizes);
/** Build new offsets that contains only the groups chosen by \a selection. */
OffsetIndices<int> gather_selected_offsets(OffsetIndices<int> src_offsets,
const IndexMask &selection,
MutableSpan<int> dst_offsets);
/**
* Create a map from indexed elements to the source indices, in other words from the larger array
* to the smaller array.

View File

@@ -38,6 +38,19 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
});
}
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
const IndexMask &selection,
MutableSpan<int> 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<int>(dst_offsets);
}
void build_reverse_map(OffsetIndices<int> offsets, MutableSpan<int> r_map)
{
threading::parallel_for(offsets.index_range(), 1024, [&](const IndexRange range) {

View File

@@ -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

View File

@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <optional>
#include "BKE_attribute.h"
struct Mesh;
namespace blender {
class IndexMask;
namespace fn {
template<typename T> class Field;
}
namespace bke {
class AnonymousAttributePropagationInfo;
}
} // namespace blender
namespace blender::geometry {
std::optional<Mesh *> mesh_copy_selection(
const Mesh &src_mesh,
const fn::Field<bool> &selection,
eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info);
std::optional<Mesh *> mesh_copy_selection_keep_verts(
const Mesh &src_mesh,
const fn::Field<bool> &selection,
eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info);
std::optional<Mesh *> mesh_copy_selection_keep_edges(
const Mesh &mesh,
const fn::Field<bool> &selection,
eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@@ -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<int> r_map)
{
#ifdef DEBUG
r_map.fill(-1);
#endif
mask.foreach_index_optimized<int>(
GrainSize(4096), [&](const int src_i, const int dst_i) { r_map[src_i] = dst_i; });
}
static void remap_verts(const OffsetIndices<int> src_polys,
const OffsetIndices<int> dst_polys,
const int src_verts_num,
const IndexMask &vert_mask,
const IndexMask &edge_mask,
const IndexMask &poly_mask,
const Span<int2> src_edges,
const Span<int> src_corner_verts,
MutableSpan<int2> dst_edges,
MutableSpan<int> dst_corner_verts)
{
Array<int> 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<int> src_polys,
const OffsetIndices<int> dst_polys,
const int src_edges_num,
const IndexMask &edge_mask,
const IndexMask &poly_mask,
const Span<int> src_corner_edges,
MutableSpan<int> dst_corner_edges)
{
Array<int> 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<int2> edges,
const IndexMask &edge_mask,
const int verts_num,
IndexMaskMemory &memory)
{
Array<bool> array(verts_num, false);
edge_mask.foreach_index_optimized<int>([&](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<int> polys,
const IndexMask &poly_mask,
const Span<int> corner_verts_or_edges,
const int verts_or_edges_num,
IndexMaskMemory &memory)
{
Array<bool> 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<int> polys,
const IndexMask &poly_mask,
const Span<int> 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<int> polys,
const IndexMask &poly_mask,
const Span<int> 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<int2> edges,
const Span<bool> 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<int> polys,
const Span<int> corner_verts_or_edges,
const Span<bool> vert_or_edge_selection,
IndexMaskMemory &memory)
{
return IndexMask::from_predicate(
polys.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
const Span<int> 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<int> polys,
const Span<int> corner_verts,
const Span<bool> 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<int> polys,
const Span<int> corner_edges,
const Span<bool> 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 &params_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, &params_mesh);
return mesh;
}
std::optional<Mesh *> mesh_copy_selection(
const Mesh &src_mesh,
const fn::Field<bool> &selection_field,
const eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Span<int2> src_edges = src_mesh.edges();
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> 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<bool> selection = evaluator.get_evaluated<bool>(0);
if (selection.is_empty()) {
return std::nullopt;
}
if (const std::optional<bool> single = selection.get_if_single()) {
return *single ? std::nullopt : std::make_optional<Mesh *>(nullptr);
}
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
IndexMask vert_mask;
IndexMask edge_mask;
IndexMask poly_mask;
switch (selection_domain) {
case ATTR_DOMAIN_POINT: {
const VArraySpan<bool> 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<bool> 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<bool> 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<int2>(".edge_verts", ATTR_DOMAIN_EDGE, bke::AttributeInitConstruct());
MutableSpan<int2> dst_edges = dst_mesh->edges_for_write();
const OffsetIndices<int> 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<int>(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
MutableSpan<int> dst_corner_verts = dst_mesh->corner_verts_for_write();
MutableSpan<int> 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 *> mesh_copy_selection_keep_verts(
const Mesh &src_mesh,
const fn::Field<bool> &selection_field,
const eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Span<int2> src_edges = src_mesh.edges();
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> 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<bool> selection = evaluator.get_evaluated<bool>(0);
if (selection.is_empty()) {
return std::nullopt;
}
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
IndexMask edge_mask;
IndexMask poly_mask;
switch (selection_domain) {
case ATTR_DOMAIN_POINT: {
const VArraySpan<bool> 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<bool> 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<bool> 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<int> 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<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
MutableSpan<int> 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 *> mesh_copy_selection_keep_edges(
const Mesh &src_mesh,
const fn::Field<bool> &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<bool> selection = evaluator.get_evaluated<bool>(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<int> 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<int>(".corner_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
dst_attributes.add<int>(".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

View File

@@ -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<typename T>
static void copy_data_based_on_map(const Span<T> src,
const Span<int> index_map,
MutableSpan<T> 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<std::string> &skip,
const Span<int> 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<T>(), index_map, dst.span.typed<T>());
});
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<std::string> &skip,
const int selected_loops_num,
const Span<int> selected_poly_indices,
const Mesh &mesh_in)
{
const OffsetIndices polys = mesh_in.polys();
Vector<int64_t> 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<int64_t>(indices, memory),
dst_attributes);
}
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map)
{
BLI_assert(src_mesh.totedge == edge_map.size());
const Span<int2> src_edges = src_mesh.edges();
MutableSpan<int2> 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<int> vertex_map,
Span<int> edge_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
const Span<int2> src_edges = src_mesh.edges();
MutableSpan<int2> 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<int> edge_map,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> src_corner_edges = src_mesh.corner_edges();
MutableSpan<int> dst_poly_offsets = dst_mesh.poly_offsets_for_write();
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
MutableSpan<int> 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<int> src_poly_verts = src_corner_verts.slice(poly_src);
const Span<int> src_poly_edges = src_corner_edges.slice(poly_src);
dst_poly_offsets[i_dst] = new_loop_starts[i_dst];
MutableSpan<int> dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst],
poly_src.size());
MutableSpan<int> 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<int> vertex_map,
Span<int> edge_map,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
const OffsetIndices src_polys = src_mesh.polys();
const Span<int> src_corner_verts = src_mesh.corner_verts();
const Span<int> src_corner_edges = src_mesh.corner_edges();
MutableSpan<int> dst_poly_offsets = dst_mesh.poly_offsets_for_write();
MutableSpan<int> dst_corner_verts = dst_mesh.corner_verts_for_write();
MutableSpan<int> 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<int> src_poly_verts = src_corner_verts.slice(poly_src);
const Span<int> src_poly_edges = src_corner_edges.slice(poly_src);
dst_poly_offsets[i_dst] = new_loop_starts[i_dst];
MutableSpan<int> dst_poly_verts = dst_corner_verts.slice(dst_poly_offsets[i_dst],
poly_src.size());
MutableSpan<int> 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<Curves *> 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<bool> vertex_selection,
MutableSpan<int> 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<bool> vertex_selection,
MutableSpan<int> r_edge_map,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == r_edge_map.size());
const Span<int2> 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<bool> vertex_selection,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<int> 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<bool> edge_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
int *r_selected_verts_num,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == edge_selection.size());
const Span<int2> 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<bool> edge_selection,
MutableSpan<int> 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<bool> edge_selection,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
const OffsetIndices polys = mesh.polys();
const Span<int> 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<Mesh *> separate_mesh_selection(
const Mesh &mesh,
const Span<bool> vertex_selection,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
const Field<bool> &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<bool> vertex_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<bool> edge_selection,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<bool> edge_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<bool> poly_selection,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<bool> poly_selection,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<int> 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<bool> poly_selection,
MutableSpan<int> r_vertex_map,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &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<int> corner_verts = mesh.corner_verts();
const Span<int> 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<bool> selection,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* Needed in all cases. */
Vector<int> selected_poly_indices;
Vector<int> 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<int> vertex_map(mesh_in.totvert);
int selected_verts_num = 0;
Array<int> 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<int> 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<bool> &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<bool> selection = evaluator.get_evaluated<bool>(0);
/* Check if there is anything to delete. */
if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) {
return;
}
const VArraySpan<bool> 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<Mesh *> 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;
}
}