Blender V5.0
uvedit_smart_stitch.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cmath>
10#include <cstdlib>
11#include <cstring>
12#include <fmt/format.h>
13
14#include "MEM_guardedalloc.h"
15
16#include "DNA_object_types.h"
17#include "DNA_scene_types.h"
19
20#include "BLI_ghash.h"
21#include "BLI_math_matrix.h"
22#include "BLI_math_rotation.h"
23#include "BLI_math_vector.h"
24
25#include "BLT_translation.hh"
26
27#include "BKE_context.hh"
28#include "BKE_customdata.hh"
29#include "BKE_editmesh.hh"
30#include "BKE_layer.hh"
31#include "BKE_mesh_mapping.hh"
32#include "BKE_report.hh"
33#include "BKE_screen.hh"
34
35#include "DEG_depsgraph.hh"
36
37#include "ED_mesh.hh"
38#include "ED_screen.hh"
39#include "ED_space_api.hh"
40#include "ED_uvedit.hh"
41
42#include "GPU_batch.hh"
43#include "GPU_state.hh"
44
45#include "RNA_access.hh"
46#include "RNA_define.hh"
47#include "RNA_prototypes.hh"
48
49#include "WM_api.hh"
50#include "WM_types.hh"
51
52#include "UI_resources.hh"
53#include "UI_view2d.hh"
54
55#include "uvedit_intern.hh"
56
57using blender::Vector;
58
59/* ********************** smart stitch operator *********************** */
60
61namespace {
62
63/* object that stores display data for previewing before confirming stitching */
64struct StitchPreviewer {
65 /* here we'll store the preview triangle indices of the mesh */
66 float *preview_polys;
67 /* uvs per face. */
68 uint *uvs_per_polygon;
69 /* Number of preview polygons. */
70 uint num_polys;
71 /* preview data. These will be either the previewed vertices or edges
72 * depending on stitch mode settings */
73 float *preview_stitchable;
74 float *preview_unstitchable;
75 /* here we'll store the number of elements to be drawn */
76 uint num_stitchable;
77 uint num_unstitchable;
78 uint preview_uvs;
79 /* ...and here we'll store the static island triangles */
80 float *static_tris;
81 uint num_static_tris;
82};
83
84struct IslandStitchData;
85
91struct IslandStitchData {
92 /* rotation can be used only for edges, for vertices there is no such notion */
93 float rotation;
94 float rotation_neg;
95 float translation[2];
96 /* Used for rotation, the island will rotate around this point */
97 float medianPoint[2];
98 int numOfElements;
99 int num_rot_elements;
100 int num_rot_elements_neg;
101 /* flag to remember if island has been added for preview */
102 char addedForPreview;
103 /* flag an island to be considered for determining static island */
104 char stitchableCandidate;
105 /* if edge rotation is used, flag so that vertex rotation is not used */
106 bool use_edge_rotation;
107};
108
109/* just for averaging UVs */
110struct UVVertAverage {
111 float uv[2];
112 ushort count;
113};
114
115struct UvEdge {
117 uint uv1;
118 uint uv2;
121 uchar flag;
126 UvElement *element;
129 UvEdge *next;
131 UvEdge *first;
132};
133
134/* stitch state object */
135struct StitchState {
137 float aspect;
138 /* object for editmesh */
139 Object *obedit;
140 /* editmesh, cached for use in modal handler */
141 BMEditMesh *em;
142
143 /* element map for getting info about uv connectivity */
144 UvElementMap *element_map;
145 /* edge container */
146 UvEdge *uvedges;
147 /* container of first of a group of coincident uvs, these will be operated upon */
148 UvElement **uvs;
149 /* maps uvelements to their first coincident uv */
150 int *map;
151 /* 2D normals per uv to calculate rotation for snapping */
152 float *normals;
153 /* edge storage */
154 UvEdge *edges;
155 /* hash for quick lookup of edges */
156 GHash *edge_hash;
157 /* which islands to stop at (to make active) when pressing 'I' */
158 bool *island_is_stitchable;
159
160 /* count of separate uvs and edges */
161 int total_separate_edges;
162 int total_separate_uvs;
163 /* hold selection related information */
164 void **selection_stack;
165 int selection_size;
166
167 /* store number of primitives per face so that we can allocate the active island buffer later */
168 uint *tris_per_island;
169 /* preview data */
170 StitchPreviewer *stitch_preview;
171};
172
173/* Stitch state container. */
174struct StitchStateContainer {
175 /* clear seams of stitched edges after stitch */
176 bool clear_seams;
177 /* use limit flag */
178 bool use_limit;
179 /* limit to operator, same as original operator */
180 float limit_dist;
181 /* snap uv islands together during stitching */
182 bool snap_islands;
183 /* stitch at midpoints or at islands */
184 bool midpoints;
185 /* vert or edge mode used for stitching */
186 char mode;
187 /* handle for drawing */
188 void *draw_handle;
189 /* island that stays in place */
190 int static_island;
191
192 /* Objects and states are aligned. */
193 int objects_len;
194 Object **objects;
195 StitchState **states;
196
197 int active_object_index;
198};
199
200struct PreviewPosition {
201 int data_position;
202 int polycount_position;
203};
204/*
205 * defines for UvElement/UcEdge flags
206 */
207enum {
208 STITCH_SELECTED = 1,
209 STITCH_STITCHABLE = 2,
210 STITCH_PROCESSED = 4,
211 STITCH_BOUNDARY = 8,
212 STITCH_STITCHABLE_CANDIDATE = 16,
213};
214
215#define STITCH_NO_PREVIEW -1
216
217enum StitchModes {
218 STITCH_VERT,
219 STITCH_EDGE,
220};
221
223struct UvElementID {
224 int faceIndex;
225 int elementIndex;
226};
227
229struct StitchStateInit {
230 int uv_selected_count;
231 UvElementID *to_select;
232};
233
234} // namespace
235
236/* constructor */
237static StitchPreviewer *stitch_preview_init()
238{
239 StitchPreviewer *stitch_preview;
240
241 stitch_preview = MEM_mallocN<StitchPreviewer>("stitch_previewer");
242 stitch_preview->preview_polys = nullptr;
243 stitch_preview->preview_stitchable = nullptr;
244 stitch_preview->preview_unstitchable = nullptr;
245 stitch_preview->uvs_per_polygon = nullptr;
246
247 stitch_preview->preview_uvs = 0;
248 stitch_preview->num_polys = 0;
249 stitch_preview->num_stitchable = 0;
250 stitch_preview->num_unstitchable = 0;
251
252 stitch_preview->static_tris = nullptr;
253
254 stitch_preview->num_static_tris = 0;
255
256 return stitch_preview;
257}
258
259/* destructor...yeah this should be C++ :) */
260static void stitch_preview_delete(StitchPreviewer *stitch_preview)
261{
262 if (stitch_preview) {
263 MEM_SAFE_FREE(stitch_preview->preview_polys);
264 MEM_SAFE_FREE(stitch_preview->uvs_per_polygon);
265 MEM_SAFE_FREE(stitch_preview->preview_stitchable);
266 MEM_SAFE_FREE(stitch_preview->preview_unstitchable);
267 MEM_SAFE_FREE(stitch_preview->static_tris);
268 MEM_freeN(stitch_preview);
269 }
270}
271
272/* This function updates the header of the UV editor when the stitch tool updates its settings */
273static void stitch_update_header(StitchStateContainer *ssc, bContext *C)
274{
276 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
277 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
278 status.item(fmt::format("{} {}",
279 IFACE_("Select"),
280 (ssc->mode == STITCH_VERT ? IFACE_("Vertices") : IFACE_("Edges"))),
281 ICON_EVENT_SHIFT,
282 ICON_MOUSE_RMB);
283 status.item(fmt::format("{} : {}",
284 IFACE_("Mode"),
285 (ssc->mode == STITCH_VERT ? IFACE_("Vertex") : IFACE_("Edge"))),
286 ICON_EVENT_TAB);
287 status.item(IFACE_("Switch Island"), ICON_EVENT_I);
288 status.item_bool(IFACE_("Snap"), ssc->snap_islands, ICON_EVENT_S);
289 status.item_bool(IFACE_("Midpoints"), ssc->midpoints, ICON_EVENT_M);
290 status.item_bool(IFACE_("Limit"), ssc->use_limit, ICON_EVENT_L);
291 if (ssc->use_limit) {
292 status.item(fmt::format("{} ({:.2f})", IFACE_("Limit Distance"), ssc->limit_dist),
293 ICON_EVENT_ALT,
294 ICON_MOUSE_MMB_SCROLL);
295 }
296}
297
298static void stitch_uv_rotate(const float mat[2][2],
299 const float medianPoint[2],
300 float uv[2],
301 float aspect)
302{
303 float uv_rotation_result[2];
304
305 uv[1] /= aspect;
306
307 sub_v2_v2(uv, medianPoint);
308 mul_v2_m2v2(uv_rotation_result, mat, uv);
309 add_v2_v2v2(uv, uv_rotation_result, medianPoint);
310
311 uv[1] *= aspect;
312}
313
314/* check if two uvelements are stitchable.
315 * This should only operate on -different- separate UvElements */
316static bool stitch_check_uvs_stitchable(const int cd_loop_uv_offset,
318 UvElement *element_iter,
319 StitchStateContainer *ssc)
320{
321 float limit;
322
323 if (element_iter == element) {
324 return false;
325 }
326
327 limit = ssc->limit_dist;
328
329 if (ssc->use_limit) {
330 BMLoop *l;
331
332 l = element->l;
333 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
334 l = element_iter->l;
335 float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
336
337 if (fabsf(luv[0] - luv_iter[0]) < limit && fabsf(luv[1] - luv_iter[1]) < limit) {
338 return true;
339 }
340 return false;
341 }
342 return true;
343}
344
345static bool stitch_check_edges_stitchable(const int cd_loop_uv_offset,
346 UvEdge *edge,
347 UvEdge *edge_iter,
348 StitchStateContainer *ssc,
349 StitchState *state)
350{
351 float limit;
352
353 if (edge_iter == edge) {
354 return false;
355 }
356
357 limit = ssc->limit_dist;
358
359 if (ssc->use_limit) {
360 float *luv_orig1 = BM_ELEM_CD_GET_FLOAT_P(state->uvs[edge->uv1]->l, cd_loop_uv_offset);
361 float *luv_iter1 = BM_ELEM_CD_GET_FLOAT_P(state->uvs[edge_iter->uv1]->l, cd_loop_uv_offset);
362
363 float *luv_orig2 = BM_ELEM_CD_GET_FLOAT_P(state->uvs[edge->uv2]->l, cd_loop_uv_offset);
364 float *luv_iter2 = BM_ELEM_CD_GET_FLOAT_P(state->uvs[edge_iter->uv2]->l, cd_loop_uv_offset);
365
366 if (fabsf(luv_orig1[0] - luv_iter1[0]) < limit && fabsf(luv_orig1[1] - luv_iter1[1]) < limit &&
367 fabsf(luv_orig2[0] - luv_iter2[0]) < limit && fabsf(luv_orig2[1] - luv_iter2[1]) < limit)
368 {
369 return true;
370 }
371 return false;
372 }
373 return true;
374}
375
376static bool stitch_check_uvs_state_stitchable(const int cd_loop_uv_offset,
378 UvElement *element_iter,
379 StitchStateContainer *ssc)
380{
381 if ((ssc->snap_islands && element->island == element_iter->island) ||
382 (!ssc->midpoints && element->island == element_iter->island))
383 {
384 return false;
385 }
386
387 return stitch_check_uvs_stitchable(cd_loop_uv_offset, element, element_iter, ssc);
388}
389
390static bool stitch_check_edges_state_stitchable(const int cd_loop_uv_offset,
391 UvEdge *edge,
392 UvEdge *edge_iter,
393 StitchStateContainer *ssc,
394 StitchState *state)
395{
396 if ((ssc->snap_islands && edge->element->island == edge_iter->element->island) ||
397 (!ssc->midpoints && edge->element->island == edge_iter->element->island))
398 {
399 return false;
400 }
401
402 return stitch_check_edges_stitchable(cd_loop_uv_offset, edge, edge_iter, ssc, state);
403}
404
405/* calculate snapping for islands */
406static void stitch_calculate_island_snapping(const int cd_loop_uv_offset,
407 StitchState *state,
408 PreviewPosition *preview_position,
409 StitchPreviewer *preview,
410 IslandStitchData *island_stitch_data,
411 int final)
412{
414
415 for (int i = 0; i < state->element_map->total_islands; i++) {
416 if (island_stitch_data[i].addedForPreview) {
417 int numOfIslandUVs = 0, j;
418 int totelem = island_stitch_data[i].num_rot_elements_neg +
419 island_stitch_data[i].num_rot_elements;
420 float rotation;
421 float rotation_mat[2][2];
422
423 /* check to avoid divide by 0 */
424 if (island_stitch_data[i].num_rot_elements > 1) {
425 island_stitch_data[i].rotation /= island_stitch_data[i].num_rot_elements;
426 }
427
428 if (island_stitch_data[i].num_rot_elements_neg > 1) {
429 island_stitch_data[i].rotation_neg /= island_stitch_data[i].num_rot_elements_neg;
430 }
431
432 if (island_stitch_data[i].numOfElements > 1) {
433 island_stitch_data[i].medianPoint[0] /= island_stitch_data[i].numOfElements;
434 island_stitch_data[i].medianPoint[1] /= island_stitch_data[i].numOfElements;
435
436 island_stitch_data[i].translation[0] /= island_stitch_data[i].numOfElements;
437 island_stitch_data[i].translation[1] /= island_stitch_data[i].numOfElements;
438 }
439
440 island_stitch_data[i].medianPoint[1] /= state->aspect;
441 if ((island_stitch_data[i].rotation + island_stitch_data[i].rotation_neg < float(M_PI_2)) ||
442 island_stitch_data[i].num_rot_elements == 0 ||
443 island_stitch_data[i].num_rot_elements_neg == 0)
444 {
445 rotation = (island_stitch_data[i].rotation * island_stitch_data[i].num_rot_elements -
446 island_stitch_data[i].rotation_neg *
447 island_stitch_data[i].num_rot_elements_neg) /
448 totelem;
449 }
450 else {
451 rotation = (island_stitch_data[i].rotation * island_stitch_data[i].num_rot_elements +
452 (2.0f * float(M_PI) - island_stitch_data[i].rotation_neg) *
453 island_stitch_data[i].num_rot_elements_neg) /
454 totelem;
455 }
456
457 angle_to_mat2(rotation_mat, rotation);
458 numOfIslandUVs = state->element_map->island_total_uvs[i];
459 element = &state->element_map->storage[state->element_map->island_indices[i]];
460 for (j = 0; j < numOfIslandUVs; j++, element++) {
461 /* stitchable uvs have already been processed, don't process */
462 if (!(element->flag & STITCH_PROCESSED)) {
463 BMLoop *l;
464
465 l = element->l;
466 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
467
468 if (final) {
469
470 stitch_uv_rotate(rotation_mat, island_stitch_data[i].medianPoint, luv, state->aspect);
471
472 add_v2_v2(luv, island_stitch_data[i].translation);
473 }
474
475 else {
476
477 int face_preview_pos =
478 preview_position[BM_elem_index_get(element->l->f)].data_position;
479
480 stitch_uv_rotate(rotation_mat,
481 island_stitch_data[i].medianPoint,
482 preview->preview_polys + face_preview_pos +
483 2 * element->loop_of_face_index,
484 state->aspect);
485
486 add_v2_v2(preview->preview_polys + face_preview_pos + 2 * element->loop_of_face_index,
487 island_stitch_data[i].translation);
488 }
489 }
490 /* cleanup */
491 element->flag &= STITCH_SELECTED;
492 }
493 }
494 }
495}
496
497static void stitch_island_calculate_edge_rotation(const int cd_loop_uv_offset,
498 UvEdge *edge,
499 StitchStateContainer *ssc,
500 StitchState *state,
501 UVVertAverage *uv_average,
502 const uint *uvfinal_map,
503 IslandStitchData *island_stitch_data)
504{
505 UvElement *element1, *element2;
506 float uv1[2], uv2[2];
507 float edgecos, edgesin;
508 int index1, index2;
509 float rotation;
510
511 element1 = state->uvs[edge->uv1];
512 element2 = state->uvs[edge->uv2];
513
514 float *luv1 = BM_ELEM_CD_GET_FLOAT_P(element1->l, cd_loop_uv_offset);
515 float *luv2 = BM_ELEM_CD_GET_FLOAT_P(element2->l, cd_loop_uv_offset);
516
517 if (ssc->mode == STITCH_VERT) {
518 index1 = uvfinal_map[element1 - state->element_map->storage];
519 index2 = uvfinal_map[element2 - state->element_map->storage];
520 }
521 else {
522 index1 = edge->uv1;
523 index2 = edge->uv2;
524 }
525 /* the idea here is to take the directions of the edges and find the rotation between
526 * final and initial direction. This, using inner and outer vector products,
527 * gives the angle. Directions are differences so... */
528 uv1[0] = luv2[0] - luv1[0];
529 uv1[1] = luv2[1] - luv1[1];
530
531 uv1[1] /= state->aspect;
532
533 uv2[0] = uv_average[index2].uv[0] - uv_average[index1].uv[0];
534 uv2[1] = uv_average[index2].uv[1] - uv_average[index1].uv[1];
535
536 uv2[1] /= state->aspect;
537
538 normalize_v2(uv1);
539 normalize_v2(uv2);
540
541 edgecos = dot_v2v2(uv1, uv2);
542 edgesin = cross_v2v2(uv1, uv2);
543 rotation = acosf(max_ff(-1.0f, min_ff(1.0f, edgecos)));
544
545 if (edgesin > 0.0f) {
546 island_stitch_data[element1->island].num_rot_elements++;
547 island_stitch_data[element1->island].rotation += rotation;
548 }
549 else {
550 island_stitch_data[element1->island].num_rot_elements_neg++;
551 island_stitch_data[element1->island].rotation_neg += rotation;
552 }
553}
554
555static void stitch_island_calculate_vert_rotation(const int cd_loop_uv_offset,
557 StitchStateContainer *ssc,
558 StitchState *state,
559 IslandStitchData *island_stitch_data)
560{
561 float rotation = 0, rotation_neg = 0;
562 int rot_elem = 0, rot_elem_neg = 0;
563
564 if (element->island == ssc->static_island && !ssc->midpoints) {
565 return;
566 }
567
568 UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
569 for (; element_iter; element_iter = element_iter->next) {
570 if (element_iter->separate &&
571 stitch_check_uvs_state_stitchable(cd_loop_uv_offset, element, element_iter, ssc))
572 {
573 float normal[2];
574
575 /* only calculate rotation against static island uv verts */
576 if (!ssc->midpoints && element_iter->island != ssc->static_island) {
577 continue;
578 }
579
580 int index_tmp1 = element_iter - state->element_map->storage;
581 index_tmp1 = state->map[index_tmp1];
582 int index_tmp2 = element - state->element_map->storage;
583 index_tmp2 = state->map[index_tmp2];
584
585 negate_v2_v2(normal, state->normals + index_tmp2 * 2);
586 float edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2);
587 float edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2);
588 if (edgesin > 0.0f) {
589 rotation += acosf(max_ff(-1.0f, min_ff(1.0f, edgecos)));
590 rot_elem++;
591 }
592 else {
593 rotation_neg += acosf(max_ff(-1.0f, min_ff(1.0f, edgecos)));
594 rot_elem_neg++;
595 }
596 }
597 }
598
599 if (ssc->midpoints) {
600 rotation /= 2.0f;
601 rotation_neg /= 2.0f;
602 }
603 island_stitch_data[element->island].num_rot_elements += rot_elem;
604 island_stitch_data[element->island].rotation += rotation;
605 island_stitch_data[element->island].num_rot_elements_neg += rot_elem_neg;
606 island_stitch_data[element->island].rotation_neg += rotation_neg;
607}
608
609static void state_delete(StitchState *state)
610{
611 if (state) {
612 if (state->island_is_stitchable) {
613 MEM_freeN(state->island_is_stitchable);
614 }
615 if (state->element_map) {
616 BM_uv_element_map_free(state->element_map);
617 }
618 if (state->uvs) {
619 MEM_freeN(state->uvs);
620 }
621 if (state->selection_stack) {
622 MEM_freeN(state->selection_stack);
623 }
624 if (state->tris_per_island) {
625 MEM_freeN(state->tris_per_island);
626 }
627 if (state->map) {
628 MEM_freeN(state->map);
629 }
630 if (state->normals) {
631 MEM_freeN(state->normals);
632 }
633 if (state->edges) {
634 MEM_freeN(state->edges);
635 }
636 stitch_preview_delete(state->stitch_preview);
637 state->stitch_preview = nullptr;
638 if (state->edge_hash) {
639 BLI_ghash_free(state->edge_hash, nullptr, nullptr);
640 }
642 }
643}
644
645static void state_delete_all(StitchStateContainer *ssc)
646{
647 if (ssc) {
648 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
649 state_delete(ssc->states[ob_index]);
650 }
651 MEM_freeN(ssc->states);
652 MEM_freeN(ssc->objects);
653 MEM_freeN(ssc);
654 }
655}
656
657static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState *state)
658{
659 UvEdge *edges = state->edges;
660 const int *map = state->map;
661 UvElementMap *element_map = state->element_map;
662 for (int i = 0; i < state->total_separate_edges; i++) {
663 UvEdge *edge = edges + i;
664
665 if (edge->first) {
666 continue;
667 }
668
669 /* only boundary edges can be stitched. Yes. Sorry about that :p */
670 if (edge->flag & STITCH_BOUNDARY) {
671 UvElement *element1 = state->uvs[edge->uv1];
672 UvElement *element2 = state->uvs[edge->uv2];
673
674 /* Now iterate through all faces and try to find edges sharing the same vertices */
675 UvElement *iter1 = BM_uv_element_get_head(state->element_map, element1);
676 UvEdge *last_set = edge;
677 int elemindex2 = BM_elem_index_get(element2->l->v);
678
679 edge->first = edge;
680
681 for (; iter1; iter1 = iter1->next) {
682 UvElement *iter2 = nullptr;
683
684 /* check to see if other vertex of edge belongs to same vertex as */
685 if (BM_elem_index_get(iter1->l->next->v) == elemindex2) {
686 iter2 = BM_uv_element_get(element_map, iter1->l->next);
687 }
688 else if (BM_elem_index_get(iter1->l->prev->v) == elemindex2) {
689 iter2 = BM_uv_element_get(element_map, iter1->l->prev);
690 }
691
692 if (iter2) {
693 int index1 = map[iter1 - element_map->storage];
694 int index2 = map[iter2 - element_map->storage];
695 UvEdge edgetmp;
696 UvEdge *edge2, *eiter;
697 bool valid = true;
698
699 /* make sure the indices are well behaved */
700 if (index1 > index2) {
701 std::swap(index1, index2);
702 }
703
704 edgetmp.uv1 = index1;
705 edgetmp.uv2 = index2;
706
707 /* get the edge from the hash */
708 edge2 = static_cast<UvEdge *>(BLI_ghash_lookup(edge_hash, &edgetmp));
709
710 /* more iteration to make sure non-manifold case is handled nicely */
711 for (eiter = edge; eiter; eiter = eiter->next) {
712 if (edge2 == eiter) {
713 valid = false;
714 break;
715 }
716 }
717
718 if (valid) {
719 /* here I am taking care of non manifold case, assuming more than two matching edges.
720 * I am not too sure we want this though */
721 last_set->next = edge2;
722 last_set = edge2;
723 /* set first, similarly to uv elements.
724 * Now we can iterate among common edges easily */
725 edge2->first = edge;
726 }
727 }
728 }
729 }
730 else {
731 /* so stitchability code works */
732 edge->first = edge;
733 }
734 }
735}
736
737/* checks for remote uvs that may be stitched with a certain uv, flags them if stitchable. */
738static void determine_uv_stitchability(const int cd_loop_uv_offset,
740 StitchStateContainer *ssc,
741 StitchState *state,
742 IslandStitchData *island_stitch_data)
743{
744 UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
745 for (; element_iter; element_iter = element_iter->next) {
746 if (element_iter->separate) {
747 if (stitch_check_uvs_stitchable(cd_loop_uv_offset, element, element_iter, ssc)) {
748 island_stitch_data[element_iter->island].stitchableCandidate = 1;
749 island_stitch_data[element->island].stitchableCandidate = 1;
750 element->flag |= STITCH_STITCHABLE_CANDIDATE;
751 }
752 }
753 }
754}
755
756static void determine_uv_edge_stitchability(const int cd_loop_uv_offset,
757 UvEdge *edge,
758 StitchStateContainer *ssc,
759 StitchState *state,
760 IslandStitchData *island_stitch_data)
761{
762 UvEdge *edge_iter = edge->first;
763
764 for (; edge_iter; edge_iter = edge_iter->next) {
765 if (stitch_check_edges_stitchable(cd_loop_uv_offset, edge, edge_iter, ssc, state)) {
766 island_stitch_data[edge_iter->element->island].stitchableCandidate = 1;
767 island_stitch_data[edge->element->island].stitchableCandidate = 1;
768 edge->flag |= STITCH_STITCHABLE_CANDIDATE;
769 }
770 }
771}
772
773/* set preview buffer position of UV face in editface->tmp.l */
775 StitchPreviewer *preview,
776 PreviewPosition *preview_position)
777{
778 int index = BM_elem_index_get(efa);
779
780 if (preview_position[index].data_position == STITCH_NO_PREVIEW) {
781 preview_position[index].data_position = preview->preview_uvs * 2;
782 preview_position[index].polycount_position = preview->num_polys++;
783 preview->preview_uvs += efa->len;
784 }
785}
786
787/* setup face preview for all coincident uvs and their faces */
789 StitchStateContainer *ssc,
790 StitchState *state,
791 IslandStitchData *island_stitch_data,
792 PreviewPosition *preview_position)
793{
794 StitchPreviewer *preview = state->stitch_preview;
795
796 /* static island does not change so returning immediately */
797 if (ssc->snap_islands && !ssc->midpoints && ssc->static_island == element->island) {
798 return;
799 }
800
801 if (ssc->snap_islands) {
802 island_stitch_data[element->island].addedForPreview = 1;
803 }
804
805 do {
806 stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position);
807 element = element->next;
808 } while (element && !element->separate);
809}
810
811/* checks if uvs are indeed stitchable and registers so that they can be shown in preview */
812static void stitch_validate_uv_stitchability(const int cd_loop_uv_offset,
814 StitchStateContainer *ssc,
815 StitchState *state,
816 IslandStitchData *island_stitch_data,
817 PreviewPosition *preview_position)
818{
819 StitchPreviewer *preview = state->stitch_preview;
820
821 /* If not the active object, then it's unstitchable */
822 if (ssc->states[ssc->active_object_index] != state) {
823 preview->num_unstitchable++;
824 return;
825 }
826
827 UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
828 for (; element_iter; element_iter = element_iter->next) {
829 if (element_iter->separate) {
830 if (element_iter == element) {
831 continue;
832 }
833 if (stitch_check_uvs_state_stitchable(cd_loop_uv_offset, element, element_iter, ssc)) {
834 if ((element_iter->island == ssc->static_island) ||
835 (element->island == ssc->static_island))
836 {
837 element->flag |= STITCH_STITCHABLE;
838 preview->num_stitchable++;
840 element, ssc, state, island_stitch_data, preview_position);
841 return;
842 }
843 }
844 }
845 }
846
847 /* this can happen if the uvs to be stitched are not on a stitchable island */
848 if (!(element->flag & STITCH_STITCHABLE)) {
849 preview->num_unstitchable++;
850 }
851}
852
853static void stitch_validate_edge_stitchability(const int cd_loop_uv_offset,
854 UvEdge *edge,
855 StitchStateContainer *ssc,
856 StitchState *state,
857 IslandStitchData *island_stitch_data,
858 PreviewPosition *preview_position)
859{
860 StitchPreviewer *preview = state->stitch_preview;
861
862 /* If not the active object, then it's unstitchable */
863 if (ssc->states[ssc->active_object_index] != state) {
864 preview->num_unstitchable++;
865 return;
866 }
867
868 UvEdge *edge_iter = edge->first;
869
870 for (; edge_iter; edge_iter = edge_iter->next) {
871 if (edge_iter == edge) {
872 continue;
873 }
874 if (stitch_check_edges_state_stitchable(cd_loop_uv_offset, edge, edge_iter, ssc, state)) {
875 if ((edge_iter->element->island == ssc->static_island) ||
876 (edge->element->island == ssc->static_island))
877 {
878 edge->flag |= STITCH_STITCHABLE;
879 preview->num_stitchable++;
881 state->uvs[edge->uv1], ssc, state, island_stitch_data, preview_position);
883 state->uvs[edge->uv2], ssc, state, island_stitch_data, preview_position);
884 return;
885 }
886 }
887 }
888
889 /* this can happen if the uvs to be stitched are not on a stitchable island */
890 if (!(edge->flag & STITCH_STITCHABLE)) {
891 preview->num_unstitchable++;
892 }
893}
894
897 int index,
898 PreviewPosition *preview_position,
899 UVVertAverage *final_position,
900 StitchStateContainer *ssc,
901 StitchState *state,
902 const bool final)
903{
904 BMesh *bm = state->em->bm;
905 StitchPreviewer *preview = state->stitch_preview;
906
907 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
908
909 if (element->flag & STITCH_STITCHABLE) {
910 UvElement *element_iter = element;
911 /* propagate to coincident uvs */
912 do {
913 BMLoop *l;
914
915 l = element_iter->l;
916 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
917
918 element_iter->flag |= STITCH_PROCESSED;
919 /* either flush to preview or to the MTFace, if final */
920 if (final) {
921 copy_v2_v2(luv, final_position[index].uv);
922
923 uvedit_uv_select_enable(scene, state->em->bm, l);
924 }
925 else {
926 int face_preview_pos =
927 preview_position[BM_elem_index_get(element_iter->l->f)].data_position;
928 if (face_preview_pos != STITCH_NO_PREVIEW) {
929 copy_v2_v2(preview->preview_polys + face_preview_pos +
930 2 * element_iter->loop_of_face_index,
931 final_position[index].uv);
932 }
933 }
934
935 /* end of calculations, keep only the selection flag */
936 if ((!ssc->snap_islands) ||
937 ((!ssc->midpoints) && (element_iter->island == ssc->static_island)))
938 {
939 element_iter->flag &= STITCH_SELECTED;
940 }
941
942 element_iter = element_iter->next;
943 } while (element_iter && !element_iter->separate);
944 }
945}
946
947/* main processing function. It calculates preview and final positions. */
948static int stitch_process_data(StitchStateContainer *ssc,
949 StitchState *state,
950 Scene *scene,
951 int final)
952{
953 int i;
954 StitchPreviewer *preview;
955 IslandStitchData *island_stitch_data = nullptr;
956 int previous_island = ssc->static_island;
957 BMesh *bm = state->em->bm;
958 BMFace *efa;
959 BMIter iter;
960 UVVertAverage *final_position = nullptr;
961 bool is_active_state = (state == ssc->states[ssc->active_object_index]);
962
963 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
964
965 char stitch_midpoints = ssc->midpoints;
966 /* Used to map UV indices to UV-average indices for selection. */
967 uint *uvfinal_map = nullptr;
968 /* per face preview position in preview buffer */
969 PreviewPosition *preview_position = nullptr;
970
971 /* cleanup previous preview */
972 stitch_preview_delete(state->stitch_preview);
973 preview = state->stitch_preview = stitch_preview_init();
974 if (preview == nullptr) {
975 return 0;
976 }
977
978 preview_position = static_cast<PreviewPosition *>(
979 MEM_mallocN(bm->totface * sizeof(*preview_position), "stitch_face_preview_position"));
980 /* each face holds its position in the preview buffer in tmp. -1 is uninitialized */
981 for (i = 0; i < bm->totface; i++) {
982 preview_position[i].data_position = STITCH_NO_PREVIEW;
983 }
984
985 island_stitch_data = MEM_calloc_arrayN<IslandStitchData>(state->element_map->total_islands,
986 "stitch_island_data");
987 if (!island_stitch_data) {
988 return 0;
989 }
990
991 /* store indices to editVerts and Faces. May be unneeded but ensuring anyway */
993
994 /****************************************
995 * First determine stitchability of uvs *
996 ****************************************/
997
998 for (i = 0; i < state->selection_size; i++) {
999 if (ssc->mode == STITCH_VERT) {
1000 UvElement *element = (UvElement *)state->selection_stack[i];
1001 determine_uv_stitchability(cd_loop_uv_offset, element, ssc, state, island_stitch_data);
1002 }
1003 else {
1004 UvEdge *edge = (UvEdge *)state->selection_stack[i];
1005 determine_uv_edge_stitchability(cd_loop_uv_offset, edge, ssc, state, island_stitch_data);
1006 }
1007 }
1008
1009 /* Remember stitchable candidates as places the 'I' button will stop at. */
1010 for (int island_idx = 0; island_idx < state->element_map->total_islands; island_idx++) {
1011 state->island_is_stitchable[island_idx] = island_stitch_data[island_idx].stitchableCandidate ?
1012 true :
1013 false;
1014 }
1015
1016 if (is_active_state) {
1017 /* set static island to one that is added for preview */
1018 ssc->static_island %= state->element_map->total_islands;
1019 while (!(island_stitch_data[ssc->static_island].stitchableCandidate)) {
1020 ssc->static_island++;
1021 ssc->static_island %= state->element_map->total_islands;
1022 /* this is entirely possible if for example limit stitching
1023 * with no stitchable verts or no selection */
1024 if (ssc->static_island == previous_island) {
1025 break;
1026 }
1027 }
1028 }
1029
1030 for (i = 0; i < state->selection_size; i++) {
1031 if (ssc->mode == STITCH_VERT) {
1032 UvElement *element = (UvElement *)state->selection_stack[i];
1033 if (element->flag & STITCH_STITCHABLE_CANDIDATE) {
1034 element->flag &= ~STITCH_STITCHABLE_CANDIDATE;
1036 cd_loop_uv_offset, element, ssc, state, island_stitch_data, preview_position);
1037 }
1038 else {
1039 /* add to preview for unstitchable */
1040 preview->num_unstitchable++;
1041 }
1042 }
1043 else {
1044 UvEdge *edge = (UvEdge *)state->selection_stack[i];
1045 if (edge->flag & STITCH_STITCHABLE_CANDIDATE) {
1046 edge->flag &= ~STITCH_STITCHABLE_CANDIDATE;
1048 cd_loop_uv_offset, edge, ssc, state, island_stitch_data, preview_position);
1049 }
1050 else {
1051 preview->num_unstitchable++;
1052 }
1053 }
1054 }
1055
1056 /*********************************************************************
1057 * Setup the stitchable & unstitchable preview buffers and fill *
1058 * them with the appropriate data *
1059 *********************************************************************/
1060 if (!final) {
1061 float *luv;
1062 int stitchBufferIndex = 0, unstitchBufferIndex = 0;
1063 int preview_size = (ssc->mode == STITCH_VERT) ? 2 : 4;
1064 /* initialize the preview buffers */
1065 preview->preview_stitchable = (float *)MEM_mallocN(
1066 preview->num_stitchable * sizeof(float) * preview_size, "stitch_preview_stitchable_data");
1067 preview->preview_unstitchable = (float *)MEM_mallocN(preview->num_unstitchable *
1068 sizeof(float) * preview_size,
1069 "stitch_preview_unstitchable_data");
1070
1071 /* will cause cancel and freeing of all data structures so OK */
1072 if (!preview->preview_stitchable || !preview->preview_unstitchable) {
1073 return 0;
1074 }
1075
1076 /* fill the appropriate preview buffers */
1077 if (ssc->mode == STITCH_VERT) {
1078 for (i = 0; i < state->total_separate_uvs; i++) {
1079 UvElement *element = state->uvs[i];
1080 if (element->flag & STITCH_STITCHABLE) {
1081 luv = BM_ELEM_CD_GET_FLOAT_P(element->l, cd_loop_uv_offset);
1082 copy_v2_v2(&preview->preview_stitchable[stitchBufferIndex * 2], luv);
1083 stitchBufferIndex++;
1084 }
1085 else if (element->flag & STITCH_SELECTED) {
1086 luv = BM_ELEM_CD_GET_FLOAT_P(element->l, cd_loop_uv_offset);
1087 copy_v2_v2(&preview->preview_unstitchable[unstitchBufferIndex * 2], luv);
1088 unstitchBufferIndex++;
1089 }
1090 }
1091 }
1092 else {
1093 for (i = 0; i < state->total_separate_edges; i++) {
1094 UvEdge *edge = state->edges + i;
1095 UvElement *element1 = state->uvs[edge->uv1];
1096 UvElement *element2 = state->uvs[edge->uv2];
1097
1098 if (edge->flag & STITCH_STITCHABLE) {
1099 luv = BM_ELEM_CD_GET_FLOAT_P(element1->l, cd_loop_uv_offset);
1100 copy_v2_v2(&preview->preview_stitchable[stitchBufferIndex * 4], luv);
1101
1102 luv = BM_ELEM_CD_GET_FLOAT_P(element2->l, cd_loop_uv_offset);
1103 copy_v2_v2(&preview->preview_stitchable[stitchBufferIndex * 4 + 2], luv);
1104
1105 stitchBufferIndex++;
1106 BLI_assert(stitchBufferIndex <= preview->num_stitchable);
1107 }
1108 else if (edge->flag & STITCH_SELECTED) {
1109 luv = BM_ELEM_CD_GET_FLOAT_P(element1->l, cd_loop_uv_offset);
1110 copy_v2_v2(&preview->preview_unstitchable[unstitchBufferIndex * 4], luv);
1111
1112 luv = BM_ELEM_CD_GET_FLOAT_P(element2->l, cd_loop_uv_offset);
1113 copy_v2_v2(&preview->preview_unstitchable[unstitchBufferIndex * 4 + 2], luv);
1114
1115 unstitchBufferIndex++;
1116 BLI_assert(unstitchBufferIndex <= preview->num_unstitchable);
1117 }
1118 }
1119 }
1120 }
1121
1122 if (ssc->states[ssc->active_object_index] != state) {
1123 /* This is not the active object/state, exit here */
1124 MEM_freeN(island_stitch_data);
1125 MEM_freeN(preview_position);
1126 return 1;
1127 }
1128
1129 /****************************************
1130 * Setup preview for stitchable islands *
1131 ****************************************/
1132 if (ssc->snap_islands) {
1133 for (i = 0; i < state->element_map->total_islands; i++) {
1134 if (island_stitch_data[i].addedForPreview) {
1135 int numOfIslandUVs = state->element_map->island_total_uvs[i];
1136 UvElement *element = &state->element_map->storage[state->element_map->island_indices[i]];
1137 for (int j = 0; j < numOfIslandUVs; j++, element++) {
1138 stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position);
1139 }
1140 }
1141 }
1142 }
1143
1144 /*********************************************************************
1145 * Setup the remaining preview buffers and fill them with the *
1146 * appropriate data *
1147 *********************************************************************/
1148 if (!final) {
1149 BMIter liter;
1150 BMLoop *l;
1151 float *luv;
1152 uint buffer_index = 0;
1153
1154 /* initialize the preview buffers */
1155 preview->preview_polys = static_cast<float *>(
1156 MEM_mallocN(sizeof(float[2]) * preview->preview_uvs, "tri_uv_stitch_prev"));
1157 preview->uvs_per_polygon = MEM_malloc_arrayN<uint>(preview->num_polys, "tri_uv_stitch_prev");
1158
1159 preview->static_tris = static_cast<float *>(
1160 MEM_mallocN((sizeof(float[6]) * state->tris_per_island[ssc->static_island]),
1161 "static_island_preview_tris"));
1162
1163 preview->num_static_tris = state->tris_per_island[ssc->static_island];
1164 /* will cause cancel and freeing of all data structures so OK */
1165 if (!preview->preview_polys) {
1166 return 0;
1167 }
1168
1169 /* copy data from UVs to the preview display buffers */
1170 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1171 /* just to test if face was added for processing.
1172 * uvs of unselected vertices will return null */
1174
1175 if (element) {
1176 int numoftris = efa->len - 2;
1177 int index = BM_elem_index_get(efa);
1178 int face_preview_pos = preview_position[index].data_position;
1179 if (face_preview_pos != STITCH_NO_PREVIEW) {
1180 preview->uvs_per_polygon[preview_position[index].polycount_position] = efa->len;
1181 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1182 luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1183 copy_v2_v2(preview->preview_polys + face_preview_pos + i * 2, luv);
1184 }
1185 }
1186
1187 /* if this is the static_island on the active object */
1188 if (element->island == ssc->static_island) {
1189 BMLoop *fl = BM_FACE_FIRST_LOOP(efa);
1190 float *fuv = BM_ELEM_CD_GET_FLOAT_P(fl, cd_loop_uv_offset);
1191
1192 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1193 if (i < numoftris) {
1194 /* using next since the first uv is already accounted for */
1195 BMLoop *lnext = l->next;
1196 float *luvnext = BM_ELEM_CD_GET_FLOAT_P(lnext->next, cd_loop_uv_offset);
1197 luv = BM_ELEM_CD_GET_FLOAT_P(lnext, cd_loop_uv_offset);
1198
1199 memcpy(preview->static_tris + buffer_index, fuv, sizeof(float[2]));
1200 memcpy(preview->static_tris + buffer_index + 2, luv, sizeof(float[2]));
1201 memcpy(preview->static_tris + buffer_index + 4, luvnext, sizeof(float[2]));
1202 buffer_index += 6;
1203 }
1204 else {
1205 break;
1206 }
1207 }
1208 }
1209 }
1210 }
1211 }
1212
1213 /******************************************************
1214 * Here we calculate the final coordinates of the uvs *
1215 ******************************************************/
1216
1217 if (ssc->mode == STITCH_VERT) {
1218 final_position = static_cast<UVVertAverage *>(
1219 MEM_callocN(state->selection_size * sizeof(*final_position), "stitch_uv_average"));
1220 uvfinal_map = static_cast<uint *>(
1221 MEM_mallocN(state->element_map->total_uvs * sizeof(*uvfinal_map), "stitch_uv_final_map"));
1222 }
1223 else {
1224 final_position = static_cast<UVVertAverage *>(
1225 MEM_callocN(state->total_separate_uvs * sizeof(*final_position), "stitch_uv_average"));
1226 }
1227
1228 /* first pass, calculate final position for stitchable uvs of the static island */
1229 for (i = 0; i < state->selection_size; i++) {
1230 if (ssc->mode == STITCH_VERT) {
1231 UvElement *element = static_cast<UvElement *>(state->selection_stack[i]);
1232
1233 if (element->flag & STITCH_STITCHABLE) {
1234 BMLoop *l = element->l;
1235 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1236
1237 uvfinal_map[element - state->element_map->storage] = i;
1238
1239 copy_v2_v2(final_position[i].uv, luv);
1240 final_position[i].count = 1;
1241
1242 if (ssc->snap_islands && element->island == ssc->static_island && !stitch_midpoints) {
1243 continue;
1244 }
1245
1246 UvElement *element_iter = state->element_map->vertex[BM_elem_index_get(l->v)];
1247 for (; element_iter; element_iter = element_iter->next) {
1248 if (element_iter->separate) {
1249 if (stitch_check_uvs_state_stitchable(cd_loop_uv_offset, element, element_iter, ssc)) {
1250 l = element_iter->l;
1251 luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1252 if (stitch_midpoints) {
1253 add_v2_v2(final_position[i].uv, luv);
1254 final_position[i].count++;
1255 }
1256 else if (element_iter->island == ssc->static_island) {
1257 /* if multiple uvs on the static island exist,
1258 * last checked remains. to disambiguate we need to limit or use
1259 * edge stitch */
1260 copy_v2_v2(final_position[i].uv, luv);
1261 }
1262 }
1263 }
1264 }
1265 }
1266 if (stitch_midpoints) {
1267 final_position[i].uv[0] /= final_position[i].count;
1268 final_position[i].uv[1] /= final_position[i].count;
1269 }
1270 }
1271 else {
1272 UvEdge *edge = static_cast<UvEdge *>(state->selection_stack[i]);
1273
1274 if (edge->flag & STITCH_STITCHABLE) {
1275 float *luv2, *luv1;
1276 BMLoop *l;
1277 UvEdge *edge_iter;
1278
1279 l = state->uvs[edge->uv1]->l;
1280 luv1 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1281 l = state->uvs[edge->uv2]->l;
1282 luv2 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1283
1284 copy_v2_v2(final_position[edge->uv1].uv, luv1);
1285 copy_v2_v2(final_position[edge->uv2].uv, luv2);
1286 final_position[edge->uv1].count = 1;
1287 final_position[edge->uv2].count = 1;
1288
1289 state->uvs[edge->uv1]->flag |= STITCH_STITCHABLE;
1290 state->uvs[edge->uv2]->flag |= STITCH_STITCHABLE;
1291
1292 if (ssc->snap_islands && edge->element->island == ssc->static_island && !stitch_midpoints)
1293 {
1294 continue;
1295 }
1296
1297 for (edge_iter = edge->first; edge_iter; edge_iter = edge_iter->next) {
1298 if (stitch_check_edges_state_stitchable(cd_loop_uv_offset, edge, edge_iter, ssc, state))
1299 {
1300 l = state->uvs[edge_iter->uv1]->l;
1301 luv1 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1302 l = state->uvs[edge_iter->uv2]->l;
1303 luv2 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1304
1305 if (stitch_midpoints) {
1306 add_v2_v2(final_position[edge->uv1].uv, luv1);
1307 final_position[edge->uv1].count++;
1308 add_v2_v2(final_position[edge->uv2].uv, luv2);
1309 final_position[edge->uv2].count++;
1310 }
1311 else if (edge_iter->element->island == ssc->static_island) {
1312 copy_v2_v2(final_position[edge->uv1].uv, luv1);
1313 copy_v2_v2(final_position[edge->uv2].uv, luv2);
1314 }
1315 }
1316 }
1317 }
1318 }
1319 }
1320
1321 /* Take mean position here.
1322 * For edge case, this can't be done inside the loop for shared UV-verts. */
1323 if (ssc->mode == STITCH_EDGE && stitch_midpoints) {
1324 for (i = 0; i < state->total_separate_uvs; i++) {
1325 final_position[i].uv[0] /= final_position[i].count;
1326 final_position[i].uv[1] /= final_position[i].count;
1327 }
1328 }
1329
1330 /* second pass, calculate island rotation and translation before modifying any uvs */
1331 if (ssc->snap_islands) {
1332 if (ssc->mode == STITCH_VERT) {
1333 for (i = 0; i < state->selection_size; i++) {
1334 UvElement *element = static_cast<UvElement *>(state->selection_stack[i]);
1335
1336 if (element->flag & STITCH_STITCHABLE) {
1337 BMLoop *l;
1338 float *luv;
1339
1340 l = element->l;
1341 luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1342
1343 /* accumulate each islands' translation from stitchable elements.
1344 * It is important to do here because in final pass MTFaces
1345 * get modified and result is zero. */
1346 island_stitch_data[element->island].translation[0] += final_position[i].uv[0] - luv[0];
1347 island_stitch_data[element->island].translation[1] += final_position[i].uv[1] - luv[1];
1348 island_stitch_data[element->island].medianPoint[0] += luv[0];
1349 island_stitch_data[element->island].medianPoint[1] += luv[1];
1350 island_stitch_data[element->island].numOfElements++;
1351 }
1352 }
1353
1354 /* only calculate rotation when an edge has been fully selected */
1355 for (i = 0; i < state->total_separate_edges; i++) {
1356 UvEdge *edge = state->edges + i;
1357 if ((edge->flag & STITCH_BOUNDARY) && (state->uvs[edge->uv1]->flag & STITCH_STITCHABLE) &&
1358 (state->uvs[edge->uv2]->flag & STITCH_STITCHABLE))
1359 {
1360 stitch_island_calculate_edge_rotation(cd_loop_uv_offset,
1361 edge,
1362 ssc,
1363 state,
1364 final_position,
1365 uvfinal_map,
1366 island_stitch_data);
1367 island_stitch_data[state->uvs[edge->uv1]->island].use_edge_rotation = true;
1368 }
1369 }
1370
1371 /* clear seams of stitched edges */
1372 if (final && ssc->clear_seams) {
1373 for (i = 0; i < state->total_separate_edges; i++) {
1374 UvEdge *edge = state->edges + i;
1375 if ((state->uvs[edge->uv1]->flag & STITCH_STITCHABLE) &&
1376 (state->uvs[edge->uv2]->flag & STITCH_STITCHABLE))
1377 {
1378 BM_elem_flag_disable(edge->element->l->e, BM_ELEM_SEAM);
1379 }
1380 }
1381 }
1382
1383 for (i = 0; i < state->selection_size; i++) {
1384 UvElement *element = static_cast<UvElement *>(state->selection_stack[i]);
1385 if (!island_stitch_data[element->island].use_edge_rotation) {
1386 if (element->flag & STITCH_STITCHABLE) {
1388 cd_loop_uv_offset, element, ssc, state, island_stitch_data);
1389 }
1390 }
1391 }
1392 }
1393 else {
1394 for (i = 0; i < state->total_separate_uvs; i++) {
1395 UvElement *element = state->uvs[i];
1396
1397 if (element->flag & STITCH_STITCHABLE) {
1398 BMLoop *l;
1399 float *luv;
1400
1401 l = element->l;
1402 luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1403
1404 /* accumulate each islands' translation from stitchable elements.
1405 * it is important to do here because in final pass MTFaces
1406 * get modified and result is zero. */
1407 island_stitch_data[element->island].translation[0] += final_position[i].uv[0] - luv[0];
1408 island_stitch_data[element->island].translation[1] += final_position[i].uv[1] - luv[1];
1409 island_stitch_data[element->island].medianPoint[0] += luv[0];
1410 island_stitch_data[element->island].medianPoint[1] += luv[1];
1411 island_stitch_data[element->island].numOfElements++;
1412 }
1413 }
1414
1415 for (i = 0; i < state->selection_size; i++) {
1416 UvEdge *edge = static_cast<UvEdge *>(state->selection_stack[i]);
1417
1418 if (edge->flag & STITCH_STITCHABLE) {
1420 cd_loop_uv_offset, edge, ssc, state, final_position, nullptr, island_stitch_data);
1421 island_stitch_data[state->uvs[edge->uv1]->island].use_edge_rotation = true;
1422 }
1423 }
1424
1425 /* clear seams of stitched edges */
1426 if (final && ssc->clear_seams) {
1427 for (i = 0; i < state->selection_size; i++) {
1428 UvEdge *edge = static_cast<UvEdge *>(state->selection_stack[i]);
1429 if (edge->flag & STITCH_STITCHABLE) {
1430 BM_elem_flag_disable(edge->element->l->e, BM_ELEM_SEAM);
1431 }
1432 }
1433 }
1434 }
1435 }
1436
1437 /* third pass, propagate changes to coincident uvs */
1438 for (i = 0; i < state->selection_size; i++) {
1439 if (ssc->mode == STITCH_VERT) {
1440 UvElement *element = static_cast<UvElement *>(state->selection_stack[i]);
1441
1443 scene, element, i, preview_position, final_position, ssc, state, final);
1444 }
1445 else {
1446 UvEdge *edge = static_cast<UvEdge *>(state->selection_stack[i]);
1447
1449 state->uvs[edge->uv1],
1450 edge->uv1,
1451 preview_position,
1452 final_position,
1453 ssc,
1454 state,
1455 final);
1457 state->uvs[edge->uv2],
1458 edge->uv2,
1459 preview_position,
1460 final_position,
1461 ssc,
1462 state,
1463 final);
1464
1465 edge->flag &= (STITCH_SELECTED | STITCH_BOUNDARY);
1466 }
1467 }
1468
1469 /* final pass, calculate Island translation/rotation if needed */
1470 if (ssc->snap_islands) {
1472 cd_loop_uv_offset, state, preview_position, preview, island_stitch_data, final);
1473 }
1474
1475 MEM_freeN(final_position);
1476 if (ssc->mode == STITCH_VERT) {
1477 MEM_freeN(uvfinal_map);
1478 }
1479 MEM_freeN(island_stitch_data);
1480 MEM_freeN(preview_position);
1481
1482 return 1;
1483}
1484
1485static int stitch_process_data_all(StitchStateContainer *ssc, Scene *scene, int final)
1486{
1487 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
1488 if (!stitch_process_data(ssc, ssc->states[ob_index], scene, final)) {
1489 return 0;
1490 }
1491 }
1492
1493 return 1;
1494}
1495
1496/* Stitch hash initialization functions */
1497static uint uv_edge_hash(const void *key)
1498{
1499 const UvEdge *edge = static_cast<const UvEdge *>(key);
1500 BLI_assert(edge->uv1 < edge->uv2);
1501 return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1));
1502}
1503
1504static bool uv_edge_compare(const void *a, const void *b)
1505{
1506 const UvEdge *edge1 = static_cast<const UvEdge *>(a);
1507 const UvEdge *edge2 = static_cast<const UvEdge *>(b);
1508 BLI_assert(edge1->uv1 < edge1->uv2);
1509 BLI_assert(edge2->uv1 < edge2->uv2);
1510
1511 if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) {
1512 return false;
1513 }
1514 return true;
1515}
1516
1517/* select all common edges */
1518static void stitch_select_edge(UvEdge *edge, StitchState *state, int always_select)
1519{
1520 UvEdge *eiter;
1521 UvEdge **selection_stack = (UvEdge **)state->selection_stack;
1522
1523 for (eiter = edge->first; eiter; eiter = eiter->next) {
1524 if (eiter->flag & STITCH_SELECTED) {
1525 int i;
1526 if (always_select) {
1527 continue;
1528 }
1529
1530 eiter->flag &= ~STITCH_SELECTED;
1531 for (i = 0; i < state->selection_size; i++) {
1532 if (selection_stack[i] == eiter) {
1533 (state->selection_size)--;
1534 selection_stack[i] = selection_stack[state->selection_size];
1535 break;
1536 }
1537 }
1538 }
1539 else {
1540 eiter->flag |= STITCH_SELECTED;
1541 selection_stack[state->selection_size++] = eiter;
1542 }
1543 }
1544}
1545
1546/* Select all common uvs */
1547static void stitch_select_uv(UvElement *element, StitchState *state, int always_select)
1548{
1549 UvElement **selection_stack = (UvElement **)state->selection_stack;
1550 UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
1551 /* first deselect all common uvs */
1552 for (; element_iter; element_iter = element_iter->next) {
1553 if (element_iter->separate) {
1554 /* only separators go to selection */
1555 if (element_iter->flag & STITCH_SELECTED) {
1556 int i;
1557 if (always_select) {
1558 continue;
1559 }
1560
1561 element_iter->flag &= ~STITCH_SELECTED;
1562 for (i = 0; i < state->selection_size; i++) {
1563 if (selection_stack[i] == element_iter) {
1564 (state->selection_size)--;
1565 selection_stack[i] = selection_stack[state->selection_size];
1566 break;
1567 }
1568 }
1569 }
1570 else {
1571 element_iter->flag |= STITCH_SELECTED;
1572 selection_stack[state->selection_size++] = element_iter;
1573 }
1574 }
1575 }
1576}
1577
1578static void stitch_set_selection_mode(StitchState *state, const char from_stitch_mode)
1579{
1580 void **old_selection_stack = state->selection_stack;
1581 int old_selection_size = state->selection_size;
1582 state->selection_size = 0;
1583
1584 if (from_stitch_mode == STITCH_VERT) {
1585 int i;
1586 state->selection_stack = static_cast<void **>(
1587 MEM_mallocN(state->total_separate_edges * sizeof(*state->selection_stack),
1588 "stitch_new_edge_selection_stack"));
1589
1590 /* check if both elements of an edge are selected */
1591 for (i = 0; i < state->total_separate_edges; i++) {
1592 UvEdge *edge = state->edges + i;
1593 UvElement *element1 = state->uvs[edge->uv1];
1594 UvElement *element2 = state->uvs[edge->uv2];
1595
1596 if ((element1->flag & STITCH_SELECTED) && (element2->flag & STITCH_SELECTED)) {
1597 stitch_select_edge(edge, state, true);
1598 }
1599 }
1600
1601 /* unselect selected uvelements */
1602 for (i = 0; i < old_selection_size; i++) {
1603 UvElement *element = static_cast<UvElement *>(old_selection_stack[i]);
1604
1605 element->flag &= ~STITCH_SELECTED;
1606 }
1607 }
1608 else {
1609 int i;
1610 state->selection_stack = static_cast<void **>(
1611 MEM_mallocN(state->total_separate_uvs * sizeof(*state->selection_stack),
1612 "stitch_new_vert_selection_stack"));
1613
1614 for (i = 0; i < old_selection_size; i++) {
1615 UvEdge *edge = static_cast<UvEdge *>(old_selection_stack[i]);
1616 UvElement *element1 = state->uvs[edge->uv1];
1617 UvElement *element2 = state->uvs[edge->uv2];
1618
1619 stitch_select_uv(element1, state, true);
1620 stitch_select_uv(element2, state, true);
1621
1622 edge->flag &= ~STITCH_SELECTED;
1623 }
1624 }
1625 MEM_freeN(old_selection_stack);
1626}
1627
1628static void stitch_switch_selection_mode_all(StitchStateContainer *ssc)
1629{
1630 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
1631 stitch_set_selection_mode(ssc->states[ob_index], ssc->mode);
1632 }
1633
1634 if (ssc->mode == STITCH_VERT) {
1635 ssc->mode = STITCH_EDGE;
1636 }
1637 else {
1638 ssc->mode = STITCH_VERT;
1639 }
1640}
1641
1642static void stitch_calculate_edge_normal(const int cd_loop_uv_offset,
1643 UvEdge *edge,
1644 float *normal,
1645 float aspect)
1646{
1647 BMLoop *l1 = edge->element->l;
1648 float tangent[2];
1649
1650 float *luv1 = BM_ELEM_CD_GET_FLOAT_P(l1, cd_loop_uv_offset);
1651 float *luv2 = BM_ELEM_CD_GET_FLOAT_P(l1->next, cd_loop_uv_offset);
1652
1653 sub_v2_v2v2(tangent, luv2, luv1);
1654
1655 tangent[1] /= aspect;
1656
1657 normal[0] = tangent[1];
1658 normal[1] = -tangent[0];
1659
1660 normalize_v2(normal);
1661}
1662
1665static void stitch_draw_vbo(blender::gpu::VertBuf *vbo, GPUPrimType prim_type, const float col[4])
1666{
1667 blender::gpu::Batch *batch = GPU_batch_create_ex(prim_type, vbo, nullptr, GPU_BATCH_OWNS_VBO);
1669 GPU_batch_uniform_4fv(batch, "color", col);
1672}
1673
1674/* TODO: make things prettier : store batches inside StitchPreviewer instead of the bare verts pos
1675 */
1676static void stitch_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
1677{
1678
1679 StitchStateContainer *ssc = (StitchStateContainer *)arg;
1680
1681 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
1682 int j, index = 0;
1683 uint num_line = 0, num_tri, tri_idx = 0, line_idx = 0;
1684 StitchState *state = ssc->states[ob_index];
1685 StitchPreviewer *stitch_preview = state->stitch_preview;
1686 blender::gpu::VertBuf *vbo, *vbo_line;
1687 float col[4];
1688
1689 static GPUVertFormat format = {0};
1690 static uint pos_id;
1691 if (format.attr_len == 0) {
1692 pos_id = GPU_vertformat_attr_add(&format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1693 }
1694
1696
1697 /* Static Triangles. */
1698 if (stitch_preview->static_tris) {
1701 GPU_vertbuf_data_alloc(*vbo, stitch_preview->num_static_tris * 3);
1702 for (int i = 0; i < stitch_preview->num_static_tris * 3; i++) {
1703 GPU_vertbuf_attr_set(vbo, pos_id, i, &stitch_preview->static_tris[i * 2]);
1704 }
1706 }
1707
1708 /* Preview Polys */
1709 if (stitch_preview->preview_polys) {
1710 for (int i = 0; i < stitch_preview->num_polys; i++) {
1711 num_line += stitch_preview->uvs_per_polygon[i];
1712 }
1713
1714 num_tri = num_line - 2 * stitch_preview->num_polys;
1715
1716 /* we need to convert the polys into triangles / lines */
1719
1720 GPU_vertbuf_data_alloc(*vbo, num_tri * 3);
1721 GPU_vertbuf_data_alloc(*vbo_line, num_line * 2);
1722
1723 for (int i = 0; i < stitch_preview->num_polys; i++) {
1724 BLI_assert(stitch_preview->uvs_per_polygon[i] >= 3);
1725
1726 /* Start line */
1727 GPU_vertbuf_attr_set(vbo_line, pos_id, line_idx++, &stitch_preview->preview_polys[index]);
1729 vbo_line, pos_id, line_idx++, &stitch_preview->preview_polys[index + 2]);
1730
1731 for (j = 1; j < stitch_preview->uvs_per_polygon[i] - 1; j++) {
1732 GPU_vertbuf_attr_set(vbo, pos_id, tri_idx++, &stitch_preview->preview_polys[index]);
1734 vbo, pos_id, tri_idx++, &stitch_preview->preview_polys[index + (j + 0) * 2]);
1736 vbo, pos_id, tri_idx++, &stitch_preview->preview_polys[index + (j + 1) * 2]);
1737
1739 vbo_line, pos_id, line_idx++, &stitch_preview->preview_polys[index + (j + 0) * 2]);
1741 vbo_line, pos_id, line_idx++, &stitch_preview->preview_polys[index + (j + 1) * 2]);
1742 }
1743
1744 /* Closing line */
1745 GPU_vertbuf_attr_set(vbo_line, pos_id, line_idx++, &stitch_preview->preview_polys[index]);
1746 /* `j = uvs_per_polygon[i] - 1` */
1748 vbo_line, pos_id, line_idx++, &stitch_preview->preview_polys[index + j * 2]);
1749
1750 index += stitch_preview->uvs_per_polygon[i] * 2;
1751 }
1752
1757 }
1758
1760
1761 /* draw stitch vert/lines preview */
1762 if (ssc->mode == STITCH_VERT) {
1764
1767 GPU_vertbuf_data_alloc(*vbo, stitch_preview->num_stitchable);
1768 for (int i = 0; i < stitch_preview->num_stitchable; i++) {
1769 GPU_vertbuf_attr_set(vbo, pos_id, i, &stitch_preview->preview_stitchable[i * 2]);
1770 }
1772
1775 GPU_vertbuf_data_alloc(*vbo, stitch_preview->num_unstitchable);
1776 for (int i = 0; i < stitch_preview->num_unstitchable; i++) {
1777 GPU_vertbuf_attr_set(vbo, pos_id, i, &stitch_preview->preview_unstitchable[i * 2]);
1778 }
1780 }
1781 else {
1784 GPU_vertbuf_data_alloc(*vbo, stitch_preview->num_stitchable * 2);
1785 for (int i = 0; i < stitch_preview->num_stitchable * 2; i++) {
1786 GPU_vertbuf_attr_set(vbo, pos_id, i, &stitch_preview->preview_stitchable[i * 2]);
1787 }
1789
1792 GPU_vertbuf_data_alloc(*vbo, stitch_preview->num_unstitchable * 2);
1793 for (int i = 0; i < stitch_preview->num_unstitchable * 2; i++) {
1794 GPU_vertbuf_attr_set(vbo, pos_id, i, &stitch_preview->preview_unstitchable[i * 2]);
1795 }
1797 }
1798 }
1799}
1800
1801static UvEdge *uv_edge_get(BMLoop *l, StitchState *state)
1802{
1803 UvEdge tmp_edge;
1804
1805 UvElement *element1 = BM_uv_element_get(state->element_map, l);
1806 UvElement *element2 = BM_uv_element_get(state->element_map, l->next);
1807
1808 if (!element1 || !element2) {
1809 return nullptr;
1810 }
1811
1812 int uv1 = state->map[element1 - state->element_map->storage];
1813 int uv2 = state->map[element2 - state->element_map->storage];
1814
1815 if (uv1 < uv2) {
1816 tmp_edge.uv1 = uv1;
1817 tmp_edge.uv2 = uv2;
1818 }
1819 else {
1820 tmp_edge.uv1 = uv2;
1821 tmp_edge.uv2 = uv1;
1822 }
1823
1824 return static_cast<UvEdge *>(BLI_ghash_lookup(state->edge_hash, &tmp_edge));
1825}
1826
1827static StitchState *stitch_init(bContext *C,
1828 wmOperator *op,
1829 StitchStateContainer *ssc,
1830 Object *obedit,
1831 StitchStateInit *state_init)
1832{
1833 /* for fast edge lookup... */
1834 GHash *edge_hash;
1835 /* ...and actual edge storage */
1836 UvEdge *edges;
1837 int total_edges;
1838 /* maps uvelements to their first coincident uv */
1839 int *map;
1840 BMFace *efa;
1841 BMLoop *l;
1842 BMIter iter, liter;
1843 GHashIterator gh_iter;
1844 UvEdge *all_edges;
1845 StitchState *state;
1846 Scene *scene = CTX_data_scene(C);
1847 ToolSettings *ts = scene->toolsettings;
1848
1850 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1851
1852 state = MEM_callocN<StitchState>("stitch state obj");
1853
1854 /* initialize state */
1855 state->obedit = obedit;
1856 state->em = em;
1857
1858 /* Workaround for sync-select & face-select mode which implies all selected faces are detached,
1859 * for stitch this isn't useful behavior, see #86924. */
1860 const int selectmode_orig = scene->toolsettings->selectmode;
1862 state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true, true);
1863 scene->toolsettings->selectmode = selectmode_orig;
1864
1865 if (!state->element_map) {
1867 return nullptr;
1868 }
1869
1870 state->aspect = ED_uvedit_get_aspect_y(obedit);
1871
1872 int unique_uvs = state->element_map->total_unique_uvs;
1873 state->total_separate_uvs = unique_uvs;
1874
1875 /* Allocate the unique uv buffers */
1876 state->uvs = static_cast<UvElement **>(
1877 MEM_mallocN(sizeof(*state->uvs) * unique_uvs, "uv_stitch_unique_uvs"));
1878 /* internal uvs need no normals but it is hard and slow to keep a map of
1879 * normals only for boundary uvs, so allocating for all uvs.
1880 * Times 2 because each `float[2]` is stored as `{n[2 * i], n[2*i + 1]}`. */
1881 state->normals = MEM_calloc_arrayN<float>(2 * unique_uvs, "uv_stitch_normals");
1882 state->map = map = MEM_malloc_arrayN<int>(state->element_map->total_uvs, "uv_stitch_unique_map");
1883 /* Allocate the edge stack */
1884 edge_hash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash");
1885 all_edges = MEM_malloc_arrayN<UvEdge>(state->element_map->total_uvs, "ssc_edges");
1886
1887 BLI_assert(!state->stitch_preview); /* Paranoia. */
1888 if (!state->uvs || !map || !edge_hash || !all_edges) {
1890 return nullptr;
1891 }
1892
1893 /* Index for the UvElements. */
1894 int counter = -1;
1895 /* initialize the unique UVs and map */
1896 for (int i = 0; i < em->bm->totvert; i++) {
1897 UvElement *element = state->element_map->vertex[i];
1898 for (; element; element = element->next) {
1899 if (element->separate) {
1900 counter++;
1901 state->uvs[counter] = element;
1902 }
1903 /* Pointer arithmetic to the rescue, as always :). */
1904 map[element - state->element_map->storage] = counter;
1905 }
1906 }
1907
1908 counter = 0;
1909 /* Now, on to generate our uv connectivity data */
1910 const bool face_selected = !(ts->uv_flag & UV_FLAG_SELECT_SYNC);
1911 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1913 continue;
1914 }
1915 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1916 continue;
1917 }
1918
1919 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1920 UvElement *element = BM_uv_element_get(state->element_map, l);
1921 int itmp1 = element - state->element_map->storage;
1922 int itmp2 = BM_uv_element_get(state->element_map, l->next) - state->element_map->storage;
1923 UvEdge *edge;
1924
1925 int offset1 = map[itmp1];
1926 int offset2 = map[itmp2];
1927
1928 all_edges[counter].next = nullptr;
1929 all_edges[counter].first = nullptr;
1930 all_edges[counter].flag = 0;
1931 all_edges[counter].element = element;
1932 /* Using an order policy, sort UVs according to address space.
1933 * This avoids having two different UvEdges with the same UVs on different positions. */
1934 if (offset1 < offset2) {
1935 all_edges[counter].uv1 = offset1;
1936 all_edges[counter].uv2 = offset2;
1937 }
1938 else {
1939 all_edges[counter].uv1 = offset2;
1940 all_edges[counter].uv2 = offset1;
1941 }
1942
1943 edge = static_cast<UvEdge *>(BLI_ghash_lookup(edge_hash, &all_edges[counter]));
1944 if (edge) {
1945 edge->flag = 0;
1946 }
1947 else {
1948 BLI_ghash_insert(edge_hash, &all_edges[counter], &all_edges[counter]);
1949 all_edges[counter].flag = STITCH_BOUNDARY;
1950 }
1951 counter++;
1952 }
1953 }
1954
1955 total_edges = BLI_ghash_len(edge_hash);
1956 state->edges = edges = MEM_malloc_arrayN<UvEdge>(total_edges, "stitch_edges");
1957
1958 /* I assume any system will be able to at least allocate an iterator :p */
1959 if (!edges) {
1961 return nullptr;
1962 }
1963
1964 state->total_separate_edges = total_edges;
1965
1966 /* fill the edges with data */
1967 int i = 0;
1968 GHASH_ITER (gh_iter, edge_hash) {
1969 edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
1970 }
1971
1972 /* cleanup temporary stuff */
1973 MEM_freeN(all_edges);
1974
1975 BLI_ghash_free(edge_hash, nullptr, nullptr);
1976
1977 /* Refill an edge hash to create edge connectivity data. */
1978 state->edge_hash = edge_hash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash");
1979 for (i = 0; i < total_edges; i++) {
1980 BLI_ghash_insert(edge_hash, edges + i, edges + i);
1981 }
1983
1984 /***** calculate 2D normals for boundary uvs *****/
1985
1986 /* we use boundary edges to calculate 2D normals.
1987 * to disambiguate the direction of the normal, we also need
1988 * a point "inside" the island, that can be provided by
1989 * the winding of the face (assuming counter-clockwise flow). */
1990
1991 for (i = 0; i < total_edges; i++) {
1992 UvEdge *edge = edges + i;
1993 float normal[2];
1994 if (edge->flag & STITCH_BOUNDARY) {
1995 stitch_calculate_edge_normal(offsets.uv, edge, normal, state->aspect);
1996
1997 add_v2_v2(state->normals + edge->uv1 * 2, normal);
1998 add_v2_v2(state->normals + edge->uv2 * 2, normal);
1999
2000 normalize_v2(state->normals + edge->uv1 * 2);
2001 normalize_v2(state->normals + edge->uv2 * 2);
2002 }
2003 }
2004
2005 /***** fill selection stack *******/
2006
2007 state->selection_size = 0;
2008
2009 /* Load old selection if redoing operator with different settings */
2010 if (state_init != nullptr) {
2011 int faceIndex, elementIndex;
2013 enum StitchModes stored_mode = StitchModes(RNA_enum_get(op->ptr, "stored_mode"));
2014
2016
2017 int selected_count = state_init->uv_selected_count;
2018
2019 if (stored_mode == STITCH_VERT) {
2020 state->selection_stack = static_cast<void **>(
2021 MEM_mallocN(sizeof(*state->selection_stack) * state->total_separate_uvs,
2022 "uv_stitch_selection_stack"));
2023
2024 while (selected_count--) {
2025 faceIndex = state_init->to_select[selected_count].faceIndex;
2026 elementIndex = state_init->to_select[selected_count].elementIndex;
2027 efa = BM_face_at_index(em->bm, faceIndex);
2029 state->element_map,
2030 static_cast<BMLoop *>(BM_iter_at_index(nullptr, BM_LOOPS_OF_FACE, efa, elementIndex)));
2032 }
2033 }
2034 else {
2035 state->selection_stack = static_cast<void **>(
2036 MEM_mallocN(sizeof(*state->selection_stack) * state->total_separate_edges,
2037 "uv_stitch_selection_stack"));
2038
2039 while (selected_count--) {
2040 UvEdge tmp_edge, *edge;
2041 int uv1, uv2;
2042 faceIndex = state_init->to_select[selected_count].faceIndex;
2043 elementIndex = state_init->to_select[selected_count].elementIndex;
2044 efa = BM_face_at_index(em->bm, faceIndex);
2046 state->element_map,
2047 static_cast<BMLoop *>(BM_iter_at_index(nullptr, BM_LOOPS_OF_FACE, efa, elementIndex)));
2048 uv1 = map[element - state->element_map->storage];
2049
2051 state->element_map,
2052 static_cast<BMLoop *>(
2053 BM_iter_at_index(nullptr, BM_LOOPS_OF_FACE, efa, (elementIndex + 1) % efa->len)));
2054 uv2 = map[element - state->element_map->storage];
2055
2056 if (uv1 < uv2) {
2057 tmp_edge.uv1 = uv1;
2058 tmp_edge.uv2 = uv2;
2059 }
2060 else {
2061 tmp_edge.uv1 = uv2;
2062 tmp_edge.uv2 = uv1;
2063 }
2064
2065 edge = static_cast<UvEdge *>(BLI_ghash_lookup(edge_hash, &tmp_edge));
2066
2067 stitch_select_edge(edge, state, true);
2068 }
2069 }
2070 /* if user has switched the operator mode after operation, we need to convert
2071 * the stored format */
2072 if (ssc->mode != stored_mode) {
2073 stitch_set_selection_mode(state, stored_mode);
2074 }
2075 }
2076 else {
2077 if (ssc->mode == STITCH_VERT) {
2078 state->selection_stack = static_cast<void **>(
2079 MEM_mallocN(sizeof(*state->selection_stack) * state->total_separate_uvs,
2080 "uv_stitch_selection_stack"));
2081
2082 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2083 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
2084 if (uvedit_uv_select_test(scene, em->bm, l, offsets)) {
2085 UvElement *element = BM_uv_element_get(state->element_map, l);
2086 if (element) {
2088 }
2089 }
2090 }
2091 }
2092 }
2093 else {
2094 state->selection_stack = static_cast<void **>(
2095 MEM_mallocN(sizeof(*state->selection_stack) * state->total_separate_edges,
2096 "uv_stitch_selection_stack"));
2097
2098 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2099 if (!(ts->uv_flag & UV_FLAG_SELECT_SYNC) &&
2101 {
2102 continue;
2103 }
2104
2105 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2106 if (uvedit_edge_select_test(scene, em->bm, l, offsets)) {
2107 UvEdge *edge = uv_edge_get(l, state);
2108 if (edge) {
2109 stitch_select_edge(edge, state, true);
2110 }
2111 }
2112 }
2113 }
2114 }
2115 }
2116
2117 /***** initialize static island preview data *****/
2118
2119 state->tris_per_island = MEM_malloc_arrayN<uint>(state->element_map->total_islands,
2120 "stitch island tris");
2121 for (i = 0; i < state->element_map->total_islands; i++) {
2122 state->tris_per_island[i] = 0;
2123 }
2124
2125 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2127
2128 if (element) {
2129 state->tris_per_island[element->island] += (efa->len > 2) ? efa->len - 2 : 0;
2130 }
2131 }
2132
2133 state->island_is_stitchable = MEM_calloc_arrayN<bool>(state->element_map->total_islands,
2134 "stitch I stops");
2135 if (!state->island_is_stitchable) {
2137 return nullptr;
2138 }
2139
2140 if (!stitch_process_data(ssc, state, scene, false)) {
2142 return nullptr;
2143 }
2144
2145 return state;
2146}
2147
2148static bool goto_next_island(StitchStateContainer *ssc)
2149{
2150 StitchState *active_state = ssc->states[ssc->active_object_index];
2151 StitchState *original_active_state = active_state;
2152
2153 int original_island = ssc->static_island;
2154
2155 do {
2156 ssc->static_island++;
2157 if (ssc->static_island >= active_state->element_map->total_islands) {
2158 /* go to next object */
2159 ssc->active_object_index++;
2160 ssc->active_object_index %= ssc->objects_len;
2161
2162 active_state = ssc->states[ssc->active_object_index];
2163 ssc->static_island = 0;
2164 }
2165
2166 if (active_state->island_is_stitchable[ssc->static_island]) {
2167 /* We're at an island to make active */
2168 return true;
2169 }
2170 } while (!(active_state == original_active_state && ssc->static_island == original_island));
2171
2172 return false;
2173}
2174
2176{
2177 ARegion *region = CTX_wm_region(C);
2178 if (!region) {
2179 return 0;
2180 }
2181
2182 Scene *scene = CTX_data_scene(C);
2183 ToolSettings *ts = scene->toolsettings;
2184
2185 ViewLayer *view_layer = CTX_data_view_layer(C);
2186 View3D *v3d = CTX_wm_view3d(C);
2188 scene, view_layer, v3d);
2189
2190 if (objects.is_empty()) {
2191 BKE_report(op->reports, RPT_ERROR, "No objects selected");
2192 return 0;
2193 }
2194
2195 if (objects.size() > RNA_MAX_ARRAY_LENGTH) {
2196 BKE_reportf(op->reports,
2197 RPT_ERROR,
2198 "Stitching only works with less than %i objects selected (%i selected)",
2200 int(objects.size()));
2201 return 0;
2202 }
2203
2204 StitchStateContainer *ssc = MEM_callocN<StitchStateContainer>("stitch collection");
2205
2206 op->customdata = ssc;
2207
2208 ssc->use_limit = RNA_boolean_get(op->ptr, "use_limit");
2209 ssc->limit_dist = RNA_float_get(op->ptr, "limit");
2210 ssc->snap_islands = RNA_boolean_get(op->ptr, "snap_islands");
2211 ssc->midpoints = RNA_boolean_get(op->ptr, "midpoint_snap");
2212 ssc->clear_seams = RNA_boolean_get(op->ptr, "clear_seams");
2213 ssc->active_object_index = RNA_int_get(op->ptr, "active_object_index");
2214 ssc->static_island = 0;
2215
2216 if (RNA_struct_property_is_set(op->ptr, "mode")) {
2217 ssc->mode = RNA_enum_get(op->ptr, "mode");
2218 }
2219 else {
2220 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2221 if (ts->selectmode & SCE_SELECT_VERTEX) {
2222 ssc->mode = STITCH_VERT;
2223 }
2224 else {
2225 ssc->mode = STITCH_EDGE;
2226 }
2227 }
2228 else {
2229 if (ts->uv_selectmode & UV_SELECT_VERT) {
2230 ssc->mode = STITCH_VERT;
2231 }
2232 else {
2233 ssc->mode = STITCH_EDGE;
2234 }
2235 }
2236 }
2237
2238 ssc->objects = MEM_calloc_arrayN<Object *>(objects.size(), "Object *ssc->objects");
2239 ssc->states = MEM_calloc_arrayN<StitchState *>(objects.size(), "StitchState");
2240 ssc->objects_len = 0;
2241
2242 int *objs_selection_count = nullptr;
2243 UvElementID *selected_uvs_arr = nullptr;
2244 StitchStateInit *state_init = nullptr;
2245
2246 if (RNA_struct_property_is_set(op->ptr, "selection") &&
2247 RNA_struct_property_is_set(op->ptr, "objects_selection_count"))
2248 {
2249 /* Retrieve list of selected UVs, one list contains all selected UVs
2250 * for all objects. */
2251
2252 objs_selection_count = static_cast<int *>(
2253 MEM_mallocN(sizeof(int *) * objects.size(), "objects_selection_count"));
2254 RNA_int_get_array(op->ptr, "objects_selection_count", objs_selection_count);
2255
2256 int total_selected = 0;
2257 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
2258 total_selected += objs_selection_count[ob_index];
2259 }
2260
2261 selected_uvs_arr = MEM_calloc_arrayN<UvElementID>(total_selected, "selected_uvs_arr");
2262 int sel_idx = 0;
2263 RNA_BEGIN (op->ptr, itemptr, "selection") {
2264 BLI_assert(sel_idx < total_selected);
2265 selected_uvs_arr[sel_idx].faceIndex = RNA_int_get(&itemptr, "face_index");
2266 selected_uvs_arr[sel_idx].elementIndex = RNA_int_get(&itemptr, "element_index");
2267 sel_idx++;
2268 }
2269 RNA_END;
2270
2271 RNA_collection_clear(op->ptr, "selection");
2272
2273 state_init = MEM_callocN<StitchStateInit>("UV_init_selected");
2274 state_init->to_select = selected_uvs_arr;
2275 }
2276
2277 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
2278 Object *obedit = objects[ob_index];
2279
2280 if (state_init != nullptr) {
2281 state_init->uv_selected_count = objs_selection_count[ob_index];
2282 }
2283
2284 StitchState *stitch_state_ob = stitch_init(C, op, ssc, obedit, state_init);
2285
2286 if (state_init != nullptr) {
2287 /* Move pointer to beginning of next object's data. */
2288 state_init->to_select += state_init->uv_selected_count;
2289 }
2290
2291 if (stitch_state_ob) {
2292 ssc->objects[ssc->objects_len] = obedit;
2293 ssc->states[ssc->objects_len] = stitch_state_ob;
2294 ssc->objects_len++;
2295 }
2296 }
2297
2298 MEM_SAFE_FREE(selected_uvs_arr);
2299 MEM_SAFE_FREE(objs_selection_count);
2300 MEM_SAFE_FREE(state_init);
2301
2302 if (ssc->objects_len == 0) {
2303 state_delete_all(ssc);
2304 BKE_report(op->reports, RPT_ERROR, "Could not initialize stitching on any selected object");
2305 return 0;
2306 }
2307
2308 ssc->active_object_index %= ssc->objects_len;
2309
2310 ssc->static_island = RNA_int_get(op->ptr, "static_island");
2311
2312 StitchState *state = ssc->states[ssc->active_object_index];
2313 ssc->static_island %= state->element_map->total_islands;
2314
2315 /* If the initial active object doesn't have any stitchable islands
2316 * then no active island will be seen in the UI.
2317 * Make sure we're on a stitchable object and island. */
2318 if (!state->island_is_stitchable[ssc->static_island]) {
2319 goto_next_island(ssc);
2320 state = ssc->states[ssc->active_object_index];
2321 }
2322
2323 /* process active stitchobj again now that it can detect it's the active stitchobj */
2324 stitch_process_data(ssc, state, scene, false);
2325
2326 stitch_update_header(ssc, C);
2327
2328 ssc->draw_handle = ED_region_draw_cb_activate(
2329 region->runtime->type, stitch_draw, ssc, REGION_DRAW_POST_VIEW);
2330
2331 return 1;
2332}
2333
2335{
2336 if (!stitch_init_all(C, op)) {
2337 return OPERATOR_CANCELLED;
2338 }
2339
2341
2342 Scene *scene = CTX_data_scene(C);
2343 ToolSettings *ts = scene->toolsettings;
2344 const bool synced_selection = (ts->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
2345
2346 StitchStateContainer *ssc = (StitchStateContainer *)op->customdata;
2347
2348 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
2349 StitchState *state = ssc->states[ob_index];
2350 Object *obedit = state->obedit;
2352
2353 if (synced_selection && (em->bm->totvertsel == 0)) {
2354 continue;
2355 }
2356
2358 }
2359
2361}
2362
2363static void stitch_exit(bContext *C, wmOperator *op, int finished)
2364{
2365 Scene *scene = CTX_data_scene(C);
2367 ScrArea *area = CTX_wm_area(C);
2368
2369 StitchStateContainer *ssc = (StitchStateContainer *)op->customdata;
2370
2371 if (finished) {
2372 RNA_float_set(op->ptr, "limit", ssc->limit_dist);
2373 RNA_boolean_set(op->ptr, "use_limit", ssc->use_limit);
2374 RNA_boolean_set(op->ptr, "snap_islands", ssc->snap_islands);
2375 RNA_boolean_set(op->ptr, "midpoint_snap", ssc->midpoints);
2376 RNA_boolean_set(op->ptr, "clear_seams", ssc->clear_seams);
2377 RNA_enum_set(op->ptr, "mode", ssc->mode);
2378 RNA_enum_set(op->ptr, "stored_mode", ssc->mode);
2379 RNA_int_set(op->ptr, "active_object_index", ssc->active_object_index);
2380
2381 RNA_int_set(op->ptr, "static_island", ssc->static_island);
2382
2383 int *objs_selection_count = nullptr;
2384 objs_selection_count = static_cast<int *>(
2385 MEM_mallocN(sizeof(int *) * ssc->objects_len, "objects_selection_count"));
2386
2387 /* Store selection for re-execution of stitch
2388 * - Store all selected UVs in "selection"
2389 * - Store how many each object has in "objects_selection_count". */
2390 RNA_collection_clear(op->ptr, "selection");
2391 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
2392 StitchState *state = ssc->states[ob_index];
2393 Object *obedit = state->obedit;
2394
2395 PointerRNA itemptr;
2396 for (int i = 0; i < state->selection_size; i++) {
2398
2399 if (ssc->mode == STITCH_VERT) {
2400 element = static_cast<UvElement *>(state->selection_stack[i]);
2401 }
2402 else {
2403 element = ((UvEdge *)state->selection_stack[i])->element;
2404 }
2405 RNA_collection_add(op->ptr, "selection", &itemptr);
2406
2407 RNA_int_set(&itemptr, "face_index", BM_elem_index_get(element->l->f));
2408 RNA_int_set(&itemptr, "element_index", element->loop_of_face_index);
2409 }
2410 uvedit_live_unwrap_update(sima, scene, obedit);
2411
2412 objs_selection_count[ob_index] = state->selection_size;
2413 }
2414
2415 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "objects_selection_count");
2416 RNA_def_property_array(prop, ssc->objects_len);
2417 RNA_int_set_array(op->ptr, "objects_selection_count", objs_selection_count);
2418 MEM_freeN(objs_selection_count);
2419 }
2420
2421 if (area) {
2422 ED_workspace_status_text(C, nullptr);
2423 }
2424
2425 ED_region_draw_cb_exit(CTX_wm_region(C)->runtime->type, ssc->draw_handle);
2426
2427 ToolSettings *ts = scene->toolsettings;
2428 const bool synced_selection = (ts->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
2429
2430 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
2431 StitchState *state = ssc->states[ob_index];
2432 Object *obedit = state->obedit;
2434
2435 if (synced_selection && (em->bm->totvertsel == 0)) {
2436 continue;
2437 }
2438
2439 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
2441 }
2442
2443 state_delete_all(ssc);
2444
2445 op->customdata = nullptr;
2446}
2447
2449{
2450 stitch_exit(C, op, 0);
2451}
2452
2454{
2455 Scene *scene = CTX_data_scene(C);
2456
2457 if (!stitch_init_all(C, op)) {
2458 return OPERATOR_CANCELLED;
2459 }
2460 if (stitch_process_data_all((StitchStateContainer *)op->customdata, scene, 1)) {
2461 stitch_exit(C, op, 1);
2462 return OPERATOR_FINISHED;
2463 }
2464 stitch_cancel(C, op);
2465 return OPERATOR_CANCELLED;
2466}
2467
2468static StitchState *stitch_select(bContext *C,
2469 Scene *scene,
2470 const wmEvent *event,
2471 StitchStateContainer *ssc)
2472{
2473 /* add uv under mouse to processed uv's */
2474 float co[2];
2475 ARegion *region = CTX_wm_region(C);
2477
2478 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2479
2480 if (ssc->mode == STITCH_VERT) {
2481 if (uv_find_nearest_vert_multi(scene, {ssc->objects, ssc->objects_len}, co, 0.0f, &hit)) {
2482 /* Add vertex to selection, deselect all common uv's of vert other than selected and
2483 * update the preview. This behavior was decided so that you can do stuff like deselect
2484 * the opposite stitchable vertex and the initial still gets deselected */
2485
2486 /* find StitchState from hit->ob */
2487 StitchState *state = nullptr;
2488 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
2489 if (hit.ob == ssc->objects[ob_index]) {
2490 state = ssc->states[ob_index];
2491 break;
2492 }
2493 }
2494
2495 /* This works due to setting of tmp in find nearest uv vert */
2496 UvElement *element = BM_uv_element_get(state->element_map, hit.l);
2497 if (element) {
2499 }
2500
2501 return state;
2502 }
2503 }
2504 else if (uv_find_nearest_edge_multi(scene, {ssc->objects, ssc->objects_len}, co, 0.0f, &hit)) {
2505 /* find StitchState from hit->ob */
2506 StitchState *state = nullptr;
2507 for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
2508 if (hit.ob == ssc->objects[ob_index]) {
2509 state = ssc->states[ob_index];
2510 break;
2511 }
2512 }
2513
2514 UvEdge *edge = uv_edge_get(hit.l, state);
2515 stitch_select_edge(edge, state, false);
2516
2517 return state;
2518 }
2519
2520 return nullptr;
2521}
2522
2524{
2525 StitchStateContainer *ssc;
2526 Scene *scene = CTX_data_scene(C);
2527
2528 ssc = static_cast<StitchStateContainer *>(op->customdata);
2529 StitchState *active_state = ssc->states[ssc->active_object_index];
2530
2531 switch (event->type) {
2532 case MIDDLEMOUSE:
2533 return OPERATOR_PASS_THROUGH;
2534
2535 /* Cancel */
2536 case EVT_ESCKEY:
2537 stitch_cancel(C, op);
2538 return OPERATOR_CANCELLED;
2539
2540 case LEFTMOUSE:
2541 case EVT_PADENTER:
2542 case EVT_RETKEY:
2543 if (event->val == KM_PRESS) {
2544 if (stitch_process_data(ssc, active_state, scene, true)) {
2545 stitch_exit(C, op, 1);
2546 return OPERATOR_FINISHED;
2547 }
2548
2549 stitch_cancel(C, op);
2550 return OPERATOR_CANCELLED;
2551 }
2552 return OPERATOR_PASS_THROUGH;
2553
2554 /* Increase limit */
2555 case EVT_PADPLUSKEY:
2556 case WHEELUPMOUSE:
2557 if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) {
2558 ssc->limit_dist += 0.01f;
2559 if (!stitch_process_data(ssc, active_state, scene, false)) {
2560 stitch_cancel(C, op);
2561 return OPERATOR_CANCELLED;
2562 }
2563 break;
2564 }
2565 else {
2566 return OPERATOR_PASS_THROUGH;
2567 }
2568 /* Decrease limit */
2569 case EVT_PADMINUS:
2570 case WHEELDOWNMOUSE:
2571 if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) {
2572 ssc->limit_dist -= 0.01f;
2573 ssc->limit_dist = std::max(0.01f, ssc->limit_dist);
2574 if (!stitch_process_data(ssc, active_state, scene, false)) {
2575 stitch_cancel(C, op);
2576 return OPERATOR_CANCELLED;
2577 }
2578 break;
2579 }
2580 else {
2581 return OPERATOR_PASS_THROUGH;
2582 }
2583
2584 /* Use Limit (Default off) */
2585 case EVT_LKEY:
2586 if (event->val == KM_PRESS) {
2587 ssc->use_limit = !ssc->use_limit;
2588 if (!stitch_process_data(ssc, active_state, scene, false)) {
2589 stitch_cancel(C, op);
2590 return OPERATOR_CANCELLED;
2591 }
2592 break;
2593 }
2595
2596 case EVT_IKEY:
2597 if (event->val == KM_PRESS) {
2598 /* Move to next island and maybe next object */
2599
2600 if (goto_next_island(ssc)) {
2601 StitchState *new_active_state = ssc->states[ssc->active_object_index];
2602
2603 /* active_state is the original active state */
2604 if (active_state != new_active_state) {
2605 if (!stitch_process_data(ssc, active_state, scene, false)) {
2606 stitch_cancel(C, op);
2607 return OPERATOR_CANCELLED;
2608 }
2609 }
2610
2611 if (!stitch_process_data(ssc, new_active_state, scene, false)) {
2612 stitch_cancel(C, op);
2613 return OPERATOR_CANCELLED;
2614 }
2615 }
2616 break;
2617 }
2619
2620 case EVT_MKEY:
2621 if (event->val == KM_PRESS) {
2622 ssc->midpoints = !ssc->midpoints;
2623 if (!stitch_process_data(ssc, active_state, scene, false)) {
2624 stitch_cancel(C, op);
2625 return OPERATOR_CANCELLED;
2626 }
2627 }
2628 break;
2629
2630 /* Select geometry */
2631 case RIGHTMOUSE:
2632 if ((event->modifier & KM_SHIFT) == 0) {
2633 stitch_cancel(C, op);
2634 return OPERATOR_CANCELLED;
2635 }
2636 if (event->val == KM_PRESS) {
2637 StitchState *selected_state = stitch_select(C, scene, event, ssc);
2638
2639 if (selected_state && !stitch_process_data(ssc, selected_state, scene, false)) {
2640 stitch_cancel(C, op);
2641 return OPERATOR_CANCELLED;
2642 }
2643 break;
2644 }
2646
2647 /* snap islands on/off */
2648 case EVT_SKEY:
2649 if (event->val == KM_PRESS) {
2650 ssc->snap_islands = !ssc->snap_islands;
2651 if (!stitch_process_data(ssc, active_state, scene, false)) {
2652 stitch_cancel(C, op);
2653 return OPERATOR_CANCELLED;
2654 }
2655 break;
2656 }
2657 else {
2659 }
2660
2661 /* switch between edge/vertex mode */
2662 case EVT_TABKEY:
2663 if (event->val == KM_PRESS) {
2665
2666 if (!stitch_process_data_all(ssc, scene, false)) {
2667 stitch_cancel(C, op);
2668 return OPERATOR_CANCELLED;
2669 }
2670 }
2671 break;
2672
2673 default:
2675 }
2676
2677 /* if updated settings, renew feedback message */
2678 stitch_update_header(ssc, C);
2680
2682}
2683
2685{
2686 PropertyRNA *prop;
2687
2688 static const EnumPropertyItem stitch_modes[] = {
2689 {STITCH_VERT, "VERTEX", 0, "Vertex", ""},
2690 {STITCH_EDGE, "EDGE", 0, "Edge", ""},
2691 {0, nullptr, 0, nullptr, nullptr},
2692 };
2693
2694 /* identifiers */
2695 ot->name = "Stitch";
2696 ot->description = "Stitch selected UV vertices by proximity";
2697 ot->idname = "UV_OT_stitch";
2698 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2699
2700 /* API callbacks. */
2701 ot->invoke = stitch_invoke;
2702 ot->modal = stitch_modal;
2703 ot->exec = stitch_exec;
2704 ot->cancel = stitch_cancel;
2705 ot->poll = ED_operator_uvedit;
2706
2707 /* properties */
2709 ot->srna, "use_limit", false, "Use Limit", "Stitch UVs within a specified limit distance");
2710 RNA_def_boolean(ot->srna,
2711 "snap_islands",
2712 true,
2713 "Snap Islands",
2714 "Snap islands together (on edge stitch mode, rotates the islands too)");
2715
2716 RNA_def_float(ot->srna,
2717 "limit",
2718 0.01f,
2719 0.0f,
2720 FLT_MAX,
2721 "Limit",
2722 "Limit distance in normalized coordinates",
2723 0.0,
2724 FLT_MAX);
2725 RNA_def_int(ot->srna,
2726 "static_island",
2727 0,
2728 0,
2729 INT_MAX,
2730 "Static Island",
2731 "Island that stays in place when stitching islands",
2732 0,
2733 INT_MAX);
2734 RNA_def_int(ot->srna,
2735 "active_object_index",
2736 0,
2737 0,
2738 INT_MAX,
2739 "Active Object",
2740 "Index of the active object",
2741 0,
2742 INT_MAX);
2743 RNA_def_boolean(ot->srna,
2744 "midpoint_snap",
2745 false,
2746 "Snap at Midpoint",
2747 "UVs are stitched at midpoint instead of at static island");
2748 RNA_def_boolean(ot->srna, "clear_seams", true, "Clear Seams", "Clear seams of stitched edges");
2749 RNA_def_enum(ot->srna,
2750 "mode",
2751 stitch_modes,
2752 STITCH_VERT,
2753 "Operation Mode",
2754 "Use vertex or edge stitching");
2755 prop = RNA_def_enum(ot->srna,
2756 "stored_mode",
2757 stitch_modes,
2758 STITCH_VERT,
2759 "Stored Operation Mode",
2760 "Use vertex or edge stitching");
2763 ot->srna, "selection", &RNA_SelectedUvElement, "Selection", "");
2764 /* Selection should not be editable or viewed in toolbar */
2766
2767 /* test should not be editable or viewed in toolbar */
2768 prop = RNA_def_int_array(ot->srna,
2769 "objects_selection_count",
2770 1,
2771 nullptr,
2772 0,
2773 INT_MAX,
2774 "Objects Selection Count",
2775 "",
2776 0,
2777 INT_MAX);
2778 RNA_def_property_array(prop, 6);
2780}
SpaceImage * CTX_wm_space_image(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GHash GHash
Definition BLI_ghash.h:39
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:295
unsigned int BLI_ghashutil_uinthash(unsigned int key)
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:318
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:686
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:702
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define M_PI_2
#define M_PI
void mul_v2_m2v2(float r[2], const float mat[2][2], const float vec[2])
void angle_to_mat2(float R[2][2], float angle)
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void negate_v2_v2(float r[2], const float a[2])
MINLINE float cross_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
unsigned char uchar
unsigned int uint
unsigned short ushort
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
struct Object Object
@ UV_SELECT_VERT
@ UV_FLAG_SELECT_SYNC
@ SCE_SELECT_VERTEX
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
void BM_uv_element_map_free(UvElementMap *element_map)
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, bool uv_selected, bool use_winding, bool use_seams, bool do_islands)
UvElement * BM_uv_element_get_head(UvElementMap *element_map, UvElement *child)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
bool ED_operator_uvedit(bContext *C)
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
bool uvedit_edge_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
float ED_uvedit_get_aspect_y(Object *obedit)
bool uvedit_uv_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void uvedit_uv_select_enable(const Scene *scene, BMesh *bm, BMLoop *l)
void GPU_batch_discard(blender::gpu::Batch *batch)
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, GPUBuiltinShader shader_id)
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, GPUBatchFlag owns_flag)
Definition gpu_batch.cc:51
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:42
void GPU_batch_draw(blender::gpu::Batch *batch)
#define GPU_batch_uniform_4fv(batch, name, val)
Definition GPU_batch.hh:279
GPUPrimType
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_TRIS
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_point_size(float size)
Definition gpu_state.cc:172
static blender::gpu::VertBuf * GPU_vertbuf_create_with_format(const GPUVertFormat &format)
void GPU_vertbuf_attr_set(blender::gpu::VertBuf *, uint a_idx, uint v_idx, const void *data)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
#define RNA_MAX_ARRAY_LENGTH
Definition RNA_define.hh:24
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
@ TH_STITCH_PREVIEW_UNSTITCHABLE
@ TH_VERTEX_SIZE
@ TH_STITCH_PREVIEW_EDGE
@ TH_STITCH_PREVIEW_ACTIVE
@ TH_STITCH_PREVIEW_STITCHABLE
@ TH_STITCH_PREVIEW_FACE
void UI_GetThemeColor4fv(int colorid, float col[4])
float UI_GetThemeValuef(int colorid)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
#define NC_GEOM
Definition WM_types.hh:393
@ KM_ALT
Definition WM_types.hh:280
@ KM_SHIFT
Definition WM_types.hh:278
#define ND_DATA
Definition WM_types.hh:509
@ KM_PRESS
Definition WM_types.hh:311
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
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)
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
BMesh * bm
return true
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const void * element
ATTR_WARN_UNUSED_RESULT const BMLoop * l
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm)
int64_t size() const
bool is_empty() const
nullptr float
#define acosf(x)
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 batch
uint col
format
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 ulong state[N]
const int status
#define fabsf
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
void RNA_collection_clear(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_int_array(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_array(PropertyRNA *prop, int length)
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
static bool uv_edge_compare(const void *a, const void *b)
Definition sculpt_uv.cc:631
static uint uv_edge_hash(const void *key)
Definition sculpt_uv.cc:625
#define FLT_MAX
Definition stdcycles.h:14
ARegionRuntimeHandle * runtime
struct BMVert * v
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
int totvert
int totvertsel
Definition DNA_ID.h:414
struct ToolSettings * toolsettings
UvElement * storage
unsigned char flag
unsigned int island
unsigned short loop_of_face_index
UvElement * next
wmEventModifierFlag modifier
Definition WM_types.hh:774
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
bool uv_find_nearest_vert_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], float penalty_dist, UvNearestHit *hit)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
bool uv_find_nearest_edge_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], float penalty, UvNearestHit *hit)
static int stitch_init_all(bContext *C, wmOperator *op)
static StitchState * stitch_init(bContext *C, wmOperator *op, StitchStateContainer *ssc, Object *obedit, StitchStateInit *state_init)
static void stitch_propagate_uv_final_position(Scene *scene, UvElement *element, int index, PreviewPosition *preview_position, UVVertAverage *final_position, StitchStateContainer *ssc, StitchState *state, const bool final)
static void stitch_switch_selection_mode_all(StitchStateContainer *ssc)
static void stitch_select_edge(UvEdge *edge, StitchState *state, int always_select)
static bool stitch_check_uvs_state_stitchable(const int cd_loop_uv_offset, UvElement *element, UvElement *element_iter, StitchStateContainer *ssc)
static wmOperatorStatus stitch_exec(bContext *C, wmOperator *op)
static void stitch_exit(bContext *C, wmOperator *op, int finished)
static void stitch_draw_vbo(blender::gpu::VertBuf *vbo, GPUPrimType prim_type, const float col[4])
void UV_OT_stitch(wmOperatorType *ot)
static void stitch_island_calculate_vert_rotation(const int cd_loop_uv_offset, UvElement *element, StitchStateContainer *ssc, StitchState *state, IslandStitchData *island_stitch_data)
static void determine_uv_stitchability(const int cd_loop_uv_offset, UvElement *element, StitchStateContainer *ssc, StitchState *state, IslandStitchData *island_stitch_data)
static void stitch_draw(const bContext *, ARegion *, void *arg)
static void stitch_update_header(StitchStateContainer *ssc, bContext *C)
static int stitch_process_data_all(StitchStateContainer *ssc, Scene *scene, int final)
static void stitch_validate_edge_stitchability(const int cd_loop_uv_offset, UvEdge *edge, StitchStateContainer *ssc, StitchState *state, IslandStitchData *island_stitch_data, PreviewPosition *preview_position)
static bool stitch_check_edges_state_stitchable(const int cd_loop_uv_offset, UvEdge *edge, UvEdge *edge_iter, StitchStateContainer *ssc, StitchState *state)
static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState *state)
static bool uv_edge_compare(const void *a, const void *b)
static void state_delete_all(StitchStateContainer *ssc)
static void stitch_island_calculate_edge_rotation(const int cd_loop_uv_offset, UvEdge *edge, StitchStateContainer *ssc, StitchState *state, UVVertAverage *uv_average, const uint *uvfinal_map, IslandStitchData *island_stitch_data)
static void stitch_validate_uv_stitchability(const int cd_loop_uv_offset, UvElement *element, StitchStateContainer *ssc, StitchState *state, IslandStitchData *island_stitch_data, PreviewPosition *preview_position)
static void stitch_set_face_preview_buffer_position(BMFace *efa, StitchPreviewer *preview, PreviewPosition *preview_position)
static wmOperatorStatus stitch_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void stitch_uv_rotate(const float mat[2][2], const float medianPoint[2], float uv[2], float aspect)
static void stitch_calculate_edge_normal(const int cd_loop_uv_offset, UvEdge *edge, float *normal, float aspect)
static StitchState * stitch_select(bContext *C, Scene *scene, const wmEvent *event, StitchStateContainer *ssc)
static void stitch_cancel(bContext *C, wmOperator *op)
static wmOperatorStatus stitch_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bool goto_next_island(StitchStateContainer *ssc)
static UvEdge * uv_edge_get(BMLoop *l, StitchState *state)
static bool stitch_check_uvs_stitchable(const int cd_loop_uv_offset, UvElement *element, UvElement *element_iter, StitchStateContainer *ssc)
static uint uv_edge_hash(const void *key)
#define STITCH_NO_PREVIEW
static void stitch_preview_delete(StitchPreviewer *stitch_preview)
static bool stitch_check_edges_stitchable(const int cd_loop_uv_offset, UvEdge *edge, UvEdge *edge_iter, StitchStateContainer *ssc, StitchState *state)
static void stitch_setup_face_preview_for_uv_group(UvElement *element, StitchStateContainer *ssc, StitchState *state, IslandStitchData *island_stitch_data, PreviewPosition *preview_position)
static void determine_uv_edge_stitchability(const int cd_loop_uv_offset, UvEdge *edge, StitchStateContainer *ssc, StitchState *state, IslandStitchData *island_stitch_data)
static void stitch_set_selection_mode(StitchState *state, const char from_stitch_mode)
static void state_delete(StitchState *state)
static void stitch_select_uv(UvElement *element, StitchState *state, int always_select)
static int stitch_process_data(StitchStateContainer *ssc, StitchState *state, Scene *scene, int final)
static void stitch_calculate_island_snapping(const int cd_loop_uv_offset, StitchState *state, PreviewPosition *preview_position, StitchPreviewer *preview, IslandStitchData *island_stitch_data, int final)
static StitchPreviewer * stitch_preview_init()
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ EVT_SKEY
@ EVT_IKEY
@ EVT_TABKEY
@ WHEELUPMOUSE
@ EVT_PADENTER
@ WHEELDOWNMOUSE
@ EVT_MKEY
@ EVT_PADMINUS
@ LEFTMOUSE
@ MIDDLEMOUSE
@ EVT_ESCKEY
@ EVT_LKEY
@ EVT_PADPLUSKEY
@ EVT_RETKEY
wmOperatorType * ot
Definition wm_files.cc:4237
uint8_t flag
Definition wm_window.cc:145