Blender V4.3
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
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/* -------------------------------------------------------------------- */
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
95/* -------------------------------------------------------------------- */
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
257/* -------------------------------------------------------------------- */
263void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
264{
265 Mesh *mesh = static_cast<Mesh *>(ob->data);
266 EDBM_mesh_make_from_mesh(ob, mesh, select_mode, add_key_index);
267}
268
270 Mesh *src_mesh,
271 const int select_mode,
272 const bool add_key_index)
273{
274 Mesh *mesh = static_cast<Mesh *>(ob->data);
275 BMeshCreateParams create_params{};
276 create_params.use_toolflags = true;
277 BMesh *bm = BKE_mesh_to_bmesh(src_mesh, ob, add_key_index, &create_params);
278
279 if (mesh->runtime->edit_mesh) {
280 /* this happens when switching shape keys */
281 EDBM_mesh_free_data(mesh->runtime->edit_mesh.get());
282 mesh->runtime->edit_mesh.reset();
283 }
284
285 /* Executing operators re-tessellates,
286 * so we can avoid doing here but at some point it may need to be added back. */
287 mesh->runtime->edit_mesh = std::make_shared<BMEditMesh>();
288 mesh->runtime->edit_mesh->bm = bm;
289
290 mesh->runtime->edit_mesh->selectmode = mesh->runtime->edit_mesh->bm->selectmode = select_mode;
291 mesh->runtime->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0;
292
293 /* we need to flush selection because the mode may have changed from when last in editmode */
294 EDBM_selectmode_flush(mesh->runtime->edit_mesh.get());
295}
296
297void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
298{
299 Mesh *mesh = static_cast<Mesh *>(ob->data);
300 BMesh *bm = mesh->runtime->edit_mesh->bm;
301
302 /* Workaround for #42360, 'ob->shapenr' should be 1 in this case.
303 * however this isn't synchronized between objects at the moment. */
304 if (UNLIKELY((ob->shapenr == 0) && (mesh->key && !BLI_listbase_is_empty(&mesh->key->block)))) {
305 bm->shapenr = 1;
306 }
307
309 params.calc_object_remap = true;
310 params.update_shapekey_indices = !free_data;
311 BM_mesh_bm_to_me(bmain, bm, mesh, &params);
312}
313
314void EDBM_mesh_load(Main *bmain, Object *ob)
315{
316 EDBM_mesh_load_ex(bmain, ob, true);
317}
318
320{
321 /* These tables aren't used yet, so it's not strictly necessary
322 * to 'end' them but if someone tries to start using them,
323 * having these in place will save a lot of pain. */
326
328}
329
332/* -------------------------------------------------------------------- */
337{
338 Scene *scene = CTX_data_scene(C);
339 Object *obedit = CTX_data_edit_object(C);
341
342 if (!em) {
343 return;
344 }
345
346 scene->toolsettings->selectmode = em->selectmode;
347
348 /* Request redraw of header buttons (to show new select mode) */
350}
351
352void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode)
353{
355}
356
361
363{
364 /* function below doesn't use. just do this to keep the values in sync */
365 em->bm->selectmode = em->selectmode;
367}
368
370{
371 /* function below doesn't use. just do this to keep the values in sync */
372 em->bm->selectmode = em->selectmode;
374}
375
376void EDBM_select_more(BMEditMesh *em, const bool use_face_step)
377{
378 BMOperator bmop;
379 const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
380
381 BMO_op_initf(em->bm,
382 &bmop,
384 "region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
386 false,
387 use_faces,
388 use_face_step);
389 BMO_op_exec(em->bm, &bmop);
390 /* Don't flush selection in edge/vertex mode. */
392 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
393 BMO_op_finish(em->bm, &bmop);
394
396}
397
398void EDBM_select_less(BMEditMesh *em, const bool use_face_step)
399{
400 BMOperator bmop;
401 const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
402
403 BMO_op_initf(em->bm,
404 &bmop,
406 "region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
408 true,
409 use_faces,
410 use_face_step);
411 BMO_op_exec(em->bm, &bmop);
412 /* Don't flush selection in edge/vertex mode. */
414 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
415 BMO_op_finish(em->bm, &bmop);
416
418
419 /* only needed for select less, ensure we don't have isolated elements remaining */
421}
422
423void EDBM_flag_disable_all(BMEditMesh *em, const char hflag)
424{
426}
427
428void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
429{
431}
432
435/* -------------------------------------------------------------------- */
439UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select)
440{
441 /* NOTE: delimiting on alternate face-winding was once supported and could be useful
442 * in some cases. If this is need see: D17137 to restore support. */
443 BMVert *ev;
444 BMFace *efa;
445 BMLoop *l;
446 BMIter iter, liter;
447 uint a;
448 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
449
451
452 const int totverts = bm->totvert;
453 int totuv = 0;
454
455 /* generate UvMapVert array */
456 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
457 if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
458 totuv += efa->len;
459 }
460 }
461
462 if (totuv == 0) {
463 return nullptr;
464 }
465 UvVertMap *vmap = (UvVertMap *)MEM_callocN(sizeof(*vmap), "UvVertMap");
466 if (!vmap) {
467 return nullptr;
468 }
469
470 vmap->vert = (UvMapVert **)MEM_callocN(sizeof(*vmap->vert) * totverts, "UvMapVert_pt");
471 UvMapVert *buf = vmap->buf = (UvMapVert *)MEM_callocN(sizeof(*vmap->buf) * totuv, "UvMapVert");
472
473 if (!vmap->vert || !vmap->buf) {
475 return nullptr;
476 }
477
478 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
479 if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
480 int i;
481 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
482 buf->loop_of_face_index = i;
483 buf->face_index = a;
484 buf->separate = false;
485
486 buf->next = vmap->vert[BM_elem_index_get(l->v)];
487 vmap->vert[BM_elem_index_get(l->v)] = buf;
488 buf++;
489 }
490 }
491 }
492
493 /* sort individual uvs for each vert */
494 BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, a) {
495 UvMapVert *newvlist = nullptr, *vlist = vmap->vert[a];
496 UvMapVert *iterv, *v, *lastv, *next;
497 const float *uv, *uv2;
498
499 while (vlist) {
500 v = vlist;
501 vlist = vlist->next;
502 v->next = newvlist;
503 newvlist = v;
504
505 efa = BM_face_at_index(bm, v->face_index);
506
507 l = static_cast<BMLoop *>(
508 BM_iter_at_index(bm, BM_LOOPS_OF_FACE, efa, v->loop_of_face_index));
509 uv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
510
511 lastv = nullptr;
512 iterv = vlist;
513
514 while (iterv) {
515 next = iterv->next;
516 efa = BM_face_at_index(bm, iterv->face_index);
517 l = static_cast<BMLoop *>(
519 uv2 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
520
521 if (compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT)) {
522 if (lastv) {
523 lastv->next = next;
524 }
525 else {
526 vlist = next;
527 }
528 iterv->next = newvlist;
529 newvlist = iterv;
530 }
531 else {
532 lastv = iterv;
533 }
534
535 iterv = next;
536 }
537
538 newvlist->separate = true;
539 }
540
541 vmap->vert[a] = newvlist;
542 }
543
544 return vmap;
545}
546
548{
549 return vmap->vert[v];
550}
551
553{
554 if (element_map->head_table) {
555 return element_map->head_table;
556 }
557
558 /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
559 element_map->head_table = static_cast<UvElement **>(
560 MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs, __func__));
561 UvElement **head_table = element_map->head_table;
562 for (int i = 0; i < element_map->total_uvs; i++) {
563 UvElement *head = element_map->storage + i;
564 if (head->separate) {
565 UvElement *element = head;
566 while (element) {
567 head_table[element - element_map->storage] = head;
568 element = element->next;
569 if (element && element->separate) {
570 break;
571 }
572 }
573 }
574 }
575 return element_map->head_table;
576}
577
579{
580 if (!element_map->unique_index_table) {
581 element_map->unique_index_table = static_cast<int *>(
582 MEM_callocN(element_map->total_uvs * sizeof(*element_map->unique_index_table), __func__));
583
584 int j = 0;
585 for (int i = 0; i < element_map->total_uvs; i++) {
586 UvElement *element = element_map->storage + i;
587 if (!element->separate) {
588 continue;
589 }
590 BLI_assert(0 <= j);
591 BLI_assert(j < element_map->total_unique_uvs);
592 while (element) {
593 element_map->unique_index_table[element - element_map->storage] = j;
594 element = element->next;
595 if (!element || element->separate) {
596 break;
597 }
598 }
599 j++;
600 }
601 BLI_assert(j == element_map->total_unique_uvs);
602 }
603
604 return element_map->unique_index_table;
605}
606
608{
609 int *unique_index = BM_uv_element_map_ensure_unique_index(element_map);
610 int index = child - element_map->storage;
611 BLI_assert(0 <= index);
612 BLI_assert(index < element_map->total_uvs);
613 return unique_index[index];
614}
615
616#define INVALID_ISLAND uint(-1)
617
618static void bm_uv_assign_island(UvElementMap *element_map,
619 UvElement *element,
620 int nisland,
621 uint *map,
622 UvElement *islandbuf,
623 int islandbufsize)
624{
625 element->island = nisland;
626 map[element - element_map->storage] = islandbufsize;
627
628 /* Copy *element to islandbuf[islandbufsize]. */
629 islandbuf[islandbufsize].l = element->l;
630 islandbuf[islandbufsize].separate = element->separate;
631 islandbuf[islandbufsize].loop_of_face_index = element->loop_of_face_index;
632 islandbuf[islandbufsize].island = element->island;
633 islandbuf[islandbufsize].flag = element->flag;
634}
635
637 const Scene *scene,
638 UvElement *islandbuf,
639 uint *map,
640 bool uv_selected,
641 const BMUVOffsets offsets)
642{
644
645 int total_uvs = element_map->total_uvs;
646
647 /* Depth first search the graph, building islands as we go. */
648 int nislands = 0;
649 int islandbufsize = 0;
650 int stack_upper_bound = total_uvs;
651 UvElement **stack_uv = static_cast<UvElement **>(
652 MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, __func__));
653 int stacksize_uv = 0;
654 for (int i = 0; i < total_uvs; i++) {
655 UvElement *element = element_map->storage + i;
656 if (element->island != INVALID_ISLAND) {
657 /* Unique UV (element and all its children) are already part of an island. */
658 continue;
659 }
660
661 /* Create a new island, i.e. nislands++. */
662
663 BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */
664
665 /* Seed the graph search. */
666 stack_uv[stacksize_uv++] = element;
667 while (element) {
668 bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
669 element = element->next;
670 if (element && element->separate) {
671 break;
672 }
673 }
674
675 /* Traverse the graph. */
676 while (stacksize_uv) {
677 BLI_assert(stacksize_uv < stack_upper_bound);
678 element = stack_uv[--stacksize_uv];
679 while (element) {
680
681 /* Scan forwards around the BMFace that contains element->l. */
682 if (!uv_selected || uvedit_edge_select_test(scene, element->l, offsets)) {
683 UvElement *next = BM_uv_element_get(element_map, element->l->next);
684 if (next && next->island == INVALID_ISLAND) {
685 UvElement *tail = element_map->head_table[next - element_map->storage];
686 stack_uv[stacksize_uv++] = tail;
687 while (tail) {
688 bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
689 tail = tail->next;
690 if (tail && tail->separate) {
691 break;
692 }
693 }
694 }
695 }
696
697 /* Scan backwards around the BMFace that contains element->l. */
698 if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, offsets)) {
699 UvElement *prev = BM_uv_element_get(element_map, element->l->prev);
700 if (prev && prev->island == INVALID_ISLAND) {
701 UvElement *tail = element_map->head_table[prev - element_map->storage];
702 stack_uv[stacksize_uv++] = tail;
703 while (tail) {
704 bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
705 tail = tail->next;
706 if (tail && tail->separate) {
707 break;
708 }
709 }
710 }
711 }
712
713 /* The same for all the UvElements in this unique UV. */
714 element = element->next;
715 if (element && element->separate) {
716 break;
717 }
718 }
719 }
720 nislands++;
721 }
722 BLI_assert(islandbufsize == total_uvs);
723
724 MEM_SAFE_FREE(stack_uv);
725 MEM_SAFE_FREE(element_map->head_table);
726
727 return nislands;
728}
729
730static void bm_uv_build_islands(UvElementMap *element_map,
731 BMesh *bm,
732 const Scene *scene,
733 bool uv_selected)
734{
735 int totuv = element_map->total_uvs;
736 int nislands = 0;
737 int islandbufsize = 0;
738
739 /* map holds the map from current vmap->buf to the new, sorted map */
740 uint *map = static_cast<uint *>(MEM_mallocN(sizeof(*map) * totuv, __func__));
741 BMFace **stack = static_cast<BMFace **>(MEM_mallocN(sizeof(*stack) * bm->totface, __func__));
742 UvElement *islandbuf = static_cast<UvElement *>(
743 MEM_callocN(sizeof(*islandbuf) * totuv, __func__));
744 /* Island number for BMFaces. */
745 int *island_number = static_cast<int *>(
746 MEM_callocN(sizeof(*island_number) * bm->totface, __func__));
747 copy_vn_i(island_number, bm->totface, INVALID_ISLAND);
748
749 const BMUVOffsets uv_offsets = BM_uv_map_get_offsets(bm);
750
751 const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ?
752 scene->toolsettings->selectmode & SCE_SELECT_EDGE :
753 scene->toolsettings->uv_selectmode & UV_SELECT_EDGE;
754 if (use_uv_edge_connectivity) {
756 element_map, scene, islandbuf, map, uv_selected, uv_offsets);
757 islandbufsize = totuv;
758 }
759
760 for (int i = 0; i < totuv; i++) {
761 if (element_map->storage[i].island == INVALID_ISLAND) {
762 int stacksize = 0;
763 element_map->storage[i].island = nislands;
764 stack[0] = element_map->storage[i].l->f;
765 island_number[BM_elem_index_get(stack[0])] = nislands;
766 stacksize = 1;
767
768 while (stacksize > 0) {
769 BMFace *efa = stack[--stacksize];
770
771 BMLoop *l;
772 BMIter liter;
773 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
774 if (uv_selected && !uvedit_uv_select_test(scene, l, uv_offsets)) {
775 continue;
776 }
777
778 UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)];
779
780 for (UvElement *element = initelement; element; element = element->next) {
781 if (element->separate) {
782 initelement = element;
783 }
784
785 if (element->l->f == efa) {
786 /* found the uv corresponding to our face and vertex.
787 * Now fill it to the buffer */
788 bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
789
790 for (element = initelement; element; element = element->next) {
791 if (element->separate && element != initelement) {
792 break;
793 }
794
795 if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
796 stack[stacksize++] = element->l->f;
797 island_number[BM_elem_index_get(element->l->f)] = nislands;
798 }
799 }
800 break;
801 }
802 }
803 }
804 }
805
806 nislands++;
807 }
808 }
809
810 MEM_SAFE_FREE(island_number);
811
812 /* remap */
813 for (int i = 0; i < bm->totvert; i++) {
814 /* important since we may do selection only. Some of these may be nullptr */
815 if (element_map->vertex[i]) {
816 element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]];
817 }
818 }
819
820 element_map->island_indices = static_cast<int *>(
821 MEM_callocN(sizeof(*element_map->island_indices) * nislands, __func__));
822 element_map->island_total_uvs = static_cast<int *>(
823 MEM_callocN(sizeof(*element_map->island_total_uvs) * nislands, __func__));
824 element_map->island_total_unique_uvs = static_cast<int *>(
825 MEM_callocN(sizeof(*element_map->island_total_unique_uvs) * nislands, __func__));
826 int j = 0;
827 for (int i = 0; i < totuv; i++) {
828 UvElement *next = element_map->storage[i].next;
829 islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : nullptr;
830
831 if (islandbuf[i].island != j) {
832 j++;
833 element_map->island_indices[j] = i;
834 }
835 BLI_assert(islandbuf[i].island == j);
836 element_map->island_total_uvs[j]++;
837 if (islandbuf[i].separate) {
838 element_map->island_total_unique_uvs[j]++;
839 }
840 }
841
842 MEM_SAFE_FREE(element_map->storage);
843 element_map->storage = islandbuf;
844 islandbuf = nullptr;
845 element_map->total_islands = nislands;
846 MEM_SAFE_FREE(stack);
847 MEM_SAFE_FREE(map);
848}
849
851static bool loop_uv_match(BMLoop *loop,
852 const float luv_a[2],
853 const float luv_b[2],
854 int cd_loop_uv_offset)
855{
856 const float *luv_c = BM_ELEM_CD_GET_FLOAT_P(loop, cd_loop_uv_offset);
857 const float *luv_d = BM_ELEM_CD_GET_FLOAT_P(loop->next, cd_loop_uv_offset);
858 return compare_v2v2(luv_a, luv_c, STD_UV_CONNECT_LIMIT) &&
859 compare_v2v2(luv_b, luv_d, STD_UV_CONNECT_LIMIT);
860}
861
881 const float luv_anchor[2],
882 const float luv_fan[2],
883 const BMLoop *needle,
884 GSet *visited,
885 const int cd_loop_uv_offset)
886{
887 BMVert *anchor = needle->v;
888 BLI_assert(edge->v1 == anchor || edge->v2 == anchor);
889
890 if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
891 return false; /* Edge is a seam, don't traverse. */
892 }
893
894 if (!BLI_gset_add(visited, edge)) {
895 return false; /* Already visited. */
896 }
897
898 BMLoop *loop;
899 BMIter liter;
900 BM_ITER_ELEM (loop, &liter, edge, BM_LOOPS_OF_EDGE) {
901 if (loop->v == anchor) {
902 if (!loop_uv_match(loop, luv_anchor, luv_fan, cd_loop_uv_offset)) {
903 continue; /* `loop` is disjoint in UV space. */
904 }
905
906 if (loop == needle) {
907 return true; /* Success. */
908 }
909
910 const float *luv_far = BM_ELEM_CD_GET_FLOAT_P(loop->prev, cd_loop_uv_offset);
912 loop->prev->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset))
913 {
914 return true;
915 }
916 }
917 else {
918 BLI_assert(loop->next->v == anchor);
919 if (!loop_uv_match(loop, luv_fan, luv_anchor, cd_loop_uv_offset)) {
920 continue; /* `loop` is disjoint in UV space. */
921 }
922
923 if (loop->next == needle) {
924 return true; /* Success. */
925 }
926
927 const float *luv_far = BM_ELEM_CD_GET_FLOAT_P(loop->next->next, cd_loop_uv_offset);
929 loop->next->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset))
930 {
931 return true;
932 }
933 }
934 }
935
936 return false;
937}
938
945static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
946{
947 BLI_assert(loop_a && loop_b);
948 BLI_assert(loop_a != loop_b);
949 BLI_assert(loop_a->v == loop_b->v);
950
951 BLI_gset_clear(visited, nullptr);
952
953 const float *luv_anchor = BM_ELEM_CD_GET_FLOAT_P(loop_a, cd_loop_uv_offset);
954 const float *luv_next_fan = BM_ELEM_CD_GET_FLOAT_P(loop_a->next, cd_loop_uv_offset);
955 bool result = seam_connected_recursive(
956 loop_a->e, luv_anchor, luv_next_fan, loop_b, visited, cd_loop_uv_offset);
957 if (!result) {
958 /* Search around `loop_a` in the opposite direction, as one of the edges may be delimited by
959 * a boundary, seam or disjoint UV, or itself be one of these. See: #103670, #103787. */
960 const float *luv_prev_fan = BM_ELEM_CD_GET_FLOAT_P(loop_a->prev, cd_loop_uv_offset);
962 loop_a->prev->e, luv_anchor, luv_prev_fan, loop_b, visited, cd_loop_uv_offset);
963 }
964
965 return result;
966}
967
969 const Scene *scene,
970 const bool uv_selected,
971 const bool use_winding,
972 const bool use_seams,
973 const bool do_islands)
974{
975 /* In uv sync selection, all UVs (from unhidden geometry) are visible. */
976 const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
977
978 BMVert *ev;
979 BMFace *efa;
980 BMIter iter, liter;
981
982 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
983 if (offsets.uv < 0) {
984 return nullptr;
985 }
986
988
989 /* Count total uvs. */
990 int totuv = 0;
991 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
993 continue;
994 }
995
996 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
997 continue;
998 }
999
1000 if (!uv_selected) {
1001 totuv += efa->len;
1002 }
1003 else {
1004 BMLoop *l;
1005 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1006 if (uvedit_uv_select_test(scene, l, offsets)) {
1007 totuv++;
1008 }
1009 }
1010 }
1011 }
1012
1013 if (totuv == 0) {
1014 return nullptr;
1015 }
1016
1017 UvElementMap *element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap");
1018 element_map->total_uvs = totuv;
1019 element_map->vertex = (UvElement **)MEM_callocN(sizeof(*element_map->vertex) * bm->totvert,
1020 "UvElementVerts");
1021 element_map->storage = (UvElement *)MEM_callocN(sizeof(*element_map->storage) * totuv,
1022 "UvElement");
1023
1024 bool *winding = use_winding ?
1025 static_cast<bool *>(MEM_callocN(sizeof(*winding) * bm->totface, "winding")) :
1026 nullptr;
1027
1028 UvElement *buf = element_map->storage;
1029 int j;
1030 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) {
1031
1033 continue;
1034 }
1035
1036 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1037 continue;
1038 }
1039
1040 int i;
1041 BMLoop *l;
1042 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1043 if (uv_selected && !uvedit_uv_select_test(scene, l, offsets)) {
1044 continue;
1045 }
1046
1047 buf->l = l;
1048 buf->island = INVALID_ISLAND;
1049 buf->loop_of_face_index = i;
1050
1051 /* Insert to head of linked list associated with BMVert. */
1052 buf->next = element_map->vertex[BM_elem_index_get(l->v)];
1053 element_map->vertex[BM_elem_index_get(l->v)] = buf;
1054
1055 buf++;
1056 }
1057
1058 if (winding) {
1059 winding[j] = BM_face_calc_area_uv_signed(efa, offsets.uv) > 0;
1060 }
1061 }
1062
1063 GSet *seam_visited_gset = use_seams ? BLI_gset_ptr_new(__func__) : nullptr;
1064
1065 /* For each BMVert, sort associated linked list into unique uvs. */
1066 int ev_index;
1067 BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) {
1068 UvElement *newvlist = nullptr;
1069 UvElement *vlist = element_map->vertex[ev_index];
1070 while (vlist) {
1071
1072 /* Detach head from unsorted list. */
1073 UvElement *v = vlist;
1074 vlist = vlist->next;
1075 v->next = newvlist;
1076 newvlist = v;
1077
1078 const float *uv = static_cast<const float *>(BM_ELEM_CD_GET_VOID_P(v->l, offsets.uv));
1079 bool uv_vert_sel = uvedit_uv_select_test(scene, v->l, offsets);
1080
1081 UvElement *lastv = nullptr;
1082 UvElement *iterv = vlist;
1083
1084 /* Scan through unsorted list, finding UvElements which are connected to `v`. */
1085 while (iterv) {
1086 UvElement *next = iterv->next;
1087
1088 bool connected = true; /* Assume connected unless we can prove otherwise. */
1089
1090 if (connected) {
1091 /* Are the two UVs close together? */
1092 const float *uv2 = BM_ELEM_CD_GET_FLOAT_P(iterv->l, offsets.uv);
1093 connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
1094 }
1095
1096 if (connected) {
1097 /* Check if the uv loops share the same selection state (if not, they are not connected
1098 * as they have been ripped or other edit commands have separated them). */
1099 const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, offsets);
1100 connected = (uv_vert_sel == uv2_vert_sel);
1101 }
1102
1103 if (connected && use_winding) {
1104 connected = winding[BM_elem_index_get(iterv->l->f)] ==
1105 winding[BM_elem_index_get(v->l->f)];
1106 }
1107
1108 if (connected && use_seams) {
1109 connected = seam_connected(iterv->l, v->l, seam_visited_gset, offsets.uv);
1110 }
1111
1112 if (connected) {
1113 if (lastv) {
1114 lastv->next = next;
1115 }
1116 else {
1117 vlist = next;
1118 }
1119 iterv->next = newvlist;
1120 newvlist = iterv;
1121 }
1122 else {
1123 lastv = iterv;
1124 }
1125
1126 iterv = next;
1127 }
1128
1129 element_map->total_unique_uvs++;
1130 newvlist->separate = true;
1131 }
1132
1133 /* Write back sorted list. */
1134 element_map->vertex[ev_index] = newvlist;
1135 }
1136
1137 if (seam_visited_gset) {
1138 BLI_gset_free(seam_visited_gset, nullptr);
1139 seam_visited_gset = nullptr;
1140 }
1141 MEM_SAFE_FREE(winding);
1142
1143 /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
1144 * Now we should sort uv's in islands. */
1145 if (do_islands) {
1146 bm_uv_build_islands(element_map, bm, scene, uv_selected);
1147 }
1148
1149 /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */
1150 element_map->total_unique_uvs = 0;
1151 for (int i = 0; i < element_map->total_uvs; i++) {
1152 if (element_map->storage[i].separate) {
1153 element_map->total_unique_uvs++;
1154 }
1155 }
1156
1157 return element_map;
1158}
1159
1161{
1162 if (vmap) {
1163 if (vmap->vert) {
1164 MEM_freeN(vmap->vert);
1165 }
1166 if (vmap->buf) {
1167 MEM_freeN(vmap->buf);
1168 }
1169 MEM_freeN(vmap);
1170 }
1171}
1172
1174{
1175 if (element_map) {
1176 MEM_SAFE_FREE(element_map->storage);
1177 MEM_SAFE_FREE(element_map->vertex);
1178 MEM_SAFE_FREE(element_map->head_table);
1179 MEM_SAFE_FREE(element_map->unique_index_table);
1180 MEM_SAFE_FREE(element_map->island_indices);
1181 MEM_SAFE_FREE(element_map->island_total_uvs);
1183 MEM_SAFE_FREE(element_map);
1184 }
1185}
1186
1188{
1189 UvElement *element = element_map->vertex[BM_elem_index_get(l->v)];
1190 while (element) {
1191 if (element->l == l) {
1192 return element;
1193 }
1194 element = element->next;
1195 }
1196
1197 return nullptr;
1198}
1199
1201{
1202 if (!child) {
1203 return nullptr;
1204 }
1205
1206 return element_map->vertex[BM_elem_index_get(child->l->v)];
1207}
1208
1211/* -------------------------------------------------------------------- */
1215BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
1216{
1217 if (!EDBM_uv_check(em)) {
1218 return nullptr;
1219 }
1220 BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
1221 if (efa) {
1222 return efa;
1223 }
1224
1225 return nullptr;
1226}
1227
1229{
1230 /* some of these checks could be a touch overkill */
1231 return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_FLOAT2);
1232}
1233
1235{
1236 /* some of these checks could be a touch overkill */
1237 return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_BYTE_COLOR);
1238}
1239
1242/* -------------------------------------------------------------------- */
1246static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
1247{
1248 intptr_t eve_i = index_lookup[index];
1249 return (eve_i == -1) ? nullptr : (BMVert *)eve_i;
1250}
1251
1268/* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a preference. */
1269#define BM_SEARCH_MAXDIST_MIRR 0.00002f
1270#define BM_CD_LAYER_ID "__mirror_index"
1271
1273 const int axis,
1274 const bool use_self,
1275 const bool use_select,
1276 const bool respecthide,
1277 /* extra args */
1278 const bool use_topology,
1279 float maxdist,
1280 int *r_index)
1281{
1282 BMesh *bm = em->bm;
1283 BMIter iter;
1284 BMVert *v;
1285 int cd_vmirr_offset = 0;
1286 int i;
1287 const float maxdist_sq = square_f(maxdist);
1288
1289 /* one or the other is used depending if topo is enabled */
1290 KDTree_3d *tree = nullptr;
1291 MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false};
1292
1294
1295 if (r_index == nullptr) {
1296 const char *layer_id = BM_CD_LAYER_ID;
1298 if (em->mirror_cdlayer == -1) {
1301 }
1302
1303 cd_vmirr_offset = CustomData_get_n_offset(
1304 &bm->vdata,
1307
1309 }
1310
1312
1313 if (use_topology) {
1314 ED_mesh_mirrtopo_init(em, nullptr, &mesh_topo_store, true);
1315 }
1316 else {
1317 tree = BLI_kdtree_3d_new(bm->totvert);
1318 BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
1319 if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
1320 continue;
1321 }
1322
1323 BLI_kdtree_3d_insert(tree, i, v->co);
1324 }
1325 BLI_kdtree_3d_balance(tree);
1326 }
1327
1328#define VERT_INTPTR(_v, _i) \
1329 (r_index ? &r_index[_i] : static_cast<int *>(BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset)))
1330
1331 BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
1332 if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
1333 continue;
1334 }
1335
1336 if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
1337 continue;
1338 }
1339
1341 BMVert *v_mirr;
1342 int *idx = VERT_INTPTR(v, i);
1343
1344 if (use_topology) {
1346 if (v_mirr != nullptr) {
1347 if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
1348 v_mirr = nullptr;
1349 }
1350 }
1351 }
1352 else {
1353 int i_mirr;
1354 float co[3];
1355 copy_v3_v3(co, v->co);
1356 co[axis] *= -1.0f;
1357
1358 v_mirr = nullptr;
1359 i_mirr = BLI_kdtree_3d_find_nearest(tree, co, nullptr);
1360 if (i_mirr != -1) {
1361 BMVert *v_test = BM_vert_at_index(bm, i_mirr);
1362 if (len_squared_v3v3(co, v_test->co) < maxdist_sq) {
1363 v_mirr = v_test;
1364 }
1365 }
1366 }
1367
1368 if (v_mirr && (use_self || (v_mirr != v))) {
1369 const int i_mirr = BM_elem_index_get(v_mirr);
1370 *idx = i_mirr;
1371 idx = VERT_INTPTR(v_mirr, i_mirr);
1372 *idx = i;
1373 }
1374 else {
1375 *idx = -1;
1376 }
1377 }
1378
1379#undef VERT_INTPTR
1380
1381 if (use_topology) {
1383 }
1384 else {
1385 BLI_kdtree_3d_free(tree);
1386 }
1387}
1388
1390 const int axis,
1391 const bool use_self,
1392 const bool use_select,
1393 const bool respecthide,
1394 const bool use_topology)
1395{
1397 axis,
1398 use_self,
1399 use_select,
1400 respecthide,
1401 /* extra args */
1402 use_topology,
1404 nullptr);
1405}
1406
1408{
1409 const int *mirr = static_cast<const int *>(
1411
1412 BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
1413
1414 if (mirr && *mirr >= 0 && *mirr < em->bm->totvert) {
1415 if (!em->bm->vtable) {
1416 printf(
1417 "err: should only be called between "
1418 "EDBM_verts_mirror_cache_begin and EDBM_verts_mirror_cache_end");
1419 return nullptr;
1420 }
1421
1422 return em->bm->vtable[*mirr];
1423 }
1424
1425 return nullptr;
1426}
1427
1429{
1430 BMVert *v1_mirr, *v2_mirr;
1431 if ((v1_mirr = EDBM_verts_mirror_get(em, e->v1)) &&
1432 (v2_mirr = EDBM_verts_mirror_get(em, e->v2)) &&
1433 /* While highly unlikely, a zero length central edges vertices can match, see #89342. */
1434 LIKELY(v1_mirr != v2_mirr))
1435 {
1436 return BM_edge_exists(v1_mirr, v2_mirr);
1437 }
1438
1439 return nullptr;
1440}
1441
1443{
1445
1446 BMLoop *l_iter, *l_first;
1447 uint i = 0;
1448
1449 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1450 do {
1451 if ((v_mirr_arr[i++] = EDBM_verts_mirror_get(em, l_iter->v)) == nullptr) {
1452 return nullptr;
1453 }
1454 } while ((l_iter = l_iter->next) != l_first);
1455
1456 return BM_face_exists(v_mirr_arr.data(), v_mirr_arr.size());
1457}
1458
1460{
1461 int *mirr = static_cast<int *>(
1463
1464 BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
1465
1466 if (mirr) {
1467 *mirr = -1;
1468 }
1469}
1470
1472{
1473 em->mirror_cdlayer = -1;
1474}
1475
1476void EDBM_verts_mirror_apply(BMEditMesh *em, const int sel_from, const int sel_to)
1477{
1478 BMIter iter;
1479 BMVert *v;
1480
1481 BLI_assert((em->bm->vtable != nullptr) && ((em->bm->elem_table_dirty & BM_VERT) == 0));
1482
1483 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1484 if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) {
1485 BMVert *mirr = EDBM_verts_mirror_get(em, v);
1486 if (mirr) {
1487 if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) {
1488 copy_v3_v3(mirr->co, v->co);
1489 mirr->co[0] *= -1.0f;
1490 }
1491 }
1492 }
1493 }
1494}
1495
1498/* -------------------------------------------------------------------- */
1502bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
1503{
1504 BMIter iter;
1505 BMElem *ele;
1506 int itermode;
1507 char hflag_swap = swap ? BM_ELEM_SELECT : 0;
1508 bool changed = true;
1509
1510 if (em->selectmode & SCE_SELECT_VERTEX) {
1511 itermode = BM_VERTS_OF_MESH;
1512 }
1513 else if (em->selectmode & SCE_SELECT_EDGE) {
1514 itermode = BM_EDGES_OF_MESH;
1515 }
1516 else {
1517 itermode = BM_FACES_OF_MESH;
1518 }
1519
1520 BM_ITER_MESH (ele, &iter, em->bm, itermode) {
1521 if (!BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
1522 if (BM_elem_flag_test(ele, BM_ELEM_SELECT) ^ hflag_swap) {
1523 BM_elem_hide_set(em->bm, ele, true);
1524 changed = true;
1525 }
1526 }
1527 }
1528
1529 if (changed) {
1531 }
1532 return changed;
1533
1534 /* original hide flushing comment (OUTDATED):
1535 * hide happens on least dominant select mode, and flushes up, not down!
1536 * (helps preventing errors in subsurf) */
1537 /* - vertex hidden, always means edge is hidden too
1538 * - edge hidden, always means face is hidden too
1539 * - face hidden, only set face hide
1540 * - then only flush back down what's absolute hidden
1541 */
1542}
1543
1545{
1546 const char iter_types[3] = {
1550 };
1551
1552 const bool sels[3] = {
1553 (em->selectmode & SCE_SELECT_VERTEX) != 0,
1554 (em->selectmode & SCE_SELECT_EDGE) != 0,
1555 (em->selectmode & SCE_SELECT_FACE) != 0,
1556 };
1557 int i;
1558 bool changed = false;
1559
1560 /* Use tag flag to remember what was hidden before all is revealed.
1561 * BM_ELEM_HIDDEN --> BM_ELEM_TAG */
1562 for (i = 0; i < 3; i++) {
1563 BMIter iter;
1564 BMElem *ele;
1565
1566 BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
1569 changed = true;
1570 }
1571 else {
1573 }
1574 }
1575 }
1576
1577 if (!changed) {
1578 return false;
1579 }
1580
1581 /* Reveal everything */
1583
1584 /* Select relevant just-revealed elements */
1585 for (i = 0; i < 3; i++) {
1586 BMIter iter;
1587 BMElem *ele;
1588
1589 if (!sels[i]) {
1590 continue;
1591 }
1592
1593 BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
1594 if (BM_elem_flag_test(ele, BM_ELEM_TAG)) {
1595 BM_elem_select_set(em->bm, ele, select);
1596 }
1597 }
1598 }
1599
1601
1602 /* hidden faces can have invalid normals */
1604
1605 return true;
1606}
1607
1610/* -------------------------------------------------------------------- */
1618
1620{
1622 params.face_normals = true;
1624}
1625
1627{
1628 const char iter_types[3] = {
1632 };
1633
1634 BMIter iter;
1635 BMElem *ele;
1636 int *tots[3];
1637 int i;
1638
1639 tots[0] = &em->bm->totvertsel;
1640 tots[1] = &em->bm->totedgesel;
1641 tots[2] = &em->bm->totfacesel;
1642
1643 em->bm->totvertsel = em->bm->totedgesel = em->bm->totfacesel = 0;
1644
1645 for (i = 0; i < 3; i++) {
1646 ele = static_cast<BMElem *>(BM_iter_new(&iter, em->bm, iter_types[i], nullptr));
1647 for (; ele; ele = static_cast<BMElem *>(BM_iter_step(&iter))) {
1649 (*tots[i])++;
1650 }
1651 }
1652 }
1653}
1654
1656{
1657 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1658 /* Order of calling isn't important. */
1660 WM_main_add_notifier(NC_GEOM | ND_DATA, &mesh->id);
1661
1662 if (params->calc_normals && params->calc_looptris) {
1663 /* Calculating both has some performance gains. */
1665 }
1666 else {
1667 if (params->calc_normals) {
1669 }
1670
1671 if (params->calc_looptris) {
1673 }
1674 }
1675
1676 if (params->is_destructive) {
1677 /* TODO(@ideasman42): we may be able to remove this now! */
1678 // BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
1679 }
1680 else {
1681 /* in debug mode double check we didn't need to recalculate */
1683 }
1685 BM_lnorspace_invalidate(em->bm, false);
1686 em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET;
1687 }
1688
1689#ifndef NDEBUG
1690 {
1693 }
1694 }
1695#endif
1696}
1697
1698void EDBM_update_extern(Mesh *mesh, const bool do_tessellation, const bool is_destructive)
1699{
1701 params.calc_looptris = do_tessellation;
1702 params.calc_normals = false;
1703 params.is_destructive = is_destructive;
1704 EDBM_update(mesh, &params);
1705}
1706
1709/* -------------------------------------------------------------------- */
1714{
1716 return true;
1717 }
1718
1719 return false;
1720}
1721
1724/* -------------------------------------------------------------------- */
1729{
1730 if ((em->selectmode & SCE_SELECT_VERTEX) && eve) {
1731 return (BMElem *)eve;
1732 }
1733 if ((em->selectmode & SCE_SELECT_EDGE) && eed) {
1734 return (BMElem *)eed;
1735 }
1736 if ((em->selectmode & SCE_SELECT_FACE) && efa) {
1737 return (BMElem *)efa;
1738 }
1739 return nullptr;
1740}
1741
1743{
1744 BMesh *bm = em->bm;
1745 int index = BM_elem_index_get(ele);
1746
1747 if (ele->head.htype == BM_VERT) {
1749 }
1750 else if (ele->head.htype == BM_EDGE) {
1752 index += bm->totvert;
1753 }
1754 else if (ele->head.htype == BM_FACE) {
1756 index += bm->totvert + bm->totedge;
1757 }
1758 else {
1759 BLI_assert(0);
1760 }
1761
1762 return index;
1763}
1764
1766{
1767 BMesh *bm = em->bm;
1768
1769 if (index < bm->totvert) {
1770 return (BMElem *)BM_vert_at_index_find_or_table(bm, index);
1771 }
1772 index -= bm->totvert;
1773 if (index < bm->totedge) {
1774 return (BMElem *)BM_edge_at_index_find_or_table(bm, index);
1775 }
1776 index -= bm->totedge;
1777 if (index < bm->totface) {
1778 return (BMElem *)BM_face_at_index_find_or_table(bm, index);
1779 }
1780
1781 return nullptr;
1782}
1783
1785 const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
1786{
1787 int elem_index = -1;
1788 *r_object_index = -1;
1789 Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, nullptr);
1790 for (const int base_index : bases.index_range()) {
1791 Base *base_iter = bases[base_index];
1792 if (BKE_editmesh_from_object(base_iter->object) == em) {
1793 *r_object_index = base_index;
1794 elem_index = EDBM_elem_to_index_any(em, ele);
1795 break;
1796 }
1797 }
1798 return elem_index;
1799}
1800
1802 ViewLayer *view_layer,
1803 uint object_index,
1804 uint elem_index,
1805 Object **r_obedit)
1806{
1807 Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, nullptr);
1808 *r_obedit = nullptr;
1809 Object *obedit = (object_index < bases.size()) ? bases[object_index]->object : nullptr;
1810 if (obedit != nullptr) {
1812 BMElem *ele = EDBM_elem_from_index_any(em, elem_index);
1813 if (ele != nullptr) {
1814 *r_obedit = obedit;
1815 return ele;
1816 }
1817 }
1818 return nullptr;
1819}
1820
1823/* -------------------------------------------------------------------- */
1828 const BMBVHTree *tree, const float co[3], const float dir[3], float *r_hitout, const BMEdge *e)
1829{
1830 BMFace *f = BKE_bmbvh_ray_cast(tree, co, dir, 0.0f, nullptr, r_hitout, nullptr);
1831
1832 if (f && BM_edge_in_face(e, f)) {
1833 return nullptr;
1834 }
1835
1836 return f;
1837}
1838
1839static void scale_point(float c1[3], const float p[3], const float s)
1840{
1841 sub_v3_v3(c1, p);
1842 mul_v3_fl(c1, s);
1843 add_v3_v3(c1, p);
1844}
1845
1847 const BMEdge *e,
1848 const Depsgraph *depsgraph,
1849 const ARegion *region,
1850 const View3D *v3d,
1851 const Object *obedit)
1852{
1853 BMFace *f;
1854 float co1[3], co2[3], co3[3], dir1[3], dir2[3], dir3[3];
1855 float origin[3], invmat[4][4];
1856 float epsilon = 0.01f;
1857 float end[3];
1858 const float mval_f[2] = {
1859 region->winx / 2.0f,
1860 region->winy / 2.0f,
1861 };
1862
1863 ED_view3d_win_to_segment_clipped(depsgraph, region, v3d, mval_f, origin, end, false);
1864
1865 invert_m4_m4(invmat, obedit->object_to_world().ptr());
1866 mul_m4_v3(invmat, origin);
1867
1868 copy_v3_v3(co1, e->v1->co);
1869 mid_v3_v3v3(co2, e->v1->co, e->v2->co);
1870 copy_v3_v3(co3, e->v2->co);
1871
1872 scale_point(co1, co2, 0.99);
1873 scale_point(co3, co2, 0.99);
1874
1875 /* OK, idea is to generate rays going from the camera origin to the
1876 * three points on the edge (v1, mid, v2). */
1877 sub_v3_v3v3(dir1, origin, co1);
1878 sub_v3_v3v3(dir2, origin, co2);
1879 sub_v3_v3v3(dir3, origin, co3);
1880
1881 normalize_v3_length(dir1, epsilon);
1882 normalize_v3_length(dir2, epsilon);
1883 normalize_v3_length(dir3, epsilon);
1884
1885 /* Offset coordinates slightly along view vectors,
1886 * to avoid hitting the faces that own the edge. */
1887 add_v3_v3v3(co1, co1, dir1);
1888 add_v3_v3v3(co2, co2, dir2);
1889 add_v3_v3v3(co3, co3, dir3);
1890
1891 normalize_v3(dir1);
1892 normalize_v3(dir2);
1893 normalize_v3(dir3);
1894
1895 /* do three samplings: left, middle, right */
1896 f = edge_ray_cast(tree, co1, dir1, nullptr, e);
1897 if (f && !edge_ray_cast(tree, co2, dir2, nullptr, e)) {
1898 return true;
1899 }
1900 if (f && !edge_ray_cast(tree, co3, dir3, nullptr, e)) {
1901 return true;
1902 }
1903 if (!f) {
1904 return true;
1905 }
1906
1907 return false;
1908}
1909
1912/* -------------------------------------------------------------------- */
1917 bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
1918{
1919 BMIter iter;
1920 BMVert *eve;
1921
1922 ED_view3d_init_mats_rv3d(obedit, static_cast<RegionView3D *>(region->regiondata));
1923
1924 Scene *scene = CTX_data_scene(C);
1926
1928 const int snap_flag = scene->toolsettings->snap_flag;
1929
1931 target_op, !(snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), SCE_SNAP_TARGET_NOT_EDITED);
1936
1937 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1939 float mval[2], co_proj[3];
1940 if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) ==
1942 {
1944 params.snap_target_select = target_op;
1945 params.edit_mode_type = SNAP_GEOM_FINAL;
1946 params.occlusion_test = SNAP_OCCLUSION_AS_SEEM;
1948 depsgraph,
1949 region,
1950 CTX_wm_view3d(C),
1952 &params,
1953 nullptr,
1954 mval,
1955 nullptr,
1956 nullptr,
1957 co_proj,
1958 nullptr))
1959 {
1960 mul_v3_m4v3(eve->co, obedit->world_to_object().ptr(), co_proj);
1961 }
1962 }
1963 }
1964 }
1965
1967}
1968
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:120
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
void BKE_editmesh_looptris_calc(BMEditMesh *em)
Definition editmesh.cc:76
void BKE_editmesh_looptris_and_normals_calc(BMEditMesh *em)
Definition editmesh.cc:83
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, Object *ob, 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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
struct GSet GSet
Definition BLI_ghash.h:341
GSet * BLI_gset_ptr_new(const char *info)
void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1029
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
A KD-tree for nearest neighbor search.
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
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 LIKELY(x)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ CD_FLAG_TEMPORARY
@ CD_PROP_BYTE_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
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
@ 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
@ UV_SYNC_SELECTION
@ SCE_SNAP_TO_FACE
void ED_mesh_mirror_topo_table_end(Object *ob)
Definition meshtools.cc:857
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)
SnapObjectContext * ED_transform_snap_object_context_create(Scene *scene, int flag)
void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx)
eSnapMode ED_transform_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])
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:266
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:243
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:277
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define NC_SCENE
Definition WM_types.hh:345
#define ND_TOOLSETTINGS
Definition WM_types.hh:416
#define BM_ALL_NOLOOP
@ BM_SPACEARR_BMO_SET
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#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 char *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)
ATTR_WARN_UNUSED_RESULT 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_flush(BMesh *bm)
void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags)
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_deselect_flush(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
#define BM_elem_hide_set(bm, ele, hide)
@ BM_SELECT_LEN_FLUSH_RECALC_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)
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_get_offsets(const BMesh *bm)
int64_t size() const
Definition BLI_array.hh:245
const T * data() const
Definition BLI_array.hh:301
int64_t size() const
IndexRange index_range() const
#define printf
const Depsgraph * depsgraph
Set< ComponentNode * > visited
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 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_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_deselect_flush(BMEditMesh *em)
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)
UvVertMap * BM_uv_vert_map_create(BMesh *bm, const bool use_select)
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)
static int bm_uv_edge_select_build_islands(UvElementMap *element_map, const Scene *scene, UvElement *islandbuf, uint *map, bool uv_selected, const BMUVOffsets offsets)
#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_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_select_flush(BMEditMesh *em)
void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
void EDBM_selectmode_to_scene(bContext *C)
void EDBM_mesh_normals_update_ex(BMEditMesh *em, const BMeshNormalsUpdate_Params *params)
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
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static ulong * next
static MirrTopoStore_t mesh_topo_store
Definition meshtools.cc:823
_W64 int intptr_t
Definition stdint.h:118
BMesh * bmcopy
Definition ED_mesh.hh:496
short selectmode
BMHeader head
void * data
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]
BMHeader head
int totvert
int totfacesel
int shapenr
char elem_index_dirty
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
CustomDataLayer * layers
intptr_t * index_lookup
Definition ED_mesh.hh:431
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
eBMOpSlotSubType_Elem elem
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)