Geometry Nodes: Rewrite mesh delete geometry node #108435
|
@ -1059,7 +1059,6 @@ void gather_attributes_group_to_group(const AttributeAccessor src_attributes,
|
||||||
const IndexMask &selection,
|
const IndexMask &selection,
|
||||||
MutableAttributeAccessor dst_attributes)
|
MutableAttributeAccessor dst_attributes)
|
||||||
{
|
{
|
||||||
const int src_size = src_attributes.domain_size(domain);
|
|
||||||
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||||
if (meta_data.domain != domain) {
|
if (meta_data.domain != domain) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -43,6 +43,7 @@ OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
|
||||||
BLI_assert(selection.size() == (dst_offsets.size() - 1));
|
BLI_assert(selection.size() == (dst_offsets.size() - 1));
|
||||||
offset_indices::gather_group_sizes(src_offsets, selection, dst_offsets);
|
offset_indices::gather_group_sizes(src_offsets, selection, dst_offsets);
|
||||||
offset_indices::accumulate_counts_to_offsets(dst_offsets);
|
offset_indices::accumulate_counts_to_offsets(dst_offsets);
|
||||||
|
return OffsetIndices<int>(dst_offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
void build_reverse_map(OffsetIndices<int> offsets, MutableSpan<int> r_map)
|
void build_reverse_map(OffsetIndices<int> offsets, MutableSpan<int> r_map)
|
||||||
HooglyBoogly marked this conversation as resolved
Outdated
|
|||||||
|
|
|
@ -18,6 +18,7 @@ set(SRC
|
||||||
intern/add_curves_on_mesh.cc
|
intern/add_curves_on_mesh.cc
|
||||||
intern/curve_constraints.cc
|
intern/curve_constraints.cc
|
||||||
intern/fillet_curves.cc
|
intern/fillet_curves.cc
|
||||||
|
intern/mesh_copy_selection.cc
|
||||||
intern/mesh_flip_faces.cc
|
intern/mesh_flip_faces.cc
|
||||||
intern/mesh_merge_by_distance.cc
|
intern/mesh_merge_by_distance.cc
|
||||||
intern/mesh_primitive_cuboid.cc
|
intern/mesh_primitive_cuboid.cc
|
||||||
|
@ -42,6 +43,7 @@ set(SRC
|
||||||
GEO_mesh_merge_by_distance.hh
|
GEO_mesh_merge_by_distance.hh
|
||||||
GEO_mesh_primitive_cuboid.hh
|
GEO_mesh_primitive_cuboid.hh
|
||||||
GEO_mesh_split_edges.hh
|
GEO_mesh_split_edges.hh
|
||||||
|
GEO_mesh_copy_selection.hh
|
||||||
GEO_mesh_to_curve.hh
|
GEO_mesh_to_curve.hh
|
||||||
GEO_mesh_to_volume.hh
|
GEO_mesh_to_volume.hh
|
||||||
GEO_point_merge_by_distance.hh
|
GEO_point_merge_by_distance.hh
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#include "BLI_enumerable_thread_specific.hh"
|
||||||
#include "BLI_index_mask.hh"
|
#include "BLI_index_mask.hh"
|
||||||
|
|
||||||
#include "BKE_attribute.hh"
|
#include "BKE_attribute.hh"
|
||||||
#include "BKE_geometry_fields.hh"
|
#include "BKE_geometry_fields.hh"
|
||||||
#include "BKE_mesh.hh"
|
#include "BKE_mesh.hh"
|
||||||
|
|
||||||
#include "GEO_mesh_separate.hh"
|
#include "GEO_mesh_copy_selection.hh"
|
||||||
|
|
||||||
namespace blender::geometry {
|
namespace blender::geometry {
|
||||||
|
|
||||||
|
@ -19,48 +20,46 @@ static void create_reverse_map(const IndexMask &mask, MutableSpan<int> r_map)
|
||||||
static void remap_verts(const OffsetIndices<int> src_polys,
|
static void remap_verts(const OffsetIndices<int> src_polys,
|
||||||
HooglyBoogly marked this conversation as resolved
Outdated
Jacques Lucke
commented
typo (4049) typo (4049)
|
|||||||
const OffsetIndices<int> dst_polys,
|
const OffsetIndices<int> dst_polys,
|
||||||
const int src_verts_num,
|
const int src_verts_num,
|
||||||
const IndexMask &vert_selection,
|
const IndexMask &vert_mask,
|
||||||
const IndexMask &edge_selection,
|
const IndexMask &edge_mask,
|
||||||
const IndexMask &poly_selection,
|
const IndexMask &poly_mask,
|
||||||
const Span<int2> src_edges,
|
const Span<int2> src_edges,
|
||||||
const Span<int> src_corner_verts,
|
const Span<int> src_corner_verts,
|
||||||
MutableSpan<int2> dst_edges,
|
MutableSpan<int2> dst_edges,
|
||||||
MutableSpan<int> dst_corner_verts)
|
MutableSpan<int> dst_corner_verts)
|
||||||
{
|
{
|
||||||
Array<int> map(src_verts_num);
|
Array<int> map(src_verts_num);
|
||||||
create_reverse_map(vert_selection, map);
|
create_reverse_map(vert_mask, map);
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
vert_selection.size() > 1024,
|
vert_mask.size() > 1024,
|
||||||
[&]() {
|
[&]() {
|
||||||
poly_selection.foreach_index(
|
poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||||
GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
const IndexRange src_poly = src_polys[src_i];
|
||||||
const IndexRange src_poly = src_polys[src_i];
|
const IndexRange dst_poly = dst_polys[dst_i];
|
||||||
const IndexRange dst_poly = dst_polys[dst_i];
|
for (const int i : src_poly.index_range()) {
|
||||||
for (const int i : src_poly.index_range()) {
|
dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]];
|
||||||
dst_corner_verts[dst_poly[i]] = map[src_corner_verts[src_poly[i]]];
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[&]() {
|
[&]() {
|
||||||
edge_selection.foreach_index(GrainSize(512),
|
edge_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||||
[&](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][0] = map[src_edges[src_i][0]];
|
dst_edges[dst_i][1] = map[src_edges[src_i][1]];
|
||||||
dst_edges[dst_i][1] = map[src_edges[src_i][1]];
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remap_edges(const OffsetIndices<int> src_polys,
|
static void remap_edges(const OffsetIndices<int> src_polys,
|
||||||
const OffsetIndices<int> dst_polys,
|
const OffsetIndices<int> dst_polys,
|
||||||
const int src_edges_num,
|
const int src_edges_num,
|
||||||
const IndexMask &edge_selection,
|
const IndexMask &edge_mask,
|
||||||
const IndexMask &poly_selection,
|
const IndexMask &poly_mask,
|
||||||
const Span<int> src_corner_edges,
|
const Span<int> src_corner_edges,
|
||||||
MutableSpan<int> dst_corner_edges)
|
MutableSpan<int> dst_corner_edges)
|
||||||
{
|
{
|
||||||
Array<int> map(src_edges_num);
|
Array<int> map(src_edges_num);
|
||||||
create_reverse_map(edge_selection, map);
|
create_reverse_map(edge_mask, map);
|
||||||
poly_selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
poly_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
|
||||||
const IndexRange src_poly = src_polys[src_i];
|
const IndexRange src_poly = src_polys[src_i];
|
||||||
const IndexRange dst_poly = dst_polys[dst_i];
|
const IndexRange dst_poly = dst_polys[dst_i];
|
||||||
for (const int i : src_poly.index_range()) {
|
for (const int i : src_poly.index_range()) {
|
||||||
|
@ -71,12 +70,12 @@ static void remap_edges(const OffsetIndices<int> src_polys,
|
||||||
|
|
||||||
/** A vertex is selected if it's used by a selected edge. */
|
/** A vertex is selected if it's used by a selected edge. */
|
||||||
static IndexMask vert_selection_from_edge(const Span<int2> edges,
|
static IndexMask vert_selection_from_edge(const Span<int2> edges,
|
||||||
const IndexMask &edge_selection,
|
const IndexMask &edge_mask,
|
||||||
const int verts_num,
|
const int verts_num,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
Array<bool> span(verts_num, false);
|
Array<bool> span(verts_num, false);
|
||||||
edge_selection.foreach_index_optimized<int>([&](const int i) {
|
edge_mask.foreach_index_optimized<int>([&](const int i) {
|
||||||
span[edges[i][0]] = true;
|
span[edges[i][0]] = true;
|
||||||
span[edges[i][1]] = true;
|
span[edges[i][1]] = true;
|
||||||
});
|
});
|
||||||
|
@ -84,13 +83,13 @@ static IndexMask vert_selection_from_edge(const Span<int2> edges,
|
||||||
}
|
}
|
||||||
|
|
||||||
static IndexMask mapped_corner_selection_from_poly(const OffsetIndices<int> polys,
|
static IndexMask mapped_corner_selection_from_poly(const OffsetIndices<int> polys,
|
||||||
const IndexMask &poly_selection,
|
const IndexMask &poly_mask,
|
||||||
const Span<int> corner_verts_or_edges,
|
const Span<int> corner_verts_or_edges,
|
||||||
const int verts_or_edges_num,
|
const int verts_or_edges_num,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
Array<bool> array(verts_or_edges_num);
|
Array<bool> array(verts_or_edges_num);
|
||||||
poly_selection.foreach_index(GrainSize(512), [&](const int64_t i) {
|
poly_mask.foreach_index(GrainSize(512), [&](const int64_t i) {
|
||||||
const Span<int> poly_edges = corner_verts_or_edges.slice(polys[i]);
|
const Span<int> poly_edges = corner_verts_or_edges.slice(polys[i]);
|
||||||
array.as_mutable_span().fill_indices(poly_edges, true);
|
array.as_mutable_span().fill_indices(poly_edges, true);
|
||||||
});
|
});
|
||||||
|
@ -99,33 +98,33 @@ static IndexMask mapped_corner_selection_from_poly(const OffsetIndices<int> poly
|
||||||
|
|
||||||
/** A vertex is selected if it is used by a selected face. */
|
/** A vertex is selected if it is used by a selected face. */
|
||||||
static IndexMask vert_selection_from_poly(const OffsetIndices<int> polys,
|
static IndexMask vert_selection_from_poly(const OffsetIndices<int> polys,
|
||||||
const IndexMask &poly_selection,
|
const IndexMask &poly_mask,
|
||||||
const Span<int> corner_verts,
|
const Span<int> corner_verts,
|
||||||
const int verts_num,
|
const int verts_num,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
return mapped_corner_selection_from_poly(polys, poly_selection, corner_verts, verts_num, memory);
|
return mapped_corner_selection_from_poly(polys, poly_mask, corner_verts, verts_num, memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An edge is selected if it is used by a selected face. */
|
/** An edge is selected if it is used by a selected face. */
|
||||||
static IndexMask edge_selection_from_poly(const OffsetIndices<int> polys,
|
static IndexMask edge_selection_from_poly(const OffsetIndices<int> polys,
|
||||||
const IndexMask &poly_selection,
|
const IndexMask &poly_mask,
|
||||||
const Span<int> corner_edges,
|
const Span<int> corner_edges,
|
||||||
const int edges_num,
|
const int edges_num,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
return mapped_corner_selection_from_poly(polys, poly_selection, corner_edges, edges_num, memory);
|
return mapped_corner_selection_from_poly(polys, poly_mask, corner_edges, edges_num, memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An edge is selected if both of its vertices are selected. */
|
/** An edge is selected if both of its vertices are selected. */
|
||||||
static IndexMask edge_selection_from_vert(const Span<int2> edges,
|
static IndexMask edge_selection_from_vert(const Span<int2> edges,
|
||||||
const Span<bool> vert_selection,
|
const Span<bool> vert_mask,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
return IndexMask::from_predicate(
|
return IndexMask::from_predicate(
|
||||||
edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
|
edges.index_range(), GrainSize(1024), memory, [&](const int64_t i) {
|
||||||
const int2 edge = edges[i];
|
const int2 edge = edges[i];
|
||||||
return vert_selection[edge[0]] && vert_selection[edge[1]];
|
return vert_mask[edge[0]] && vert_mask[edge[1]];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,23 +144,23 @@ static IndexMask poly_selection_from_mapped_corner(const OffsetIndices<int> poly
|
||||||
/** A face is selected if all of its vertices are selected. */
|
/** A face is selected if all of its vertices are selected. */
|
||||||
static IndexMask poly_selection_from_vert(const OffsetIndices<int> polys,
|
static IndexMask poly_selection_from_vert(const OffsetIndices<int> polys,
|
||||||
const Span<int> corner_verts,
|
const Span<int> corner_verts,
|
||||||
const Span<bool> vert_selection,
|
const Span<bool> vert_mask,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
return poly_selection_from_mapped_corner(polys, corner_verts, vert_selection, memory);
|
return poly_selection_from_mapped_corner(polys, corner_verts, vert_mask, memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A face is selected if all of its edges are selected. */
|
/** A face is selected if all of its edges are selected. */
|
||||||
static IndexMask poly_selection_from_edge(const OffsetIndices<int> polys,
|
static IndexMask poly_selection_from_edge(const OffsetIndices<int> polys,
|
||||||
const Span<int> corner_edges,
|
const Span<int> corner_edges,
|
||||||
const Span<bool> edge_selection,
|
const Span<bool> edge_mask,
|
||||||
IndexMaskMemory &memory)
|
IndexMaskMemory &memory)
|
||||||
{
|
{
|
||||||
return poly_selection_from_mapped_corner(polys, corner_edges, edge_selection, memory);
|
return poly_selection_from_mapped_corner(polys, corner_edges, edge_mask, memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh *mesh_copy_selection(const Mesh &src_mesh,
|
Mesh *mesh_copy_selection(const Mesh &src_mesh,
|
||||||
const fn::Field<bool> &selection,
|
const fn::Field<bool> &selection_field,
|
||||||
const eAttrDomain selection_domain,
|
const eAttrDomain selection_domain,
|
||||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||||
{
|
{
|
||||||
|
@ -169,69 +168,61 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh,
|
||||||
const OffsetIndices src_polys = src_mesh.polys();
|
const OffsetIndices src_polys = src_mesh.polys();
|
||||||
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
||||||
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
||||||
|
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||||
|
|
||||||
// TODO: Local
|
const bke::MeshFieldContext context(src_mesh, selection_domain);
|
||||||
IndexMaskMemory memory;
|
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
|
||||||
IndexMask vert_selection;
|
evaluator.add(selection_field);
|
||||||
IndexMask edge_selection;
|
evaluator.evaluate();
|
||||||
IndexMask poly_selection;
|
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||||
|
if (const std::optional<bool> single = selection.get_if_single()) {
|
||||||
|
return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
|
||||||
|
IndexMask vert_mask;
|
||||||
|
IndexMask edge_mask;
|
||||||
|
IndexMask poly_mask;
|
||||||
switch (selection_domain) {
|
switch (selection_domain) {
|
||||||
case ATTR_DOMAIN_POINT: {
|
case ATTR_DOMAIN_POINT: {
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT);
|
const VArraySpan<bool> span(selection);
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totvert);
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
vert_selection.size() > 1024,
|
src_mesh.totvert > 1024,
|
||||||
[&]() { vert_selection = IndexMask::from_bools(selection, memory); },
|
[&]() { vert_mask = IndexMask::from_bools(span, memory.local()); },
|
||||||
[&]() { edge_selection = edge_selection_from_vert(src_edges, selection, memory); },
|
[&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
|
||||||
[&]() {
|
[&]() {
|
||||||
poly_selection = poly_selection_from_vert(
|
poly_mask = poly_selection_from_vert(
|
||||||
src_polys, src_corner_verts, selection, memory);
|
src_polys, src_corner_verts, span, memory.local());
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ATTR_DOMAIN_EDGE: {
|
case ATTR_DOMAIN_EDGE: {
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE);
|
const VArraySpan<bool> span(selection);
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totedge);
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
edge_selection.size() > 1024,
|
src_edges.size() > 1024,
|
||||||
[&]() {
|
[&]() {
|
||||||
edge_selection = IndexMask::from_bools(selection, memory);
|
edge_mask = IndexMask::from_bools(span, memory.local());
|
||||||
vert_selection = vert_selection_from_edge(
|
vert_mask = vert_selection_from_edge(
|
||||||
src_edges, edge_selection, src_mesh.totvert, memory);
|
src_edges, edge_mask, src_mesh.totvert, memory.local());
|
||||||
},
|
},
|
||||||
[&]() {
|
[&]() {
|
||||||
poly_selection = poly_selection_from_edge(
|
poly_mask = poly_selection_from_edge(
|
||||||
src_polys, src_corner_edges, selection, memory);
|
src_polys, src_corner_edges, span, memory.local());
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ATTR_DOMAIN_FACE: {
|
case ATTR_DOMAIN_FACE: {
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT);
|
const VArraySpan<bool> span(selection);
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totpoly);
|
poly_mask = IndexMask::from_bools(span, memory.local());
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
poly_selection = IndexMask::from_bools(evaluator.get_evaluated<bool>(0), memory);
|
|
||||||
if (poly_selection.is_empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (poly_selection.size() == src_mesh.totpoly) {
|
|
||||||
return BKE_mesh_copy_for_eval(&src_mesh);
|
|
||||||
}
|
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
poly_selection.size() > 1024,
|
poly_mask.size() > 1024,
|
||||||
[&]() {
|
[&]() {
|
||||||
vert_selection = vert_selection_from_poly(
|
vert_mask = vert_selection_from_poly(
|
||||||
src_polys, poly_selection, src_corner_verts, src_mesh.totvert, memory);
|
src_polys, poly_mask, src_corner_verts, src_mesh.totvert, memory.local());
|
||||||
},
|
},
|
||||||
[&]() {
|
[&]() {
|
||||||
edge_selection = edge_selection_from_poly(
|
edge_mask = edge_selection_from_poly(
|
||||||
src_polys, poly_selection, src_corner_edges, src_mesh.totedge, memory);
|
src_polys, poly_mask, src_corner_edges, src_mesh.totedge, memory.local());
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -240,69 +231,78 @@ Mesh *mesh_copy_selection(const Mesh &src_mesh,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh *dst_mesh = BKE_mesh_new_nomain(
|
if (vert_mask.is_empty()) {
|
||||||
vert_selection.size(), edge_selection.size(), poly_selection.size(), 0);
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (vert_mask.size() == src_mesh.totvert) {
|
||||||
|
return BKE_mesh_copy_for_eval(&src_mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *dst_mesh = BKE_mesh_new_nomain(vert_mask.size(), edge_mask.size(), poly_mask.size(), 0);
|
||||||
|
CustomData_free_layer_named(&dst_mesh->ldata, ".corner_vert", 0);
|
||||||
|
CustomData_free_layer_named(&dst_mesh->ldata, ".corner_edge", 0);
|
||||||
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
||||||
|
MutableSpan<int2> dst_edges = dst_mesh->edges_for_write();
|
||||||
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
|
||||||
src_polys, poly_selection, dst_mesh->poly_offsets_for_write());
|
|
||||||
dst_mesh->totloop = dst_polys.total_size();
|
|
||||||
|
|
||||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
|
||||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
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_vert", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||||
dst_attributes.add<int>(".corner_edge", 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(
|
threading::parallel_invoke(
|
||||||
vert_selection.size() > 1024,
|
vert_mask.size() > 1024,
|
||||||
[&]() {
|
[&]() {
|
||||||
remap_verts(src_polys,
|
remap_verts(src_polys,
|
||||||
dst_polys,
|
dst_polys,
|
||||||
src_mesh.totvert,
|
src_mesh.totvert,
|
||||||
vert_selection,
|
vert_mask,
|
||||||
edge_selection,
|
edge_mask,
|
||||||
poly_selection,
|
poly_mask,
|
||||||
src_edges,
|
src_edges,
|
||||||
src_corner_verts,
|
src_corner_verts,
|
||||||
dst_mesh->edges_for_write(),
|
dst_edges,
|
||||||
dst_mesh->corner_verts_for_write());
|
dst_corner_verts);
|
||||||
},
|
},
|
||||||
[&]() {
|
[&]() {
|
||||||
remap_edges(src_polys,
|
remap_edges(src_polys,
|
||||||
dst_polys,
|
dst_polys,
|
||||||
src_edges.size(),
|
src_edges.size(),
|
||||||
edge_selection,
|
edge_mask,
|
||||||
poly_selection,
|
poly_mask,
|
||||||
src_corner_edges,
|
src_corner_edges,
|
||||||
dst_mesh->corner_edges_for_write());
|
dst_corner_edges);
|
||||||
|
},
|
||||||
|
[&]() {
|
||||||
|
bke::gather_attributes(
|
||||||
|
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_mask, dst_attributes);
|
||||||
|
bke::gather_attributes(src_attributes,
|
||||||
|
ATTR_DOMAIN_EDGE,
|
||||||
|
propagation_info,
|
||||||
|
{".edge_verts"},
|
||||||
|
edge_mask,
|
||||||
|
dst_attributes);
|
||||||
|
bke::gather_attributes(
|
||||||
|
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
|
||||||
|
bke::gather_attributes_group_to_group(src_attributes,
|
||||||
|
ATTR_DOMAIN_CORNER,
|
||||||
|
propagation_info,
|
||||||
|
{".corner_edge", ".corner_vert"},
|
||||||
|
src_polys,
|
||||||
|
dst_polys,
|
||||||
|
poly_mask,
|
||||||
|
dst_attributes);
|
||||||
});
|
});
|
||||||
|
|
||||||
bke::gather_attributes(
|
|
||||||
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, vert_selection, dst_attributes);
|
|
||||||
bke::gather_attributes(src_attributes,
|
|
||||||
ATTR_DOMAIN_EDGE,
|
|
||||||
propagation_info,
|
|
||||||
{".edge_verts"},
|
|
||||||
edge_selection,
|
|
||||||
dst_attributes);
|
|
||||||
bke::gather_attributes(
|
|
||||||
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes);
|
|
||||||
bke::gather_attributes_group_to_group(src_attributes,
|
|
||||||
ATTR_DOMAIN_CORNER,
|
|
||||||
propagation_info,
|
|
||||||
{".corner_edge", ".corner_vert"},
|
|
||||||
src_polys,
|
|
||||||
dst_polys,
|
|
||||||
poly_selection,
|
|
||||||
dst_attributes);
|
|
||||||
|
|
||||||
return dst_mesh;
|
return dst_mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh *mesh_copy_selection_keep_verts(
|
Mesh *mesh_copy_selection_keep_verts(
|
||||||
const Mesh &src_mesh,
|
const Mesh &src_mesh,
|
||||||
const fn::Field<bool> &selection,
|
const fn::Field<bool> &selection_field,
|
||||||
const eAttrDomain selection_domain,
|
const eAttrDomain selection_domain,
|
||||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||||
{
|
{
|
||||||
|
@ -310,66 +310,52 @@ Mesh *mesh_copy_selection_keep_verts(
|
||||||
const OffsetIndices src_polys = src_mesh.polys();
|
const OffsetIndices src_polys = src_mesh.polys();
|
||||||
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
const Span<int> src_corner_verts = src_mesh.corner_verts();
|
||||||
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
const Span<int> src_corner_edges = src_mesh.corner_edges();
|
||||||
|
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||||
|
|
||||||
IndexMaskMemory memory;
|
const bke::MeshFieldContext context(src_mesh, selection_domain);
|
||||||
IndexMask edge_selection;
|
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
|
||||||
IndexMask poly_selection;
|
evaluator.add(selection_field);
|
||||||
|
evaluator.evaluate();
|
||||||
|
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||||
|
if (const std::optional<bool> single = selection.get_if_single()) {
|
||||||
|
return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
threading::EnumerableThreadSpecific<IndexMaskMemory> memory;
|
||||||
|
IndexMask edge_mask;
|
||||||
|
IndexMask poly_mask;
|
||||||
switch (selection_domain) {
|
switch (selection_domain) {
|
||||||
case ATTR_DOMAIN_POINT: {
|
case ATTR_DOMAIN_POINT: {
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT);
|
const VArraySpan<bool> span(selection);
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totvert);
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
poly_selection.size() > 1024,
|
src_edges.size() > 1024,
|
||||||
[&]() { edge_selection = edge_selection_from_vert(src_edges, selection, memory); },
|
[&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
|
||||||
[&]() {
|
[&]() {
|
||||||
poly_selection = poly_selection_from_vert(
|
poly_mask = poly_selection_from_vert(
|
||||||
src_polys, src_corner_verts, selection, memory);
|
src_polys, src_corner_verts, span, memory.local());
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ATTR_DOMAIN_EDGE: {
|
case ATTR_DOMAIN_EDGE: {
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE);
|
const VArraySpan<bool> span(selection);
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totedge);
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
poly_selection.size() > 1024,
|
src_edges.size() > 1024,
|
||||||
[&]() { edge_selection = IndexMask::from_bools(selection, memory); },
|
[&]() { edge_mask = IndexMask::from_bools(span, memory.local()); },
|
||||||
[&]() {
|
[&]() {
|
||||||
poly_selection = poly_selection_from_edge(
|
poly_mask = poly_selection_from_edge(
|
||||||
src_polys, src_corner_edges, selection, memory);
|
src_polys, src_corner_edges, span, memory.local());
|
||||||
});
|
});
|
||||||
if (edge_selection.is_empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (edge_selection.size() == src_mesh.totedge) {
|
|
||||||
return BKE_mesh_copy_for_eval(&src_mesh);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ATTR_DOMAIN_FACE: {
|
case ATTR_DOMAIN_FACE: {
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_FACE);
|
const VArraySpan<bool> span(selection);
|
||||||
fn::FieldEvaluator evaluator(context, src_polys.size());
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
threading::parallel_invoke(
|
threading::parallel_invoke(
|
||||||
poly_selection.size() > 1024,
|
src_edges.size() > 1024,
|
||||||
[&]() {
|
[&]() {
|
||||||
edge_selection = edge_selection_from_poly(
|
edge_mask = edge_selection_from_poly(
|
||||||
src_polys, poly_selection, src_corner_edges, src_edges.size(), memory);
|
src_polys, poly_mask, src_corner_edges, src_edges.size(), memory.local());
|
||||||
},
|
},
|
||||||
[&]() { poly_selection = IndexMask::from_bools(selection, memory); });
|
[&]() { poly_mask = IndexMask::from_bools(span, memory.local()); });
|
||||||
if (poly_selection.is_empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (poly_selection.size() == src_mesh.totpoly) {
|
|
||||||
return BKE_mesh_copy_for_eval(&src_mesh);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -377,42 +363,53 @@ Mesh *mesh_copy_selection_keep_verts(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh *dst_mesh = BKE_mesh_new_nomain(
|
if (edge_mask.is_empty()) {
|
||||||
src_mesh.totvert, edge_selection.size(), poly_selection.size(), 0);
|
return nullptr;
|
||||||
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
}
|
||||||
CustomData_free_layer_named(&dst_mesh->vdata, "position", 0);
|
if (edge_mask.size() == src_mesh.totedge) {
|
||||||
dst_mesh->totvert = src_mesh.totvert;
|
return BKE_mesh_copy_for_eval(&src_mesh);
|
||||||
|
}
|
||||||
|
|
||||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
Mesh *dst_mesh = BKE_mesh_new_nomain(0, edge_mask.size(), poly_mask.size(), 0);
|
||||||
|
CustomData_free_layer_named(&dst_mesh->vdata, "position", 0);
|
||||||
|
CustomData_free_layer_named(&dst_mesh->ldata, ".corner_vert", 0);
|
||||||
|
CustomData_free_layer_named(&dst_mesh->ldata, ".corner_edge", 0);
|
||||||
|
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
||||||
|
dst_mesh->totvert = src_mesh.totvert;
|
||||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
||||||
|
|
||||||
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
||||||
src_polys, poly_selection, dst_mesh->poly_offsets_for_write());
|
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
|
||||||
dst_mesh->totloop = dst_polys.total_size();
|
dst_mesh->totloop = dst_polys.total_size();
|
||||||
|
|
||||||
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
dst_attributes.add<int>(".corner_edge", ATTR_DOMAIN_CORNER, bke::AttributeInitConstruct());
|
||||||
|
MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
|
||||||
|
|
||||||
remap_edges(src_polys,
|
threading::parallel_invoke(
|
||||||
dst_polys,
|
[&]() {
|
||||||
src_edges.size(),
|
remap_edges(src_polys,
|
||||||
edge_selection,
|
dst_polys,
|
||||||
poly_selection,
|
src_edges.size(),
|
||||||
src_corner_edges,
|
edge_mask,
|
||||||
dst_mesh->corner_edges_for_write());
|
poly_mask,
|
||||||
|
src_corner_edges,
|
||||||
bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
|
dst_corner_edges);
|
||||||
bke::gather_attributes(
|
},
|
||||||
src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_selection, dst_attributes);
|
[&]() {
|
||||||
bke::gather_attributes(
|
bke::copy_attributes(
|
||||||
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes);
|
src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
|
||||||
bke::gather_attributes_group_to_group(src_attributes,
|
bke::gather_attributes(
|
||||||
ATTR_DOMAIN_CORNER,
|
src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, edge_mask, dst_attributes);
|
||||||
propagation_info,
|
bke::gather_attributes(
|
||||||
{".corner_edge"},
|
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
|
||||||
src_polys,
|
bke::gather_attributes_group_to_group(src_attributes,
|
||||||
dst_polys,
|
ATTR_DOMAIN_CORNER,
|
||||||
poly_selection,
|
propagation_info,
|
||||||
dst_attributes);
|
{".corner_edge"},
|
||||||
|
src_polys,
|
||||||
|
dst_polys,
|
||||||
|
poly_mask,
|
||||||
|
dst_attributes);
|
||||||
|
});
|
||||||
|
|
||||||
/* Positions are not changed by the operation, so the bounds are the same. */
|
/* Positions are not changed by the operation, so the bounds are the same. */
|
||||||
dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
|
dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
|
||||||
|
@ -421,80 +418,70 @@ Mesh *mesh_copy_selection_keep_verts(
|
||||||
|
|
||||||
Mesh *mesh_copy_selection_keep_edges(
|
Mesh *mesh_copy_selection_keep_edges(
|
||||||
const Mesh &src_mesh,
|
const Mesh &src_mesh,
|
||||||
const fn::Field<bool> &selection,
|
const fn::Field<bool> &selection_field,
|
||||||
const eAttrDomain selection_domain,
|
const eAttrDomain selection_domain,
|
||||||
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
const bke::AnonymousAttributePropagationInfo &propagation_info)
|
||||||
{
|
{
|
||||||
|
const OffsetIndices src_polys = src_mesh.polys();
|
||||||
|
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
||||||
|
|
||||||
|
const bke::MeshFieldContext context(src_mesh, selection_domain);
|
||||||
|
fn::FieldEvaluator evaluator(context, src_attributes.domain_size(selection_domain));
|
||||||
|
evaluator.add(selection_field);
|
||||||
|
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
|
||||||
|
if (const std::optional<bool> single = selection.get_if_single()) {
|
||||||
|
return *single ? BKE_mesh_copy_for_eval(&src_mesh) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
IndexMaskMemory memory;
|
IndexMaskMemory memory;
|
||||||
IndexMask poly_selection;
|
IndexMask poly_mask;
|
||||||
switch (selection_domain) {
|
switch (selection_domain) {
|
||||||
case ATTR_DOMAIN_POINT: {
|
case ATTR_DOMAIN_POINT:
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_POINT);
|
poly_mask = poly_selection_from_vert(
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totvert);
|
src_polys, src_mesh.corner_verts(), VArraySpan(selection), memory);
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> vert_selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
poly_selection = poly_selection_from_vert(
|
|
||||||
src_mesh.polys(), src_mesh.corner_verts(), vert_selection, memory);
|
|
||||||
break;
|
break;
|
||||||
}
|
case ATTR_DOMAIN_EDGE:
|
||||||
case ATTR_DOMAIN_EDGE: {
|
poly_mask = poly_selection_from_edge(
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_EDGE);
|
src_polys, src_mesh.corner_edges(), VArraySpan(selection), memory);
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totedge);
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
const VArraySpan<bool> edge_selection = evaluator.get_evaluated<bool>(0);
|
|
||||||
poly_selection = poly_selection_from_edge(
|
|
||||||
src_mesh.polys(), src_mesh.corner_edges(), edge_selection, memory);
|
|
||||||
break;
|
break;
|
||||||
}
|
case ATTR_DOMAIN_FACE:
|
||||||
case ATTR_DOMAIN_FACE: {
|
poly_mask = IndexMask::from_bools(evaluator.get_evaluated<bool>(0), memory);
|
||||||
const bke::MeshFieldContext context(src_mesh, ATTR_DOMAIN_FACE);
|
|
||||||
fn::FieldEvaluator evaluator(context, src_mesh.totpoly);
|
|
||||||
evaluator.add(selection);
|
|
||||||
evaluator.evaluate();
|
|
||||||
poly_selection = IndexMask::from_bools(evaluator.get_evaluated<bool>(0), memory);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
BLI_assert_unreachable();
|
BLI_assert_unreachable();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (poly_selection.is_empty()) {
|
if (poly_mask.is_empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (poly_selection.size() == src_mesh.totpoly) {
|
if (poly_mask.size() == src_mesh.totpoly) {
|
||||||
return BKE_mesh_copy_for_eval(&src_mesh);
|
return BKE_mesh_copy_for_eval(&src_mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_selection.size(), 0);
|
Mesh *dst_mesh = BKE_mesh_new_nomain(0, 0, poly_mask.size(), 0);
|
||||||
CustomData_free_layer_named(&dst_mesh->vdata, "position", 0);
|
CustomData_free_layer_named(&dst_mesh->vdata, "position", 0);
|
||||||
CustomData_free_layer_named(&dst_mesh->edata, ".edge_verts", 0);
|
CustomData_free_layer_named(&dst_mesh->edata, ".edge_verts", 0);
|
||||||
dst_mesh->totvert = src_mesh.totvert;
|
dst_mesh->totvert = src_mesh.totvert;
|
||||||
dst_mesh->totedge = src_mesh.totedge;
|
dst_mesh->totedge = src_mesh.totedge;
|
||||||
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
|
||||||
|
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
||||||
const OffsetIndices src_polys = src_mesh.polys();
|
|
||||||
|
|
||||||
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
const OffsetIndices<int> dst_polys = offset_indices::gather_selected_offsets(
|
||||||
src_polys, poly_selection, dst_mesh->poly_offsets_for_write());
|
src_polys, poly_mask, dst_mesh->poly_offsets_for_write());
|
||||||
dst_mesh->totloop = dst_polys.total_size();
|
dst_mesh->totloop = dst_polys.total_size();
|
||||||
|
|
||||||
const bke::AttributeAccessor src_attributes = src_mesh.attributes();
|
|
||||||
bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
|
|
||||||
|
|
||||||
bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
|
bke::copy_attributes(src_attributes, ATTR_DOMAIN_POINT, propagation_info, {}, dst_attributes);
|
||||||
bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes);
|
bke::copy_attributes(src_attributes, ATTR_DOMAIN_EDGE, propagation_info, {}, dst_attributes);
|
||||||
bke::gather_attributes(
|
bke::gather_attributes(
|
||||||
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_selection, dst_attributes);
|
src_attributes, ATTR_DOMAIN_FACE, propagation_info, {}, poly_mask, dst_attributes);
|
||||||
bke::gather_attributes_group_to_group(src_attributes,
|
bke::gather_attributes_group_to_group(src_attributes,
|
||||||
ATTR_DOMAIN_CORNER,
|
ATTR_DOMAIN_CORNER,
|
||||||
propagation_info,
|
propagation_info,
|
||||||
{},
|
{},
|
||||||
src_polys,
|
src_polys,
|
||||||
dst_polys,
|
dst_polys,
|
||||||
poly_selection,
|
poly_mask,
|
||||||
dst_attributes);
|
dst_attributes);
|
||||||
|
|
||||||
/* Positions are not changed by the operation, so the bounds are the same. */
|
/* Positions are not changed by the operation, so the bounds are the same. */
|
|
@ -17,193 +17,12 @@
|
||||||
#include "BKE_mesh.hh"
|
#include "BKE_mesh.hh"
|
||||||
#include "BKE_pointcloud.h"
|
#include "BKE_pointcloud.h"
|
||||||
|
|
||||||
|
#include "GEO_mesh_copy_selection.hh"
|
||||||
|
|
||||||
#include "node_geometry_util.hh"
|
#include "node_geometry_util.hh"
|
||||||
|
|
||||||
namespace blender::nodes::node_geo_delete_geometry_cc {
|
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. */
|
/** \return std::nullopt if the geometry should remain unchanged. */
|
||||||
static std::optional<Curves *> separate_curves_selection(
|
static std::optional<Curves *> separate_curves_selection(
|
||||||
const Curves &src_curves_id,
|
const Curves &src_curves_id,
|
||||||
|
@ -289,729 +108,23 @@ static void delete_selected_instances(GeometrySet &geometry_set,
|
||||||
instances.remove(selection, propagation_info);
|
instances.remove(selection, propagation_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
|
static Mesh *separate_mesh_selection(const Mesh &mesh,
|
||||||
MutableSpan<int> r_vertex_map,
|
const Field<bool> &selection,
|
||||||
int *r_selected_verts_num)
|
const eAttrDomain selection_domain,
|
||||||
|
const GeometryNodeDeleteGeometryMode mode,
|
||||||
|
const AnonymousAttributePropagationInfo &propagation_info)
|
||||||
{
|
{
|
||||||
BLI_assert(vertex_selection.size() == r_vertex_map.size());
|
|
||||||
|
|
||||||
int selected_verts_num = 0;
|
|
||||||
for (const int i : r_vertex_map.index_range()) {
|
|
||||||
if (vertex_selection[i]) {
|
|
||||||
r_vertex_map[i] = selected_verts_num;
|
|
||||||
selected_verts_num++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
r_vertex_map[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*r_selected_verts_num = selected_verts_num;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
|
|
||||||
const Span<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(
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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) {
|
switch (mode) {
|
||||||
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: {
|
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL:
|
||||||
Array<int> vertex_map(mesh_in.totvert);
|
return geometry::mesh_copy_selection(mesh, selection, selection_domain, propagation_info);
|
||||||
int selected_verts_num = 0;
|
case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE:
|
||||||
|
return geometry::mesh_copy_selection_keep_verts(
|
||||||
Array<int> edge_map(mesh_in.totedge);
|
mesh, selection, selection_domain, propagation_info);
|
||||||
int selected_edges_num = 0;
|
case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE:
|
||||||
|
return geometry::mesh_copy_selection_keep_edges(
|
||||||
switch (domain) {
|
mesh, selection, selection_domain, propagation_info);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::nodes::node_geo_delete_geometry_cc
|
} // namespace blender::nodes::node_geo_delete_geometry_cc
|
||||||
|
@ -1038,9 +151,9 @@ void separate_geometry(GeometrySet &geometry_set,
|
||||||
some_valid_domain = true;
|
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)) {
|
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) {
|
||||||
file_ns::separate_mesh_selection(geometry_set, selection, domain, mode, propagation_info);
|
file_ns::separate_mesh_selection(*mesh, selection, domain, mode, propagation_info);
|
||||||
some_valid_domain = true;
|
some_valid_domain = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
The
offset_indices::
is not necessary here.