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