1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup eduv
22 */
23
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "DNA_image_types.h"
31 #include "DNA_material_types.h"
32 #include "DNA_mesh_types.h"
33 #include "DNA_meshdata_types.h"
34 #include "DNA_node_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_space_types.h"
38
39 #include "BLI_array.h"
40 #include "BLI_kdtree.h"
41 #include "BLI_math.h"
42 #include "BLI_utildefines.h"
43
44 #include "BLT_translation.h"
45
46 #include "BKE_context.h"
47 #include "BKE_customdata.h"
48 #include "BKE_editmesh.h"
49 #include "BKE_layer.h"
50 #include "BKE_main.h"
51 #include "BKE_material.h"
52 #include "BKE_mesh_mapping.h"
53 #include "BKE_node.h"
54
55 #include "DEG_depsgraph.h"
56
57 #include "ED_image.h"
58 #include "ED_mesh.h"
59 #include "ED_node.h"
60 #include "ED_screen.h"
61 #include "ED_uvedit.h"
62
63 #include "RNA_access.h"
64 #include "RNA_define.h"
65
66 #include "WM_api.h"
67 #include "WM_message.h"
68 #include "WM_types.h"
69
70 #include "UI_interface.h"
71 #include "UI_resources.h"
72 #include "UI_view2d.h"
73
74 #include "uvedit_intern.h"
75
76 /* -------------------------------------------------------------------- */
77 /** \name State Testing
78 * \{ */
79
ED_uvedit_test(Object * obedit)80 bool ED_uvedit_test(Object *obedit)
81 {
82 BMEditMesh *em;
83 int ret;
84
85 if (!obedit) {
86 return 0;
87 }
88
89 if (obedit->type != OB_MESH) {
90 return 0;
91 }
92
93 em = BKE_editmesh_from_object(obedit);
94 ret = EDBM_uv_check(em);
95
96 return ret;
97 }
98
UNUSED_FUNCTION(ED_operator_uvmap_mesh)99 static int UNUSED_FUNCTION(ED_operator_uvmap_mesh)(bContext *C)
100 {
101 Object *ob = CTX_data_active_object(C);
102
103 if (ob && ob->type == OB_MESH) {
104 Mesh *me = ob->data;
105
106 if (CustomData_get_layer(&me->ldata, CD_MLOOPUV) != NULL) {
107 return 1;
108 }
109 }
110
111 return 0;
112 }
113
114 /** \} */
115
116 /* -------------------------------------------------------------------- */
117 /** \name Object Active Image
118 * \{ */
119
is_image_texture_node(bNode * node)120 static bool is_image_texture_node(bNode *node)
121 {
122 return ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT);
123 }
124
ED_object_get_active_image(Object * ob,int mat_nr,Image ** r_ima,ImageUser ** r_iuser,bNode ** r_node,bNodeTree ** r_ntree)125 bool ED_object_get_active_image(Object *ob,
126 int mat_nr,
127 Image **r_ima,
128 ImageUser **r_iuser,
129 bNode **r_node,
130 bNodeTree **r_ntree)
131 {
132 Material *ma = BKE_object_material_get(ob, mat_nr);
133 bNodeTree *ntree = (ma && ma->use_nodes) ? ma->nodetree : NULL;
134 bNode *node = (ntree) ? nodeGetActiveTexture(ntree) : NULL;
135
136 if (node && is_image_texture_node(node)) {
137 if (r_ima) {
138 *r_ima = (Image *)node->id;
139 }
140 if (r_iuser) {
141 if (node->type == SH_NODE_TEX_IMAGE) {
142 *r_iuser = &((NodeTexImage *)node->storage)->iuser;
143 }
144 else if (node->type == SH_NODE_TEX_ENVIRONMENT) {
145 *r_iuser = &((NodeTexEnvironment *)node->storage)->iuser;
146 }
147 else {
148 *r_iuser = NULL;
149 }
150 }
151 if (r_node) {
152 *r_node = node;
153 }
154 if (r_ntree) {
155 *r_ntree = ntree;
156 }
157 return true;
158 }
159
160 if (r_ima) {
161 *r_ima = NULL;
162 }
163 if (r_iuser) {
164 *r_iuser = NULL;
165 }
166 if (r_node) {
167 *r_node = node;
168 }
169 if (r_ntree) {
170 *r_ntree = ntree;
171 }
172
173 return false;
174 }
175
ED_object_assign_active_image(Main * bmain,Object * ob,int mat_nr,Image * ima)176 void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
177 {
178 Material *ma = BKE_object_material_get(ob, mat_nr);
179 bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL;
180
181 if (node && is_image_texture_node(node)) {
182 node->id = &ima->id;
183 ED_node_tag_update_nodetree(bmain, ma->nodetree, node);
184 }
185 }
186
187 /** \} */
188
189 /* -------------------------------------------------------------------- */
190 /** \name Space Conversion
191 * \{ */
192
uvedit_pixel_to_float(SpaceImage * sima,float pixeldist,float r_dist[2])193 void uvedit_pixel_to_float(SpaceImage *sima, float pixeldist, float r_dist[2])
194 {
195 int width, height;
196
197 if (sima) {
198 ED_space_image_get_size(sima, &width, &height);
199 }
200 else {
201 width = IMG_SIZE_FALLBACK;
202 height = IMG_SIZE_FALLBACK;
203 }
204
205 r_dist[0] = pixeldist / width;
206 r_dist[1] = pixeldist / height;
207 }
208
209 /** \} */
210
211 /* -------------------------------------------------------------------- */
212 /** \name Live Unwrap Utilities
213 * \{ */
214
uvedit_live_unwrap_update(SpaceImage * sima,Scene * scene,Object * obedit)215 void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
216 {
217 if (sima && (sima->flag & SI_LIVE_UNWRAP)) {
218 ED_uvedit_live_unwrap_begin(scene, obedit);
219 ED_uvedit_live_unwrap_re_solve();
220 ED_uvedit_live_unwrap_end(0);
221 }
222 }
223
224 /** \} */
225
226 /* -------------------------------------------------------------------- */
227 /** \name Geometric Utilities
228 * \{ */
229
uv_poly_copy_aspect(float uv_orig[][2],float uv[][2],float aspx,float aspy,int len)230 void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len)
231 {
232 int i;
233 for (i = 0; i < len; i++) {
234 uv[i][0] = uv_orig[i][0] * aspx;
235 uv[i][1] = uv_orig[i][1] * aspy;
236 }
237 }
238
ED_uvedit_minmax_multi(const Scene * scene,Object ** objects_edit,uint objects_len,float r_min[2],float r_max[2])239 bool ED_uvedit_minmax_multi(
240 const Scene *scene, Object **objects_edit, uint objects_len, float r_min[2], float r_max[2])
241 {
242 bool changed = false;
243 INIT_MINMAX2(r_min, r_max);
244
245 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
246 Object *obedit = objects_edit[ob_index];
247
248 BMEditMesh *em = BKE_editmesh_from_object(obedit);
249 BMFace *efa;
250 BMLoop *l;
251 BMIter iter, liter;
252 MLoopUV *luv;
253
254 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
255
256 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
257 if (!uvedit_face_visible_test(scene, efa)) {
258 continue;
259 }
260
261 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
262 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
263 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
264 minmax_v2v2_v2(r_min, r_max, luv->uv);
265 changed = true;
266 }
267 }
268 }
269 }
270 return changed;
271 }
272
ED_uvedit_minmax(const Scene * scene,Object * obedit,float r_min[2],float r_max[2])273 bool ED_uvedit_minmax(const Scene *scene, Object *obedit, float r_min[2], float r_max[2])
274 {
275 return ED_uvedit_minmax_multi(scene, &obedit, 1, r_min, r_max);
276 }
277
278 /* Be careful when using this, it bypasses all synchronization options */
ED_uvedit_select_all(BMesh * bm)279 void ED_uvedit_select_all(BMesh *bm)
280 {
281 BMFace *efa;
282 BMLoop *l;
283 BMIter iter, liter;
284 MLoopUV *luv;
285
286 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
287
288 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
289 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
290 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
291 luv->flag |= MLOOPUV_VERTSEL;
292 }
293 }
294 }
295
ED_uvedit_median_multi(const Scene * scene,Object ** objects_edit,uint objects_len,float co[2])296 static bool ED_uvedit_median_multi(const Scene *scene,
297 Object **objects_edit,
298 uint objects_len,
299 float co[2])
300 {
301 uint sel = 0;
302 zero_v2(co);
303
304 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
305 Object *obedit = objects_edit[ob_index];
306
307 BMEditMesh *em = BKE_editmesh_from_object(obedit);
308 BMFace *efa;
309 BMLoop *l;
310 BMIter iter, liter;
311 MLoopUV *luv;
312
313 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
314
315 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
316 if (!uvedit_face_visible_test(scene, efa)) {
317 continue;
318 }
319
320 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
321 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
322 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
323 add_v2_v2(co, luv->uv);
324 sel++;
325 }
326 }
327 }
328 }
329
330 mul_v2_fl(co, 1.0f / (float)sel);
331
332 return (sel != 0);
333 }
334
ED_uvedit_center_multi(const Scene * scene,Object ** objects_edit,uint objects_len,float cent[2],char mode)335 bool ED_uvedit_center_multi(
336 const Scene *scene, Object **objects_edit, uint objects_len, float cent[2], char mode)
337 {
338 bool changed = false;
339
340 if (mode == V3D_AROUND_CENTER_BOUNDS) { /* bounding box */
341 float min[2], max[2];
342 if (ED_uvedit_minmax_multi(scene, objects_edit, objects_len, min, max)) {
343 mid_v2_v2v2(cent, min, max);
344 changed = true;
345 }
346 }
347 else {
348 if (ED_uvedit_median_multi(scene, objects_edit, objects_len, cent)) {
349 changed = true;
350 }
351 }
352
353 return changed;
354 }
355
ED_uvedit_center_from_pivot_ex(SpaceImage * sima,Scene * scene,ViewLayer * view_layer,float r_center[2],char mode,bool * r_has_select)356 bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima,
357 Scene *scene,
358 ViewLayer *view_layer,
359 float r_center[2],
360 char mode,
361 bool *r_has_select)
362 {
363 bool changed = false;
364 switch (mode) {
365 case V3D_AROUND_CURSOR: {
366 copy_v2_v2(r_center, sima->cursor);
367 changed = true;
368 if (r_has_select != NULL) {
369 uint objects_len = 0;
370 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
371 view_layer, ((View3D *)NULL), &objects_len);
372 *r_has_select = uvedit_select_is_any_selected_multi(scene, objects, objects_len);
373 MEM_freeN(objects);
374 }
375 break;
376 }
377 default: {
378 uint objects_len = 0;
379 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
380 view_layer, ((View3D *)NULL), &objects_len);
381 changed = ED_uvedit_center_multi(scene, objects, objects_len, r_center, mode);
382 MEM_freeN(objects);
383 if (r_has_select != NULL) {
384 *r_has_select = changed;
385 }
386 break;
387 }
388 }
389 return changed;
390 }
391
ED_uvedit_center_from_pivot(SpaceImage * sima,Scene * scene,ViewLayer * view_layer,float r_center[2],char mode)392 bool ED_uvedit_center_from_pivot(
393 SpaceImage *sima, Scene *scene, ViewLayer *view_layer, float r_center[2], char mode)
394 {
395 return ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, mode, NULL);
396 }
397
398 /** \} */
399
400 /* -------------------------------------------------------------------- */
401 /** \name Weld Align Operator
402 * \{ */
403
404 typedef enum eUVWeldAlign {
405 UV_STRAIGHTEN,
406 UV_STRAIGHTEN_X,
407 UV_STRAIGHTEN_Y,
408 UV_ALIGN_AUTO,
409 UV_ALIGN_X,
410 UV_ALIGN_Y,
411 UV_WELD,
412 } eUVWeldAlign;
413
uv_weld_align(bContext * C,eUVWeldAlign tool)414 static void uv_weld_align(bContext *C, eUVWeldAlign tool)
415 {
416 Scene *scene = CTX_data_scene(C);
417 ViewLayer *view_layer = CTX_data_view_layer(C);
418 SpaceImage *sima = CTX_wm_space_image(C);
419 const ToolSettings *ts = scene->toolsettings;
420 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
421 float cent[2], min[2], max[2];
422
423 INIT_MINMAX2(min, max);
424
425 uint objects_len = 0;
426 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
427 view_layer, ((View3D *)NULL), &objects_len);
428
429 if (tool == UV_ALIGN_AUTO) {
430 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
431 Object *obedit = objects[ob_index];
432 BMEditMesh *em = BKE_editmesh_from_object(obedit);
433
434 if (synced_selection && (em->bm->totvertsel == 0)) {
435 continue;
436 }
437
438 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
439
440 BMIter iter, liter;
441 BMFace *efa;
442 BMLoop *l;
443
444 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
445 if (!uvedit_face_visible_test(scene, efa)) {
446 continue;
447 }
448
449 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
450 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
451 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
452 minmax_v2v2_v2(min, max, luv->uv);
453 }
454 }
455 }
456 }
457 tool = (max[0] - min[0] >= max[1] - min[1]) ? UV_ALIGN_Y : UV_ALIGN_X;
458 }
459
460 ED_uvedit_center_multi(scene, objects, objects_len, cent, 0);
461
462 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
463 Object *obedit = objects[ob_index];
464 BMEditMesh *em = BKE_editmesh_from_object(obedit);
465 bool changed = false;
466
467 if (synced_selection && (em->bm->totvertsel == 0)) {
468 continue;
469 }
470
471 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
472
473 if (ELEM(tool, UV_ALIGN_X, UV_WELD)) {
474 BMIter iter, liter;
475 BMFace *efa;
476 BMLoop *l;
477
478 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
479 if (!uvedit_face_visible_test(scene, efa)) {
480 continue;
481 }
482
483 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
484 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
485 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
486 luv->uv[0] = cent[0];
487 changed = true;
488 }
489 }
490 }
491 }
492
493 if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) {
494 BMIter iter, liter;
495 BMFace *efa;
496 BMLoop *l;
497
498 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
499 if (!uvedit_face_visible_test(scene, efa)) {
500 continue;
501 }
502
503 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
504 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
505 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
506 luv->uv[1] = cent[1];
507 changed = true;
508 }
509 }
510 }
511 }
512
513 if (ELEM(tool, UV_STRAIGHTEN, UV_STRAIGHTEN_X, UV_STRAIGHTEN_Y)) {
514 BMEdge *eed;
515 BMLoop *l;
516 BMVert *eve;
517 BMVert *eve_start;
518 BMIter iter, liter, eiter;
519
520 /* clear tag */
521 BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
522
523 /* tag verts with a selected UV */
524 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
525 BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
526 if (!uvedit_face_visible_test(scene, l->f)) {
527 continue;
528 }
529
530 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
531 BM_elem_flag_enable(eve, BM_ELEM_TAG);
532 break;
533 }
534 }
535 }
536
537 /* flush vertex tags to edges */
538 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
539 BM_elem_flag_set(
540 eed,
541 BM_ELEM_TAG,
542 (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) && BM_elem_flag_test(eed->v2, BM_ELEM_TAG)));
543 }
544
545 /* find a vertex with only one tagged edge */
546 eve_start = NULL;
547 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
548 int tot_eed_tag = 0;
549 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
550 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
551 tot_eed_tag++;
552 }
553 }
554
555 if (tot_eed_tag == 1) {
556 eve_start = eve;
557 break;
558 }
559 }
560
561 if (eve_start) {
562 BMVert **eve_line = NULL;
563 BMVert *eve_next = NULL;
564 BLI_array_declare(eve_line);
565 int i;
566
567 eve = eve_start;
568
569 /* walk over edges, building an array of verts in a line */
570 while (eve) {
571 BLI_array_append(eve_line, eve);
572 /* don't touch again */
573 BM_elem_flag_disable(eve, BM_ELEM_TAG);
574
575 eve_next = NULL;
576
577 /* find next eve */
578 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
579 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
580 BMVert *eve_other = BM_edge_other_vert(eed, eve);
581 if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) {
582 /* this is a tagged vert we didn't walk over yet, step onto it */
583 eve_next = eve_other;
584 break;
585 }
586 }
587 }
588
589 eve = eve_next;
590 }
591
592 /* now we have all verts, make into a line */
593 if (BLI_array_len(eve_line) > 2) {
594
595 /* we know the returns from these must be valid */
596 const float *uv_start = uvedit_first_selected_uv_from_vertex(
597 scene, eve_line[0], cd_loop_uv_offset);
598 const float *uv_end = uvedit_first_selected_uv_from_vertex(
599 scene, eve_line[BLI_array_len(eve_line) - 1], cd_loop_uv_offset);
600 /* For UV_STRAIGHTEN_X & UV_STRAIGHTEN_Y modes */
601 float a = 0.0f;
602 eUVWeldAlign tool_local = tool;
603
604 if (tool_local == UV_STRAIGHTEN_X) {
605 if (uv_start[1] == uv_end[1]) {
606 tool_local = UV_STRAIGHTEN;
607 }
608 else {
609 a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
610 }
611 }
612 else if (tool_local == UV_STRAIGHTEN_Y) {
613 if (uv_start[0] == uv_end[0]) {
614 tool_local = UV_STRAIGHTEN;
615 }
616 else {
617 a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
618 }
619 }
620
621 /* go over all verts except for endpoints */
622 for (i = 0; i < BLI_array_len(eve_line); i++) {
623 BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) {
624 if (!uvedit_face_visible_test(scene, l->f)) {
625 continue;
626 }
627
628 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
629 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
630 /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
631 * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
632 * Maybe this should be a BLI func? Or is it already existing?
633 * Could use interp_v2_v2v2, but not sure it's worth it here...*/
634 if (tool_local == UV_STRAIGHTEN_X) {
635 luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
636 }
637 else if (tool_local == UV_STRAIGHTEN_Y) {
638 luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
639 }
640 else {
641 closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
642 }
643 changed = true;
644 }
645 }
646 }
647 }
648 else {
649 /* error - not a line, needs 3+ points */
650 }
651
652 if (eve_line) {
653 MEM_freeN(eve_line);
654 }
655 }
656 else {
657 /* error - cant find an endpoint */
658 }
659 }
660
661 if (changed) {
662 uvedit_live_unwrap_update(sima, scene, obedit);
663 DEG_id_tag_update(obedit->data, 0);
664 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
665 }
666 }
667
668 MEM_freeN(objects);
669 }
670
uv_align_exec(bContext * C,wmOperator * op)671 static int uv_align_exec(bContext *C, wmOperator *op)
672 {
673 uv_weld_align(C, RNA_enum_get(op->ptr, "axis"));
674
675 return OPERATOR_FINISHED;
676 }
677
UV_OT_align(wmOperatorType * ot)678 static void UV_OT_align(wmOperatorType *ot)
679 {
680 static const EnumPropertyItem axis_items[] = {
681 {UV_STRAIGHTEN,
682 "ALIGN_S",
683 0,
684 "Straighten",
685 "Align UVs along the line defined by the endpoints"},
686 {UV_STRAIGHTEN_X,
687 "ALIGN_T",
688 0,
689 "Straighten X",
690 "Align UVs along the line defined by the endpoints along the X axis"},
691 {UV_STRAIGHTEN_Y,
692 "ALIGN_U",
693 0,
694 "Straighten Y",
695 "Align UVs along the line defined by the endpoints along the Y axis"},
696 {UV_ALIGN_AUTO,
697 "ALIGN_AUTO",
698 0,
699 "Align Auto",
700 "Automatically choose the axis on which there is most alignment already"},
701 {UV_ALIGN_X, "ALIGN_X", 0, "Align X", "Align UVs on X axis"},
702 {UV_ALIGN_Y, "ALIGN_Y", 0, "Align Y", "Align UVs on Y axis"},
703 {0, NULL, 0, NULL, NULL},
704 };
705
706 /* identifiers */
707 ot->name = "Align";
708 ot->description = "Align selected UV vertices to an axis";
709 ot->idname = "UV_OT_align";
710 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
711
712 /* api callbacks */
713 ot->exec = uv_align_exec;
714 ot->poll = ED_operator_uvedit;
715
716 /* properties */
717 RNA_def_enum(
718 ot->srna, "axis", axis_items, UV_ALIGN_AUTO, "Axis", "Axis to align UV locations on");
719 }
720
721 /** \} */
722
723 /* -------------------------------------------------------------------- */
724 /** \name Remove Doubles Operator
725 * \{ */
726
uv_remove_doubles_to_selected(bContext * C,wmOperator * op)727 static int uv_remove_doubles_to_selected(bContext *C, wmOperator *op)
728 {
729 Scene *scene = CTX_data_scene(C);
730 ViewLayer *view_layer = CTX_data_view_layer(C);
731 SpaceImage *sima = CTX_wm_space_image(C);
732 const ToolSettings *ts = scene->toolsettings;
733
734 const float threshold = RNA_float_get(op->ptr, "threshold");
735 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
736
737 uint objects_len = 0;
738 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
739 view_layer, ((View3D *)NULL), &objects_len);
740
741 bool *changed = MEM_callocN(sizeof(bool) * objects_len, "uv_remove_doubles_selected.changed");
742
743 /* Maximum index of an objects[i]'s MLoopUVs in MLoopUV_arr.
744 * It helps find which MLoopUV in *MLoopUV_arr belongs to which object. */
745 uint *ob_mloopuv_max_idx = MEM_callocN(sizeof(uint) * objects_len,
746 "uv_remove_doubles_selected.ob_mloopuv_max_idx");
747
748 /* Calculate max possible number of kdtree nodes. */
749 int uv_maxlen = 0;
750 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
751 Object *obedit = objects[ob_index];
752 BMEditMesh *em = BKE_editmesh_from_object(obedit);
753
754 if (synced_selection && (em->bm->totvertsel == 0)) {
755 continue;
756 }
757
758 uv_maxlen += em->bm->totloop;
759 }
760
761 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
762
763 int *duplicates = NULL;
764 BLI_array_declare(duplicates);
765
766 MLoopUV **mloopuv_arr = NULL;
767 BLI_array_declare(mloopuv_arr);
768
769 int mloopuv_count = 0; /* Also used for *duplicates count. */
770
771 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
772 BMIter iter, liter;
773 BMFace *efa;
774 BMLoop *l;
775 Object *obedit = objects[ob_index];
776 BMEditMesh *em = BKE_editmesh_from_object(obedit);
777
778 if (synced_selection && (em->bm->totvertsel == 0)) {
779 continue;
780 }
781
782 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
783
784 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
785 if (!uvedit_face_visible_test(scene, efa)) {
786 continue;
787 }
788
789 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
790 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
791 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
792 BLI_kdtree_2d_insert(tree, mloopuv_count, luv->uv);
793 BLI_array_append(duplicates, -1);
794 BLI_array_append(mloopuv_arr, luv);
795 mloopuv_count++;
796 }
797 }
798 }
799
800 ob_mloopuv_max_idx[ob_index] = mloopuv_count - 1;
801 }
802
803 BLI_kdtree_2d_balance(tree);
804 int found_duplicates = BLI_kdtree_2d_calc_duplicates_fast(tree, threshold, false, duplicates);
805
806 if (found_duplicates > 0) {
807 /* Calculate average uv for duplicates. */
808 int *uv_duplicate_count = MEM_callocN(sizeof(int) * mloopuv_count,
809 "uv_remove_doubles_selected.uv_duplicate_count");
810 for (int i = 0; i < mloopuv_count; i++) {
811 if (duplicates[i] == -1) { /* If doesn't reference another */
812 uv_duplicate_count[i]++; /* self */
813 continue;
814 }
815
816 if (duplicates[i] != i) {
817 /* If not self then accumulate uv for averaging.
818 * Self uv is already present in accumulator */
819 add_v2_v2(mloopuv_arr[duplicates[i]]->uv, mloopuv_arr[i]->uv);
820 }
821 uv_duplicate_count[duplicates[i]]++;
822 }
823
824 for (int i = 0; i < mloopuv_count; i++) {
825 if (uv_duplicate_count[i] < 2) {
826 continue;
827 }
828
829 mul_v2_fl(mloopuv_arr[i]->uv, 1.0f / (float)uv_duplicate_count[i]);
830 }
831 MEM_freeN(uv_duplicate_count);
832
833 /* Update duplicated uvs. */
834 uint ob_index = 0;
835 for (int i = 0; i < mloopuv_count; i++) {
836 /* Make sure we know which object owns the MLoopUV at this index.
837 * Remember that in some cases the object will have no loop uv,
838 * thus we need the while loop, and not simply an if check. */
839 while (ob_mloopuv_max_idx[ob_index] < i) {
840 ob_index++;
841 }
842
843 if (duplicates[i] == -1) {
844 continue;
845 }
846
847 copy_v2_v2(mloopuv_arr[i]->uv, mloopuv_arr[duplicates[i]]->uv);
848 changed[ob_index] = true;
849 }
850
851 for (ob_index = 0; ob_index < objects_len; ob_index++) {
852 if (changed[ob_index]) {
853 Object *obedit = objects[ob_index];
854 uvedit_live_unwrap_update(sima, scene, obedit);
855 DEG_id_tag_update(obedit->data, 0);
856 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
857 }
858 }
859 }
860
861 BLI_kdtree_2d_free(tree);
862 BLI_array_free(mloopuv_arr);
863 BLI_array_free(duplicates);
864 MEM_freeN(changed);
865 MEM_freeN(objects);
866 MEM_freeN(ob_mloopuv_max_idx);
867
868 return OPERATOR_FINISHED;
869 }
870
uv_remove_doubles_to_unselected(bContext * C,wmOperator * op)871 static int uv_remove_doubles_to_unselected(bContext *C, wmOperator *op)
872 {
873 Scene *scene = CTX_data_scene(C);
874 ViewLayer *view_layer = CTX_data_view_layer(C);
875 SpaceImage *sima = CTX_wm_space_image(C);
876 const ToolSettings *ts = scene->toolsettings;
877
878 const float threshold = RNA_float_get(op->ptr, "threshold");
879 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
880
881 uint objects_len = 0;
882 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
883 view_layer, ((View3D *)NULL), &objects_len);
884
885 /* Calculate max possible number of kdtree nodes. */
886 int uv_maxlen = 0;
887 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
888 Object *obedit = objects[ob_index];
889 BMEditMesh *em = BKE_editmesh_from_object(obedit);
890 uv_maxlen += em->bm->totloop;
891 }
892
893 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
894
895 MLoopUV **mloopuv_arr = NULL;
896 BLI_array_declare(mloopuv_arr);
897
898 int mloopuv_count = 0;
899
900 /* Add visible non-selected uvs to tree */
901 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
902 BMIter iter, liter;
903 BMFace *efa;
904 BMLoop *l;
905 Object *obedit = objects[ob_index];
906 BMEditMesh *em = BKE_editmesh_from_object(obedit);
907
908 if (synced_selection && (em->bm->totvertsel == em->bm->totvert)) {
909 continue;
910 }
911
912 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
913
914 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
915 if (!uvedit_face_visible_test(scene, efa)) {
916 continue;
917 }
918
919 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
920 if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
921 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
922 BLI_kdtree_2d_insert(tree, mloopuv_count, luv->uv);
923 BLI_array_append(mloopuv_arr, luv);
924 mloopuv_count++;
925 }
926 }
927 }
928 }
929
930 BLI_kdtree_2d_balance(tree);
931
932 /* For each selected uv, find duplicate non selected uv. */
933 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
934 BMIter iter, liter;
935 BMFace *efa;
936 BMLoop *l;
937 bool changed = false;
938 Object *obedit = objects[ob_index];
939 BMEditMesh *em = BKE_editmesh_from_object(obedit);
940
941 if (synced_selection && (em->bm->totvertsel == 0)) {
942 continue;
943 }
944
945 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
946
947 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
948 if (!uvedit_face_visible_test(scene, efa)) {
949 continue;
950 }
951
952 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
953 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
954 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
955 KDTreeNearest_2d nearest;
956 const int i = BLI_kdtree_2d_find_nearest(tree, luv->uv, &nearest);
957
958 if (i != -1 && nearest.dist < threshold) {
959 copy_v2_v2(luv->uv, mloopuv_arr[i]->uv);
960 changed = true;
961 }
962 }
963 }
964 }
965
966 if (changed) {
967 uvedit_live_unwrap_update(sima, scene, obedit);
968 DEG_id_tag_update(obedit->data, 0);
969 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
970 }
971 }
972
973 BLI_kdtree_2d_free(tree);
974 BLI_array_free(mloopuv_arr);
975 MEM_freeN(objects);
976
977 return OPERATOR_FINISHED;
978 }
979
uv_remove_doubles_exec(bContext * C,wmOperator * op)980 static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
981 {
982 if (RNA_boolean_get(op->ptr, "use_unselected")) {
983 return uv_remove_doubles_to_unselected(C, op);
984 }
985 return uv_remove_doubles_to_selected(C, op);
986 }
987
UV_OT_remove_doubles(wmOperatorType * ot)988 static void UV_OT_remove_doubles(wmOperatorType *ot)
989 {
990 /* identifiers */
991 ot->name = "Merge UVs by Distance";
992 ot->description =
993 "Selected UV vertices that are within a radius of each other are welded together";
994 ot->idname = "UV_OT_remove_doubles";
995 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
996
997 /* api callbacks */
998 ot->exec = uv_remove_doubles_exec;
999 ot->poll = ED_operator_uvedit;
1000
1001 RNA_def_float(ot->srna,
1002 "threshold",
1003 0.02f,
1004 0.0f,
1005 10.0f,
1006 "Merge Distance",
1007 "Maximum distance between welded vertices",
1008 0.0f,
1009 1.0f);
1010 RNA_def_boolean(
1011 ot->srna, "use_unselected", 0, "Unselected", "Merge selected to other unselected vertices");
1012 }
1013
1014 /** \} */
1015
1016 /* -------------------------------------------------------------------- */
1017 /** \name Weld Near Operator
1018 * \{ */
1019
uv_weld_exec(bContext * C,wmOperator * UNUSED (op))1020 static int uv_weld_exec(bContext *C, wmOperator *UNUSED(op))
1021 {
1022 uv_weld_align(C, UV_WELD);
1023
1024 return OPERATOR_FINISHED;
1025 }
1026
UV_OT_weld(wmOperatorType * ot)1027 static void UV_OT_weld(wmOperatorType *ot)
1028 {
1029 /* identifiers */
1030 ot->name = "Weld";
1031 ot->description = "Weld selected UV vertices together";
1032 ot->idname = "UV_OT_weld";
1033 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1034
1035 /* api callbacks */
1036 ot->exec = uv_weld_exec;
1037 ot->poll = ED_operator_uvedit;
1038 }
1039
1040 /** \} */
1041
1042 /* -------------------------------------------------------------------- */
1043 /** \name Snap Cursor Operator
1044 * \{ */
1045
uv_snap_to_pixel(float uvco[2],float w,float h)1046 static void uv_snap_to_pixel(float uvco[2], float w, float h)
1047 {
1048 uvco[0] = roundf(uvco[0] * w) / w;
1049 uvco[1] = roundf(uvco[1] * h) / h;
1050 }
1051
uv_snap_cursor_to_pixels(SpaceImage * sima)1052 static void uv_snap_cursor_to_pixels(SpaceImage *sima)
1053 {
1054 int width = 0, height = 0;
1055
1056 ED_space_image_get_size(sima, &width, &height);
1057 uv_snap_to_pixel(sima->cursor, width, height);
1058 }
1059
uv_snap_cursor_to_selection(Scene * scene,Object ** objects_edit,uint objects_len,SpaceImage * sima)1060 static bool uv_snap_cursor_to_selection(Scene *scene,
1061 Object **objects_edit,
1062 uint objects_len,
1063 SpaceImage *sima)
1064 {
1065 return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around);
1066 }
1067
uv_snap_cursor_exec(bContext * C,wmOperator * op)1068 static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
1069 {
1070 SpaceImage *sima = CTX_wm_space_image(C);
1071
1072 bool changed = false;
1073
1074 switch (RNA_enum_get(op->ptr, "target")) {
1075 case 0:
1076 uv_snap_cursor_to_pixels(sima);
1077 changed = true;
1078 break;
1079 case 1: {
1080 Scene *scene = CTX_data_scene(C);
1081 ViewLayer *view_layer = CTX_data_view_layer(C);
1082
1083 uint objects_len = 0;
1084 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1085 view_layer, ((View3D *)NULL), &objects_len);
1086 changed = uv_snap_cursor_to_selection(scene, objects, objects_len, sima);
1087 MEM_freeN(objects);
1088 break;
1089 }
1090 }
1091
1092 if (!changed) {
1093 return OPERATOR_CANCELLED;
1094 }
1095
1096 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, sima);
1097
1098 return OPERATOR_FINISHED;
1099 }
1100
UV_OT_snap_cursor(wmOperatorType * ot)1101 static void UV_OT_snap_cursor(wmOperatorType *ot)
1102 {
1103 static const EnumPropertyItem target_items[] = {
1104 {0, "PIXELS", 0, "Pixels", ""},
1105 {1, "SELECTED", 0, "Selected", ""},
1106 {0, NULL, 0, NULL, NULL},
1107 };
1108
1109 /* identifiers */
1110 ot->name = "Snap Cursor";
1111 ot->description = "Snap cursor to target type";
1112 ot->idname = "UV_OT_snap_cursor";
1113 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1114
1115 /* api callbacks */
1116 ot->exec = uv_snap_cursor_exec;
1117 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
1118
1119 /* properties */
1120 RNA_def_enum(
1121 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1122 }
1123
1124 /** \} */
1125
1126 /* -------------------------------------------------------------------- */
1127 /** \name Snap Selection Operator
1128 * \{ */
1129
uv_snap_uvs_to_cursor(Scene * scene,Object * obedit,const float cursor[2])1130 static bool uv_snap_uvs_to_cursor(Scene *scene, Object *obedit, const float cursor[2])
1131 {
1132 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1133 BMFace *efa;
1134 BMLoop *l;
1135 BMIter iter, liter;
1136 MLoopUV *luv;
1137 bool changed = false;
1138
1139 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1140
1141 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1142 if (!uvedit_face_visible_test(scene, efa)) {
1143 continue;
1144 }
1145
1146 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1147 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1148 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1149 copy_v2_v2(luv->uv, cursor);
1150 changed = true;
1151 }
1152 }
1153 }
1154
1155 return changed;
1156 }
1157
uv_snap_uvs_offset(Scene * scene,Object * obedit,const float offset[2])1158 static bool uv_snap_uvs_offset(Scene *scene, Object *obedit, const float offset[2])
1159 {
1160 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1161 BMFace *efa;
1162 BMLoop *l;
1163 BMIter iter, liter;
1164 MLoopUV *luv;
1165 bool changed = false;
1166
1167 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1168
1169 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1170 if (!uvedit_face_visible_test(scene, efa)) {
1171 continue;
1172 }
1173
1174 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1175 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1176 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1177 add_v2_v2(luv->uv, offset);
1178 changed = true;
1179 }
1180 }
1181 }
1182
1183 return changed;
1184 }
1185
uv_snap_uvs_to_adjacent_unselected(Scene * scene,Object * obedit)1186 static bool uv_snap_uvs_to_adjacent_unselected(Scene *scene, Object *obedit)
1187 {
1188 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1189 BMesh *bm = em->bm;
1190 BMFace *f;
1191 BMLoop *l, *lsub;
1192 BMIter iter, liter, lsubiter;
1193 MLoopUV *luv;
1194 bool changed = false;
1195 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1196
1197 /* index every vert that has a selected UV using it, but only once so as to
1198 * get unique indices and to count how much to malloc */
1199 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1200 if (uvedit_face_visible_test(scene, f)) {
1201 BM_elem_flag_enable(f, BM_ELEM_TAG);
1202 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1203 BM_elem_flag_set(l, BM_ELEM_TAG, uvedit_uv_select_test(scene, l, cd_loop_uv_offset));
1204 }
1205 }
1206 else {
1207 BM_elem_flag_disable(f, BM_ELEM_TAG);
1208 }
1209 }
1210
1211 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1212 if (BM_elem_flag_test(f, BM_ELEM_TAG)) { /* face: visible */
1213 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1214 if (BM_elem_flag_test(l, BM_ELEM_TAG)) { /* loop: selected*/
1215 float uv[2] = {0.0f, 0.0f};
1216 int uv_tot = 0;
1217
1218 BM_ITER_ELEM (lsub, &lsubiter, l->v, BM_LOOPS_OF_VERT) {
1219 if (BM_elem_flag_test(lsub->f, BM_ELEM_TAG) && /* face: visible */
1220 !BM_elem_flag_test(lsub, BM_ELEM_TAG)) /* loop: unselected */
1221 {
1222 luv = BM_ELEM_CD_GET_VOID_P(lsub, cd_loop_uv_offset);
1223 add_v2_v2(uv, luv->uv);
1224 uv_tot++;
1225 }
1226 }
1227
1228 if (uv_tot) {
1229 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1230 mul_v2_v2fl(luv->uv, uv, 1.0f / (float)uv_tot);
1231 changed = true;
1232 }
1233 }
1234 }
1235 }
1236 }
1237
1238 return changed;
1239 }
1240
uv_snap_uvs_to_pixels(SpaceImage * sima,Scene * scene,Object * obedit)1241 static bool uv_snap_uvs_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit)
1242 {
1243 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1244 BMFace *efa;
1245 BMLoop *l;
1246 BMIter iter, liter;
1247 MLoopUV *luv;
1248 int width = 0, height = 0;
1249 float w, h;
1250 bool changed = false;
1251
1252 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1253
1254 ED_space_image_get_size(sima, &width, &height);
1255 w = (float)width;
1256 h = (float)height;
1257
1258 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1259 if (!uvedit_face_visible_test(scene, efa)) {
1260 continue;
1261 }
1262
1263 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1264 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1265 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1266 uv_snap_to_pixel(luv->uv, w, h);
1267 }
1268 }
1269
1270 changed = true;
1271 }
1272
1273 return changed;
1274 }
1275
uv_snap_selection_exec(bContext * C,wmOperator * op)1276 static int uv_snap_selection_exec(bContext *C, wmOperator *op)
1277 {
1278 Scene *scene = CTX_data_scene(C);
1279 ViewLayer *view_layer = CTX_data_view_layer(C);
1280 SpaceImage *sima = CTX_wm_space_image(C);
1281 const ToolSettings *ts = scene->toolsettings;
1282 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1283 const int target = RNA_enum_get(op->ptr, "target");
1284 float offset[2] = {0};
1285
1286 uint objects_len = 0;
1287 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1288 view_layer, ((View3D *)NULL), &objects_len);
1289
1290 if (target == 2) {
1291 float center[2];
1292 if (!ED_uvedit_center_multi(scene, objects, objects_len, center, sima->around)) {
1293 MEM_freeN(objects);
1294 return OPERATOR_CANCELLED;
1295 }
1296 sub_v2_v2v2(offset, sima->cursor, center);
1297 }
1298
1299 bool changed_multi = false;
1300 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1301 Object *obedit = objects[ob_index];
1302 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1303
1304 if (synced_selection && (em->bm->totvertsel == 0)) {
1305 continue;
1306 }
1307
1308 bool changed = false;
1309 switch (target) {
1310 case 0:
1311 changed = uv_snap_uvs_to_pixels(sima, scene, obedit);
1312 break;
1313 case 1:
1314 changed = uv_snap_uvs_to_cursor(scene, obedit, sima->cursor);
1315 break;
1316 case 2:
1317 changed = uv_snap_uvs_offset(scene, obedit, offset);
1318 break;
1319 case 3:
1320 changed = uv_snap_uvs_to_adjacent_unselected(scene, obedit);
1321 break;
1322 }
1323
1324 if (changed) {
1325 changed_multi = true;
1326 uvedit_live_unwrap_update(sima, scene, obedit);
1327 DEG_id_tag_update(obedit->data, 0);
1328 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1329 }
1330 }
1331 MEM_freeN(objects);
1332
1333 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1334 }
1335
UV_OT_snap_selected(wmOperatorType * ot)1336 static void UV_OT_snap_selected(wmOperatorType *ot)
1337 {
1338 static const EnumPropertyItem target_items[] = {
1339 {0, "PIXELS", 0, "Pixels", ""},
1340 {1, "CURSOR", 0, "Cursor", ""},
1341 {2, "CURSOR_OFFSET", 0, "Cursor (Offset)", ""},
1342 {3, "ADJACENT_UNSELECTED", 0, "Adjacent Unselected", ""},
1343 {0, NULL, 0, NULL, NULL},
1344 };
1345
1346 /* identifiers */
1347 ot->name = "Snap Selection";
1348 ot->description = "Snap selected UV vertices to target type";
1349 ot->idname = "UV_OT_snap_selected";
1350 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1351
1352 /* api callbacks */
1353 ot->exec = uv_snap_selection_exec;
1354 ot->poll = ED_operator_uvedit_space_image;
1355
1356 /* properties */
1357 RNA_def_enum(
1358 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1359 }
1360
1361 /** \} */
1362
1363 /* -------------------------------------------------------------------- */
1364 /** \name Pin UV's Operator
1365 * \{ */
1366
uv_pin_exec(bContext * C,wmOperator * op)1367 static int uv_pin_exec(bContext *C, wmOperator *op)
1368 {
1369 Scene *scene = CTX_data_scene(C);
1370 ViewLayer *view_layer = CTX_data_view_layer(C);
1371 BMFace *efa;
1372 BMLoop *l;
1373 BMIter iter, liter;
1374 MLoopUV *luv;
1375 const ToolSettings *ts = scene->toolsettings;
1376 const bool clear = RNA_boolean_get(op->ptr, "clear");
1377 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1378
1379 uint objects_len = 0;
1380 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1381 view_layer, ((View3D *)NULL), &objects_len);
1382
1383 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1384 Object *obedit = objects[ob_index];
1385 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1386
1387 bool changed = false;
1388 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1389
1390 if (synced_selection && (em->bm->totvertsel == 0)) {
1391 continue;
1392 }
1393
1394 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1395 if (!uvedit_face_visible_test(scene, efa)) {
1396 continue;
1397 }
1398
1399 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1400 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1401
1402 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1403 changed = true;
1404 if (clear) {
1405 luv->flag &= ~MLOOPUV_PINNED;
1406 }
1407 else {
1408 luv->flag |= MLOOPUV_PINNED;
1409 }
1410 }
1411 }
1412 }
1413
1414 if (changed) {
1415 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1416 DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE);
1417 }
1418 }
1419 MEM_freeN(objects);
1420
1421 return OPERATOR_FINISHED;
1422 }
1423
UV_OT_pin(wmOperatorType * ot)1424 static void UV_OT_pin(wmOperatorType *ot)
1425 {
1426 /* identifiers */
1427 ot->name = "Pin";
1428 ot->description =
1429 "Set/clear selected UV vertices as anchored between multiple unwrap operations";
1430 ot->idname = "UV_OT_pin";
1431 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1432
1433 /* api callbacks */
1434 ot->exec = uv_pin_exec;
1435 ot->poll = ED_operator_uvedit;
1436
1437 /* properties */
1438 RNA_def_boolean(
1439 ot->srna, "clear", 0, "Clear", "Clear pinning for the selection instead of setting it");
1440 }
1441
1442 /** \} */
1443
1444 /* -------------------------------------------------------------------- */
1445 /** \name Hide Operator
1446 * \{ */
1447
1448 /* check if we are selected or unselected based on 'bool_test' arg,
1449 * needed for select swap support */
1450 #define UV_SEL_TEST(luv, bool_test) \
1451 ((((luv)->flag & MLOOPUV_VERTSEL) == MLOOPUV_VERTSEL) == bool_test)
1452
1453 /* is every UV vert selected or unselected depending on bool_test */
bm_face_is_all_uv_sel(BMFace * f,bool select_test,const int cd_loop_uv_offset)1454 static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const int cd_loop_uv_offset)
1455 {
1456 BMLoop *l_iter;
1457 BMLoop *l_first;
1458
1459 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1460 do {
1461 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
1462 if (!UV_SEL_TEST(luv, select_test)) {
1463 return false;
1464 }
1465 } while ((l_iter = l_iter->next) != l_first);
1466
1467 return true;
1468 }
1469
uv_hide_exec(bContext * C,wmOperator * op)1470 static int uv_hide_exec(bContext *C, wmOperator *op)
1471 {
1472 ViewLayer *view_layer = CTX_data_view_layer(C);
1473 Scene *scene = CTX_data_scene(C);
1474 const ToolSettings *ts = scene->toolsettings;
1475 const bool swap = RNA_boolean_get(op->ptr, "unselected");
1476 const int use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1477
1478 uint objects_len = 0;
1479 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1480 view_layer, ((View3D *)NULL), &objects_len);
1481
1482 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1483 Object *ob = objects[ob_index];
1484 BMEditMesh *em = BKE_editmesh_from_object(ob);
1485 BMFace *efa;
1486 BMLoop *l;
1487 BMIter iter, liter;
1488 MLoopUV *luv;
1489
1490 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1491
1492 if (ts->uv_flag & UV_SYNC_SELECTION) {
1493 if (EDBM_mesh_hide(em, swap)) {
1494 EDBM_update_generic(ob->data, true, false);
1495 }
1496 return OPERATOR_FINISHED;
1497 }
1498
1499 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1500 int hide = 0;
1501
1502 if (!uvedit_face_visible_test(scene, efa)) {
1503 continue;
1504 }
1505
1506 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1507 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1508
1509 if (UV_SEL_TEST(luv, !swap)) {
1510 hide = 1;
1511 break;
1512 }
1513 }
1514
1515 if (hide) {
1516 /* note, a special case for edges could be used,
1517 * for now edges act like verts and get flushed */
1518 if (use_face_center) {
1519 if (em->selectmode == SCE_SELECT_FACE) {
1520 /* check that every UV is selected */
1521 if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
1522 BM_face_select_set(em->bm, efa, false);
1523 }
1524 uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
1525 }
1526 else {
1527 if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
1528 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1529 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1530 if (UV_SEL_TEST(luv, !swap)) {
1531 BM_vert_select_set(em->bm, l->v, false);
1532 }
1533 }
1534 }
1535 if (!swap) {
1536 uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
1537 }
1538 }
1539 }
1540 else if (em->selectmode == SCE_SELECT_FACE) {
1541 /* check if a UV is de-selected */
1542 if (bm_face_is_all_uv_sel(efa, false, cd_loop_uv_offset) != !swap) {
1543 BM_face_select_set(em->bm, efa, false);
1544 uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
1545 }
1546 }
1547 else {
1548 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1549 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1550 if (UV_SEL_TEST(luv, !swap)) {
1551 BM_vert_select_set(em->bm, l->v, false);
1552 if (!swap) {
1553 luv->flag &= ~MLOOPUV_VERTSEL;
1554 }
1555 }
1556 }
1557 }
1558 }
1559 }
1560
1561 /* flush vertex selection changes */
1562 if (em->selectmode != SCE_SELECT_FACE) {
1563 EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX | SCE_SELECT_EDGE);
1564 }
1565
1566 BM_select_history_validate(em->bm);
1567
1568 DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
1569 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
1570 }
1571
1572 MEM_freeN(objects);
1573
1574 return OPERATOR_FINISHED;
1575 }
1576
1577 #undef UV_SEL_TEST
1578
UV_OT_hide(wmOperatorType * ot)1579 static void UV_OT_hide(wmOperatorType *ot)
1580 {
1581 /* identifiers */
1582 ot->name = "Hide Selected";
1583 ot->description = "Hide (un)selected UV vertices";
1584 ot->idname = "UV_OT_hide";
1585 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1586
1587 /* api callbacks */
1588 ot->exec = uv_hide_exec;
1589 ot->poll = ED_operator_uvedit;
1590
1591 /* props */
1592 RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
1593 }
1594
1595 /** \} */
1596
1597 /* -------------------------------------------------------------------- */
1598 /** \name Reveal Operator
1599 * \{ */
1600
uv_reveal_exec(bContext * C,wmOperator * op)1601 static int uv_reveal_exec(bContext *C, wmOperator *op)
1602 {
1603 ViewLayer *view_layer = CTX_data_view_layer(C);
1604 SpaceImage *sima = CTX_wm_space_image(C);
1605 Scene *scene = CTX_data_scene(C);
1606 const ToolSettings *ts = scene->toolsettings;
1607
1608 const int use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1609 const int stickymode = sima ? (sima->sticky != SI_STICKY_DISABLE) : 1;
1610 const bool select = RNA_boolean_get(op->ptr, "select");
1611
1612 uint objects_len = 0;
1613 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1614 view_layer, ((View3D *)NULL), &objects_len);
1615
1616 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1617 Object *ob = objects[ob_index];
1618 BMEditMesh *em = BKE_editmesh_from_object(ob);
1619 BMFace *efa;
1620 BMLoop *l;
1621 BMIter iter, liter;
1622 MLoopUV *luv;
1623
1624 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1625
1626 /* note on tagging, selecting faces needs to be delayed so it doesn't select the verts and
1627 * confuse our checks on selected verts. */
1628
1629 /* call the mesh function if we are in mesh sync sel */
1630 if (ts->uv_flag & UV_SYNC_SELECTION) {
1631 if (EDBM_mesh_reveal(em, select)) {
1632 EDBM_update_generic(ob->data, true, false);
1633 }
1634 return OPERATOR_FINISHED;
1635 }
1636 if (use_face_center) {
1637 if (em->selectmode == SCE_SELECT_FACE) {
1638 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1639 BM_elem_flag_disable(efa, BM_ELEM_TAG);
1640 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1641 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1642 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1643 SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1644 }
1645 /* BM_face_select_set(em->bm, efa, true); */
1646 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1647 }
1648 }
1649 }
1650 else {
1651 /* enable adjacent faces to have disconnected UV selections if sticky is disabled */
1652 if (!stickymode) {
1653 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1654 BM_elem_flag_disable(efa, BM_ELEM_TAG);
1655 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
1656 !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1657 int totsel = 0;
1658 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1659 totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT);
1660 }
1661
1662 if (!totsel) {
1663 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1664 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1665 SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1666 }
1667 /* BM_face_select_set(em->bm, efa, true); */
1668 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1669 }
1670 }
1671 }
1672 }
1673 else {
1674 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1675 BM_elem_flag_disable(efa, BM_ELEM_TAG);
1676 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
1677 !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1678 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1679 if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) {
1680 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1681 SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1682 }
1683 }
1684 /* BM_face_select_set(em->bm, efa, true); */
1685 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1686 }
1687 }
1688 }
1689 }
1690 }
1691 else if (em->selectmode == SCE_SELECT_FACE) {
1692 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1693 BM_elem_flag_disable(efa, BM_ELEM_TAG);
1694 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1695 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1696 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1697 SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1698 }
1699 /* BM_face_select_set(em->bm, efa, true); */
1700 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1701 }
1702 }
1703 }
1704 else {
1705 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1706 BM_elem_flag_disable(efa, BM_ELEM_TAG);
1707 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1708 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1709 if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) {
1710 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1711 SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1712 }
1713 }
1714 /* BM_face_select_set(em->bm, efa, true); */
1715 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1716 }
1717 }
1718 }
1719
1720 /* re-select tagged faces */
1721 BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG);
1722
1723 DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
1724 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
1725 }
1726
1727 MEM_freeN(objects);
1728
1729 return OPERATOR_FINISHED;
1730 }
1731
UV_OT_reveal(wmOperatorType * ot)1732 static void UV_OT_reveal(wmOperatorType *ot)
1733 {
1734 /* identifiers */
1735 ot->name = "Reveal Hidden";
1736 ot->description = "Reveal all hidden UV vertices";
1737 ot->idname = "UV_OT_reveal";
1738 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1739
1740 /* api callbacks */
1741 ot->exec = uv_reveal_exec;
1742 ot->poll = ED_operator_uvedit;
1743
1744 RNA_def_boolean(ot->srna, "select", true, "Select", "");
1745 }
1746
1747 /** \} */
1748
1749 /* -------------------------------------------------------------------- */
1750 /** \name Set 2D Cursor Operator
1751 * \{ */
1752
uv_set_2d_cursor_exec(bContext * C,wmOperator * op)1753 static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op)
1754 {
1755 SpaceImage *sima = CTX_wm_space_image(C);
1756
1757 if (!sima) {
1758 return OPERATOR_CANCELLED;
1759 }
1760
1761 RNA_float_get_array(op->ptr, "location", sima->cursor);
1762
1763 {
1764 struct wmMsgBus *mbus = CTX_wm_message_bus(C);
1765 bScreen *screen = CTX_wm_screen(C);
1766 WM_msg_publish_rna_prop(mbus, &screen->id, sima, SpaceImageEditor, cursor_location);
1767 }
1768
1769 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
1770
1771 return OPERATOR_FINISHED;
1772 }
1773
uv_set_2d_cursor_invoke(bContext * C,wmOperator * op,const wmEvent * event)1774 static int uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1775 {
1776 ARegion *region = CTX_wm_region(C);
1777 float location[2];
1778
1779 if (region->regiontype == RGN_TYPE_WINDOW) {
1780 if (event->mval[1] <= 16) {
1781 SpaceImage *sima = CTX_wm_space_image(C);
1782 if (sima && ED_space_image_show_cache(sima)) {
1783 return OPERATOR_PASS_THROUGH;
1784 }
1785 }
1786 }
1787
1788 UI_view2d_region_to_view(
1789 ®ion->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
1790 RNA_float_set_array(op->ptr, "location", location);
1791
1792 return uv_set_2d_cursor_exec(C, op);
1793 }
1794
UV_OT_cursor_set(wmOperatorType * ot)1795 static void UV_OT_cursor_set(wmOperatorType *ot)
1796 {
1797 /* identifiers */
1798 ot->name = "Set 2D Cursor";
1799 ot->description = "Set 2D cursor location";
1800 ot->idname = "UV_OT_cursor_set";
1801
1802 /* api callbacks */
1803 ot->exec = uv_set_2d_cursor_exec;
1804 ot->invoke = uv_set_2d_cursor_invoke;
1805 ot->poll = ED_space_image_cursor_poll;
1806
1807 /* properties */
1808 RNA_def_float_vector(ot->srna,
1809 "location",
1810 2,
1811 NULL,
1812 -FLT_MAX,
1813 FLT_MAX,
1814 "Location",
1815 "Cursor location in normalized (0.0-1.0) coordinates",
1816 -10.0f,
1817 10.0f);
1818 }
1819
1820 /** \} */
1821
1822 /* -------------------------------------------------------------------- */
1823 /** \name Seam from UV Islands Operator
1824 * \{ */
1825
uv_seams_from_islands_exec(bContext * C,wmOperator * op)1826 static int uv_seams_from_islands_exec(bContext *C, wmOperator *op)
1827 {
1828 Scene *scene = CTX_data_scene(C);
1829 ViewLayer *view_layer = CTX_data_view_layer(C);
1830 const bool mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
1831 const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
1832 bool changed_multi = false;
1833
1834 uint objects_len = 0;
1835 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1836 view_layer, ((View3D *)NULL), &objects_len);
1837
1838 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1839 Object *ob = objects[ob_index];
1840 Mesh *me = (Mesh *)ob->data;
1841 BMEditMesh *em = me->edit_mesh;
1842 BMesh *bm = em->bm;
1843 BMIter iter;
1844
1845 if (!EDBM_uv_check(em)) {
1846 continue;
1847 }
1848
1849 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1850 bool changed = false;
1851
1852 BMFace *f;
1853 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1854 if (!uvedit_face_visible_test(scene, f)) {
1855 continue;
1856 }
1857
1858 BMLoop *l_iter;
1859 BMLoop *l_first;
1860
1861 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1862 do {
1863 if (l_iter == l_iter->radial_next) {
1864 continue;
1865 }
1866 if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) {
1867 continue;
1868 }
1869
1870 bool mark = false;
1871 BMLoop *l_other = l_iter->radial_next;
1872 do {
1873 if (!BM_loop_uv_share_edge_check(l_iter, l_other, cd_loop_uv_offset)) {
1874 mark = true;
1875 break;
1876 }
1877 } while ((l_other = l_other->radial_next) != l_iter);
1878
1879 if (mark) {
1880 if (mark_seams) {
1881 BM_elem_flag_enable(l_iter->e, BM_ELEM_SEAM);
1882 }
1883 if (mark_sharp) {
1884 BM_elem_flag_disable(l_iter->e, BM_ELEM_SMOOTH);
1885 }
1886 changed = true;
1887 }
1888 } while ((l_iter = l_iter->next) != l_first);
1889 }
1890
1891 if (changed) {
1892 changed_multi = true;
1893 DEG_id_tag_update(&me->id, 0);
1894 WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
1895 }
1896 }
1897 MEM_freeN(objects);
1898
1899 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1900 }
1901
UV_OT_seams_from_islands(wmOperatorType * ot)1902 static void UV_OT_seams_from_islands(wmOperatorType *ot)
1903 {
1904 /* identifiers */
1905 ot->name = "Seams from Islands";
1906 ot->description = "Set mesh seams according to island setup in the UV editor";
1907 ot->idname = "UV_OT_seams_from_islands";
1908
1909 /* flags */
1910 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1911
1912 /* api callbacks */
1913 ot->exec = uv_seams_from_islands_exec;
1914 ot->poll = ED_operator_uvedit;
1915
1916 RNA_def_boolean(ot->srna, "mark_seams", 1, "Mark Seams", "Mark boundary edges as seams");
1917 RNA_def_boolean(ot->srna, "mark_sharp", 0, "Mark Sharp", "Mark boundary edges as sharp");
1918 }
1919
1920 /** \} */
1921
1922 /* -------------------------------------------------------------------- */
1923 /** \name Mark Seam Operator
1924 * \{ */
1925
uv_mark_seam_exec(bContext * C,wmOperator * op)1926 static int uv_mark_seam_exec(bContext *C, wmOperator *op)
1927 {
1928 Scene *scene = CTX_data_scene(C);
1929 ViewLayer *view_layer = CTX_data_view_layer(C);
1930 const ToolSettings *ts = scene->toolsettings;
1931
1932 BMFace *efa;
1933 BMLoop *loop;
1934 BMIter iter, liter;
1935
1936 const bool flag_set = !RNA_boolean_get(op->ptr, "clear");
1937 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1938
1939 uint objects_len = 0;
1940 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1941 view_layer, ((View3D *)NULL), &objects_len);
1942
1943 bool changed = false;
1944
1945 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1946 Object *ob = objects[ob_index];
1947 Mesh *me = (Mesh *)ob->data;
1948 BMEditMesh *em = me->edit_mesh;
1949 BMesh *bm = em->bm;
1950
1951 if (synced_selection && (bm->totedgesel == 0)) {
1952 continue;
1953 }
1954
1955 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1956
1957 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1958 if (uvedit_face_visible_test(scene, efa)) {
1959 BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) {
1960 if (uvedit_edge_select_test(scene, loop, cd_loop_uv_offset)) {
1961 BM_elem_flag_set(loop->e, BM_ELEM_SEAM, flag_set);
1962 changed = true;
1963 }
1964 }
1965 }
1966 }
1967
1968 if (changed) {
1969 DEG_id_tag_update(&me->id, 0);
1970 WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
1971 }
1972 }
1973
1974 if (changed) {
1975 ED_uvedit_live_unwrap(scene, objects, objects_len);
1976 }
1977
1978 MEM_freeN(objects);
1979
1980 return OPERATOR_FINISHED;
1981 }
1982
uv_mark_seam_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1983 static int uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1984 {
1985 uiPopupMenu *pup;
1986 uiLayout *layout;
1987
1988 if (RNA_struct_property_is_set(op->ptr, "clear")) {
1989 return uv_mark_seam_exec(C, op);
1990 }
1991
1992 pup = UI_popup_menu_begin(C, IFACE_("Edges"), ICON_NONE);
1993 layout = UI_popup_menu_layout(pup);
1994
1995 uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
1996 uiItemBooleanO(layout,
1997 CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Mark Seam"),
1998 ICON_NONE,
1999 op->type->idname,
2000 "clear",
2001 false);
2002 uiItemBooleanO(layout,
2003 CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Seam"),
2004 ICON_NONE,
2005 op->type->idname,
2006 "clear",
2007 true);
2008
2009 UI_popup_menu_end(C, pup);
2010
2011 return OPERATOR_INTERFACE;
2012 }
2013
UV_OT_mark_seam(wmOperatorType * ot)2014 static void UV_OT_mark_seam(wmOperatorType *ot)
2015 {
2016 /* identifiers */
2017 ot->name = "Mark Seam";
2018 ot->description = "Mark selected UV edges as seams";
2019 ot->idname = "UV_OT_mark_seam";
2020
2021 /* flags */
2022 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2023
2024 /* api callbacks */
2025 ot->exec = uv_mark_seam_exec;
2026 ot->invoke = uv_mark_seam_invoke;
2027 ot->poll = ED_operator_uvedit;
2028
2029 RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams");
2030 }
2031
2032 /** \} */
2033
2034 /* -------------------------------------------------------------------- */
2035 /** \name Operator Registration & Keymap
2036 * \{ */
2037
ED_operatortypes_uvedit(void)2038 void ED_operatortypes_uvedit(void)
2039 {
2040 /* uvedit_select.c */
2041 WM_operatortype_append(UV_OT_select_all);
2042 WM_operatortype_append(UV_OT_select);
2043 WM_operatortype_append(UV_OT_select_loop);
2044 WM_operatortype_append(UV_OT_select_edge_ring);
2045 WM_operatortype_append(UV_OT_select_linked);
2046 WM_operatortype_append(UV_OT_select_linked_pick);
2047 WM_operatortype_append(UV_OT_select_split);
2048 WM_operatortype_append(UV_OT_select_pinned);
2049 WM_operatortype_append(UV_OT_select_box);
2050 WM_operatortype_append(UV_OT_select_lasso);
2051 WM_operatortype_append(UV_OT_select_circle);
2052 WM_operatortype_append(UV_OT_select_more);
2053 WM_operatortype_append(UV_OT_select_less);
2054 WM_operatortype_append(UV_OT_select_overlap);
2055
2056 WM_operatortype_append(UV_OT_snap_cursor);
2057 WM_operatortype_append(UV_OT_snap_selected);
2058
2059 WM_operatortype_append(UV_OT_align);
2060
2061 WM_operatortype_append(UV_OT_rip);
2062 WM_operatortype_append(UV_OT_stitch);
2063 WM_operatortype_append(UV_OT_shortest_path_pick);
2064 WM_operatortype_append(UV_OT_shortest_path_select);
2065
2066 WM_operatortype_append(UV_OT_seams_from_islands);
2067 WM_operatortype_append(UV_OT_mark_seam);
2068 WM_operatortype_append(UV_OT_weld);
2069 WM_operatortype_append(UV_OT_remove_doubles);
2070 WM_operatortype_append(UV_OT_pin);
2071
2072 WM_operatortype_append(UV_OT_average_islands_scale);
2073 WM_operatortype_append(UV_OT_cube_project);
2074 WM_operatortype_append(UV_OT_cylinder_project);
2075 WM_operatortype_append(UV_OT_project_from_view);
2076 WM_operatortype_append(UV_OT_minimize_stretch);
2077 WM_operatortype_append(UV_OT_pack_islands);
2078 WM_operatortype_append(UV_OT_reset);
2079 WM_operatortype_append(UV_OT_sphere_project);
2080 WM_operatortype_append(UV_OT_unwrap);
2081 WM_operatortype_append(UV_OT_smart_project);
2082
2083 WM_operatortype_append(UV_OT_reveal);
2084 WM_operatortype_append(UV_OT_hide);
2085
2086 WM_operatortype_append(UV_OT_cursor_set);
2087 }
2088
ED_operatormacros_uvedit(void)2089 void ED_operatormacros_uvedit(void)
2090 {
2091 wmOperatorType *ot;
2092 wmOperatorTypeMacro *otmacro;
2093
2094 ot = WM_operatortype_append_macro("UV_OT_rip_move",
2095 "UV Rip Move",
2096 "Unstitch UV's and move the result",
2097 OPTYPE_UNDO | OPTYPE_REGISTER);
2098 WM_operatortype_macro_define(ot, "UV_OT_rip");
2099 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
2100 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
2101 RNA_boolean_set(otmacro->ptr, "mirror", false);
2102 }
2103
ED_keymap_uvedit(wmKeyConfig * keyconf)2104 void ED_keymap_uvedit(wmKeyConfig *keyconf)
2105 {
2106 wmKeyMap *keymap;
2107
2108 keymap = WM_keymap_ensure(keyconf, "UV Editor", 0, 0);
2109 keymap->poll = ED_operator_uvedit;
2110 }
2111
2112 /** \} */
2113