Blender V4.3
mesh_split_edges.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 "BLI_array_utils.hh"
6#include "BLI_index_mask.hh"
7#include "BLI_ordered_edge.hh"
8
9#include "BKE_attribute.hh"
10#include "BKE_attribute_math.hh"
11#include "BKE_customdata.hh"
12#include "BKE_mesh.hh"
13#include "BKE_mesh_mapping.hh"
14
16#include "GEO_randomize.hh"
17
18namespace blender::geometry {
19
20static void propagate_vert_attributes(Mesh &mesh, const Span<int> new_to_old_verts_map)
21{
22 /* These types aren't supported for interpolation below. */
27 &mesh.vert_data, mesh.verts_num, mesh.verts_num + new_to_old_verts_map.size());
28 mesh.verts_num += new_to_old_verts_map.size();
29
30 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
31 for (const StringRef id : attributes.all_ids()) {
32 if (attributes.lookup_meta_data(id)->domain != bke::AttrDomain::Point) {
33 continue;
34 }
36 if (!attribute) {
37 continue;
38 }
39
41 new_to_old_verts_map,
42 attribute.span.take_back(new_to_old_verts_map.size()));
43
44 attribute.finish();
45 }
46 if (float3 *orco = static_cast<float3 *>(
48 {
50 new_to_old_verts_map,
51 MutableSpan(orco, mesh.verts_num).take_back(new_to_old_verts_map.size()));
52 }
53 if (int *orig_indices = static_cast<int *>(
55 {
57 Span(orig_indices, mesh.verts_num),
58 new_to_old_verts_map,
59 MutableSpan(orig_indices, mesh.verts_num).take_back(new_to_old_verts_map.size()));
60 }
61}
62
63static void propagate_edge_attributes(Mesh &mesh, const Span<int> new_to_old_edge_map)
64{
66 CustomData_realloc(&mesh.edge_data, mesh.edges_num, mesh.edges_num + new_to_old_edge_map.size());
67 mesh.edges_num += new_to_old_edge_map.size();
68
69 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
70 for (const StringRef id : attributes.all_ids()) {
71 if (attributes.lookup_meta_data(id)->domain != bke::AttrDomain::Edge) {
72 continue;
73 }
74 if (id == ".edge_verts") {
75 /* Edge vertices are updated and combined with new edges separately. */
76 continue;
77 }
79 if (!attribute) {
80 continue;
81 }
83 attribute.span, new_to_old_edge_map, attribute.span.take_back(new_to_old_edge_map.size()));
84 attribute.finish();
85 }
86
87 if (int *orig_indices = static_cast<int *>(
89 {
91 Span(orig_indices, mesh.edges_num),
92 new_to_old_edge_map,
93 MutableSpan(orig_indices, mesh.edges_num).take_back(new_to_old_edge_map.size()));
94 }
95}
96
99 const IndexMask &selected_edges,
100 const int verts_num,
101 IndexMaskMemory &memory)
102{
103 Array<bool> array(verts_num, false);
104 selected_edges.foreach_index_optimized<int>(GrainSize(4096), [&](const int i) {
105 array[edges[i][0]] = true;
106 array[edges[i][1]] = true;
107 });
108 return IndexMask::from_bools(array, memory);
109}
110
111static BitVector<> selection_to_bit_vector(const IndexMask &selection, const int total_size)
112{
113 BitVector<> bits(total_size);
114 selection.to_bits(bits);
115 return bits;
116}
117
125static int corner_on_edge_connected_to_vert(const Span<int> corner_verts,
126 const int corner,
127 const IndexRange face,
128 const int vert)
129{
130 if (corner_verts[corner] == vert) {
131 return corner;
132 }
133 const int other = bke::mesh::face_corner_next(face, corner);
134 BLI_assert(corner_verts[other] == vert);
135 return other;
136}
137
139
149 const Span<int> corner_verts,
150 const Span<int> corner_edges,
151 const GroupedSpan<int> edge_to_corner_map,
152 const Span<int> corner_to_face_map,
153 const BitSpan split_edges,
154 const Span<int> connected_corners,
155 const int vert)
156{
157 Vector<CornerGroup> groups;
158 /* Each corner should only be added to a single group. */
159 BitVector<> used_corners(connected_corners.size());
160 for (const int start_corner : connected_corners) {
161 CornerGroup group;
162 Vector<int> corner_stack({start_corner});
163 while (!corner_stack.is_empty()) {
164 const int corner = corner_stack.pop_last();
165 const int i = connected_corners.first_index(corner);
166 if (used_corners[i]) {
167 continue;
168 }
169 used_corners[i].set();
170 group.append(corner);
171 const int face = corner_to_face_map[corner];
172 const int prev_corner = bke::mesh::face_corner_prev(faces[face], corner);
173 /* Travel across the two edges neighboring this vertex, if they aren't split. */
174 for (const int edge : {corner_edges[corner], corner_edges[prev_corner]}) {
175 if (split_edges[edge]) {
176 continue;
177 }
178 for (const int other_corner : edge_to_corner_map[edge]) {
179 const int other_face = corner_to_face_map[other_corner];
180 if (other_face == face) {
181 /* Avoid continuing back to the same face. */
182 continue;
183 }
184 const int neighbor_corner = corner_on_edge_connected_to_vert(
185 corner_verts, other_corner, faces[other_face], vert);
186 corner_stack.append(neighbor_corner);
187 }
188 }
189 }
190 if (!group.is_empty()) {
191 groups.append(std::move(group));
192 }
193 }
194
195 return groups;
196}
197
198/* Calculate groups of corners that are contiguously connected to each input vertex.
199 * BLI_NOINLINE because MSVC 17.7 has a codegen bug here, given there is only a single call to this
200 * function, not inlining it for all platforms won't affect performance. See
201 * https://developercommunity.visualstudio.com/t/10448291 for details. */
204 const Span<int> corner_verts,
205 const Span<int> corner_edges,
206 const GroupedSpan<int> vert_to_corner_map,
207 const GroupedSpan<int> edge_to_corner_map,
208 const Span<int> corner_to_face_map,
209 const BitSpan split_edges,
210 const IndexMask &affected_verts)
211{
212 Array<Vector<CornerGroup>> corner_groups(affected_verts.size(), NoInitialization());
213 affected_verts.foreach_index(GrainSize(512), [&](const int vert, const int mask) {
214 new (&corner_groups[mask])
216 corner_verts,
217 corner_edges,
218 edge_to_corner_map,
219 corner_to_face_map,
221 vert_to_corner_map[vert],
222 vert));
223 });
224 return corner_groups;
225}
226
232
235 const BitSpan loose_edges,
236 const BitSpan split_edges,
237 const int vert)
238{
239 VertLooseEdges info;
240 for (const int edge : vert_to_edge_map[vert]) {
241 if (loose_edges[edge]) {
242 if (split_edges[edge]) {
243 info.selected.append(edge);
244 }
245 else {
246 info.unselected.append(edge);
247 }
248 }
249 }
250 return info;
251}
252
265 const IndexMask &affected_verts,
266 const Span<Vector<CornerGroup>> corner_groups,
267 const GroupedSpan<int> vert_to_edge_map,
268 const BitSpan loose_edges,
269 const BitSpan split_edges,
270 Array<int> &offset_data)
271{
272 offset_data.reinitialize(affected_verts.size() + 1);
273 MutableSpan<int> new_verts_nums = offset_data;
274 threading::parallel_for(affected_verts.index_range(), 2048, [&](const IndexRange range) {
275 /* Start with -1 for the reused vertex. None of the final sizes should be negative. */
276 new_verts_nums.slice(range).fill(-1);
277 for (const int i : range) {
278 new_verts_nums[i] += corner_groups[i].size();
279 }
280 });
281 if (!loose_edges.is_empty()) {
282 affected_verts.foreach_index(GrainSize(512), [&](const int vert, const int mask) {
283 const VertLooseEdges info = calc_vert_loose_edges(
284 vert_to_edge_map, loose_edges, split_edges, vert);
285 new_verts_nums[mask] += info.selected.size();
286 if (corner_groups[mask].is_empty()) {
287 /* Loose edges share their vertex with a corner group if possible. */
288 new_verts_nums[mask] += info.unselected.size() > 0;
289 }
290 });
291 }
293}
294
300static void update_corner_verts(const int orig_verts_num,
301 const Span<Vector<CornerGroup>> corner_groups,
302 const OffsetIndices<int> new_verts_by_affected_vert,
303 MutableSpan<int> new_corner_verts)
304{
305 threading::parallel_for(corner_groups.index_range(), 512, [&](const IndexRange range) {
306 for (const int new_vert : range) {
307 const Span<CornerGroup> groups = corner_groups[new_vert];
308 const IndexRange new_verts = new_verts_by_affected_vert[new_vert];
309 for (const int group : groups.index_range().drop_back(1)) {
310 const int new_vert = orig_verts_num + new_verts[group];
311 new_corner_verts.fill_indices(groups[group].as_span(), new_vert);
312 }
313 }
314 });
315}
316
318 const Span<int> corner_verts,
319 const Span<int> corner_to_face_map,
320 const int corner)
321{
322 const int face = corner_to_face_map[corner];
323 const int corner_next = bke::mesh::face_corner_next(faces[face], corner);
324 return OrderedEdge(corner_verts[corner], corner_verts[corner_next]);
325}
326
335 const Span<int> corner_verts,
336 const GroupedSpan<int> edge_to_corner_map,
337 const Span<int> corner_to_face_map,
338 const IndexMask &selected_edges,
339 MutableSpan<int2> edges,
340 MutableSpan<int> corner_edges,
341 MutableSpan<int> r_new_edge_offsets)
342{
343 /* Calculate the offset of new edges assuming no new edges are identical and are merged. */
344 selected_edges.foreach_index_optimized<int>(
345 GrainSize(4096), [&](const int edge, const int mask) {
346 r_new_edge_offsets[mask] = std::max<int>(edge_to_corner_map[edge].size() - 1, 0);
347 });
348 const OffsetIndices offsets = offset_indices::accumulate_counts_to_offsets(r_new_edge_offsets);
349
350 Array<int2> new_edges(offsets.total_size());
351
352 /* Count the number of final new edges per edge, to use as offsets if there are duplicates. */
353 Array<int> num_edges_per_edge_merged(r_new_edge_offsets.size());
354 std::atomic<bool> found_duplicate = false;
355
356 /* The first new edge for each selected edge is reused-- we modify the existing edge in
357 * place. Simply reusing the first new edge isn't enough because deduplication might make
358 * multiple new edges reuse the original. */
359 Array<bool> is_reused(corner_verts.size(), false);
360
361 /* Calculate per-original split edge deduplication of new edges, which are stored by the
362 * corner vertices of connected faces. Update corner verts to store the updated indices. */
363 selected_edges.foreach_index(GrainSize(1024), [&](const int edge, const int mask) {
364 if (edge_to_corner_map[edge].is_empty()) {
365 /* Handle loose edges. */
366 num_edges_per_edge_merged[mask] = 0;
367 return;
368 }
369
370 const int new_edges_start = offsets[mask].start();
371 Vector<OrderedEdge> deduplication;
372 for (const int corner : edge_to_corner_map[edge]) {
373 const OrderedEdge edge = edge_from_corner(faces, corner_verts, corner_to_face_map, corner);
374 int index = deduplication.first_index_of_try(edge);
375 if (UNLIKELY(index != -1)) {
376 found_duplicate.store(true, std::memory_order_relaxed);
377 }
378 else {
379 index = deduplication.append_and_get_index(edge);
380 }
381
382 if (index == 0) {
383 is_reused[corner] = true;
384 }
385 else {
386 corner_edges[corner] = edges.size() + new_edges_start + index - 1;
387 }
388 }
389
390 const int new_edges_num = deduplication.size() - 1;
391
392 edges[edge] = int2(deduplication.first().v_low, deduplication.first().v_high);
393 new_edges.as_mutable_span()
394 .slice(new_edges_start, new_edges_num)
395 .copy_from(deduplication.as_span().drop_front(1).cast<int2>());
396
397 num_edges_per_edge_merged[mask] = new_edges_num;
398 });
399
400 if (!found_duplicate) {
401 /* No edges were merged, we can use the existing output array and offsets. */
402 return new_edges;
403 }
404
405 /* Update corner edges to remove the "holes" left by merged new edges. */
407 num_edges_per_edge_merged);
408 selected_edges.foreach_index(GrainSize(2048), [&](const int edge, const int mask) {
409 const int difference = offsets[mask].start() - offsets_merged[mask].start();
410 for (const int corner : edge_to_corner_map[edge]) {
411 if (!is_reused[corner]) {
412 corner_edges[corner] -= difference;
413 }
414 }
415 });
416
417 /* Create new edges without the empty slots for the duplicates */
418 Array<int2> new_edges_merged(offsets_merged.total_size());
419 threading::parallel_for(offsets_merged.index_range(), 1024, [&](const IndexRange range) {
420 for (const int i : range) {
421 new_edges_merged.as_mutable_span()
422 .slice(offsets_merged[i])
423 .copy_from(new_edges.as_span().slice(offsets[i].start(), offsets_merged[i].size()));
424 }
425 });
426
427 r_new_edge_offsets.copy_from(num_edges_per_edge_merged);
428 return new_edges_merged;
429}
430
432 const Span<int> corner_verts,
433 const GroupedSpan<int> edge_to_corner_map,
434 const Span<int> corner_to_face_map,
435 const IndexMask &unselected_edges,
436 MutableSpan<int2> edges)
437{
438 unselected_edges.foreach_index(GrainSize(1024), [&](const int edge) {
439 const Span<int> edge_corners = edge_to_corner_map[edge];
440 if (edge_corners.is_empty()) {
441 return;
442 }
443 const int corner = edge_corners.first();
444 const OrderedEdge new_edge = edge_from_corner(faces, corner_verts, corner_to_face_map, corner);
445 edges[edge] = int2(new_edge.v_low, new_edge.v_high);
446 });
447}
448
449static void swap_edge_vert(int2 &edge, const int old_vert, const int new_vert)
450{
451 if (edge[0] == old_vert) {
452 edge[0] = new_vert;
453 }
454 else if (edge[1] == old_vert) {
455 edge[1] = new_vert;
456 }
457}
458
464static void reassign_loose_edge_verts(const int orig_verts_num,
465 const IndexMask &affected_verts,
466 const GroupedSpan<int> vert_to_edge_map,
467 const BitSpan loose_edges,
468 const BitSpan split_edges,
469 const Span<Vector<CornerGroup>> corner_groups,
470 const OffsetIndices<int> new_verts_by_affected_vert,
471 MutableSpan<int2> edges)
472{
473 affected_verts.foreach_index(GrainSize(1024), [&](const int vert, const int mask) {
474 const IndexRange new_verts = new_verts_by_affected_vert[mask];
475 /* Account for the reuse of the original vertex by non-loose corner groups. In practice this
476 * means using the new vertices for each split loose edge until we run out of new vertices.
477 * We then expect the count to match up with the number of new vertices reserved by
478 * #calc_vert_ranges_per_old_vert. */
479 int new_vert_i = std::max<int>(corner_groups[mask].size() - 1, 0);
480 if (new_vert_i == new_verts.size()) {
481 return;
482 }
483 const VertLooseEdges vert_info = calc_vert_loose_edges(
484 vert_to_edge_map, loose_edges, split_edges, vert);
485 for (const int edge : vert_info.selected) {
486 const int new_vert = orig_verts_num + new_verts[new_vert_i];
487 swap_edge_vert(edges[edge], vert, new_vert);
488 new_vert_i++;
489 if (new_vert_i == new_verts.size()) {
490 return;
491 }
492 }
493 const int new_vert = orig_verts_num + new_verts[new_vert_i];
494 for (const int orig_edge : vert_info.unselected) {
495 swap_edge_vert(edges[orig_edge], vert, new_vert);
496 }
497 });
498}
499
505{
506 Array<int> map(offsets.total_size());
507 mask.foreach_index(GrainSize(1024), [&](const int i, const int mask) {
508 map.as_mutable_span().slice(offsets[mask]).fill(i);
509 });
510 return map;
511}
512
513void split_edges(Mesh &mesh,
514 const IndexMask &selected_edges,
515 const bke::AttributeFilter & /*attribute_filter*/)
516{
517 const int orig_verts_num = mesh.verts_num;
518 const Span<int2> orig_edges = mesh.edges();
519 const OffsetIndices faces = mesh.faces();
520
521 IndexMaskMemory memory;
522 const IndexMask affected_verts = vert_selection_from_edge(
523 orig_edges, selected_edges, orig_verts_num, memory);
524 const BitVector<> selection_bits = selection_to_bit_vector(selected_edges, orig_edges.size());
525 const bke::LooseEdgeCache &loose_edges = mesh.loose_edges();
526
527 const GroupedSpan<int> vert_to_corner_map = mesh.vert_to_corner_map();
528
529 Array<int> edge_to_corner_offsets;
530 Array<int> edge_to_corner_indices;
531 const GroupedSpan<int> edge_to_corner_map = bke::mesh::build_edge_to_corner_map(
532 mesh.corner_edges(), orig_edges.size(), edge_to_corner_offsets, edge_to_corner_indices);
533
534 Array<int> vert_to_edge_offsets;
535 Array<int> vert_to_edge_indices;
536 GroupedSpan<int> vert_to_edge_map;
537 if (loose_edges.count > 0) {
538 vert_to_edge_map = bke::mesh::build_vert_to_edge_map(
539 orig_edges, orig_verts_num, vert_to_edge_offsets, vert_to_edge_indices);
540 }
541
542 const Array<int> corner_to_face_map = mesh.corner_to_face_map();
543
545 mesh.corner_verts(),
546 mesh.corner_edges(),
547 vert_to_corner_map,
548 edge_to_corner_map,
549 corner_to_face_map,
550 selection_bits,
551 affected_verts);
552
553 Array<int> vert_new_vert_offset_data;
554 const OffsetIndices new_verts_by_affected_vert = calc_vert_ranges_per_old_vert(
555 affected_verts,
556 corner_groups,
557 vert_to_edge_map,
558 loose_edges.is_loose_bits,
559 selection_bits,
560 vert_new_vert_offset_data);
561
562 MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
563 update_corner_verts(orig_verts_num, corner_groups, new_verts_by_affected_vert, corner_verts);
564
565 Array<int> new_edge_offsets(selected_edges.size() + 1);
566 Array<int2> new_edges = calc_new_edges(faces,
567 corner_verts,
568 edge_to_corner_map,
569 corner_to_face_map,
570 selected_edges,
571 mesh.edges_for_write(),
572 mesh.corner_edges_for_write(),
573 new_edge_offsets);
574 const IndexMask unselected_edges = selected_edges.complement(orig_edges.index_range(), memory);
576 corner_verts,
577 edge_to_corner_map,
578 corner_to_face_map,
579 unselected_edges,
580 mesh.edges_for_write());
581
582 if (loose_edges.count > 0) {
583 reassign_loose_edge_verts(orig_verts_num,
584 affected_verts,
585 vert_to_edge_map,
586 loose_edges.is_loose_bits,
587 selection_bits,
588 corner_groups,
589 new_verts_by_affected_vert,
590 mesh.edges_for_write());
591 }
592
593 const Array<int> edge_map = offsets_to_map(selected_edges, new_edge_offsets.as_span());
594 propagate_edge_attributes(mesh, edge_map);
595 mesh.edges_for_write().take_back(new_edges.size()).copy_from(new_edges);
596
597 const Array<int> vert_map = offsets_to_map(affected_verts, new_verts_by_affected_vert);
598 propagate_vert_attributes(mesh, vert_map);
599
600 mesh.tag_edges_split();
601
604}
605
606} // namespace blender::geometry
CustomData interface, see also DNA_customdata_types.h.
void CustomData_realloc(CustomData *data, int old_size, int new_size, eCDAllocType alloctype=CD_CONSTRUCT)
void CustomData_free_layers(CustomData *data, eCustomDataType type, int totelem)
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_NOINLINE
#define UNLIKELY(x)
@ CD_MVERT_SKIN
@ CD_FREESTYLE_EDGE
@ CD_CLOTH_ORCO
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:245
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan take_back(const int64_t n) const
Definition BLI_span.hh:641
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:378
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
int64_t append_and_get_index(const T &value)
void append(const T &value)
bool is_empty() const
Span< T > as_span() const
const T & first() const
int64_t first_index_of_try(const T &value) const
Set< StringRefNull > all_ids() const
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
void foreach_index_optimized(Fn &&fn) const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
ccl_device_inline float4 mask(const int4 mask, const float4 a)
static char faces[256]
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
GroupedSpan< int > build_edge_to_corner_map(Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
int face_corner_prev(const IndexRange face, const int corner)
Definition BKE_mesh.hh:243
int face_corner_next(const IndexRange face, const int corner)
Definition BKE_mesh.hh:252
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
void debug_randomize_edge_order(Mesh *mesh)
Definition randomize.cc:108
static Array< int > offsets_to_map(const IndexMask &mask, const OffsetIndices< int > offsets)
static BitVector selection_to_bit_vector(const IndexMask &selection, const int total_size)
void split_edges(Mesh &mesh, const IndexMask &mask, const bke::AttributeFilter &attribute_filter={})
static Vector< CornerGroup > calc_corner_groups_for_vertex(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const BitSpan split_edges, const Span< int > connected_corners, const int vert)
static void reassign_loose_edge_verts(const int orig_verts_num, const IndexMask &affected_verts, const GroupedSpan< int > vert_to_edge_map, const BitSpan loose_edges, const BitSpan split_edges, const Span< Vector< CornerGroup > > corner_groups, const OffsetIndices< int > new_verts_by_affected_vert, MutableSpan< int2 > edges)
static Array< int2 > calc_new_edges(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const IndexMask &selected_edges, MutableSpan< int2 > edges, MutableSpan< int > corner_edges, MutableSpan< int > r_new_edge_offsets)
static void propagate_vert_attributes(Mesh &mesh, const Span< int > new_to_old_verts_map)
Vector< int > CornerGroup
static OrderedEdge edge_from_corner(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_to_face_map, const int corner)
static BLI_NOINLINE Array< Vector< CornerGroup > > calc_all_corner_groups(const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const GroupedSpan< int > vert_to_corner_map, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const BitSpan split_edges, const IndexMask &affected_verts)
static OffsetIndices< int > calc_vert_ranges_per_old_vert(const IndexMask &affected_verts, const Span< Vector< CornerGroup > > corner_groups, const GroupedSpan< int > vert_to_edge_map, const BitSpan loose_edges, const BitSpan split_edges, Array< int > &offset_data)
void debug_randomize_vert_order(Mesh *mesh)
Definition randomize.cc:87
static VertLooseEdges calc_vert_loose_edges(const GroupedSpan< int > vert_to_edge_map, const BitSpan loose_edges, const BitSpan split_edges, const int vert)
static void swap_edge_vert(int2 &edge, const int old_vert, const int new_vert)
static int corner_on_edge_connected_to_vert(const Span< int > corner_verts, const int corner, const IndexRange face, const int vert)
static void update_unselected_edges(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > edge_to_corner_map, const Span< int > corner_to_face_map, const IndexMask &unselected_edges, MutableSpan< int2 > edges)
static void propagate_edge_attributes(Mesh &mesh, const Span< int > new_to_old_edge_map)
IndexMask vert_selection_from_edge(Span< int2 > edges, const IndexMask &edge_mask, int verts_num, IndexMaskMemory &memory)
static void update_corner_verts(const int orig_verts_num, const Span< Vector< CornerGroup > > corner_groups, const OffsetIndices< int > new_verts_by_affected_vert, MutableSpan< int > new_corner_verts)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
VecBase< int32_t, 2 > int2
VecBase< float, 3 > float3
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
CustomData edge_data
int edges_num
CustomData vert_data
int verts_num
blender::BitVector is_loose_bits