Blender V4.5
mesh_mapping.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
11
12#include <algorithm>
13
14#include "MEM_guardedalloc.h"
15
16#include "atomic_ops.h"
17
18#include "BLI_array.hh"
19#include "BLI_bitmap.h"
20#include "BLI_buffer.h"
21#include "BLI_function_ref.hh"
22#include "BLI_math_geom.h"
23#include "BLI_math_vector.h"
24#include "BLI_task.hh"
25#include "BLI_utildefines.h"
26
27#include "BKE_customdata.hh"
28#include "BKE_mesh.hh"
29#include "BKE_mesh_mapping.hh"
30#include "BLI_memarena.h"
31
32#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
33
34/* -------------------------------------------------------------------- */
37
39 const bool *hide_poly,
40 const bool *select_poly,
41 const int *corner_verts,
42 const float (*mloopuv)[2],
43 uint totvert,
44 const float limit[2],
45 const bool selected,
46 const bool use_winding)
47{
48 /* NOTE: N-gon version WIP, based on #BM_uv_vert_map_create. */
49
50 UvVertMap *vmap;
51 UvMapVert *buf;
52 int i, totuv, nverts;
53
55
56 totuv = 0;
57
58 /* generate UvMapVert array */
59 for (const int64_t a : faces.index_range()) {
60 if (!selected || (!(hide_poly && hide_poly[a]) && (select_poly && select_poly[a]))) {
61 totuv += int(faces[a].size());
62 }
63 }
64
65 if (totuv == 0) {
66 return nullptr;
67 }
68
69 vmap = MEM_callocN<UvVertMap>("UvVertMap");
70 buf = vmap->buf = MEM_calloc_arrayN<UvMapVert>(size_t(totuv), "UvMapVert");
71 vmap->vert = MEM_calloc_arrayN<UvMapVert *>(totvert, "UvMapVert*");
72
73 if (!vmap->vert || !vmap->buf) {
75 return nullptr;
76 }
77
78 bool *winding = nullptr;
79 if (use_winding) {
80 winding = MEM_calloc_arrayN<bool>(size_t(faces.size()), "winding");
81 }
82
83 for (const int64_t a : faces.index_range()) {
84 const blender::IndexRange face = faces[a];
85 if (!selected || (!(hide_poly && hide_poly[a]) && (select_poly && select_poly[a]))) {
86 float(*tf_uv)[2] = nullptr;
87
88 if (use_winding) {
89 tf_uv = (float(*)[2])BLI_buffer_reinit_data(
90 &tf_uv_buf, blender::float2, size_t(face.size()));
91 }
92
93 nverts = int(face.size());
94
95 for (i = 0; i < nverts; i++) {
97 buf->face_index = uint(a);
98 buf->separate = false;
99 buf->next = vmap->vert[corner_verts[face[i]]];
100 vmap->vert[corner_verts[face[i]]] = buf;
101
102 if (use_winding) {
103 copy_v2_v2(tf_uv[i], mloopuv[face[i]]);
104 }
105
106 buf++;
107 }
108
109 if (use_winding) {
110 winding[a] = cross_poly_v2(tf_uv, uint(nverts)) < 0;
111 }
112 }
113 }
114
115 /* sort individual uvs for each vert */
116 for (uint a = 0; a < totvert; a++) {
117 UvMapVert *newvlist = nullptr, *vlist = vmap->vert[a];
118 UvMapVert *iterv, *v, *lastv, *next;
119 const float *uv, *uv2;
120 float uvdiff[2];
121
122 while (vlist) {
123 v = vlist;
124 vlist = vlist->next;
125 v->next = newvlist;
126 newvlist = v;
127
128 uv = mloopuv[faces[v->face_index].start() + v->loop_of_face_index];
129 lastv = nullptr;
130 iterv = vlist;
131
132 while (iterv) {
133 next = iterv->next;
134
135 uv2 = mloopuv[faces[iterv->face_index].start() + iterv->loop_of_face_index];
136 sub_v2_v2v2(uvdiff, uv2, uv);
137
138 if (fabsf(uv[0] - uv2[0]) < limit[0] && fabsf(uv[1] - uv2[1]) < limit[1] &&
139 (!use_winding || winding[iterv->face_index] == winding[v->face_index]))
140 {
141 if (lastv) {
142 lastv->next = next;
143 }
144 else {
145 vlist = next;
146 }
147 iterv->next = newvlist;
148 newvlist = iterv;
149 }
150 else {
151 lastv = iterv;
152 }
153
154 iterv = next;
155 }
156
157 newvlist->separate = true;
158 }
159
160 vmap->vert[a] = newvlist;
161 }
162
163 if (use_winding) {
164 MEM_freeN(winding);
165 }
166
167 BLI_buffer_free(&tf_uv_buf);
168
169 return vmap;
170}
171
173{
174 return vmap->vert[v];
175}
176
178{
179 if (vmap) {
180 if (vmap->vert) {
181 MEM_freeN(vmap->vert);
182 }
183 if (vmap->buf) {
184 MEM_freeN(vmap->buf);
185 }
186 MEM_freeN(vmap);
187 }
188}
189
191 int **r_mem,
192 const int totvert,
193 const blender::int3 *corner_tris,
194 const int tris_num,
195 const int *corner_verts,
196 const int /*corners_num*/)
197{
198 MeshElemMap *map = MEM_calloc_arrayN<MeshElemMap>(size_t(totvert), __func__);
199 int *indices = MEM_malloc_arrayN<int>(size_t(tris_num) * 3, __func__);
200 int *index_step;
201 int i;
202
203 /* count face users */
204 for (i = 0; i < tris_num; i++) {
205 for (int j = 3; j--;) {
206 map[corner_verts[corner_tris[i][j]]].count++;
207 }
208 }
209
210 /* create offsets */
211 index_step = indices;
212 for (i = 0; i < totvert; i++) {
213 map[i].indices = index_step;
214 index_step += map[i].count;
215
216 /* re-count, using this as an index below */
217 map[i].count = 0;
218 }
219
220 /* assign corner_tri-edge users */
221 for (i = 0; i < tris_num; i++) {
222 for (int j = 3; j--;) {
223 MeshElemMap *map_ele = &map[corner_verts[corner_tris[i][j]]];
224 map_ele->indices[map_ele->count++] = i;
225 }
226 }
227
228 *r_map = map;
229 *r_mem = indices;
230}
231
233 int **r_mem,
234 const int totsource,
235 const int *final_origindex,
236 const int totfinal)
237{
238 MeshElemMap *map = MEM_calloc_arrayN<MeshElemMap>(size_t(totsource), __func__);
239 int *indices = MEM_malloc_arrayN<int>(size_t(totfinal), __func__);
240 int *index_step;
241 int i;
242
243 /* count face users */
244 for (i = 0; i < totfinal; i++) {
245 if (final_origindex[i] != ORIGINDEX_NONE) {
246 BLI_assert(final_origindex[i] < totsource);
247 map[final_origindex[i]].count++;
248 }
249 }
250
251 /* create offsets */
252 index_step = indices;
253 for (i = 0; i < totsource; i++) {
254 map[i].indices = index_step;
255 index_step += map[i].count;
256
257 /* re-count, using this as an index below */
258 map[i].count = 0;
259 }
260
261 /* Assign face-tessellation users. */
262 for (i = 0; i < totfinal; i++) {
263 if (final_origindex[i] != ORIGINDEX_NONE) {
264 MeshElemMap *map_ele = &map[final_origindex[i]];
265 map_ele->indices[map_ele->count++] = i;
266 }
267 }
268
269 *r_map = map;
270 *r_mem = indices;
271}
272
274 int **r_mem,
276 const int *corner_tri_faces,
277 const int corner_tris_num)
278{
279 MeshElemMap *map = MEM_calloc_arrayN<MeshElemMap>(size_t(faces.size()), __func__);
280 int *indices = MEM_malloc_arrayN<int>(size_t(corner_tris_num), __func__);
281 int *index_step;
282
283 /* create offsets */
284 index_step = indices;
285 for (const int64_t i : faces.index_range()) {
286 map[i].indices = index_step;
287 index_step += blender::bke::mesh::face_triangles_num(int(faces[i].size()));
288 }
289
290 /* Assign face-tessellation users. */
291 for (int i = 0; i < corner_tris_num; i++) {
292 MeshElemMap *map_ele = &map[corner_tri_faces[i]];
293 map_ele->indices[map_ele->count++] = i;
294 }
295
296 *r_map = map;
297 *r_mem = indices;
298}
299
300namespace blender::bke::mesh {
301
302static Array<int> create_reverse_offsets(const Span<int> indices, const int items_num)
303{
304 Array<int> offsets(items_num + 1, 0);
306 return offsets;
307}
308
309static void sort_small_groups(const OffsetIndices<int> groups,
310 const int grain_size,
312{
313 threading::parallel_for(groups.index_range(), grain_size, [&](const IndexRange range) {
314 for (const int64_t index : range) {
315 MutableSpan<int> group = indices.slice(groups[index]);
316 std::sort(group.begin(), group.end());
317 }
318 });
319}
320
322 const OffsetIndices<int> offsets)
323{
324 if (group_indices.is_empty()) {
325 return {};
326 }
327 BLI_assert(*std::max_element(group_indices.begin(), group_indices.end()) < offsets.size());
328 BLI_assert(*std::min_element(group_indices.begin(), group_indices.end()) >= 0);
329
330 /* `counts` keeps track of how many elements have been added to each group, and is incremented
331 * atomically by many threads in parallel. `calloc` can be measurably faster than a parallel fill
332 * of zero. Alternatively the offsets could be copied and incremented directly, but the cost of
333 * the copy is slightly higher than the cost of `calloc`. */
334 int *counts = MEM_calloc_arrayN<int>(size_t(offsets.size()), __func__);
335 BLI_SCOPED_DEFER([&]() { MEM_freeN(counts); })
336 Array<int> results(group_indices.size());
337 threading::parallel_for(group_indices.index_range(), 1024, [&](const IndexRange range) {
338 for (const int64_t i : range) {
339 const int group_index = group_indices[i];
340 const int index_in_group = atomic_fetch_and_add_int32(&counts[group_index], 1);
341 results[offsets[group_index][index_in_group]] = int(i);
342 }
343 });
344 sort_small_groups(offsets, 1024, results);
345 return results;
346}
347
348/* A version of #reverse_indices_in_groups that stores face indices instead of corner indices. */
350 const Span<int> group_to_elem,
351 const OffsetIndices<int> offsets,
352 MutableSpan<int> results)
353{
354 int *counts = MEM_calloc_arrayN<int>(size_t(offsets.size()), __func__);
355 BLI_SCOPED_DEFER([&]() { MEM_freeN(counts); })
356 threading::parallel_for(groups.index_range(), 1024, [&](const IndexRange range) {
357 for (const int64_t face : range) {
358 for (const int elem : group_to_elem.slice(groups[face])) {
359 const int index_in_group = atomic_fetch_and_add_int32(&counts[elem], 1);
360 results[offsets[elem][index_in_group]] = int(face);
361 }
362 }
363 });
364 sort_small_groups(offsets, 1024, results);
365}
366
367static GroupedSpan<int> gather_groups(const Span<int> group_indices,
368 const int groups_num,
369 Array<int> &r_offsets,
370 Array<int> &r_indices)
371{
372 r_offsets = create_reverse_offsets(group_indices, groups_num);
373 r_indices = reverse_indices_in_groups(group_indices, r_offsets.as_span());
374 return {OffsetIndices<int>(r_offsets), r_indices};
375}
376
383
385 const int verts_num,
386 Array<int> &r_offsets,
387 Array<int> &r_indices)
388{
389 r_offsets = create_reverse_offsets(edges.cast<int>(), verts_num);
390 const OffsetIndices<int> offsets(r_offsets);
391 r_indices.reinitialize(offsets.total_size());
392
393 /* Version of #reverse_indices_in_groups that accounts for storing two indices for each edge. */
394 int *counts = MEM_calloc_arrayN<int>(size_t(offsets.size()), __func__);
395 BLI_SCOPED_DEFER([&]() { MEM_freeN(counts); })
396 threading::parallel_for(edges.index_range(), 1024, [&](const IndexRange range) {
397 for (const int64_t edge : range) {
398 for (const int vert : {edges[edge][0], edges[edge][1]}) {
399 const int index_in_group = atomic_fetch_and_add_int32(&counts[vert], 1);
400 r_indices[offsets[vert][index_in_group]] = int(edge);
401 }
402 }
403 });
404 sort_small_groups(offsets, 1024, r_indices);
405 return {offsets, r_indices};
406}
407
409 const Span<int> corner_verts,
410 const OffsetIndices<int> offsets,
411 MutableSpan<int> face_indices)
412{
413 reverse_group_indices_in_groups(faces, corner_verts, offsets, face_indices);
414}
415
417 const Span<int> corner_verts,
418 const int verts_num,
419 Array<int> &r_offsets,
420 Array<int> &r_indices)
421{
422 r_offsets = create_reverse_offsets(corner_verts, verts_num);
423 r_indices.reinitialize(r_offsets.last());
424 build_vert_to_face_indices(faces, corner_verts, OffsetIndices<int>(r_offsets), r_indices);
425 return {OffsetIndices<int>(r_offsets), r_indices};
426}
427
429 const OffsetIndices<int> offsets)
430{
431 return reverse_indices_in_groups(corner_verts, offsets);
432}
433
435 const int verts_num,
436 Array<int> &r_offsets,
437 Array<int> &r_indices)
438{
439 return gather_groups(corner_verts, verts_num, r_offsets, r_indices);
440}
441
443 const int edges_num,
444 Array<int> &r_offsets,
445 Array<int> &r_indices)
446{
447 return gather_groups(corner_edges, edges_num, r_offsets, r_indices);
448}
449
451 const Span<int> corner_edges,
452 const int edges_num,
453 Array<int> &r_offsets,
454 Array<int> &r_indices)
455{
456 r_offsets = create_reverse_offsets(corner_edges, edges_num);
457 r_indices.reinitialize(r_offsets.last());
458 reverse_group_indices_in_groups(faces, corner_edges, OffsetIndices<int>(r_offsets), r_indices);
459 return {OffsetIndices<int>(r_offsets), r_indices};
460}
461
462} // namespace blender::bke::mesh
463
465
466/* -------------------------------------------------------------------- */
470
475 blender::FunctionRef<bool(int face_index,
476 int corner,
477 int edge_index,
478 int edge_user_count,
479 const blender::Span<int> edge_face_map_elem)>;
480
482 const int *face_groups,
483 const blender::Span<int> faces_from_item,
484 const int face_group_id,
485 const int face_group_id_overflowed,
486 int &r_bit_face_group_mask)
487{
488 /* Find neighbor faces (either from a boundary edge, or a boundary vertex) that already have a
489 * group assigned, and exclude these groups' bits from the available set of groups bits that can
490 * be assigned to the currently processed group. */
491 for (const int face_idx : faces_from_item) {
492 int bit = face_groups[face_idx];
493 if (!ELEM(bit, 0, face_group_id, face_group_id_overflowed) && !(r_bit_face_group_mask & bit)) {
494 r_bit_face_group_mask |= bit;
495 }
496 }
497}
498
515static void face_edge_loop_islands_calc(const int totedge,
516 const int totvert,
518 const blender::Span<int> corner_edges,
519 const blender::Span<int> corner_verts,
520 blender::GroupedSpan<int> edge_face_map,
521 blender::GroupedSpan<int> vert_face_map,
522 const bool use_bitflags,
523 const bool use_boundary_vertices_for_bitflags,
524 MeshRemap_CheckIslandBoundary edge_boundary_check,
525 int **r_face_groups,
526 int *r_totgroup,
527 BLI_bitmap **r_edge_boundaries,
528 int *r_totedgeboundaries)
529{
530 int *face_groups;
531 int *face_stack;
532
533 BLI_bitmap *edge_boundaries = nullptr;
534 int num_edgeboundaries = 0;
535
536 int face_prev = 0;
537 constexpr int temp_face_group_id = 3; /* Placeholder value. */
538
539 /* For bitflags groups, group we could not find any available bit for, will be reset to 0 at the
540 * end. */
541 constexpr int face_group_id_overflowed = 5;
542
543 int tot_group = 0;
544 bool group_id_overflow = false;
545
546 if (faces.is_empty()) {
547 *r_totgroup = 0;
548 *r_face_groups = nullptr;
549 if (r_edge_boundaries) {
550 *r_edge_boundaries = nullptr;
551 *r_totedgeboundaries = 0;
552 }
553 return;
554 }
555
556 if (r_edge_boundaries) {
557 edge_boundaries = BLI_BITMAP_NEW(totedge, __func__);
558 *r_totedgeboundaries = 0;
559 }
560
561 blender::Array<int> edge_to_face_src_offsets;
562 blender::Array<int> edge_to_face_src_indices;
563 if (edge_face_map.is_empty()) {
565 faces, corner_edges, totedge, edge_to_face_src_offsets, edge_to_face_src_indices);
566 }
567 blender::Array<int> vert_to_face_src_offsets;
568 blender::Array<int> vert_to_face_src_indices;
569 if (use_bitflags && vert_face_map.is_empty()) {
571 faces, corner_verts, totvert, vert_to_face_src_offsets, vert_to_face_src_indices);
572 }
573
574 face_groups = MEM_calloc_arrayN<int>(size_t(faces.size()), __func__);
575 face_stack = MEM_malloc_arrayN<int>(size_t(faces.size()), __func__);
576
577 while (true) {
578 int face;
579 int bit_face_group_mask = 0;
580 int face_group_id;
581 int ps_curr_idx = 0, ps_end_idx = 0; /* stack indices */
582
583 for (face = face_prev; face < int(faces.size()); face++) {
584 if (face_groups[face] == 0) {
585 break;
586 }
587 }
588
589 if (face == int(faces.size())) {
590 /* all done */
591 break;
592 }
593
594 face_group_id = use_bitflags ? temp_face_group_id : ++tot_group;
595
596 /* start searching from here next time */
597 face_prev = face + 1;
598
599 face_groups[face] = face_group_id;
600 face_stack[ps_end_idx++] = face;
601
602 while (ps_curr_idx != ps_end_idx) {
603 face = face_stack[ps_curr_idx++];
604 BLI_assert(face_groups[face] == face_group_id);
605
606 for (const int64_t loop : faces[face]) {
607 const int edge = corner_edges[loop];
608 /* loop over face users */
609 const blender::Span<int> map_ele = edge_face_map[edge];
610 const int *p = map_ele.data();
611 int i = int(map_ele.size());
612 if (!edge_boundary_check(face, int(loop), edge, i, map_ele)) {
613 for (; i--; p++) {
614 /* if we meet other non initialized its a bug */
615 BLI_assert(ELEM(face_groups[*p], 0, face_group_id));
616
617 if (face_groups[*p] == 0) {
618 face_groups[*p] = face_group_id;
619 face_stack[ps_end_idx++] = *p;
620 }
621 }
622 }
623 else {
624 if (edge_boundaries && !BLI_BITMAP_TEST(edge_boundaries, edge)) {
625 BLI_BITMAP_ENABLE(edge_boundaries, edge);
626 num_edgeboundaries++;
627 }
628 if (use_bitflags) {
629 /* Exclude bits used in other groups sharing the same boundary edge. */
631 map_ele,
632 face_group_id,
633 face_group_id_overflowed,
634 bit_face_group_mask);
635 if (use_boundary_vertices_for_bitflags) {
636 /* Exclude bits used in other groups sharing the same boundary vertex. */
637 /* NOTE: Checking one vertex for each edge (the corner vertex) should be enough:
638 * - Thanks to winding, a fully boundary vertex (i.e. a vertex for which at least
639 * two of the adjacent edges in the same group are boundary ones) will be
640 * processed by at least one of the edges/corners. If not when processing the
641 * first face's corner, then when processing the other face's corner in the same
642 * group.
643 * - Isolated boundary edges (i.e. boundary edges only connected to faces of the
644 * same group) cannot be represented by bit-flags groups, at least not with
645 * current algorithm (they cannot define more than one group).
646 * - Inversions of winding (aka flipped faces) always generate boundary edges in
647 * current use-case (smooth groups), i.e. two faces with opposed winding cannot
648 * belong to the same group. */
649 const int vert = corner_verts[loop];
651 vert_face_map[vert],
652 face_group_id,
653 face_group_id_overflowed,
654 bit_face_group_mask);
655 }
656 }
657 }
658 }
659 }
660 /* And now, we have all our face from current group in face_stack
661 * (from 0 to (ps_end_idx - 1)),
662 * as well as all smoothgroups bits we can't use in bit_face_group_mask.
663 */
664 if (use_bitflags) {
665 int i, *p, gid_bit = 0;
666 face_group_id = 1;
667
668 /* Find first bit available! */
669 for (; (face_group_id & bit_face_group_mask) && (gid_bit < 32); gid_bit++) {
670 face_group_id <<= 1; /* will 'overflow' on last possible iteration. */
671 }
672 if (UNLIKELY(gid_bit > 31)) {
673 /* All bits used in contiguous smooth groups, not much to do.
674 *
675 * NOTE: If only considering boundary edges, this is *very* unlikely to happen.
676 * Theoretically, four groups are enough, this is probably not achievable with such a
677 * simple algorithm, but 32 groups should always be more than enough.
678 *
679 * When also considering boundary vertices (which is the case currently, see comment
680 * above), a fairly simple fan case with over 30 faces all belonging to different groups
681 * will be enough to cause an overflow.
682 */
683 printf(
684 "Warning, could not find an available id for current smooth group, faces will me "
685 "marked "
686 "as out of any smooth group...\n");
687
688 /* Can't use 0, will have to set them to this value later. */
689 face_group_id = face_group_id_overflowed;
690
691 group_id_overflow = true;
692 }
693 tot_group = std::max(gid_bit, tot_group);
694 /* And assign the final smooth group id to that face group! */
695 for (i = ps_end_idx, p = face_stack; i--; p++) {
696 face_groups[*p] = face_group_id;
697 }
698 }
699 }
700
701 if (use_bitflags) {
702 /* used bits are zero-based. */
703 tot_group++;
704 }
705
706 if (UNLIKELY(group_id_overflow)) {
707 int i = int(faces.size()), *gid = face_groups;
708 for (; i--; gid++) {
709 if (*gid == face_group_id_overflowed) {
710 *gid = 0;
711 }
712 }
713 /* Using 0 as group id adds one more group! */
714 tot_group++;
715 }
716
717 MEM_freeN(face_stack);
718
719 *r_totgroup = tot_group;
720 *r_face_groups = face_groups;
721 if (r_edge_boundaries) {
722 *r_edge_boundaries = edge_boundaries;
723 *r_totedgeboundaries = num_edgeboundaries;
724 }
725}
726
727static int *mesh_calc_smoothgroups(const int edges_num,
728 const int verts_num,
730 const blender::Span<int> corner_edges,
731 const blender::Span<int> corner_verts,
732 const blender::Span<bool> sharp_edges,
733 const blender::Span<bool> sharp_faces,
734 int *r_totgroup,
735 const bool use_bitflags,
736 const bool use_boundary_vertices_for_bitflags)
737{
738 int *face_groups = nullptr;
739
740 auto face_is_smooth = [&](const int i) { return sharp_faces.is_empty() || !sharp_faces[i]; };
741
742 auto face_is_island_boundary_smooth = [&](const int face_index,
743 const int /*corner*/,
744 const int edge_index,
745 const int edge_user_count,
746 const blender::Span<int> edge_face_map_elem) {
747 /* Edge is sharp if one of its faces is flat, or edge itself is sharp,
748 * or edge is not used by exactly two faces. */
749 if (face_is_smooth(face_index) && !(!sharp_edges.is_empty() && sharp_edges[edge_index]) &&
750 (edge_user_count == 2))
751 {
752 /* In that case, edge appears to be smooth, but we need to check its other face too. */
753 const int other_face_index = (face_index == edge_face_map_elem[0]) ? edge_face_map_elem[1] :
754 edge_face_map_elem[0];
755 return !face_is_smooth(other_face_index);
756 }
757 return true;
758 };
759
761 verts_num,
762 faces,
763 corner_edges,
764 corner_verts,
765 {},
766 {},
767 use_bitflags,
768 use_boundary_vertices_for_bitflags,
769 face_is_island_boundary_smooth,
770 &face_groups,
771 r_totgroup,
772 nullptr,
773 nullptr);
774
775 return face_groups;
776}
777
778int *BKE_mesh_calc_smoothgroups(int edges_num,
780 const blender::Span<int> corner_edges,
781 const blender::Span<bool> sharp_edges,
782 const blender::Span<bool> sharp_faces,
783 int *r_totgroup)
784{
786 edges_num, 0, faces, corner_edges, {}, sharp_edges, sharp_faces, r_totgroup, false, false);
787}
788
790 int verts_num,
792 const blender::Span<int> corner_edges,
793 const blender::Span<int> corner_verts,
794 const blender::Span<bool> sharp_edges,
795 const blender::Span<bool> sharp_faces,
796 const bool use_boundary_vertices_for_bitflags,
797 int *r_totgroup)
798{
799 return mesh_calc_smoothgroups(edges_num,
800 verts_num,
801 faces,
802 corner_edges,
803 corner_verts,
804 sharp_edges,
805 sharp_faces,
806 r_totgroup,
807 true,
808 use_boundary_vertices_for_bitflags);
809}
810
811#define MISLAND_DEFAULT_BUFSIZE 64
812
814 const short item_type,
815 const int items_num,
816 const short island_type,
817 const short innercut_type)
818{
819 MemArena *mem = island_store->mem;
820
821 if (mem == nullptr) {
823 island_store->mem = mem;
824 }
825 /* else memarena should be cleared */
826
831
832 island_store->item_type = item_type;
833 island_store->items_to_islands_num = items_num;
834 island_store->items_to_islands = static_cast<int *>(
835 BLI_memarena_alloc(mem, sizeof(*island_store->items_to_islands) * size_t(items_num)));
836
837 island_store->island_type = island_type;
839 island_store->islands = static_cast<MeshElemMap **>(
840 BLI_memarena_alloc(mem, sizeof(*island_store->islands) * island_store->islands_num_alloc));
841
842 island_store->innercut_type = innercut_type;
843 island_store->innercuts = static_cast<MeshElemMap **>(
844 BLI_memarena_alloc(mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc));
845}
846
848{
849 island_store->item_type = MISLAND_TYPE_NONE;
850 island_store->items_to_islands_num = 0;
851 island_store->items_to_islands = nullptr;
852
853 island_store->island_type = MISLAND_TYPE_NONE;
854 island_store->islands_num = 0;
855 island_store->islands = nullptr;
856
857 island_store->innercut_type = MISLAND_TYPE_NONE;
858 island_store->innercuts = nullptr;
859
860 if (island_store->mem) {
861 BLI_memarena_clear(island_store->mem);
862 }
863
864 island_store->islands_num_alloc = 0;
865}
866
868{
869 if (island_store->mem) {
870 BLI_memarena_free(island_store->mem);
871 island_store->mem = nullptr;
872 }
873}
874
876 const int item_num,
877 const int *items_indices,
878 const int num_island_items,
879 int *island_item_indices,
880 const int num_innercut_items,
881 int *innercut_item_indices)
882{
883 MemArena *mem = island_store->mem;
884
885 MeshElemMap *isld, *innrcut;
886 const int curr_island_idx = island_store->islands_num++;
887 const size_t curr_num_islands = size_t(island_store->islands_num);
888 int i = item_num;
889
890 while (i--) {
891 island_store->items_to_islands[items_indices[i]] = curr_island_idx;
892 }
893
894 if (UNLIKELY(curr_num_islands > island_store->islands_num_alloc)) {
895 MeshElemMap **islds, **innrcuts;
896
897 island_store->islands_num_alloc *= 2;
898 islds = static_cast<MeshElemMap **>(
899 BLI_memarena_alloc(mem, sizeof(*islds) * island_store->islands_num_alloc));
900 memcpy(islds, island_store->islands, sizeof(*islds) * (curr_num_islands - 1));
901 island_store->islands = islds;
902
903 innrcuts = static_cast<MeshElemMap **>(
904 BLI_memarena_alloc(mem, sizeof(*innrcuts) * island_store->islands_num_alloc));
905 memcpy(innrcuts, island_store->innercuts, sizeof(*innrcuts) * (curr_num_islands - 1));
906 island_store->innercuts = innrcuts;
907 }
908
909 island_store->islands[curr_island_idx] = isld = static_cast<MeshElemMap *>(
910 BLI_memarena_alloc(mem, sizeof(*isld)));
911 isld->count = num_island_items;
912 isld->indices = static_cast<int *>(
913 BLI_memarena_alloc(mem, sizeof(*isld->indices) * size_t(num_island_items)));
914 memcpy(isld->indices, island_item_indices, sizeof(*isld->indices) * size_t(num_island_items));
915
916 island_store->innercuts[curr_island_idx] = innrcut = static_cast<MeshElemMap *>(
917 BLI_memarena_alloc(mem, sizeof(*innrcut)));
918 innrcut->count = num_innercut_items;
919 innrcut->indices = static_cast<int *>(
920 BLI_memarena_alloc(mem, sizeof(*innrcut->indices) * size_t(num_innercut_items)));
921 memcpy(innrcut->indices,
922 innercut_item_indices,
923 sizeof(*innrcut->indices) * size_t(num_innercut_items));
924}
925
926static bool mesh_calc_islands_loop_face_uv(const int totedge,
927 const bool *uv_seams,
929 const int *corner_verts,
930 const int *corner_edges,
931 const int corners_num,
932 const float (*luvs)[2],
933 MeshIslandStore *r_island_store)
934{
935 using namespace blender;
936 int *face_groups = nullptr;
937 int num_face_groups;
938
939 int *face_indices;
940 int *loop_indices;
941 int num_pidx, num_lidx;
942
943 /* Those are used to detect 'inner cuts', i.e. edges that are boundaries,
944 * and yet have two or more faces of a same group using them
945 * (typical case: seam used to unwrap properly a cylinder). */
946 BLI_bitmap *edge_boundaries = nullptr;
947 int num_edge_boundaries = 0;
948 char *edge_boundary_count = nullptr;
949 int *edge_innercut_indices = nullptr;
950 int num_einnercuts = 0;
951
952 int grp_idx;
953
954 BKE_mesh_loop_islands_clear(r_island_store);
956 r_island_store, MISLAND_TYPE_LOOP, corners_num, MISLAND_TYPE_POLY, MISLAND_TYPE_EDGE);
957
958 Array<int> edge_to_face_offsets;
959 Array<int> edge_to_face_indices;
961 faces, {corner_edges, corners_num}, totedge, edge_to_face_offsets, edge_to_face_indices);
962
963 Array<int> edge_to_corner_offsets;
964 Array<int> edge_to_corner_indices;
965 GroupedSpan<int> edge_to_corner_map;
966 if (luvs) {
967 edge_to_corner_map = bke::mesh::build_edge_to_corner_map(
968 {corner_edges, corners_num}, totedge, edge_to_corner_offsets, edge_to_corner_indices);
969 }
970
971 /* TODO: I'm not sure edge seam flag is enough to define UV islands?
972 * Maybe we should also consider UV-maps values
973 * themselves (i.e. different UV-edges for a same mesh-edge => boundary edge too?).
974 * Would make things much more complex though,
975 * and each UVMap would then need its own mesh mapping, not sure we want that at all!
976 */
977 auto mesh_check_island_boundary_uv = [&](const int /*face_index*/,
978 const int corner,
979 const int edge_index,
980 const int /*edge_user_count*/,
981 const Span<int> /*edge_face_map_elem*/) -> bool {
982 if (luvs) {
983 const Span<int> edge_to_corners = edge_to_corner_map[corner_edges[corner]];
984
985 BLI_assert(edge_to_corners.size() >= 2 && (edge_to_corners.size() % 2) == 0);
986
987 const int v1 = corner_verts[edge_to_corners[0]];
988 const int v2 = corner_verts[edge_to_corners[1]];
989 const float *uvco_v1 = luvs[edge_to_corners[0]];
990 const float *uvco_v2 = luvs[edge_to_corners[1]];
991 for (int i = 2; i < edge_to_corners.size(); i += 2) {
992 if (corner_verts[edge_to_corners[i]] == v1) {
993 if (!equals_v2v2(uvco_v1, luvs[edge_to_corners[i]]) ||
994 !equals_v2v2(uvco_v2, luvs[edge_to_corners[i + 1]]))
995 {
996 return true;
997 }
998 }
999 else {
1000 BLI_assert(corner_verts[edge_to_corners[i]] == v2);
1002 if (!equals_v2v2(uvco_v2, luvs[edge_to_corners[i]]) ||
1003 !equals_v2v2(uvco_v1, luvs[edge_to_corners[i + 1]]))
1004 {
1005 return true;
1006 }
1007 }
1008 }
1009 return false;
1010 }
1011
1012 /* Edge is UV boundary if tagged as seam. */
1013 return uv_seams && uv_seams[edge_index];
1014 };
1015
1017 0,
1018 faces,
1019 {corner_edges, corners_num},
1020 {},
1021 edge_to_face_map,
1022 {},
1023 false,
1024 false,
1025 mesh_check_island_boundary_uv,
1026 &face_groups,
1027 &num_face_groups,
1028 &edge_boundaries,
1029 &num_edge_boundaries);
1030
1031 if (!num_face_groups) {
1032 if (num_edge_boundaries) {
1033 MEM_freeN(edge_boundaries);
1034 }
1035 return false;
1036 }
1037
1038 if (num_edge_boundaries) {
1039 edge_boundary_count = MEM_malloc_arrayN<char>(size_t(totedge), __func__);
1040 edge_innercut_indices = MEM_malloc_arrayN<int>(size_t(num_edge_boundaries), __func__);
1041 }
1042
1043 face_indices = MEM_malloc_arrayN<int>(size_t(faces.size()), __func__);
1044 loop_indices = MEM_malloc_arrayN<int>(size_t(corners_num), __func__);
1045
1046 /* NOTE: here we ignore '0' invalid group - this should *never* happen in this case anyway? */
1047 for (grp_idx = 1; grp_idx <= num_face_groups; grp_idx++) {
1048 num_pidx = num_lidx = 0;
1049 if (num_edge_boundaries) {
1050 num_einnercuts = 0;
1051 memset(edge_boundary_count, 0, sizeof(*edge_boundary_count) * size_t(totedge));
1052 }
1053
1054 for (const int64_t p_idx : faces.index_range()) {
1055 if (face_groups[p_idx] != grp_idx) {
1056 continue;
1057 }
1058 face_indices[num_pidx++] = int(p_idx);
1059 for (const int64_t corner : faces[p_idx]) {
1060 const int edge_i = corner_edges[corner];
1061 loop_indices[num_lidx++] = int(corner);
1062 if (num_edge_boundaries && BLI_BITMAP_TEST(edge_boundaries, edge_i) &&
1063 (edge_boundary_count[edge_i] < 2))
1064 {
1065 edge_boundary_count[edge_i]++;
1066 if (edge_boundary_count[edge_i] == 2) {
1067 edge_innercut_indices[num_einnercuts++] = edge_i;
1068 }
1069 }
1070 }
1071 }
1072
1073 BKE_mesh_loop_islands_add(r_island_store,
1074 num_lidx,
1075 loop_indices,
1076 num_pidx,
1077 face_indices,
1078 num_einnercuts,
1079 edge_innercut_indices);
1080 }
1081
1082 MEM_freeN(face_indices);
1083 MEM_freeN(loop_indices);
1084 MEM_freeN(face_groups);
1085
1086 if (num_edge_boundaries) {
1087 MEM_freeN(edge_boundaries);
1088 }
1089
1090 if (num_edge_boundaries) {
1091 MEM_freeN(edge_boundary_count);
1092 MEM_freeN(edge_innercut_indices);
1093 }
1094 return true;
1095}
1096
1097bool BKE_mesh_calc_islands_loop_face_edgeseam(const float (*vert_positions)[3],
1098 const int totvert,
1099 const blender::int2 *edges,
1100 const int totedge,
1101 const bool *uv_seams,
1103 const int *corner_verts,
1104 const int *corner_edges,
1105 const int corners_num,
1106 MeshIslandStore *r_island_store)
1107{
1108 UNUSED_VARS(vert_positions, totvert, edges);
1110 totedge, uv_seams, faces, corner_verts, corner_edges, corners_num, nullptr, r_island_store);
1111}
1112
1113bool BKE_mesh_calc_islands_loop_face_uvmap(float (*vert_positions)[3],
1114 const int totvert,
1115 blender::int2 *edges,
1116 const int totedge,
1117 const bool *uv_seams,
1119 const int *corner_verts,
1120 const int *corner_edges,
1121 const int corners_num,
1122 const float (*luvs)[2],
1123 MeshIslandStore *r_island_store)
1124{
1125 UNUSED_VARS(vert_positions, totvert, edges);
1126 BLI_assert(luvs != nullptr);
1128 totedge, uv_seams, faces, corner_verts, corner_edges, corners_num, luvs, r_island_store);
1129}
1130
CustomData interface, see also DNA_customdata_types.h.
#define ORIGINDEX_NONE
@ MISLAND_TYPE_POLY
@ MISLAND_TYPE_VERT
@ MISLAND_TYPE_LOOP
@ MISLAND_TYPE_EDGE
@ MISLAND_TYPE_NONE
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:78
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
#define BLI_buffer_reinit_data(buffer_, type_, new_count_)
Definition BLI_buffer.h:47
@ BLI_BUFFER_NOP
Definition BLI_buffer.h:21
#define BLI_buffer_declare_static(type_, name_, flag_, static_count_)
Definition BLI_buffer.h:25
#define BLI_buffer_free(name_)
Definition BLI_buffer.h:94
float cross_poly_v2(const float verts[][2], unsigned int nr)
Definition math_geom.cc:149
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
#define BLI_SCOPED_DEFER(function_to_defer)
unsigned int uint
unsigned short ushort
#define UNUSED_VARS(...)
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
#define ELEM(...)
Read Guarded memory(de)allocation.
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr int64_t size() const
Definition BLI_span.hh:252
Span< T > as_span() const
Definition BLI_array.hh:232
const T & last(const int64_t n=0) const
Definition BLI_array.hh:285
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
constexpr int64_t size() const
Span< NewT > constexpr cast() const
Definition BLI_span.hh:418
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr bool is_empty() const
Definition BLI_span.hh:260
#define fabsf(x)
static ushort indices[]
#define printf(...)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
static char faces[256]
UvMapVert * BKE_mesh_uv_vert_map_get_vert(UvVertMap *vmap, uint v)
int * BKE_mesh_calc_smoothgroups_bitflags(int edges_num, int verts_num, const blender::OffsetIndices< int > faces, const blender::Span< int > corner_edges, const blender::Span< int > corner_verts, const blender::Span< bool > sharp_edges, const blender::Span< bool > sharp_faces, const bool use_boundary_vertices_for_bitflags, int *r_totgroup)
void BKE_mesh_origindex_map_create_corner_tri(MeshElemMap **r_map, int **r_mem, const blender::OffsetIndices< int > faces, const int *corner_tri_faces, const int corner_tris_num)
bool BKE_mesh_calc_islands_loop_face_edgeseam(const float(*vert_positions)[3], const int totvert, const blender::int2 *edges, const int totedge, const bool *uv_seams, const blender::OffsetIndices< int > faces, const int *corner_verts, const int *corner_edges, const int corners_num, MeshIslandStore *r_island_store)
void BKE_mesh_origindex_map_create(MeshElemMap **r_map, int **r_mem, const int totsource, const int *final_origindex, const int totfinal)
void BKE_mesh_loop_islands_add(MeshIslandStore *island_store, const int item_num, const int *items_indices, const int num_island_items, int *island_item_indices, const int num_innercut_items, int *innercut_item_indices)
int * BKE_mesh_calc_smoothgroups(int edges_num, const blender::OffsetIndices< int > faces, const blender::Span< int > corner_edges, const blender::Span< bool > sharp_edges, const blender::Span< bool > sharp_faces, int *r_totgroup)
static void face_edge_loop_islands_calc(const int totedge, const int totvert, const blender::OffsetIndices< int > faces, const blender::Span< int > corner_edges, const blender::Span< int > corner_verts, blender::GroupedSpan< int > edge_face_map, blender::GroupedSpan< int > vert_face_map, const bool use_bitflags, const bool use_boundary_vertices_for_bitflags, MeshRemap_CheckIslandBoundary edge_boundary_check, int **r_face_groups, int *r_totgroup, BLI_bitmap **r_edge_boundaries, int *r_totedgeboundaries)
void BKE_mesh_loop_islands_init(MeshIslandStore *island_store, const short item_type, const int items_num, const short island_type, const short innercut_type)
#define MISLAND_DEFAULT_BUFSIZE
void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store)
blender::FunctionRef< bool(int face_index, int corner, int edge_index, int edge_user_count, const blender::Span< int > edge_face_map_elem)> MeshRemap_CheckIslandBoundary
bool BKE_mesh_calc_islands_loop_face_uvmap(float(*vert_positions)[3], const int totvert, blender::int2 *edges, const int totedge, const bool *uv_seams, const blender::OffsetIndices< int > faces, const int *corner_verts, const int *corner_edges, const int corners_num, const float(*luvs)[2], MeshIslandStore *r_island_store)
void BKE_mesh_uv_vert_map_free(UvVertMap *vmap)
void BKE_mesh_vert_corner_tri_map_create(MeshElemMap **r_map, int **r_mem, const int totvert, const blender::int3 *corner_tris, const int tris_num, const int *corner_verts, const int)
UvVertMap * BKE_mesh_uv_vert_map_create(const blender::OffsetIndices< int > faces, const bool *hide_poly, const bool *select_poly, const int *corner_verts, const float(*mloopuv)[2], uint totvert, const float limit[2], const bool selected, const bool use_winding)
static int * mesh_calc_smoothgroups(const int edges_num, const int verts_num, const blender::OffsetIndices< int > faces, const blender::Span< int > corner_edges, const blender::Span< int > corner_verts, const blender::Span< bool > sharp_edges, const blender::Span< bool > sharp_faces, int *r_totgroup, const bool use_bitflags, const bool use_boundary_vertices_for_bitflags)
void BKE_mesh_loop_islands_free(MeshIslandStore *island_store)
static void face_edge_loop_islands_calc_bitflags_exclude_at_boundary(const int *face_groups, const blender::Span< int > faces_from_item, const int face_group_id, const int face_group_id_overflowed, int &r_bit_face_group_mask)
static bool mesh_calc_islands_loop_face_uv(const int totedge, const bool *uv_seams, const blender::OffsetIndices< int > faces, const int *corner_verts, const int *corner_edges, const int corners_num, const float(*luvs)[2], MeshIslandStore *r_island_store)
void build_vert_to_face_indices(OffsetIndices< int > faces, Span< int > corner_verts, OffsetIndices< int > offsets, MutableSpan< int > face_indices)
GroupedSpan< int > build_vert_to_corner_map(Span< int > corner_verts, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
static Array< int > reverse_indices_in_groups(const Span< int > group_indices, const OffsetIndices< int > offsets)
GroupedSpan< int > build_edge_to_corner_map(Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
static Array< int > create_reverse_offsets(const Span< int > indices, const int items_num)
static GroupedSpan< int > gather_groups(const Span< int > group_indices, const int groups_num, Array< int > &r_offsets, Array< int > &r_indices)
static void sort_small_groups(const OffsetIndices< int > groups, const int grain_size, MutableSpan< int > indices)
int face_triangles_num(const int face_size)
Definition BKE_mesh.hh:334
GroupedSpan< int > build_edge_to_face_map(OffsetIndices< int > faces, Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
Array< int > build_corner_to_face_map(OffsetIndices< int > faces)
Array< int > build_vert_to_corner_indices(Span< int > corner_verts, OffsetIndices< int > offsets)
GroupedSpan< int > build_vert_to_face_map(OffsetIndices< int > faces, Span< int > corner_verts, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
static void reverse_group_indices_in_groups(const OffsetIndices< int > groups, const Span< int > group_to_elem, const OffsetIndices< int > offsets, MutableSpan< int > results)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
void build_reverse_map(OffsetIndices< int > offsets, MutableSpan< int > r_map)
void build_reverse_offsets(Span< int > indices, MutableSpan< int > offsets)
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:93
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
MeshElemMap ** islands
MeshElemMap ** innercuts
unsigned int face_index
UvMapVert * next
unsigned short loop_of_face_index
UvMapVert * buf
UvMapVert ** vert
i
Definition text_draw.cc:230