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