Blender V5.0
mesh_copy_selection.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "DNA_object_types.h"
6
8#include "BLI_index_mask.hh"
9#include "BLI_listbase.h"
10
11#include "BKE_attribute.hh"
12#include "BKE_deform.hh"
13#include "BKE_mesh.hh"
14
16#include "GEO_mesh_selection.hh"
17
18namespace blender::geometry {
19
20static void remap_verts(const OffsetIndices<int> src_faces,
21 const OffsetIndices<int> dst_faces,
22 const int src_verts_num,
23 const IndexMask &vert_mask,
24 const IndexMask &edge_mask,
25 const IndexMask &face_mask,
26 const Span<int2> src_edges,
27 const Span<int> src_corner_verts,
28 MutableSpan<int2> dst_edges,
29 MutableSpan<int> dst_corner_verts)
30{
31 Array<int> map(src_verts_num);
34 vert_mask.size() > 1024,
35 [&]() {
36 face_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
37 const IndexRange src_face = src_faces[src_i];
38 const IndexRange dst_face = dst_faces[dst_i];
39 for (const int i : src_face.index_range()) {
40 dst_corner_verts[dst_face[i]] = map[src_corner_verts[src_face[i]]];
41 }
42 });
43 },
44 [&]() {
45 edge_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
46 dst_edges[dst_i][0] = map[src_edges[src_i][0]];
47 dst_edges[dst_i][1] = map[src_edges[src_i][1]];
48 });
49 });
50}
51
52static void remap_edges(const OffsetIndices<int> src_faces,
53 const OffsetIndices<int> dst_faces,
54 const int src_edges_num,
55 const IndexMask &edge_mask,
56 const IndexMask &face_mask,
57 const Span<int> src_corner_edges,
58 MutableSpan<int> dst_corner_edges)
59{
60 Array<int> map(src_edges_num);
62 face_mask.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
63 const IndexRange src_face = src_faces[src_i];
64 const IndexRange dst_face = dst_faces[dst_i];
65 for (const int i : src_face.index_range()) {
66 dst_corner_edges[dst_face[i]] = map[src_corner_edges[src_face[i]]];
67 }
68 });
69}
70
71static void copy_loose_vert_hint(const Mesh &src, Mesh &dst)
72{
73 const auto &src_cache = src.runtime->loose_verts_cache;
74 if (src_cache.is_cached() && src_cache.data().count == 0) {
75 dst.tag_loose_verts_none();
76 }
77}
78
79static void copy_loose_edge_hint(const Mesh &src, Mesh &dst)
80{
81 const auto &src_cache = src.runtime->loose_edges_cache;
82 if (src_cache.is_cached() && src_cache.data().count == 0) {
83 dst.tag_loose_edges_none();
84 }
85}
86
87static void copy_overlapping_hint(const Mesh &src, Mesh &dst)
88{
89 if (src.no_overlapping_topology()) {
90 dst.tag_overlapping_none();
91 }
92}
93
95static void gather_vert_attributes(const Mesh &mesh_src,
96 const bke::AttributeFilter &attribute_filter,
97 const IndexMask &vert_mask,
98 Mesh &mesh_dst)
99{
100 Set<std::string> vertex_group_names;
101 LISTBASE_FOREACH (bDeformGroup *, group, &mesh_src.vertex_group_names) {
102 vertex_group_names.add(group->name);
103 }
104
105 const Span<MDeformVert> src = mesh_src.deform_verts();
106 if (!vertex_group_names.is_empty() && !src.is_empty()) {
107 MutableSpan<MDeformVert> dst = mesh_dst.deform_verts_for_write();
108 bke::gather_deform_verts(src, vert_mask, dst);
109 }
110
114 bke::attribute_filter_with_skip_ref(attribute_filter, vertex_group_names),
115 vert_mask,
116 mesh_dst.attributes_for_write());
117}
118
119std::optional<Mesh *> mesh_copy_selection(const Mesh &src_mesh,
120 const VArray<bool> &selection,
121 const bke::AttrDomain selection_domain,
122 const bke::AttributeFilter &attribute_filter)
123{
124 const Span<int2> src_edges = src_mesh.edges();
125 const OffsetIndices src_faces = src_mesh.faces();
126 const Span<int> src_corner_verts = src_mesh.corner_verts();
127 const Span<int> src_corner_edges = src_mesh.corner_edges();
128 const bke::AttributeAccessor src_attributes = src_mesh.attributes();
129
130 if (selection.is_empty()) {
131 return std::nullopt;
132 }
133 if (const std::optional<bool> single = selection.get_if_single()) {
134 return *single ? std::nullopt : std::make_optional<Mesh *>(nullptr);
135 }
136
138 IndexMask vert_mask;
139 IndexMask edge_mask;
140 IndexMask face_mask;
141 switch (selection_domain) {
143 const VArraySpan<bool> span(selection);
145 src_mesh.verts_num > 1024,
146 [&]() { vert_mask = IndexMask::from_bools(span, memory.local()); },
147 [&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
148 [&]() {
149 face_mask = face_selection_from_vert(
150 src_faces, src_corner_verts, span, memory.local());
151 });
152 break;
153 }
155 const VArraySpan<bool> span(selection);
157 src_edges.size() > 1024,
158 [&]() {
159 edge_mask = IndexMask::from_bools(span, memory.local());
160 vert_mask = vert_selection_from_edge(
161 src_edges, edge_mask, src_mesh.verts_num, memory.local());
162 },
163 [&]() {
164 face_mask = face_selection_from_edge(
165 src_faces, src_corner_edges, span, memory.local());
166 });
167 break;
168 }
170 const VArraySpan<bool> span(selection);
171 face_mask = IndexMask::from_bools(span, memory.local());
173 face_mask.size() > 1024,
174 [&]() {
175 vert_mask = vert_selection_from_face(
176 src_faces, face_mask, src_corner_verts, src_mesh.verts_num, memory.local());
177 },
178 [&]() {
179 edge_mask = edge_selection_from_face(
180 src_faces, face_mask, src_corner_edges, src_mesh.edges_num, memory.local());
181 });
182 break;
183 }
184 default:
186 break;
187 }
188
189 if (vert_mask.is_empty()) {
190 return nullptr;
191 }
192 const bool same_verts = vert_mask.size() == src_mesh.verts_num;
193 const bool same_edges = edge_mask.size() == src_mesh.edges_num;
194 const bool same_faces = face_mask.size() == src_mesh.faces_num;
195 if (same_verts && same_edges && same_faces) {
196 return std::nullopt;
197 }
198
200 vert_mask.size(), edge_mask.size(), face_mask.size(), 0);
201 BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
202 bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
203 dst_attributes.add<int2>(".edge_verts", bke::AttrDomain::Edge, bke::AttributeInitConstruct());
204 MutableSpan<int2> dst_edges = dst_mesh->edges_for_write();
205
207 src_faces, face_mask, dst_mesh->face_offsets_for_write());
208 dst_mesh->corners_num = dst_faces.total_size();
209 dst_attributes.add<int>(".corner_vert", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
210 dst_attributes.add<int>(".corner_edge", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
211 MutableSpan<int> dst_corner_verts = dst_mesh->corner_verts_for_write();
212 MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
213
215 vert_mask.size() > 1024,
216 [&]() {
217 remap_verts(src_faces,
218 dst_faces,
219 src_mesh.verts_num,
220 vert_mask,
221 edge_mask,
222 face_mask,
223 src_edges,
224 src_corner_verts,
225 dst_edges,
226 dst_corner_verts);
227 },
228 [&]() {
229 remap_edges(src_faces,
230 dst_faces,
231 src_edges.size(),
232 edge_mask,
233 face_mask,
234 src_corner_edges,
235 dst_corner_edges);
236 },
237 [&]() {
238 gather_vert_attributes(src_mesh, attribute_filter, vert_mask, *dst_mesh);
239 bke::gather_attributes(
240 src_attributes,
241 bke::AttrDomain::Edge,
242 bke::AttrDomain::Edge,
243 bke::attribute_filter_with_skip_ref(attribute_filter, {".edge_verts"}),
244 edge_mask,
245 dst_attributes);
246 bke::gather_attributes(src_attributes,
249 attribute_filter,
250 face_mask,
251 dst_attributes);
253 src_attributes,
257 {".corner_edge", ".corner_vert"}),
258 src_faces,
259 dst_faces,
260 face_mask,
261 dst_attributes);
262 });
263
264 if (selection_domain == bke::AttrDomain::Edge) {
265 copy_loose_vert_hint(src_mesh, *dst_mesh);
266 }
267 else if (selection_domain == bke::AttrDomain::Face) {
268 copy_loose_vert_hint(src_mesh, *dst_mesh);
269 copy_loose_edge_hint(src_mesh, *dst_mesh);
270 }
271 copy_overlapping_hint(src_mesh, *dst_mesh);
272
273 return dst_mesh;
274}
275
276std::optional<Mesh *> mesh_copy_selection_keep_verts(const Mesh &src_mesh,
277 const VArray<bool> &selection,
278 const bke::AttrDomain selection_domain,
279 const bke::AttributeFilter &attribute_filter)
280{
281 const Span<int2> src_edges = src_mesh.edges();
282 const OffsetIndices src_faces = src_mesh.faces();
283 const Span<int> src_corner_verts = src_mesh.corner_verts();
284 const Span<int> src_corner_edges = src_mesh.corner_edges();
285 const bke::AttributeAccessor src_attributes = src_mesh.attributes();
286
287 if (selection.is_empty()) {
288 return std::nullopt;
289 }
290
292 IndexMask edge_mask;
293 IndexMask face_mask;
294 switch (selection_domain) {
296 const VArraySpan<bool> span(selection);
298 src_edges.size() > 1024,
299 [&]() { edge_mask = edge_selection_from_vert(src_edges, span, memory.local()); },
300 [&]() {
301 face_mask = face_selection_from_vert(
302 src_faces, src_corner_verts, span, memory.local());
303 });
304 break;
305 }
307 const VArraySpan<bool> span(selection);
309 src_edges.size() > 1024,
310 [&]() { edge_mask = IndexMask::from_bools(span, memory.local()); },
311 [&]() {
312 face_mask = face_selection_from_edge(
313 src_faces, src_corner_edges, span, memory.local());
314 });
315 break;
316 }
318 const VArraySpan<bool> span(selection);
319 face_mask = IndexMask::from_bools(span, memory.local());
320 edge_mask = edge_selection_from_face(
321 src_faces, face_mask, src_corner_edges, src_edges.size(), memory.local());
322 break;
323 }
324 default:
326 break;
327 }
328
329 const bool same_edges = edge_mask.size() == src_mesh.edges_num;
330 const bool same_faces = face_mask.size() == src_mesh.faces_num;
331 if (same_edges && same_faces) {
332 return std::nullopt;
333 }
334
336 src_mesh.verts_num, edge_mask.size(), face_mask.size(), 0);
337 BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
338 bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
339
341 src_faces, face_mask, dst_mesh->face_offsets_for_write());
342 dst_mesh->corners_num = dst_faces.total_size();
343 dst_attributes.add<int>(".corner_edge", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
344 MutableSpan<int> dst_corner_edges = dst_mesh->corner_edges_for_write();
345
347 [&]() {
348 remap_edges(src_faces,
349 dst_faces,
350 src_edges.size(),
351 edge_mask,
352 face_mask,
353 src_corner_edges,
354 dst_corner_edges);
355 },
356 [&]() {
357 bke::copy_attributes(src_attributes,
360 attribute_filter,
361 dst_attributes);
362 bke::gather_attributes(src_attributes,
365 attribute_filter,
366 edge_mask,
367 dst_attributes);
368 bke::gather_attributes(src_attributes,
371 attribute_filter,
372 face_mask,
373 dst_attributes);
375 src_attributes,
378 bke::attribute_filter_with_skip_ref(attribute_filter, {".corner_edge"}),
379 src_faces,
380 dst_faces,
381 face_mask,
382 dst_attributes);
383 });
384
385 /* Positions are not changed by the operation, so the bounds are the same. */
386 dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
387 if (selection_domain == bke::AttrDomain::Face) {
388 copy_loose_edge_hint(src_mesh, *dst_mesh);
389 }
390 copy_overlapping_hint(src_mesh, *dst_mesh);
391
392 return dst_mesh;
393}
394
395std::optional<Mesh *> mesh_copy_selection_keep_edges(const Mesh &src_mesh,
396 const VArray<bool> &selection,
397 const bke::AttrDomain selection_domain,
398 const bke::AttributeFilter &attribute_filter)
399{
400 const OffsetIndices src_faces = src_mesh.faces();
401 const bke::AttributeAccessor src_attributes = src_mesh.attributes();
402
403 if (selection.is_empty()) {
404 return std::nullopt;
405 }
406
407 IndexMaskMemory memory;
408 IndexMask face_mask;
409 switch (selection_domain) {
411 face_mask = face_selection_from_vert(
412 src_faces, src_mesh.corner_verts(), VArraySpan(selection), memory);
413 break;
415 face_mask = face_selection_from_edge(
416 src_faces, src_mesh.corner_edges(), VArraySpan(selection), memory);
417 break;
419 face_mask = IndexMask::from_bools(selection, memory);
420 break;
421 default:
423 break;
424 }
425
426 const bool same_faces = face_mask.size() == src_mesh.faces_num;
427 if (same_faces) {
428 return std::nullopt;
429 }
430
432 src_mesh.verts_num, src_mesh.edges_num, face_mask.size(), 0);
433 BKE_mesh_copy_parameters_for_eval(dst_mesh, &src_mesh);
434 bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
435
437 src_faces, face_mask, dst_mesh->face_offsets_for_write());
438 dst_mesh->corners_num = dst_faces.total_size();
439 dst_attributes.add<int>(".corner_vert", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
440 dst_attributes.add<int>(".corner_edge", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
441
442 bke::copy_attributes(src_attributes,
445 attribute_filter,
446 dst_attributes);
447 bke::copy_attributes(src_attributes,
450 attribute_filter,
451 dst_attributes);
452 bke::gather_attributes(src_attributes,
455 attribute_filter,
456 face_mask,
457 dst_attributes);
461 attribute_filter,
462 src_faces,
463 dst_faces,
464 face_mask,
465 dst_attributes);
466
467 /* Positions are not changed by the operation, so the bounds are the same. */
468 dst_mesh->runtime->bounds_cache = src_mesh.runtime->bounds_cache;
469 copy_loose_vert_hint(src_mesh, *dst_mesh);
470 copy_overlapping_hint(src_mesh, *dst_mesh);
471 return dst_mesh;
472}
473
474} // namespace blender::geometry
support for deformation groups and hooks.
void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define LISTBASE_FOREACH(type, var, list)
Object is a sort of wrapper for general info.
long long int int64_t
AttributeSet attributes
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr IndexRange index_range() const
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:595
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
std::optional< T > get_if_single() const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
void foreach_index(Fn &&fn) const
static void copy_loose_edge_hint(const Mesh &src, Mesh &dst)
static void copy_overlapping_hint(const Mesh &src, Mesh &dst)
static void copy_loose_vert_hint(const Mesh &src, Mesh &dst)
void copy_attributes(const AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, MutableAttributeAccessor dst_attributes)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
Mesh * mesh_new_no_attributes(int verts_num, int edges_num, int faces_num, int corners_num)
void gather_deform_verts(Span< MDeformVert > src, Span< int > indices, MutableSpan< MDeformVert > dst)
Definition deform.cc:1768
void gather_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static void gather_vert_attributes(const Mesh &mesh_src, const bke::AttributeFilter &attribute_filter, const IndexMask &vert_mask, Mesh &mesh_dst)
IndexMask edge_selection_from_face(OffsetIndices< int > faces, const IndexMask &face_mask, Span< int > corner_edges, int edges_num, IndexMaskMemory &memory)
std::optional< Mesh * > mesh_copy_selection_keep_edges(const Mesh &mesh, const VArray< bool > &selection, bke::AttrDomain selection_domain, const bke::AttributeFilter &attribute_filter={})
static void remap_verts(const OffsetIndices< int > src_faces, const OffsetIndices< int > dst_faces, const int src_verts_num, const IndexMask &vert_mask, const IndexMask &edge_mask, const IndexMask &face_mask, const Span< int2 > src_edges, const Span< int > src_corner_verts, MutableSpan< int2 > dst_edges, MutableSpan< int > dst_corner_verts)
static void remap_edges(const OffsetIndices< int > src_faces, const OffsetIndices< int > dst_faces, const int src_edges_num, const IndexMask &edge_mask, const IndexMask &face_mask, const Span< int > src_corner_edges, MutableSpan< int > dst_corner_edges)
std::optional< Mesh * > mesh_copy_selection_keep_verts(const Mesh &src_mesh, const VArray< bool > &selection, bke::AttrDomain selection_domain, const bke::AttributeFilter &attribute_filter={})
IndexMask face_selection_from_edge(OffsetIndices< int > faces, Span< int > corner_edges, Span< bool > edge_mask, IndexMaskMemory &memory)
IndexMask face_selection_from_vert(OffsetIndices< int > faces, Span< int > corner_verts, Span< bool > vert_selection, IndexMaskMemory &memory)
std::optional< Mesh * > mesh_copy_selection(const Mesh &src_mesh, const VArray< bool > &selection, bke::AttrDomain selection_domain, const bke::AttributeFilter &attribute_filter={})
void build_reverse_map(const IndexMask &mask, MutableSpan< T > r_map)
Definition index_mask.cc:34
template void build_reverse_map< int >(const IndexMask &mask, MutableSpan< int > r_map)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:221
VecBase< int32_t, 2 > int2
int corners_num
int edges_num
MeshRuntimeHandle * runtime
ListBase vertex_group_names
int faces_num
int verts_num
i
Definition text_draw.cc:230