Blender V5.0
editmesh_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "DNA_key_types.h"
12#include "DNA_mesh_types.h"
13#include "DNA_object_types.h"
14
15#include "BLI_array.hh"
16#include "BLI_kdtree.h"
17#include "BLI_listbase.h"
18#include "BLI_math_matrix.h"
19#include "BLI_math_vector.h"
20
21#include "BKE_context.hh"
22#include "BKE_customdata.hh"
23#include "BKE_editmesh.hh"
24#include "BKE_editmesh_bvh.hh"
25#include "BKE_layer.hh"
26#include "BKE_mesh.hh"
27#include "BKE_mesh_mapping.hh"
28#include "BKE_report.hh"
29
30#include "DEG_depsgraph.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "ED_mesh.hh"
36#include "ED_screen.hh"
38#include "ED_uvedit.hh"
39#include "ED_view3d.hh"
40
41#include "mesh_intern.hh" /* own include */
42
43using blender::Vector;
44
45/* -------------------------------------------------------------------- */
48
49/* Mesh backup implementation.
50 * This would greatly benefit from some sort of binary diffing
51 * just as the undo stack would.
52 * So leaving this as an interface for further work */
53
55{
56 BMBackup backup;
57 backup.bmcopy = BM_mesh_copy(em->bm);
58 return backup;
59}
60
61void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
62{
64 BMesh *tmpbm = BM_mesh_copy(backup->bmcopy);
65 *em->bm = *tmpbm;
66 MEM_freeN(tmpbm);
67 tmpbm = nullptr;
68
69 if (recalc_looptris) {
71 }
72}
73
74void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
75{
77 *em->bm = *backup->bmcopy;
78 MEM_freeN(backup->bmcopy);
79 backup->bmcopy = nullptr;
80 if (recalc_looptris) {
82 }
83}
84
86{
87 if (backup->bmcopy) {
89 MEM_freeN(backup->bmcopy);
90 }
91}
92
94
95/* -------------------------------------------------------------------- */
98
99bool EDBM_op_init(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const char *fmt, ...)
100{
101 BMesh *bm = em->bm;
102 va_list list;
103
104 va_start(list, fmt);
105
106 if (!BMO_op_vinitf(bm, bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
107 BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
108 va_end(list);
109 return false;
110 }
111
112 va_end(list);
113
114 return true;
115}
116
117bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report)
118{
119 const char *errmsg;
120
121#ifndef NDEBUG
122 struct StatePrev {
123 int verts_len, edges_len, loops_len, faces_len;
124 };
125 StatePrev em_state_prev = {
126 em->bm->totvert,
127 em->bm->totedge,
128 em->bm->totloop,
129 em->bm->totface,
130 };
131#endif
132
133 BMO_op_finish(em->bm, bmop);
134
135 bool changed = false;
136 bool changed_was_set = false;
137
138 eBMOpErrorLevel level;
139 while (BMO_error_pop(em->bm, &errmsg, nullptr, &level)) {
140 eReportType type = RPT_INFO;
141 switch (level) {
142 case BMO_ERROR_CANCEL: {
143 changed_was_set = true;
144 break;
145 }
146 case BMO_ERROR_WARN: {
147 type = RPT_WARNING;
148 changed_was_set = true;
149 changed = true;
150 break;
151 }
152 case BMO_ERROR_FATAL: {
153 type = RPT_ERROR;
154 changed_was_set = true;
155 changed = true;
156 break;
157 }
158 }
159
160 if (do_report) {
161 BKE_report(op->reports, type, errmsg);
162 }
163 }
164 if (changed_was_set == false) {
165 changed = true;
166 }
167
168#ifndef NDEBUG
169 if (changed == false) {
170 BLI_assert((em_state_prev.verts_len == em->bm->totvert) &&
171 (em_state_prev.edges_len == em->bm->totedge) &&
172 (em_state_prev.loops_len == em->bm->totloop) &&
173 (em_state_prev.faces_len == em->bm->totface));
174 }
175#endif
176
177 return changed;
178}
179
180bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt, ...)
181{
182 BMesh *bm = em->bm;
183 BMOperator bmop;
184 va_list list;
185
186 va_start(list, fmt);
187
188 if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
189 BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
190 va_end(list);
191 return false;
192 }
193
194 BMO_op_exec(bm, &bmop);
195
196 va_end(list);
197 return EDBM_op_finish(em, &bmop, op, true);
198}
199
201 wmOperator *op,
202 const char *select_slot_out,
203 const bool select_extend,
204 const char *fmt,
205 ...)
206{
207 BMesh *bm = em->bm;
208 BMOperator bmop;
209 va_list list;
210
211 va_start(list, fmt);
212
213 if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
214 BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
215 va_end(list);
216 return false;
217 }
218
219 BMO_op_exec(bm, &bmop);
220
221 BMOpSlot *slot_select_out = BMO_slot_get(bmop.slots_out, select_slot_out);
222 char hflag = slot_select_out->slot_subtype.elem & BM_ALL_NOLOOP;
223 BLI_assert(hflag != 0);
224
225 if (select_extend == false) {
227 }
228
230 em->bm, bmop.slots_out, select_slot_out, hflag, BM_ELEM_SELECT, true);
231
232 va_end(list);
233 return EDBM_op_finish(em, &bmop, op, true);
234}
235
236bool EDBM_op_call_silentf(BMEditMesh *em, const char *fmt, ...)
237{
238 BMesh *bm = em->bm;
239 BMOperator bmop;
240 va_list list;
241
242 va_start(list, fmt);
243
244 if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
245 va_end(list);
246 return false;
247 }
248
249 BMO_op_exec(bm, &bmop);
250
251 va_end(list);
252 return EDBM_op_finish(em, &bmop, nullptr, false);
253}
254
256
257/* -------------------------------------------------------------------- */
262
274{
275 const Mesh *mesh = static_cast<const Mesh *>(ob->data);
276 if (UNLIKELY((ob->shapenr == 0) && (mesh->key && !BLI_listbase_is_empty(&mesh->key->block)))) {
277 return 1;
278 }
279 return ob->shapenr;
280}
281
282void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
283{
284 Mesh *mesh = static_cast<Mesh *>(ob->data);
285 EDBM_mesh_make_from_mesh(ob, mesh, select_mode, add_key_index);
286}
287
289 Mesh *src_mesh,
290 const int select_mode,
291 const bool add_key_index)
292{
293 Mesh *mesh = static_cast<Mesh *>(ob->data);
294 BMeshCreateParams create_params{};
295 create_params.use_toolflags = true;
296 /* Clamp the index, so the behavior of enter & exit edit-mode matches, see #43998. */
297 const int shapenr = object_shapenr_basis_index_ensured(ob);
298
299 BMesh *bm = BKE_mesh_to_bmesh(src_mesh, shapenr, add_key_index, &create_params);
300
301 if (mesh->runtime->edit_mesh) {
302 /* this happens when switching shape keys */
303 EDBM_mesh_free_data(mesh->runtime->edit_mesh.get());
304 mesh->runtime->edit_mesh.reset();
305 }
306
307 /* Executing operators re-tessellates,
308 * so we can avoid doing here but at some point it may need to be added back. */
309 mesh->runtime->edit_mesh = std::make_shared<BMEditMesh>();
310 mesh->runtime->edit_mesh->bm = bm;
311
312 mesh->runtime->edit_mesh->selectmode = mesh->runtime->edit_mesh->bm->selectmode = select_mode;
313 mesh->runtime->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0;
314
315 /* we need to flush selection because the mode may have changed from when last in editmode */
316 EDBM_selectmode_flush(mesh->runtime->edit_mesh.get());
317}
318
319void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
320{
321 Mesh *mesh = static_cast<Mesh *>(ob->data);
322 BMesh *bm = mesh->runtime->edit_mesh->bm;
323
324 /* Workaround for #42360, 'ob->shapenr' should be 1 in this case.
325 * however this isn't synchronized between objects at the moment. */
326 if (UNLIKELY((ob->shapenr == 0) && (object_shapenr_basis_index_ensured(ob) > 0))) {
327 bm->shapenr = 1;
328 }
329
331 params.calc_object_remap = true;
332 params.update_shapekey_indices = !free_data;
333 BM_mesh_bm_to_me(bmain, bm, mesh, &params);
334}
335
336void EDBM_mesh_load(Main *bmain, Object *ob)
337{
338 EDBM_mesh_load_ex(bmain, ob, true);
339}
340
342{
343 /* These tables aren't used yet, so it's not strictly necessary
344 * to 'end' them but if someone tries to start using them,
345 * having these in place will save a lot of pain. */
348
350}
351
353
354/* -------------------------------------------------------------------- */
357
359{
360 Scene *scene = CTX_data_scene(C);
361 Object *obedit = CTX_data_edit_object(C);
363
364 if (!em) {
365 return;
366 }
367
368 scene->toolsettings->selectmode = em->selectmode;
369
370 /* Request redraw of header buttons (to show new select mode) */
372}
373
374void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode)
375{
377}
378
383
385{
386 /* Function below doesn't use. just do this to keep the values in sync. */
387 em->bm->selectmode = em->selectmode;
389}
390
391void EDBM_select_more(BMEditMesh *em, const bool use_face_step)
392{
393 BMOperator bmop;
394 const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
395
396 BMO_op_initf(em->bm,
397 &bmop,
399 "region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
401 false,
402 use_faces,
403 use_face_step);
404 BMO_op_exec(em->bm, &bmop);
405 /* Don't flush selection in edge/vertex mode. */
407 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
408 BMO_op_finish(em->bm, &bmop);
409
412}
413
414void EDBM_select_less(BMEditMesh *em, const bool use_face_step)
415{
416 BMOperator bmop;
417 const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
418
419 BMO_op_initf(em->bm,
420 &bmop,
422 "region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
424 true,
425 use_faces,
426 use_face_step);
427 BMO_op_exec(em->bm, &bmop);
428 /* Don't flush selection in edge/vertex mode. */
430 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
431 BMO_op_finish(em->bm, &bmop);
432
435
436 /* only needed for select less, ensure we don't have isolated elements remaining */
438}
439
440void EDBM_flag_disable_all(BMEditMesh *em, const char hflag)
441{
443
444 /* Keep this as there is no need to maintain UV selection when all are disabled. */
445 if (hflag & BM_ELEM_SELECT) {
447 }
448}
449
450void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
451{
453
454 /* Keep this as there is no need to maintain UV selection when all are enabled. */
455 if (hflag & BM_ELEM_SELECT) {
457 }
458}
459
461{
462 return BM_mesh_uvselect_clear(em->bm);
463}
464
466
467/* -------------------------------------------------------------------- */
470
471UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool respect_hide)
472{
473 /* NOTE: delimiting on alternate face-winding was once supported and could be useful
474 * in some cases. If this is need see: D17137 to restore support. */
475 BMVert *ev;
476 BMFace *efa;
477 BMLoop *l;
478 BMIter iter, liter;
479 uint a;
480 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
481
483
484 const int totverts = bm->totvert;
485 int totuv = 0;
486
487 /* generate UvMapVert array */
488 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
489 if (use_select && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
490 continue;
491 }
492 if (respect_hide && BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
493 continue;
494 }
495 totuv += efa->len;
496 }
497
498 if (totuv == 0) {
499 return nullptr;
500 }
501 UvVertMap *vmap = (UvVertMap *)MEM_callocN(sizeof(*vmap), "UvVertMap");
502 if (!vmap) {
503 return nullptr;
504 }
505
506 vmap->vert = MEM_calloc_arrayN<UvMapVert *>(totverts, "UvMapVert_pt");
507 UvMapVert *buf = vmap->buf = MEM_calloc_arrayN<UvMapVert>(totuv, "UvMapVert");
508
509 if (!vmap->vert || !vmap->buf) {
511 return nullptr;
512 }
513
514 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
515 if (use_select && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
516 continue;
517 }
518 if (respect_hide && BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
519 continue;
520 }
521 int i;
522 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
523 buf->loop_of_face_index = i;
524 buf->face_index = a;
525 buf->separate = false;
526
527 buf->next = vmap->vert[BM_elem_index_get(l->v)];
528 vmap->vert[BM_elem_index_get(l->v)] = buf;
529 buf++;
530 }
531 }
532
533 /* sort individual uvs for each vert */
534 BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, a) {
535 UvMapVert *newvlist = nullptr, *vlist = vmap->vert[a];
536 UvMapVert *iterv, *v, *lastv, *next;
537 const float *uv, *uv2;
538
539 while (vlist) {
540 v = vlist;
541 vlist = vlist->next;
542 v->next = newvlist;
543 newvlist = v;
544
545 efa = BM_face_at_index(bm, v->face_index);
546
547 l = static_cast<BMLoop *>(
548 BM_iter_at_index(bm, BM_LOOPS_OF_FACE, efa, v->loop_of_face_index));
549 uv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
550
551 lastv = nullptr;
552 iterv = vlist;
553
554 while (iterv) {
555 next = iterv->next;
556 efa = BM_face_at_index(bm, iterv->face_index);
557 l = static_cast<BMLoop *>(
559 uv2 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
560
561 if (compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT)) {
562 if (lastv) {
563 lastv->next = next;
564 }
565 else {
566 vlist = next;
567 }
568 iterv->next = newvlist;
569 newvlist = iterv;
570 }
571 else {
572 lastv = iterv;
573 }
574
575 iterv = next;
576 }
577
578 newvlist->separate = true;
579 }
580
581 vmap->vert[a] = newvlist;
582 }
583
584 return vmap;
585}
586
588{
589 return vmap->vert[v];
590}
591
593{
594 if (element_map->head_table) {
595 return element_map->head_table;
596 }
597
598 /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
599 element_map->head_table = static_cast<UvElement **>(
600 MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs, __func__));
601 UvElement **head_table = element_map->head_table;
602 for (int i = 0; i < element_map->total_uvs; i++) {
603 UvElement *head = element_map->storage + i;
604 if (head->separate) {
605 UvElement *element = head;
606 while (element) {
607 head_table[element - element_map->storage] = head;
608 element = element->next;
609 if (element && element->separate) {
610 break;
611 }
612 }
613 }
614 }
615 return element_map->head_table;
616}
617
619{
620 if (!element_map->unique_index_table) {
621 element_map->unique_index_table = static_cast<int *>(
622 MEM_callocN(element_map->total_uvs * sizeof(*element_map->unique_index_table), __func__));
623
624 int j = 0;
625 for (int i = 0; i < element_map->total_uvs; i++) {
626 UvElement *element = element_map->storage + i;
627 if (!element->separate) {
628 continue;
629 }
630 BLI_assert(0 <= j);
631 BLI_assert(j < element_map->total_unique_uvs);
632 while (element) {
633 element_map->unique_index_table[element - element_map->storage] = j;
634 element = element->next;
635 if (!element || element->separate) {
636 break;
637 }
638 }
639 j++;
640 }
641 BLI_assert(j == element_map->total_unique_uvs);
642 }
643
644 return element_map->unique_index_table;
645}
646
648{
649 int *unique_index = BM_uv_element_map_ensure_unique_index(element_map);
650 int index = child - element_map->storage;
651 BLI_assert(0 <= index);
652 BLI_assert(index < element_map->total_uvs);
653 return unique_index[index];
654}
655
656#define INVALID_ISLAND uint(-1)
657
658static void bm_uv_assign_island(UvElementMap *element_map,
660 int nisland,
661 uint *map,
662 UvElement *islandbuf,
663 int islandbufsize)
664{
665 element->island = nisland;
666 map[element - element_map->storage] = islandbufsize;
667
668 /* Copy *element to islandbuf[islandbufsize]. */
669 islandbuf[islandbufsize].l = element->l;
670 islandbuf[islandbufsize].separate = element->separate;
671 islandbuf[islandbufsize].loop_of_face_index = element->loop_of_face_index;
672 islandbuf[islandbufsize].island = element->island;
673 islandbuf[islandbufsize].flag = element->flag;
674}
675
677 const Scene *scene,
678 const BMesh *bm,
679 UvElement *islandbuf,
680 uint *map,
681 bool uv_selected,
682 const BMUVOffsets &offsets)
683{
685
686 int total_uvs = element_map->total_uvs;
687
688 /* Depth first search the graph, building islands as we go. */
689 int nislands = 0;
690 int islandbufsize = 0;
691 int stack_upper_bound = total_uvs;
692 UvElement **stack_uv = static_cast<UvElement **>(
693 MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, __func__));
694 int stacksize_uv = 0;
695 for (int i = 0; i < total_uvs; i++) {
696 UvElement *element = element_map->storage + i;
697 if (element->island != INVALID_ISLAND) {
698 /* Unique UV (element and all its children) are already part of an island. */
699 continue;
700 }
701
702 /* Create a new island, i.e. nislands++. */
703
704 BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */
705
706 /* Seed the graph search. */
707 stack_uv[stacksize_uv++] = element;
708 while (element) {
709 bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
710 element = element->next;
711 if (element && element->separate) {
712 break;
713 }
714 }
715
716 /* Traverse the graph. */
717 while (stacksize_uv) {
718 BLI_assert(stacksize_uv < stack_upper_bound);
719 element = stack_uv[--stacksize_uv];
720 while (element) {
721
722 /* Scan forwards around the BMFace that contains element->l. */
723 if (!uv_selected || uvedit_edge_select_test(scene, bm, element->l, offsets)) {
724 UvElement *next = BM_uv_element_get(element_map, element->l->next);
725 if (next && next->island == INVALID_ISLAND) {
726 UvElement *tail = element_map->head_table[next - element_map->storage];
727 stack_uv[stacksize_uv++] = tail;
728 while (tail) {
729 bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
730 tail = tail->next;
731 if (tail && tail->separate) {
732 break;
733 }
734 }
735 }
736 }
737
738 /* Scan backwards around the BMFace that contains element->l. */
739 if (!uv_selected || uvedit_edge_select_test(scene, bm, element->l->prev, offsets)) {
740 UvElement *prev = BM_uv_element_get(element_map, element->l->prev);
741 if (prev && prev->island == INVALID_ISLAND) {
742 UvElement *tail = element_map->head_table[prev - element_map->storage];
743 stack_uv[stacksize_uv++] = tail;
744 while (tail) {
745 bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
746 tail = tail->next;
747 if (tail && tail->separate) {
748 break;
749 }
750 }
751 }
752 }
753
754 /* The same for all the UvElements in this unique UV. */
755 element = element->next;
756 if (element && element->separate) {
757 break;
758 }
759 }
760 }
761 nislands++;
762 }
763 BLI_assert(islandbufsize == total_uvs);
764
765 MEM_SAFE_FREE(stack_uv);
766 MEM_SAFE_FREE(element_map->head_table);
767
768 return nislands;
769}
770
771static void bm_uv_build_islands(UvElementMap *element_map,
772 BMesh *bm,
773 const Scene *scene,
774 bool uv_selected)
775{
776 int totuv = element_map->total_uvs;
777 int nislands = 0;
778 int islandbufsize = 0;
779
780 /* map holds the map from current vmap->buf to the new, sorted map */
781 uint *map = MEM_malloc_arrayN<uint>(totuv, __func__);
782 BMFace **stack = MEM_malloc_arrayN<BMFace *>(bm->totface, __func__);
783 UvElement *islandbuf = MEM_calloc_arrayN<UvElement>(totuv, __func__);
784 /* Island number for BMFaces. */
785 int *island_number = MEM_calloc_arrayN<int>(bm->totface, __func__);
786 copy_vn_i(island_number, bm->totface, INVALID_ISLAND);
787
788 const BMUVOffsets uv_offsets = BM_uv_map_offsets_get(bm);
789
790 const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_FLAG_SELECT_SYNC ?
793 if (use_uv_edge_connectivity) {
795 element_map, scene, bm, islandbuf, map, uv_selected, uv_offsets);
796 islandbufsize = totuv;
797 }
798
799 for (int i = 0; i < totuv; i++) {
800 if (element_map->storage[i].island == INVALID_ISLAND) {
801 int stacksize = 0;
802 element_map->storage[i].island = nislands;
803 stack[0] = element_map->storage[i].l->f;
804 island_number[BM_elem_index_get(stack[0])] = nislands;
805 stacksize = 1;
806
807 while (stacksize > 0) {
808 BMFace *efa = stack[--stacksize];
809
810 BMLoop *l;
811 BMIter liter;
812 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
813 if (uv_selected && !uvedit_uv_select_test(scene, bm, l, uv_offsets)) {
814 continue;
815 }
816
817 UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)];
818
819 for (UvElement *element = initelement; element; element = element->next) {
820 if (element->separate) {
821 initelement = element;
822 }
823
824 if (element->l->f == efa) {
825 /* found the uv corresponding to our face and vertex.
826 * Now fill it to the buffer */
827 bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
828
829 for (element = initelement; element; element = element->next) {
830 if (element->separate && element != initelement) {
831 break;
832 }
833
834 if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
835 stack[stacksize++] = element->l->f;
836 island_number[BM_elem_index_get(element->l->f)] = nislands;
837 }
838 }
839 break;
840 }
841 }
842 }
843 }
844
845 nislands++;
846 }
847 }
848
849 MEM_SAFE_FREE(island_number);
850
851 /* remap */
852 for (int i = 0; i < bm->totvert; i++) {
853 /* important since we may do selection only. Some of these may be nullptr */
854 if (element_map->vertex[i]) {
855 element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]];
856 }
857 }
858
859 element_map->island_indices = MEM_calloc_arrayN<int>(nislands, __func__);
860 element_map->island_total_uvs = MEM_calloc_arrayN<int>(nislands, __func__);
861 element_map->island_total_unique_uvs = MEM_calloc_arrayN<int>(nislands, __func__);
862 int j = 0;
863 for (int i = 0; i < totuv; i++) {
864 UvElement *next = element_map->storage[i].next;
865 islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : nullptr;
866
867 if (islandbuf[i].island != j) {
868 j++;
869 element_map->island_indices[j] = i;
870 }
871 BLI_assert(islandbuf[i].island == j);
872 element_map->island_total_uvs[j]++;
873 if (islandbuf[i].separate) {
874 element_map->island_total_unique_uvs[j]++;
875 }
876 }
877
878 MEM_SAFE_FREE(element_map->storage);
879 element_map->storage = islandbuf;
880 islandbuf = nullptr;
881 element_map->total_islands = nislands;
882 MEM_SAFE_FREE(stack);
883 MEM_SAFE_FREE(map);
884}
885
887static bool loop_uv_match(BMLoop *loop,
888 const float luv_a[2],
889 const float luv_b[2],
890 int cd_loop_uv_offset)
891{
892 const float *luv_c = BM_ELEM_CD_GET_FLOAT_P(loop, cd_loop_uv_offset);
893 const float *luv_d = BM_ELEM_CD_GET_FLOAT_P(loop->next, cd_loop_uv_offset);
894 return compare_v2v2(luv_a, luv_c, STD_UV_CONNECT_LIMIT) &&
895 compare_v2v2(luv_b, luv_d, STD_UV_CONNECT_LIMIT);
896}
897
917 const float luv_anchor[2],
918 const float luv_fan[2],
919 const BMLoop *needle,
920 GSet *visited,
921 const int cd_loop_uv_offset)
922{
923 BMVert *anchor = needle->v;
924 BLI_assert(edge->v1 == anchor || edge->v2 == anchor);
925
926 if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
927 return false; /* Edge is a seam, don't traverse. */
928 }
929
930 if (!BLI_gset_add(visited, edge)) {
931 return false; /* Already visited. */
932 }
933
934 BMLoop *loop;
935 BMIter liter;
936 BM_ITER_ELEM (loop, &liter, edge, BM_LOOPS_OF_EDGE) {
937 if (loop->v == anchor) {
938 if (!loop_uv_match(loop, luv_anchor, luv_fan, cd_loop_uv_offset)) {
939 continue; /* `loop` is disjoint in UV space. */
940 }
941
942 if (loop == needle) {
943 return true; /* Success. */
944 }
945
946 const float *luv_far = BM_ELEM_CD_GET_FLOAT_P(loop->prev, cd_loop_uv_offset);
948 loop->prev->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset))
949 {
950 return true;
951 }
952 }
953 else {
954 BLI_assert(loop->next->v == anchor);
955 if (!loop_uv_match(loop, luv_fan, luv_anchor, cd_loop_uv_offset)) {
956 continue; /* `loop` is disjoint in UV space. */
957 }
958
959 if (loop->next == needle) {
960 return true; /* Success. */
961 }
962
963 const float *luv_far = BM_ELEM_CD_GET_FLOAT_P(loop->next->next, cd_loop_uv_offset);
965 loop->next->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset))
966 {
967 return true;
968 }
969 }
970 }
971
972 return false;
973}
974
981static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
982{
983 BLI_assert(loop_a && loop_b);
984 BLI_assert(loop_a != loop_b);
985 BLI_assert(loop_a->v == loop_b->v);
986
987 BLI_gset_clear(visited, nullptr);
988
989 const float *luv_anchor = BM_ELEM_CD_GET_FLOAT_P(loop_a, cd_loop_uv_offset);
990 const float *luv_next_fan = BM_ELEM_CD_GET_FLOAT_P(loop_a->next, cd_loop_uv_offset);
992 loop_a->e, luv_anchor, luv_next_fan, loop_b, visited, cd_loop_uv_offset);
993 if (!result) {
994 /* Search around `loop_a` in the opposite direction, as one of the edges may be delimited by
995 * a boundary, seam or disjoint UV, or itself be one of these. See: #103670, #103787. */
996 const float *luv_prev_fan = BM_ELEM_CD_GET_FLOAT_P(loop_a->prev, cd_loop_uv_offset);
998 loop_a->prev->e, luv_anchor, luv_prev_fan, loop_b, visited, cd_loop_uv_offset);
999 }
1000
1001 return result;
1002}
1003
1005 const Scene *scene,
1006 const bool uv_selected,
1007 const bool use_winding,
1008 const bool use_seams,
1009 const bool do_islands)
1010{
1011 /* In uv sync selection, all UVs (from unhidden geometry) are visible. */
1012 const bool face_selected = !(scene->toolsettings->uv_flag & UV_FLAG_SELECT_SYNC);
1013
1014 BMVert *ev;
1015 BMFace *efa;
1016 BMIter iter, liter;
1017
1018 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1019 if (offsets.uv < 0) {
1020 return nullptr;
1021 }
1022
1024
1025 /* Count total uvs. */
1026 int totuv = 0;
1027 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1029 continue;
1030 }
1031
1032 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1033 continue;
1034 }
1035
1036 if (!uv_selected) {
1037 totuv += efa->len;
1038 }
1039 else {
1040 BMLoop *l;
1041 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1042 if (uvedit_uv_select_test(scene, bm, l, offsets)) {
1043 totuv++;
1044 }
1045 }
1046 }
1047 }
1048
1049 if (totuv == 0) {
1050 return nullptr;
1051 }
1052
1053 UvElementMap *element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap");
1054 element_map->total_uvs = totuv;
1055 element_map->vertex = (UvElement **)MEM_callocN(sizeof(*element_map->vertex) * bm->totvert,
1056 "UvElementVerts");
1057 element_map->storage = (UvElement *)MEM_callocN(sizeof(*element_map->storage) * totuv,
1058 "UvElement");
1059
1060 bool *winding = use_winding ? MEM_calloc_arrayN<bool>(bm->totface, "winding") : nullptr;
1061
1062 UvElement *buf = element_map->storage;
1063 int j;
1064 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) {
1065
1067 continue;
1068 }
1069
1070 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1071 continue;
1072 }
1073
1074 int i;
1075 BMLoop *l;
1076 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1077 if (uv_selected && !uvedit_uv_select_test(scene, bm, l, offsets)) {
1078 continue;
1079 }
1080
1081 buf->l = l;
1082 buf->island = INVALID_ISLAND;
1083 buf->loop_of_face_index = i;
1084
1085 /* Insert to head of linked list associated with BMVert. */
1086 buf->next = element_map->vertex[BM_elem_index_get(l->v)];
1087 element_map->vertex[BM_elem_index_get(l->v)] = buf;
1088
1089 buf++;
1090 }
1091
1092 if (winding) {
1093 winding[j] = BM_face_calc_area_uv_signed(efa, offsets.uv) > 0;
1094 }
1095 }
1096
1097 GSet *seam_visited_gset = use_seams ? BLI_gset_ptr_new(__func__) : nullptr;
1098
1099 /* For each BMVert, sort associated linked list into unique uvs. */
1100 int ev_index;
1101 BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) {
1102 UvElement *newvlist = nullptr;
1103 UvElement *vlist = element_map->vertex[ev_index];
1104 while (vlist) {
1105
1106 /* Detach head from unsorted list. */
1107 UvElement *v = vlist;
1108 vlist = vlist->next;
1109 v->next = newvlist;
1110 newvlist = v;
1111
1112 const float *uv = static_cast<const float *>(BM_ELEM_CD_GET_VOID_P(v->l, offsets.uv));
1113 bool uv_vert_sel = uvedit_uv_select_test(scene, bm, v->l, offsets);
1114
1115 UvElement *lastv = nullptr;
1116 UvElement *iterv = vlist;
1117
1118 /* Scan through unsorted list, finding UvElements which are connected to `v`. */
1119 while (iterv) {
1120 UvElement *next = iterv->next;
1121
1122 bool connected = true; /* Assume connected unless we can prove otherwise. */
1123
1124 if (connected) {
1125 /* Are the two UVs close together? */
1126 const float *uv2 = BM_ELEM_CD_GET_FLOAT_P(iterv->l, offsets.uv);
1127 connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
1128 }
1129
1130 if (connected) {
1131 /* Check if the uv loops share the same selection state (if not, they are not connected
1132 * as they have been ripped or other edit commands have separated them). */
1133 const bool uv2_vert_sel = uvedit_uv_select_test(scene, bm, iterv->l, offsets);
1134 connected = (uv_vert_sel == uv2_vert_sel);
1135 }
1136
1137 if (connected && use_winding) {
1138 connected = winding[BM_elem_index_get(iterv->l->f)] ==
1139 winding[BM_elem_index_get(v->l->f)];
1140 }
1141
1142 if (connected && use_seams) {
1143 connected = seam_connected(iterv->l, v->l, seam_visited_gset, offsets.uv);
1144 }
1145
1146 if (connected) {
1147 if (lastv) {
1148 lastv->next = next;
1149 }
1150 else {
1151 vlist = next;
1152 }
1153 iterv->next = newvlist;
1154 newvlist = iterv;
1155 }
1156 else {
1157 lastv = iterv;
1158 }
1159
1160 iterv = next;
1161 }
1162
1163 element_map->total_unique_uvs++;
1164 newvlist->separate = true;
1165 }
1166
1167 /* Write back sorted list. */
1168 element_map->vertex[ev_index] = newvlist;
1169 }
1170
1171 if (seam_visited_gset) {
1172 BLI_gset_free(seam_visited_gset, nullptr);
1173 seam_visited_gset = nullptr;
1174 }
1175 MEM_SAFE_FREE(winding);
1176
1177 /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
1178 * Now we should sort uv's in islands. */
1179 if (do_islands) {
1180 bm_uv_build_islands(element_map, bm, scene, uv_selected);
1181 }
1182
1183 /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */
1184 element_map->total_unique_uvs = 0;
1185 for (int i = 0; i < element_map->total_uvs; i++) {
1186 if (element_map->storage[i].separate) {
1187 element_map->total_unique_uvs++;
1188 }
1189 }
1190
1191 return element_map;
1192}
1193
1195{
1196 if (vmap) {
1197 if (vmap->vert) {
1198 MEM_freeN(vmap->vert);
1199 }
1200 if (vmap->buf) {
1201 MEM_freeN(vmap->buf);
1202 }
1203 MEM_freeN(vmap);
1204 }
1205}
1206
1208{
1209 if (element_map) {
1210 MEM_SAFE_FREE(element_map->storage);
1211 MEM_SAFE_FREE(element_map->vertex);
1212 MEM_SAFE_FREE(element_map->head_table);
1213 MEM_SAFE_FREE(element_map->unique_index_table);
1214 MEM_SAFE_FREE(element_map->island_indices);
1215 MEM_SAFE_FREE(element_map->island_total_uvs);
1217 MEM_SAFE_FREE(element_map);
1218 }
1219}
1220
1222{
1223 UvElement *element = element_map->vertex[BM_elem_index_get(l->v)];
1224 while (element) {
1225 if (element->l == l) {
1226 return element;
1227 }
1228 element = element->next;
1229 }
1230
1231 return nullptr;
1232}
1233
1235{
1236 if (!child) {
1237 return nullptr;
1238 }
1239
1240 return element_map->vertex[BM_elem_index_get(child->l->v)];
1241}
1242
1244
1245/* -------------------------------------------------------------------- */
1248
1249BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
1250{
1251 if (!EDBM_uv_check(em)) {
1252 return nullptr;
1253 }
1254 BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
1255 if (efa) {
1256 return efa;
1257 }
1258
1259 return nullptr;
1260}
1261
1263{
1264 /* some of these checks could be a touch overkill */
1265 return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_FLOAT2);
1266}
1267
1269{
1270 /* some of these checks could be a touch overkill */
1271 return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_BYTE_COLOR);
1272}
1273
1275
1276/* -------------------------------------------------------------------- */
1279
1280static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
1281{
1282 intptr_t eve_i = index_lookup[index];
1283 return (eve_i == -1) ? nullptr : (BMVert *)eve_i;
1284}
1285
1301
1302/* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a preference. */
1303#define BM_SEARCH_MAXDIST_MIRR 0.00002f
1304#define BM_CD_LAYER_ID "__mirror_index"
1305
1307 const int axis,
1308 const bool use_self,
1309 const bool use_select,
1310 const bool respecthide,
1311 /* extra args */
1312 const bool use_topology,
1313 float maxdist,
1314 int *r_index)
1315{
1316 BMesh *bm = em->bm;
1317 BMIter iter;
1318 BMVert *v;
1319 int cd_vmirr_offset = 0;
1320 int i;
1321 const float maxdist_sq = square_f(maxdist);
1322
1323 /* one or the other is used depending if topo is enabled */
1324 KDTree_3d *tree = nullptr;
1325 MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false};
1326
1328
1329 if (r_index == nullptr) {
1330 const char *layer_id = BM_CD_LAYER_ID;
1332 if (em->mirror_cdlayer == -1) {
1333 BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT32, layer_id);
1335 }
1336
1337 cd_vmirr_offset = CustomData_get_n_offset(
1338 &bm->vdata,
1341
1342 bm->vdata.layers[em->mirror_cdlayer].flag |= CD_FLAG_TEMPORARY;
1343 }
1344
1346
1347 if (use_topology) {
1348 ED_mesh_mirrtopo_init(em, nullptr, &mesh_topo_store, true);
1349 }
1350 else {
1351 tree = BLI_kdtree_3d_new(bm->totvert);
1353 if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
1354 continue;
1355 }
1356
1357 BLI_kdtree_3d_insert(tree, i, v->co);
1358 }
1359 BLI_kdtree_3d_balance(tree);
1360 }
1361
1362#define VERT_INTPTR(_v, _i) \
1363 (r_index ? &r_index[_i] : static_cast<int *>(BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset)))
1364
1366 if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
1367 continue;
1368 }
1369
1370 if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
1371 continue;
1372 }
1373
1375 BMVert *v_mirr;
1376 int *idx = VERT_INTPTR(v, i);
1377
1378 if (use_topology) {
1379 v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
1380 if (v_mirr != nullptr) {
1381 if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
1382 v_mirr = nullptr;
1383 }
1384 }
1385 }
1386 else {
1387 int i_mirr;
1388 float co[3];
1389 copy_v3_v3(co, v->co);
1390 co[axis] *= -1.0f;
1391
1392 v_mirr = nullptr;
1393 i_mirr = BLI_kdtree_3d_find_nearest(tree, co, nullptr);
1394 if (i_mirr != -1) {
1395 BMVert *v_test = BM_vert_at_index(bm, i_mirr);
1396 if (len_squared_v3v3(co, v_test->co) < maxdist_sq) {
1397 v_mirr = v_test;
1398 }
1399 }
1400 }
1401
1402 if (v_mirr && (use_self || (v_mirr != v))) {
1403 const int i_mirr = BM_elem_index_get(v_mirr);
1404 *idx = i_mirr;
1405 idx = VERT_INTPTR(v_mirr, i_mirr);
1406 *idx = i;
1407 }
1408 else {
1409 *idx = -1;
1410 }
1411 }
1412
1413#undef VERT_INTPTR
1414
1415 if (use_topology) {
1417 }
1418 else {
1419 BLI_kdtree_3d_free(tree);
1420 }
1421}
1422
1424 const int axis,
1425 const bool use_self,
1426 const bool use_select,
1427 const bool respecthide,
1428 const bool use_topology)
1429{
1431 axis,
1432 use_self,
1433 use_select,
1434 respecthide,
1435 /* extra args */
1436 use_topology,
1438 nullptr);
1439}
1440
1442{
1443 const int *mirr = static_cast<const int *>(
1444 CustomData_bmesh_get_layer_n(&em->bm->vdata, v->head.data, em->mirror_cdlayer));
1445
1446 BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
1447
1448 if (mirr && *mirr >= 0 && *mirr < em->bm->totvert) {
1449 if (!em->bm->vtable) {
1450 printf(
1451 "err: should only be called between "
1452 "EDBM_verts_mirror_cache_begin and EDBM_verts_mirror_cache_end");
1453 return nullptr;
1454 }
1455
1456 return em->bm->vtable[*mirr];
1457 }
1458
1459 return nullptr;
1460}
1461
1463{
1464 BMVert *v1_mirr, *v2_mirr;
1465 if ((v1_mirr = EDBM_verts_mirror_get(em, e->v1)) &&
1466 (v2_mirr = EDBM_verts_mirror_get(em, e->v2)) &&
1467 /* While highly unlikely, a zero length central edges vertices can match, see #89342. */
1468 LIKELY(v1_mirr != v2_mirr))
1469 {
1470 return BM_edge_exists(v1_mirr, v2_mirr);
1471 }
1472
1473 return nullptr;
1474}
1475
1477{
1479
1480 BMLoop *l_iter, *l_first;
1481 uint i = 0;
1482
1483 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1484 do {
1485 if ((v_mirr_arr[i++] = EDBM_verts_mirror_get(em, l_iter->v)) == nullptr) {
1486 return nullptr;
1487 }
1488 } while ((l_iter = l_iter->next) != l_first);
1489
1490 return BM_face_exists(v_mirr_arr.data(), v_mirr_arr.size());
1491}
1492
1494{
1495 int *mirr = static_cast<int *>(
1496 CustomData_bmesh_get_layer_n(&em->bm->vdata, v->head.data, em->mirror_cdlayer));
1497
1498 BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
1499
1500 if (mirr) {
1501 *mirr = -1;
1502 }
1503}
1504
1506{
1507 em->mirror_cdlayer = -1;
1508}
1509
1510void EDBM_verts_mirror_apply(BMEditMesh *em, const int sel_from, const int sel_to)
1511{
1512 BMIter iter;
1513 BMVert *v;
1514
1515 BLI_assert((em->bm->vtable != nullptr) && ((em->bm->elem_table_dirty & BM_VERT) == 0));
1516
1517 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1518 if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) {
1519 BMVert *mirr = EDBM_verts_mirror_get(em, v);
1520 if (mirr) {
1521 if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) {
1522 copy_v3_v3(mirr->co, v->co);
1523 mirr->co[0] *= -1.0f;
1524 }
1525 }
1526 }
1527 }
1528}
1529
1531
1532/* -------------------------------------------------------------------- */
1535
1537{
1538 BMIter iter;
1539 BMElem *ele;
1540 int itermode;
1541 char hflag_swap = swap ? BM_ELEM_SELECT : 0;
1542 bool changed = false;
1543
1544 if (em->selectmode & SCE_SELECT_VERTEX) {
1545 itermode = BM_VERTS_OF_MESH;
1546 }
1547 else if (em->selectmode & SCE_SELECT_EDGE) {
1548 itermode = BM_EDGES_OF_MESH;
1549 }
1550 else {
1551 itermode = BM_FACES_OF_MESH;
1552 }
1553
1554 BM_ITER_MESH (ele, &iter, em->bm, itermode) {
1555 if (!BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
1556 if (BM_elem_flag_test(ele, BM_ELEM_SELECT) ^ hflag_swap) {
1557 BM_elem_hide_set(em->bm, ele, true);
1558 changed = true;
1559 }
1560 }
1561 }
1562
1563 /* Hiding unselected. */
1564 if (swap) {
1565 /* In face select mode, also hide loose edges that aren't part of any visible face. */
1566 if (itermode == BM_FACES_OF_MESH) {
1567 BMEdge *e;
1568 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
1569 if (!BM_edge_is_wire(e)) {
1570 continue;
1571 }
1573 BM_elem_hide_set(em->bm, (BMElem *)e, true);
1574 changed = true;
1575 }
1576 }
1577 }
1578 /* In edge or face select mode, also hide isolated verts that aren't connected to an edge. */
1579 if (ELEM(itermode, BM_EDGES_OF_MESH, BM_FACES_OF_MESH)) {
1580 BMVert *v;
1581 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1582 if (v->e) {
1583 continue;
1584 }
1586 BM_elem_hide_set(em->bm, (BMElem *)v, true);
1587 changed = true;
1588 }
1589 }
1590 }
1591 }
1592
1593 if (changed) {
1596 }
1597 return changed;
1598
1599 /* original hide flushing comment (OUTDATED):
1600 * hide happens on least dominant select mode, and flushes up, not down!
1601 * (helps preventing errors in subsurf) */
1602 /* - vertex hidden, always means edge is hidden too
1603 * - edge hidden, always means face is hidden too
1604 * - face hidden, only set face hide
1605 * - then only flush back down what's absolute hidden
1606 */
1607}
1608
1610{
1611 const char iter_types[3] = {
1615 };
1616
1617 const bool sels[3] = {
1618 (em->selectmode & SCE_SELECT_VERTEX) != 0,
1619 (em->selectmode & SCE_SELECT_EDGE) != 0,
1620 (em->selectmode & SCE_SELECT_FACE) != 0,
1621 };
1622 int i;
1623 bool changed = false;
1624
1625 /* Use tag flag to remember what was hidden before all is revealed.
1626 * BM_ELEM_HIDDEN --> BM_ELEM_TAG */
1627 for (i = 0; i < 3; i++) {
1628 BMIter iter;
1629 BMElem *ele;
1630
1631 BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
1634 changed = true;
1635 }
1636 else {
1638 }
1639 }
1640 }
1641
1642 if (!changed) {
1643 return false;
1644 }
1645
1646 /* Reveal everything */
1648
1649 /* Select relevant just-revealed elements */
1650 for (i = 0; i < 3; i++) {
1651 BMIter iter;
1652 BMElem *ele;
1653
1654 if (!sels[i]) {
1655 continue;
1656 }
1657
1658 BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
1659 if (BM_elem_flag_test(ele, BM_ELEM_TAG)) {
1660 BM_elem_select_set(em->bm, ele, select);
1661 }
1662 }
1663 }
1664
1665 if (em->bm->uv_select_sync_valid) {
1666 BMesh *bm = em->bm;
1667 /* NOTE(@ideasman42): this could/should use the "sticky" tool setting.
1668 * Although in practice it's OK to assume "connected" sticky in this case. */
1669 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
1670 if (cd_loop_uv_offset == -1) {
1671 /* Not expected but not an error either, clear if the UV's have been removed. */
1673 }
1674 else {
1675 BMIter iter;
1676 BMFace *f;
1677
1678 if (em->selectmode & SCE_SELECT_VERTEX) {
1679 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1680 BMLoop *l_iter, *l_first;
1681 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1682 do {
1683 if (BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) {
1684 BM_loop_vert_uvselect_set_shared(bm, l_iter, select, cd_loop_uv_offset);
1685 }
1686 } while ((l_iter = l_iter->next) != l_first);
1687 }
1688 }
1689 else if (em->selectmode & SCE_SELECT_EDGE) {
1690 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1691 BMLoop *l_iter, *l_first;
1692 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1693 do {
1694 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG)) {
1695 BM_loop_edge_uvselect_set_shared(bm, l_iter, select, cd_loop_uv_offset);
1696 }
1697 } while ((l_iter = l_iter->next) != l_first);
1698 }
1699 }
1700 else {
1701 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1703 BM_face_uvselect_set_shared(bm, f, select, cd_loop_uv_offset);
1704 }
1705 }
1706 }
1707
1709 }
1710 }
1711
1713
1714 /* hidden faces can have invalid normals */
1716
1717 return true;
1718}
1719
1721
1722/* -------------------------------------------------------------------- */
1725
1730
1732{
1734 params.face_normals = true;
1736}
1737
1739{
1740 const char iter_types[3] = {
1744 };
1745
1746 BMIter iter;
1747 BMElem *ele;
1748 int *tots[3];
1749 int i;
1750
1751 tots[0] = &em->bm->totvertsel;
1752 tots[1] = &em->bm->totedgesel;
1753 tots[2] = &em->bm->totfacesel;
1754
1755 em->bm->totvertsel = em->bm->totedgesel = em->bm->totfacesel = 0;
1756
1757 for (i = 0; i < 3; i++) {
1758 ele = static_cast<BMElem *>(BM_iter_new(&iter, em->bm, iter_types[i], nullptr));
1759 for (; ele; ele = static_cast<BMElem *>(BM_iter_step(&iter))) {
1761 (*tots[i])++;
1762 }
1763 }
1764 }
1765}
1766
1768{
1769 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1770 /* Order of calling isn't important. */
1773
1774 if (params->calc_normals && params->calc_looptris) {
1775 /* Calculating both has some performance gains. */
1777 }
1778 else {
1779 if (params->calc_normals) {
1781 }
1782
1783 if (params->calc_looptris) {
1785 }
1786 }
1787
1788 if (params->is_destructive) {
1789 /* TODO(@ideasman42): we may be able to remove this now! */
1790 // BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
1791 }
1792 else {
1793 /* in debug mode double check we didn't need to recalculate */
1795 }
1797 BM_lnorspace_invalidate(em->bm, false);
1799 }
1800
1801#ifndef NDEBUG
1802 {
1805 }
1806 }
1807#endif
1808}
1809
1810void EDBM_update_extern(Mesh *mesh, const bool do_tessellation, const bool is_destructive)
1811{
1813 params.calc_looptris = do_tessellation;
1814 params.calc_normals = false;
1815 params.is_destructive = is_destructive;
1816 EDBM_update(mesh, &params);
1817}
1818
1820
1821/* -------------------------------------------------------------------- */
1824
1826{
1828 return true;
1829 }
1830
1831 return false;
1832}
1833
1835
1836/* -------------------------------------------------------------------- */
1839
1841{
1842 if ((em->selectmode & SCE_SELECT_VERTEX) && eve) {
1843 return (BMElem *)eve;
1844 }
1845 if ((em->selectmode & SCE_SELECT_EDGE) && eed) {
1846 return (BMElem *)eed;
1847 }
1848 if ((em->selectmode & SCE_SELECT_FACE) && efa) {
1849 return (BMElem *)efa;
1850 }
1851 return nullptr;
1852}
1853
1855{
1856 BMesh *bm = em->bm;
1857 int index = BM_elem_index_get(ele);
1858
1859 if (ele->head.htype == BM_VERT) {
1860 BLI_assert(!(bm->elem_index_dirty & BM_VERT));
1861 }
1862 else if (ele->head.htype == BM_EDGE) {
1863 BLI_assert(!(bm->elem_index_dirty & BM_EDGE));
1864 index += bm->totvert;
1865 }
1866 else if (ele->head.htype == BM_FACE) {
1867 BLI_assert(!(bm->elem_index_dirty & BM_FACE));
1868 index += bm->totvert + bm->totedge;
1869 }
1870 else {
1871 BLI_assert(0);
1872 }
1873
1874 return index;
1875}
1876
1878{
1879 BMesh *bm = em->bm;
1880
1881 if (index < bm->totvert) {
1882 return (BMElem *)BM_vert_at_index_find_or_table(bm, index);
1883 }
1884 index -= bm->totvert;
1885 if (index < bm->totedge) {
1886 return (BMElem *)BM_edge_at_index_find_or_table(bm, index);
1887 }
1888 index -= bm->totedge;
1889 if (index < bm->totface) {
1890 return (BMElem *)BM_face_at_index_find_or_table(bm, index);
1891 }
1892
1893 return nullptr;
1894}
1895
1897 const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
1898{
1899 int elem_index = -1;
1900 *r_object_index = -1;
1901 Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, nullptr);
1902 for (const int base_index : bases.index_range()) {
1903 Base *base_iter = bases[base_index];
1904 if (BKE_editmesh_from_object(base_iter->object) == em) {
1905 *r_object_index = base_index;
1906 elem_index = EDBM_elem_to_index_any(em, ele);
1907 break;
1908 }
1909 }
1910 return elem_index;
1911}
1912
1914 ViewLayer *view_layer,
1915 uint object_index,
1916 uint elem_index,
1917 Object **r_obedit)
1918{
1919 Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, nullptr);
1920 *r_obedit = nullptr;
1921 Object *obedit = (object_index < bases.size()) ? bases[object_index]->object : nullptr;
1922 if (obedit != nullptr) {
1924 BMElem *ele = EDBM_elem_from_index_any(em, elem_index);
1925 if (ele != nullptr) {
1926 *r_obedit = obedit;
1927 return ele;
1928 }
1929 }
1930 return nullptr;
1931}
1932
1934
1935/* -------------------------------------------------------------------- */
1938
1940 const BMBVHTree *tree, const float co[3], const float dir[3], float *r_hitout, const BMEdge *e)
1941{
1942 BMFace *f = BKE_bmbvh_ray_cast(tree, co, dir, 0.0f, nullptr, r_hitout, nullptr);
1943
1944 if (f && BM_edge_in_face(e, f)) {
1945 return nullptr;
1946 }
1947
1948 return f;
1949}
1950
1951static void scale_point(float c1[3], const float p[3], const float s)
1952{
1953 sub_v3_v3(c1, p);
1954 mul_v3_fl(c1, s);
1955 add_v3_v3(c1, p);
1956}
1957
1959 const BMEdge *e,
1960 const Depsgraph *depsgraph,
1961 const ARegion *region,
1962 const View3D *v3d,
1963 const Object *obedit)
1964{
1965 BMFace *f;
1966 float co1[3], co2[3], co3[3], dir1[3], dir2[3], dir3[3];
1967 float origin[3], invmat[4][4];
1968 float epsilon = 0.01f;
1969 float end[3];
1970 const float mval_f[2] = {
1971 region->winx / 2.0f,
1972 region->winy / 2.0f,
1973 };
1974
1975 ED_view3d_win_to_segment_clipped(depsgraph, region, v3d, mval_f, origin, end, false);
1976
1977 invert_m4_m4(invmat, obedit->object_to_world().ptr());
1978 mul_m4_v3(invmat, origin);
1979
1980 copy_v3_v3(co1, e->v1->co);
1981 mid_v3_v3v3(co2, e->v1->co, e->v2->co);
1982 copy_v3_v3(co3, e->v2->co);
1983
1984 scale_point(co1, co2, 0.99);
1985 scale_point(co3, co2, 0.99);
1986
1987 /* OK, idea is to generate rays going from the camera origin to the
1988 * three points on the edge (v1, mid, v2). */
1989 sub_v3_v3v3(dir1, origin, co1);
1990 sub_v3_v3v3(dir2, origin, co2);
1991 sub_v3_v3v3(dir3, origin, co3);
1992
1993 normalize_v3_length(dir1, epsilon);
1994 normalize_v3_length(dir2, epsilon);
1995 normalize_v3_length(dir3, epsilon);
1996
1997 /* Offset coordinates slightly along view vectors,
1998 * to avoid hitting the faces that own the edge. */
1999 add_v3_v3v3(co1, co1, dir1);
2000 add_v3_v3v3(co2, co2, dir2);
2001 add_v3_v3v3(co3, co3, dir3);
2002
2003 normalize_v3(dir1);
2004 normalize_v3(dir2);
2005 normalize_v3(dir3);
2006
2007 /* do three samplings: left, middle, right */
2008 f = edge_ray_cast(tree, co1, dir1, nullptr, e);
2009 if (f && !edge_ray_cast(tree, co2, dir2, nullptr, e)) {
2010 return true;
2011 }
2012 if (f && !edge_ray_cast(tree, co3, dir3, nullptr, e)) {
2013 return true;
2014 }
2015 if (!f) {
2016 return true;
2017 }
2018
2019 return false;
2020}
2021
2023
2024/* -------------------------------------------------------------------- */
2027
2029 bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
2030{
2031 using namespace blender::ed;
2032 BMIter iter;
2033 BMVert *eve;
2034
2035 ED_view3d_init_mats_rv3d(obedit, static_cast<RegionView3D *>(region->regiondata));
2036
2037 Scene *scene = CTX_data_scene(C);
2039
2041 const int snap_flag = scene->toolsettings->snap_flag;
2042
2044 target_op, !(snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), SCE_SNAP_TARGET_NOT_EDITED);
2049
2050 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2052 float mval[2], co_proj[3];
2053 if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) ==
2055 {
2057 params.snap_target_select = target_op;
2058 params.edit_mode_type = transform ::SNAP_GEOM_FINAL;
2059 params.occlusion_test = transform ::SNAP_OCCLUSION_AS_SEEM;
2061 depsgraph,
2062 region,
2065 &params,
2066 nullptr,
2067 mval,
2068 nullptr,
2069 nullptr,
2070 co_proj,
2071 nullptr))
2072 {
2073 mul_v3_m4v3(eve->co, obedit->world_to_object().ptr(), co_proj);
2074 }
2075 }
2076 }
2077 }
2078
2080}
2081
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_layer_index(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
void * CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n)
void BKE_editmesh_free_data(BMEditMesh *em)
Definition editmesh.cc:132
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_editmesh_looptris_calc(BMEditMesh *em)
Definition editmesh.cc:88
void BKE_editmesh_looptris_and_normals_calc(BMEditMesh *em)
Definition editmesh.cc:95
struct BMFace * BKE_bmbvh_ray_cast(const BMBVHTree *tree, const float co[3], const float dir[3], float radius, float *r_dist, float r_hitout[3], float r_cagehit[3])
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
BMesh * BKE_mesh_to_bmesh(Mesh *mesh, int active_shapekey, bool add_key_index, const BMeshCreateParams *params)
void BKE_mesh_uv_vert_map_free(UvVertMap *vmap)
#define STD_UV_CONNECT_LIMIT
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
eReportType
Definition BKE_report.hh:33
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GSet GSet
Definition BLI_ghash.h:337
GSet * BLI_gset_ptr_new(const char *info)
void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp)
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
A KD-tree for nearest neighbor search.
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
MINLINE float square_f(float a)
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool compare_v2v2(const float v1[2], const float v2[2], float limit) ATTR_WARN_UNUSED_RESULT
void copy_vn_i(int *array_tar, int size, int val)
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3_length(float n[3], float unit_length)
MINLINE float normalize_v3(float n[3])
unsigned int uint
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ CD_PROP_BYTE_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_FLAG_TEMPORARY
Object is a sort of wrapper for general info.
@ UV_SELECT_EDGE
@ SCE_SNAP_TO_INCLUDE_EDITED
@ SCE_SNAP_TO_INCLUDE_NONEDITED
@ SCE_SNAP_TO_ONLY_SELECTABLE
@ UV_FLAG_SELECT_SYNC
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
eSnapTargetOP
@ SCE_SNAP_TARGET_NOT_ACTIVE
@ SCE_SNAP_TARGET_NOT_NONEDITED
@ SCE_SNAP_TARGET_ONLY_SELECTABLE
@ SCE_SNAP_TARGET_NOT_EDITED
@ SCE_SNAP_TO_FACE
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
void ED_mesh_mirror_topo_table_end(Object *ob)
Definition meshtools.cc:258
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store)
void ED_mesh_mirror_spatial_table_end(Object *ob)
void ED_mesh_mirrtopo_init(BMEditMesh *em, Mesh *mesh, MirrTopoStore_t *mesh_topo_store, bool skip_em_vert_array_init)
bool ED_operator_view3d_active(bContext *C)
bool ED_operator_editmesh(bContext *C)
bool uvedit_edge_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_uv_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
eV3DProjStatus ED_view3d_project_float_object(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
bool ED_view3d_win_to_segment_clipped(const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], bool do_clip_planes)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static void free_data(ModifierData *md)
Definition MOD_bevel.cc:272
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
#define NC_SCENE
Definition WM_types.hh:378
#define ND_TOOLSETTINGS
Definition WM_types.hh:449
@ BM_SPACEARR_BMO_SET
#define BM_ALL_NOLOOP
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
BMesh * BM_mesh_copy(BMesh *bm_old)
eBMOpErrorLevel
@ BMO_ERROR_WARN
@ BMO_ERROR_FATAL
@ BMO_ERROR_CANCEL
bool BMO_error_pop(BMesh *bm, const char **r_msg, BMOperator **r_op, eBMOpErrorLevel *r_level)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const StringRef name)
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_EDGE
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
#define BM_iter_new(iter, bm, itype, data)
BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select)
void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, BMSelectFlushFlag flag)
Select Mode Flush.
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
void BM_mesh_select_mode_clean(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_select_flush_from_verts(BMesh *bm, const bool select)
#define BM_elem_hide_set(bm, ele, hide)
#define BMSelectFlushFlag_All
void BM_mesh_data_free(BMesh *bm)
BMesh Free Mesh Data.
BMVert * BM_vert_at_index_find_or_table(BMesh *bm, const int index)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMEdge * BM_edge_at_index_find_or_table(BMesh *bm, const int index)
bool BM_mesh_elem_table_check(BMesh *bm)
BMFace * BM_face_at_index_find_or_table(BMesh *bm, const int index)
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
void BM_mesh_normals_update_ex(BMesh *bm, const BMeshNormalsUpdate_Params *params)
BMesh Compute Normals.
void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
bool BMO_op_vinitf(BMesh *bm, BMOperator *op, int flag, const char *fmt, va_list vlist)
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
#define BMO_FLAG_DEFAULTS
ATTR_WARN_UNUSED_RESULT const void * element
float BM_face_calc_area_uv_signed(const BMFace *f, int cd_loop_uv_offset)
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm)
void BM_mesh_uvselect_mode_flush(BMesh *bm)
void BM_face_uvselect_set_shared(BMesh *bm, BMFace *f, bool select, const int cd_loop_uv_offset)
bool BM_mesh_uvselect_clear(BMesh *bm)
void BM_loop_vert_uvselect_set_shared(BMesh *bm, BMLoop *l, bool select, const int cd_loop_uv_offset)
void BM_loop_edge_uvselect_set_shared(BMesh *bm, BMLoop *l, bool select, const int cd_loop_uv_offset)
BPy_StructRNA * depsgraph
int64_t size() const
Definition BLI_array.hh:256
const T * data() const
Definition BLI_array.hh:312
int64_t size() const
IndexRange index_range() const
bool EDBM_vert_color_check(BMEditMesh *em)
#define INVALID_ISLAND
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, const bool uv_selected, const bool use_winding, const bool use_seams, const bool do_islands)
BMFace * EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
static BMVert * cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
UvMapVert * BM_uv_vert_map_at_index(UvVertMap *vmap, uint v)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
void EDBM_select_more(BMEditMesh *em, const bool use_face_step)
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt,...)
static int bm_uv_edge_select_build_islands(UvElementMap *element_map, const Scene *scene, const BMesh *bm, UvElement *islandbuf, uint *map, bool uv_selected, const BMUVOffsets &offsets)
static void bm_uv_assign_island(UvElementMap *element_map, UvElement *element, int nisland, uint *map, UvElement *islandbuf, int islandbufsize)
#define BM_CD_LAYER_ID
void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
bool EDBM_uvselect_clear(BMEditMesh *em)
bool EDBM_op_init(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const char *fmt,...)
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
bool EDBM_op_call_and_selectf(BMEditMesh *em, wmOperator *op, const char *select_slot_out, const bool select_extend, const char *fmt,...)
int BM_uv_element_get_unique_index(UvElementMap *element_map, UvElement *child)
void EDBM_mesh_normals_update(BMEditMesh *em)
bool EDBM_mesh_reveal(BMEditMesh *em, bool select)
void EDBM_project_snap_verts(bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report)
bool EDBM_uv_check(BMEditMesh *em)
void BM_uv_element_map_free(UvElementMap *element_map)
void EDBM_stats_update(BMEditMesh *em)
bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool use_self, const bool use_select, const bool respecthide, const bool use_topology, float maxdist, int *r_index)
static void scale_point(float c1[3], const float p[3], const float s)
static bool seam_connected_recursive(BMEdge *edge, const float luv_anchor[2], const float luv_fan[2], const BMLoop *needle, GSet *visited, const int cd_loop_uv_offset)
void EDBM_mesh_free_data(BMEditMesh *em)
void EDBM_selectmode_flush(BMEditMesh *em)
BMElem * EDBM_elem_from_selectmode(BMEditMesh *em, BMVert *eve, BMEdge *eed, BMFace *efa)
void EDBM_mesh_load(Main *bmain, Object *ob)
BMBackup EDBM_redo_state_store(BMEditMesh *em)
BMElem * EDBM_elem_from_index_any_multi(const Scene *scene, ViewLayer *view_layer, uint object_index, uint elem_index, Object **r_obedit)
#define VERT_INTPTR(_v, _i)
void EDBM_verts_mirror_cache_begin(BMEditMesh *em, const int axis, const bool use_self, const bool use_select, const bool respecthide, const bool use_topology)
BMElem * EDBM_elem_from_index_any(BMEditMesh *em, uint index)
bool EDBM_op_call_silentf(BMEditMesh *em, const char *fmt,...)
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
void EDBM_verts_mirror_cache_clear(BMEditMesh *em, BMVert *v)
int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele)
BMEdge * EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e)
UvElement ** BM_uv_element_map_ensure_head_table(UvElementMap *element_map)
void EDBM_select_less(BMEditMesh *em, const bool use_face_step)
BMFace * EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
void EDBM_select_flush_from_verts(BMEditMesh *em, const bool select)
void EDBM_mesh_make_from_mesh(Object *ob, Mesh *src_mesh, const int select_mode, const bool add_key_index)
UvElement * BM_uv_element_get_head(UvElementMap *element_map, UvElement *child)
bool EDBM_view3d_poll(bContext *C)
int * BM_uv_element_map_ensure_unique_index(UvElementMap *element_map)
void EDBM_flag_disable_all(BMEditMesh *em, const char hflag)
#define BM_SEARCH_MAXDIST_MIRR
int EDBM_elem_to_index_any_multi(const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
void EDBM_selectmode_to_scene(bContext *C)
static int object_shapenr_basis_index_ensured(const Object *ob)
void EDBM_mesh_normals_update_ex(BMEditMesh *em, const BMeshNormalsUpdate_Params *params)
UvVertMap * BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool respect_hide)
void EDBM_verts_mirror_apply(BMEditMesh *em, const int sel_from, const int sel_to)
static BMFace * edge_ray_cast(const BMBVHTree *tree, const float co[3], const float dir[3], float *r_hitout, const BMEdge *e)
void BM_uv_vert_map_free(UvVertMap *vmap)
void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode)
static void bm_uv_build_islands(UvElementMap *element_map, BMesh *bm, const Scene *scene, bool uv_selected)
void EDBM_update_extern(Mesh *mesh, const bool do_tessellation, const bool is_destructive)
bool BMBVH_EdgeVisible(const BMBVHTree *tree, const BMEdge *e, const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const Object *obedit)
void EDBM_redo_state_free(BMBackup *backup)
static bool loop_uv_match(BMLoop *loop, const float luv_a[2], const float luv_b[2], int cd_loop_uv_offset)
KDTree_3d * tree
#define printf(...)
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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 MirrTopoStore_t mesh_topo_store
Definition meshtools.cc:224
void snap_object_context_destroy(SnapObjectContext *sctx)
eSnapMode snap_object_project_view3d(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const eSnapMode snap_to, const SnapObjectParams *params, const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3])
SnapObjectContext * snap_object_context_create(Scene *scene, int flag)
#define swap(a, b)
Definition sort.cc:59
void * regiondata
BMesh * bmcopy
Definition ED_mesh.hh:555
BMVert * v1
BMVert * v2
short selectmode
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
eBMOpSlotSubType_Union slot_subtype
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
float co[3]
int totvert
bool uv_select_sync_valid
int totfacesel
CustomData vdata
int totedge
char elem_table_dirty
ListBase selected
int totvertsel
int totloop
short selectmode
BMVert ** vtable
int totedgesel
char spacearr_dirty
CustomData ldata
int totface
struct Object * object
ListBase block
MeshRuntimeHandle * runtime
struct Key * key
struct ToolSettings * toolsettings
UvElement * storage
int * island_total_unique_uvs
UvElement ** vertex
UvElement ** head_table
unsigned char flag
unsigned int island
unsigned short loop_of_face_index
UvElement * next
unsigned int face_index
UvMapVert * next
unsigned short loop_of_face_index
UvMapVert * buf
UvMapVert ** vert
struct ReportList * reports
i
Definition text_draw.cc:230
eBMOpSlotSubType_Elem elem
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)