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_camera_types.h"
31 #include "DNA_mesh_types.h"
32 #include "DNA_meshdata_types.h"
33 #include "DNA_modifier_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_scene_types.h"
36
37 #include "BLI_alloca.h"
38 #include "BLI_array.h"
39 #include "BLI_linklist.h"
40 #include "BLI_math.h"
41 #include "BLI_memarena.h"
42 #include "BLI_string.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_uvproject.h"
45
46 #include "BLT_translation.h"
47
48 #include "BKE_cdderivedmesh.h"
49 #include "BKE_context.h"
50 #include "BKE_customdata.h"
51 #include "BKE_editmesh.h"
52 #include "BKE_image.h"
53 #include "BKE_layer.h"
54 #include "BKE_lib_id.h"
55 #include "BKE_main.h"
56 #include "BKE_material.h"
57 #include "BKE_mesh.h"
58 #include "BKE_report.h"
59 #include "BKE_scene.h"
60 #include "BKE_subsurf.h"
61
62 #include "DEG_depsgraph.h"
63
64 #include "PIL_time.h"
65
66 #include "UI_interface.h"
67
68 #include "ED_image.h"
69 #include "ED_mesh.h"
70 #include "ED_screen.h"
71 #include "ED_uvedit.h"
72 #include "ED_view3d.h"
73
74 #include "RNA_access.h"
75 #include "RNA_define.h"
76
77 #include "WM_api.h"
78 #include "WM_types.h"
79
80 #include "uvedit_intern.h"
81 #include "uvedit_parametrizer.h"
82
83 /* -------------------------------------------------------------------- */
84 /** \name Utility Functions
85 * \{ */
86
modifier_unwrap_state(Object * obedit,const Scene * scene,bool * r_use_subsurf)87 static void modifier_unwrap_state(Object *obedit, const Scene *scene, bool *r_use_subsurf)
88 {
89 ModifierData *md;
90 bool subsurf = (scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF) != 0;
91
92 md = obedit->modifiers.first;
93
94 /* subsurf will take the modifier settings only if modifier is first or right after mirror */
95 if (subsurf) {
96 if (md && md->type == eModifierType_Subsurf) {
97 subsurf = true;
98 }
99 else {
100 subsurf = false;
101 }
102 }
103
104 *r_use_subsurf = subsurf;
105 }
106
ED_uvedit_ensure_uvs(Object * obedit)107 static bool ED_uvedit_ensure_uvs(Object *obedit)
108 {
109 if (ED_uvedit_test(obedit)) {
110 return 1;
111 }
112
113 BMEditMesh *em = BKE_editmesh_from_object(obedit);
114 BMFace *efa;
115 BMIter iter;
116 int cd_loop_uv_offset;
117
118 if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) {
119 ED_mesh_uv_texture_add(obedit->data, NULL, true, true);
120 }
121
122 /* Happens when there are no faces. */
123 if (!ED_uvedit_test(obedit)) {
124 return 0;
125 }
126
127 cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
128
129 /* select new UV's (ignore UV_SYNC_SELECTION in this case) */
130 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
131 BMIter liter;
132 BMLoop *l;
133
134 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
135 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
136 luv->flag |= MLOOPUV_VERTSEL;
137 }
138 }
139
140 return 1;
141 }
142
143 /** \} */
144
145 /* -------------------------------------------------------------------- */
146 /** \name Parametrizer Conversion
147 * \{ */
148
149 typedef struct UnwrapOptions {
150 /** Connectivity based on UV coordinates instead of seams. */
151 bool topology_from_uvs;
152 /** Also use seams as well as UV coordinates (only valid when `topology_from_uvs` is enabled). */
153 bool topology_from_uvs_use_seams;
154 /** Only affect selected faces. */
155 bool only_selected_faces;
156 /**
157 * Only affect selected UV's.
158 * \note Disable this for operations that don't run in the image-window.
159 * Unwrapping from the 3D view for example, where only 'only_selected_faces' should be used.
160 */
161 bool only_selected_uvs;
162 /** Fill holes to better preserve shape. */
163 bool fill_holes;
164 /** Correct for mapped image texture aspect ratio. */
165 bool correct_aspect;
166 } UnwrapOptions;
167
uvedit_have_selection(const Scene * scene,BMEditMesh * em,const UnwrapOptions * options)168 static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
169 {
170 BMFace *efa;
171 BMLoop *l;
172 BMIter iter, liter;
173 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
174
175 if (cd_loop_uv_offset == -1) {
176 return (em->bm->totfacesel != 0);
177 }
178
179 /* verify if we have any selected uv's before unwrapping,
180 * so we can cancel the operator early */
181 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
182 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
183 if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
184 continue;
185 }
186 }
187 else if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
188 continue;
189 }
190
191 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
192 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
193 break;
194 }
195 }
196
197 if (options->topology_from_uvs && !l) {
198 continue;
199 }
200
201 return true;
202 }
203
204 return false;
205 }
206
uvedit_have_selection_multi(const Scene * scene,Object ** objects,const uint objects_len,const UnwrapOptions * options)207 static bool uvedit_have_selection_multi(const Scene *scene,
208 Object **objects,
209 const uint objects_len,
210 const UnwrapOptions *options)
211 {
212 bool have_select = false;
213 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
214 Object *obedit = objects[ob_index];
215 BMEditMesh *em = BKE_editmesh_from_object(obedit);
216 if (uvedit_have_selection(scene, em, options)) {
217 have_select = true;
218 break;
219 }
220 }
221 return have_select;
222 }
223
ED_uvedit_get_aspect(Object * ob,float * r_aspx,float * r_aspy)224 void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
225 {
226 BMEditMesh *em = BKE_editmesh_from_object(ob);
227 BLI_assert(em != NULL);
228 bool sloppy = true;
229 bool selected = false;
230 BMFace *efa;
231 Image *ima;
232
233 efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
234
235 if (efa) {
236 ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
237
238 ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
239 }
240 else {
241 *r_aspx = 1.0f;
242 *r_aspy = 1.0f;
243 }
244 }
245
construct_param_handle_face_add(ParamHandle * handle,const Scene * scene,BMFace * efa,int face_index,const int cd_loop_uv_offset)246 static void construct_param_handle_face_add(ParamHandle *handle,
247 const Scene *scene,
248 BMFace *efa,
249 int face_index,
250 const int cd_loop_uv_offset)
251 {
252 ParamKey key;
253 ParamKey *vkeys = BLI_array_alloca(vkeys, efa->len);
254 ParamBool *pin = BLI_array_alloca(pin, efa->len);
255 ParamBool *select = BLI_array_alloca(select, efa->len);
256 float **co = BLI_array_alloca(co, efa->len);
257 float **uv = BLI_array_alloca(uv, efa->len);
258 int i;
259
260 BMIter liter;
261 BMLoop *l;
262
263 key = (ParamKey)face_index;
264
265 /* let parametrizer split the ngon, it can make better decisions
266 * about which split is best for unwrapping than poly-fill. */
267 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
268 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
269
270 vkeys[i] = (ParamKey)BM_elem_index_get(l->v);
271 co[i] = l->v->co;
272 uv[i] = luv->uv;
273 pin[i] = (luv->flag & MLOOPUV_PINNED) != 0;
274 select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
275 }
276
277 param_face_add(handle, key, i, vkeys, co, uv, pin, select);
278 }
279
280 /* See: construct_param_handle_multi to handle multiple objects at once. */
construct_param_handle(const Scene * scene,Object * ob,BMesh * bm,const UnwrapOptions * options)281 static ParamHandle *construct_param_handle(const Scene *scene,
282 Object *ob,
283 BMesh *bm,
284 const UnwrapOptions *options)
285 {
286 ParamHandle *handle;
287 BMFace *efa;
288 BMLoop *l;
289 BMEdge *eed;
290 BMIter iter, liter;
291 int i;
292
293 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
294
295 handle = param_construct_begin();
296
297 if (options->correct_aspect) {
298 float aspx, aspy;
299
300 ED_uvedit_get_aspect(ob, &aspx, &aspy);
301
302 if (aspx != aspy) {
303 param_aspect_ratio(handle, aspx, aspy);
304 }
305 }
306
307 /* we need the vert indices */
308 BM_mesh_elem_index_ensure(bm, BM_VERT);
309
310 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
311
312 if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
313 (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
314 continue;
315 }
316
317 if (options->topology_from_uvs) {
318 bool is_loopsel = false;
319
320 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
321 if (options->only_selected_uvs &&
322 (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
323 continue;
324 }
325 is_loopsel = true;
326 break;
327 }
328 if (is_loopsel == false) {
329 continue;
330 }
331 }
332
333 construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset);
334 }
335
336 if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) {
337 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
338 if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
339 ParamKey vkeys[2];
340 vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1);
341 vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2);
342 param_edge_set_seam(handle, vkeys);
343 }
344 }
345 }
346
347 param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
348
349 return handle;
350 }
351
352 /**
353 * Version of #construct_param_handle_single that handles multiple objects.
354 */
construct_param_handle_multi(const Scene * scene,Object ** objects,const uint objects_len,const UnwrapOptions * options)355 static ParamHandle *construct_param_handle_multi(const Scene *scene,
356 Object **objects,
357 const uint objects_len,
358 const UnwrapOptions *options)
359 {
360 ParamHandle *handle;
361 BMFace *efa;
362 BMLoop *l;
363 BMEdge *eed;
364 BMIter iter, liter;
365 int i;
366
367 handle = param_construct_begin();
368
369 if (options->correct_aspect) {
370 Object *ob = objects[0];
371 float aspx, aspy;
372
373 ED_uvedit_get_aspect(ob, &aspx, &aspy);
374 if (aspx != aspy) {
375 param_aspect_ratio(handle, aspx, aspy);
376 }
377 }
378
379 /* we need the vert indices */
380 EDBM_mesh_elem_index_ensure_multi(objects, objects_len, BM_VERT);
381
382 int offset = 0;
383
384 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
385 Object *obedit = objects[ob_index];
386 BMEditMesh *em = BKE_editmesh_from_object(obedit);
387 BMesh *bm = em->bm;
388
389 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
390
391 if (cd_loop_uv_offset == -1) {
392 continue;
393 }
394
395 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
396
397 if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) ||
398 (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
399 continue;
400 }
401
402 if (options->topology_from_uvs) {
403 bool is_loopsel = false;
404
405 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
406 if (options->only_selected_uvs &&
407 (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
408 continue;
409 }
410 is_loopsel = true;
411 break;
412 }
413 if (is_loopsel == false) {
414 continue;
415 }
416 }
417
418 construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
419 }
420
421 if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) {
422 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
423 if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
424 ParamKey vkeys[2];
425 vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1);
426 vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2);
427 param_edge_set_seam(handle, vkeys);
428 }
429 }
430 }
431 offset += bm->totface;
432 }
433
434 param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
435
436 return handle;
437 }
438
texface_from_original_index(const Scene * scene,const int cd_loop_uv_offset,BMFace * efa,int index,float ** r_uv,ParamBool * r_pin,ParamBool * r_select)439 static void texface_from_original_index(const Scene *scene,
440 const int cd_loop_uv_offset,
441 BMFace *efa,
442 int index,
443 float **r_uv,
444 ParamBool *r_pin,
445 ParamBool *r_select)
446 {
447 BMLoop *l;
448 BMIter liter;
449 MLoopUV *luv;
450
451 *r_uv = NULL;
452 *r_pin = 0;
453 *r_select = 1;
454
455 if (index == ORIGINDEX_NONE) {
456 return;
457 }
458
459 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
460 if (BM_elem_index_get(l->v) == index) {
461 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
462 *r_uv = luv->uv;
463 *r_pin = (luv->flag & MLOOPUV_PINNED) ? 1 : 0;
464 *r_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
465 break;
466 }
467 }
468 }
469
470 /**
471 * Unwrap handle initialization for subsurf aware-unwrapper.
472 * The many modifications required to make the original function(see above)
473 * work justified the existence of a new function.
474 */
construct_param_handle_subsurfed(const Scene * scene,Object * ob,BMEditMesh * em,const UnwrapOptions * options)475 static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
476 Object *ob,
477 BMEditMesh *em,
478 const UnwrapOptions *options)
479 {
480 ParamHandle *handle;
481 /* index pointers */
482 MPoly *mpoly;
483 MLoop *mloop;
484 MEdge *edge;
485 int i;
486
487 /* pointers to modifier data for unwrap control */
488 ModifierData *md;
489 SubsurfModifierData *smd_real;
490 /* modifier initialization data, will control what type of subdivision will happen*/
491 SubsurfModifierData smd = {{NULL}};
492 /* Used to hold subsurfed Mesh */
493 DerivedMesh *derivedMesh, *initialDerived;
494 /* holds original indices for subsurfed mesh */
495 const int *origVertIndices, *origEdgeIndices, *origPolyIndices;
496 /* Holds vertices of subsurfed mesh */
497 MVert *subsurfedVerts;
498 MEdge *subsurfedEdges;
499 MPoly *subsurfedPolys;
500 MLoop *subsurfedLoops;
501 /* number of vertices and faces for subsurfed mesh*/
502 int numOfEdges, numOfFaces;
503
504 /* holds a map to editfaces for every subsurfed MFace.
505 * These will be used to get hidden/ selected flags etc. */
506 BMFace **faceMap;
507 /* similar to the above, we need a way to map edges to their original ones */
508 BMEdge **edgeMap;
509
510 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
511
512 handle = param_construct_begin();
513
514 if (options->correct_aspect) {
515 float aspx, aspy;
516
517 ED_uvedit_get_aspect(ob, &aspx, &aspy);
518
519 if (aspx != aspy) {
520 param_aspect_ratio(handle, aspx, aspy);
521 }
522 }
523
524 /* number of subdivisions to perform */
525 md = ob->modifiers.first;
526 smd_real = (SubsurfModifierData *)md;
527
528 smd.levels = smd_real->levels;
529 smd.subdivType = smd_real->subdivType;
530
531 {
532 Mesh *me_from_em = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, ob->data);
533 initialDerived = CDDM_from_mesh(me_from_em);
534 derivedMesh = subsurf_make_derived_from_derived(
535 initialDerived, &smd, scene, NULL, SUBSURF_IN_EDIT_MODE);
536
537 initialDerived->release(initialDerived);
538 BKE_id_free(NULL, me_from_em);
539 }
540
541 /* get the derived data */
542 subsurfedVerts = derivedMesh->getVertArray(derivedMesh);
543 subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh);
544 subsurfedPolys = derivedMesh->getPolyArray(derivedMesh);
545 subsurfedLoops = derivedMesh->getLoopArray(derivedMesh);
546
547 origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX);
548 origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX);
549 origPolyIndices = derivedMesh->getPolyDataArray(derivedMesh, CD_ORIGINDEX);
550
551 numOfEdges = derivedMesh->getNumEdges(derivedMesh);
552 numOfFaces = derivedMesh->getNumPolys(derivedMesh);
553
554 faceMap = MEM_mallocN(numOfFaces * sizeof(BMFace *), "unwrap_edit_face_map");
555
556 BM_mesh_elem_index_ensure(em->bm, BM_VERT);
557 BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_FACE);
558
559 /* map subsurfed faces to original editFaces */
560 for (i = 0; i < numOfFaces; i++) {
561 faceMap[i] = BM_face_at_index(em->bm, origPolyIndices[i]);
562 }
563
564 edgeMap = MEM_mallocN(numOfEdges * sizeof(BMEdge *), "unwrap_edit_edge_map");
565
566 /* map subsurfed edges to original editEdges */
567 for (i = 0; i < numOfEdges; i++) {
568 /* not all edges correspond to an old edge */
569 edgeMap[i] = (origEdgeIndices[i] != ORIGINDEX_NONE) ?
570 BM_edge_at_index(em->bm, origEdgeIndices[i]) :
571 NULL;
572 }
573
574 /* Prepare and feed faces to the solver */
575 for (i = 0, mpoly = subsurfedPolys; i < numOfFaces; i++, mpoly++) {
576 ParamKey key, vkeys[4];
577 ParamBool pin[4], select[4];
578 float *co[4];
579 float *uv[4];
580 BMFace *origFace = faceMap[i];
581
582 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
583 if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN)) {
584 continue;
585 }
586 }
587 else {
588 if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) ||
589 (options->only_selected_faces && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) {
590 continue;
591 }
592 }
593
594 mloop = &subsurfedLoops[mpoly->loopstart];
595
596 /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */
597 BLI_assert(mpoly->totloop == 4);
598 key = (ParamKey)i;
599 vkeys[0] = (ParamKey)mloop[0].v;
600 vkeys[1] = (ParamKey)mloop[1].v;
601 vkeys[2] = (ParamKey)mloop[2].v;
602 vkeys[3] = (ParamKey)mloop[3].v;
603
604 co[0] = subsurfedVerts[mloop[0].v].co;
605 co[1] = subsurfedVerts[mloop[1].v].co;
606 co[2] = subsurfedVerts[mloop[2].v].co;
607 co[3] = subsurfedVerts[mloop[3].v].co;
608
609 /* This is where all the magic is done.
610 * If the vertex exists in the, we pass the original uv pointer to the solver, thus
611 * flushing the solution to the edit mesh. */
612 texface_from_original_index(scene,
613 cd_loop_uv_offset,
614 origFace,
615 origVertIndices[mloop[0].v],
616 &uv[0],
617 &pin[0],
618 &select[0]);
619 texface_from_original_index(scene,
620 cd_loop_uv_offset,
621 origFace,
622 origVertIndices[mloop[1].v],
623 &uv[1],
624 &pin[1],
625 &select[1]);
626 texface_from_original_index(scene,
627 cd_loop_uv_offset,
628 origFace,
629 origVertIndices[mloop[2].v],
630 &uv[2],
631 &pin[2],
632 &select[2]);
633 texface_from_original_index(scene,
634 cd_loop_uv_offset,
635 origFace,
636 origVertIndices[mloop[3].v],
637 &uv[3],
638 &pin[3],
639 &select[3]);
640
641 param_face_add(handle, key, 4, vkeys, co, uv, pin, select);
642 }
643
644 /* these are calculated from original mesh too */
645 for (edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) {
646 if ((edgeMap[i] != NULL) && BM_elem_flag_test(edgeMap[i], BM_ELEM_SEAM)) {
647 ParamKey vkeys[2];
648 vkeys[0] = (ParamKey)edge->v1;
649 vkeys[1] = (ParamKey)edge->v2;
650 param_edge_set_seam(handle, vkeys);
651 }
652 }
653
654 param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
655
656 /* cleanup */
657 MEM_freeN(faceMap);
658 MEM_freeN(edgeMap);
659 derivedMesh->release(derivedMesh);
660
661 return handle;
662 }
663
664 /** \} */
665
666 /* -------------------------------------------------------------------- */
667 /** \name Minimize Stretch Operator
668 * \{ */
669
670 typedef struct MinStretch {
671 const Scene *scene;
672 Object **objects_edit;
673 uint objects_len;
674 ParamHandle *handle;
675 float blend;
676 double lasttime;
677 int i, iterations;
678 wmTimer *timer;
679 } MinStretch;
680
minimize_stretch_init(bContext * C,wmOperator * op)681 static bool minimize_stretch_init(bContext *C, wmOperator *op)
682 {
683 const Scene *scene = CTX_data_scene(C);
684 ViewLayer *view_layer = CTX_data_view_layer(C);
685
686 const UnwrapOptions options = {
687 .topology_from_uvs = true,
688 .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"),
689 .only_selected_faces = true,
690 .only_selected_uvs = true,
691 .correct_aspect = true,
692 };
693
694 uint objects_len = 0;
695 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
696 view_layer, CTX_wm_view3d(C), &objects_len);
697
698 if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
699 MEM_freeN(objects);
700 return false;
701 }
702
703 MinStretch *ms = MEM_callocN(sizeof(MinStretch), "MinStretch");
704 ms->scene = scene;
705 ms->objects_edit = objects;
706 ms->objects_len = objects_len;
707 ms->blend = RNA_float_get(op->ptr, "blend");
708 ms->iterations = RNA_int_get(op->ptr, "iterations");
709 ms->i = 0;
710 ms->handle = construct_param_handle_multi(scene, objects, objects_len, &options);
711 ms->lasttime = PIL_check_seconds_timer();
712
713 param_stretch_begin(ms->handle);
714 if (ms->blend != 0.0f) {
715 param_stretch_blend(ms->handle, ms->blend);
716 }
717
718 op->customdata = ms;
719
720 return true;
721 }
722
minimize_stretch_iteration(bContext * C,wmOperator * op,bool interactive)723 static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interactive)
724 {
725 MinStretch *ms = op->customdata;
726 ScrArea *area = CTX_wm_area(C);
727 const Scene *scene = CTX_data_scene(C);
728 ToolSettings *ts = scene->toolsettings;
729 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
730
731 param_stretch_blend(ms->handle, ms->blend);
732 param_stretch_iter(ms->handle);
733
734 ms->i++;
735 RNA_int_set(op->ptr, "iterations", ms->i);
736
737 if (interactive && (PIL_check_seconds_timer() - ms->lasttime > 0.5)) {
738 char str[UI_MAX_DRAW_STR];
739
740 param_flush(ms->handle);
741
742 if (area) {
743 BLI_snprintf(str, sizeof(str), TIP_("Minimize Stretch. Blend %.2f"), ms->blend);
744 ED_area_status_text(area, str);
745 ED_workspace_status_text(C, TIP_("Press + and -, or scroll wheel to set blending"));
746 }
747
748 ms->lasttime = PIL_check_seconds_timer();
749
750 for (uint ob_index = 0; ob_index < ms->objects_len; ob_index++) {
751 Object *obedit = ms->objects_edit[ob_index];
752 BMEditMesh *em = BKE_editmesh_from_object(obedit);
753
754 if (synced_selection && (em->bm->totfacesel == 0)) {
755 continue;
756 }
757
758 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
759 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
760 }
761 }
762 }
763
minimize_stretch_exit(bContext * C,wmOperator * op,bool cancel)764 static void minimize_stretch_exit(bContext *C, wmOperator *op, bool cancel)
765 {
766 MinStretch *ms = op->customdata;
767 ScrArea *area = CTX_wm_area(C);
768 const Scene *scene = CTX_data_scene(C);
769 ToolSettings *ts = scene->toolsettings;
770 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
771
772 ED_area_status_text(area, NULL);
773 ED_workspace_status_text(C, NULL);
774
775 if (ms->timer) {
776 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), ms->timer);
777 }
778
779 if (cancel) {
780 param_flush_restore(ms->handle);
781 }
782 else {
783 param_flush(ms->handle);
784 }
785
786 param_stretch_end(ms->handle);
787 param_delete(ms->handle);
788
789 for (uint ob_index = 0; ob_index < ms->objects_len; ob_index++) {
790 Object *obedit = ms->objects_edit[ob_index];
791 BMEditMesh *em = BKE_editmesh_from_object(obedit);
792
793 if (synced_selection && (em->bm->totfacesel == 0)) {
794 continue;
795 }
796
797 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
798 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
799 }
800
801 MEM_freeN(ms->objects_edit);
802 MEM_freeN(ms);
803 op->customdata = NULL;
804 }
805
minimize_stretch_exec(bContext * C,wmOperator * op)806 static int minimize_stretch_exec(bContext *C, wmOperator *op)
807 {
808 int i, iterations;
809
810 if (!minimize_stretch_init(C, op)) {
811 return OPERATOR_CANCELLED;
812 }
813
814 iterations = RNA_int_get(op->ptr, "iterations");
815 for (i = 0; i < iterations; i++) {
816 minimize_stretch_iteration(C, op, false);
817 }
818 minimize_stretch_exit(C, op, false);
819
820 return OPERATOR_FINISHED;
821 }
822
minimize_stretch_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))823 static int minimize_stretch_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
824 {
825 MinStretch *ms;
826
827 if (!minimize_stretch_init(C, op)) {
828 return OPERATOR_CANCELLED;
829 }
830
831 minimize_stretch_iteration(C, op, true);
832
833 ms = op->customdata;
834 WM_event_add_modal_handler(C, op);
835 ms->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
836
837 return OPERATOR_RUNNING_MODAL;
838 }
839
minimize_stretch_modal(bContext * C,wmOperator * op,const wmEvent * event)840 static int minimize_stretch_modal(bContext *C, wmOperator *op, const wmEvent *event)
841 {
842 MinStretch *ms = op->customdata;
843
844 switch (event->type) {
845 case EVT_ESCKEY:
846 case RIGHTMOUSE:
847 minimize_stretch_exit(C, op, true);
848 return OPERATOR_CANCELLED;
849 case EVT_RETKEY:
850 case EVT_PADENTER:
851 case LEFTMOUSE:
852 minimize_stretch_exit(C, op, false);
853 return OPERATOR_FINISHED;
854 case EVT_PADPLUSKEY:
855 case WHEELUPMOUSE:
856 if (event->val == KM_PRESS) {
857 if (ms->blend < 0.95f) {
858 ms->blend += 0.1f;
859 ms->lasttime = 0.0f;
860 RNA_float_set(op->ptr, "blend", ms->blend);
861 minimize_stretch_iteration(C, op, true);
862 }
863 }
864 break;
865 case EVT_PADMINUS:
866 case WHEELDOWNMOUSE:
867 if (event->val == KM_PRESS) {
868 if (ms->blend > 0.05f) {
869 ms->blend -= 0.1f;
870 ms->lasttime = 0.0f;
871 RNA_float_set(op->ptr, "blend", ms->blend);
872 minimize_stretch_iteration(C, op, true);
873 }
874 }
875 break;
876 case TIMER:
877 if (ms->timer == event->customdata) {
878 double start = PIL_check_seconds_timer();
879
880 do {
881 minimize_stretch_iteration(C, op, true);
882 } while (PIL_check_seconds_timer() - start < 0.01);
883 }
884 break;
885 }
886
887 if (ms->iterations && ms->i >= ms->iterations) {
888 minimize_stretch_exit(C, op, false);
889 return OPERATOR_FINISHED;
890 }
891
892 return OPERATOR_RUNNING_MODAL;
893 }
894
minimize_stretch_cancel(bContext * C,wmOperator * op)895 static void minimize_stretch_cancel(bContext *C, wmOperator *op)
896 {
897 minimize_stretch_exit(C, op, true);
898 }
899
UV_OT_minimize_stretch(wmOperatorType * ot)900 void UV_OT_minimize_stretch(wmOperatorType *ot)
901 {
902 /* identifiers */
903 ot->name = "Minimize Stretch";
904 ot->idname = "UV_OT_minimize_stretch";
905 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING;
906 ot->description = "Reduce UV stretching by relaxing angles";
907
908 /* api callbacks */
909 ot->exec = minimize_stretch_exec;
910 ot->invoke = minimize_stretch_invoke;
911 ot->modal = minimize_stretch_modal;
912 ot->cancel = minimize_stretch_cancel;
913 ot->poll = ED_operator_uvedit;
914
915 /* properties */
916 RNA_def_boolean(ot->srna,
917 "fill_holes",
918 1,
919 "Fill Holes",
920 "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and "
921 "preserve symmetry");
922 RNA_def_float_factor(ot->srna,
923 "blend",
924 0.0f,
925 0.0f,
926 1.0f,
927 "Blend",
928 "Blend factor between stretch minimized and original",
929 0.0f,
930 1.0f);
931 RNA_def_int(ot->srna,
932 "iterations",
933 0,
934 0,
935 INT_MAX,
936 "Iterations",
937 "Number of iterations to run, 0 is unlimited when run interactively",
938 0,
939 100);
940 }
941
942 /** \} */
943
944 /* -------------------------------------------------------------------- */
945 /** \name Pack UV Islands Operator
946 * \{ */
947
uvedit_pack_islands(const Scene * scene,Object * ob,BMesh * bm)948 static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm)
949 {
950 const UnwrapOptions options = {
951 .topology_from_uvs = true,
952 .only_selected_faces = false,
953 .only_selected_uvs = true,
954 .fill_holes = false,
955 .correct_aspect = false,
956 };
957
958 bool rotate = true;
959 bool ignore_pinned = false;
960
961 ParamHandle *handle;
962 handle = construct_param_handle(scene, ob, bm, &options);
963 param_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
964 param_flush(handle);
965 param_delete(handle);
966 }
967
968 /**
969 * \warning Since this uses #ParamHandle it doesn't work with non-manifold meshes (see T82637).
970 * Use #ED_uvedit_pack_islands_multi for a more general solution.
971 *
972 * TODO: remove this function, in favor of #ED_uvedit_pack_islands_multi.
973 */
uvedit_pack_islands_multi(const Scene * scene,Object ** objects,const uint objects_len,const UnwrapOptions * options,bool rotate,bool ignore_pinned)974 static void uvedit_pack_islands_multi(const Scene *scene,
975 Object **objects,
976 const uint objects_len,
977 const UnwrapOptions *options,
978 bool rotate,
979 bool ignore_pinned)
980 {
981 ParamHandle *handle;
982 handle = construct_param_handle_multi(scene, objects, objects_len, options);
983 param_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
984 param_flush(handle);
985 param_delete(handle);
986
987 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
988 Object *obedit = objects[ob_index];
989 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
990 WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
991 }
992 }
993
pack_islands_exec(bContext * C,wmOperator * op)994 static int pack_islands_exec(bContext *C, wmOperator *op)
995 {
996 ViewLayer *view_layer = CTX_data_view_layer(C);
997 const Scene *scene = CTX_data_scene(C);
998
999 const UnwrapOptions options = {
1000 .topology_from_uvs = true,
1001 .only_selected_faces = true,
1002 .only_selected_uvs = true,
1003 .fill_holes = false,
1004 .correct_aspect = true,
1005 };
1006
1007 bool rotate = RNA_boolean_get(op->ptr, "rotate");
1008
1009 uint objects_len = 0;
1010 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1011 view_layer, CTX_wm_view3d(C), &objects_len);
1012
1013 if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
1014 MEM_freeN(objects);
1015 return OPERATOR_CANCELLED;
1016 }
1017
1018 if (RNA_struct_property_is_set(op->ptr, "margin")) {
1019 scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
1020 }
1021 else {
1022 RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
1023 }
1024
1025 ED_uvedit_pack_islands_multi(scene,
1026 objects,
1027 objects_len,
1028 &(struct UVPackIsland_Params){
1029 .rotate = rotate,
1030 .rotate_align_axis = -1,
1031 .only_selected_uvs = true,
1032 .only_selected_faces = true,
1033 .correct_aspect = true,
1034 });
1035
1036 MEM_freeN(objects);
1037
1038 return OPERATOR_FINISHED;
1039 }
1040
UV_OT_pack_islands(wmOperatorType * ot)1041 void UV_OT_pack_islands(wmOperatorType *ot)
1042 {
1043 /* identifiers */
1044 ot->name = "Pack Islands";
1045 ot->idname = "UV_OT_pack_islands";
1046 ot->description = "Transform all islands so that they fill up the UV space as much as possible";
1047
1048 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1049
1050 /* api callbacks */
1051 ot->exec = pack_islands_exec;
1052 ot->poll = ED_operator_uvedit;
1053
1054 /* properties */
1055 RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
1056 RNA_def_float_factor(
1057 ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
1058 }
1059
1060 /** \} */
1061
1062 /* -------------------------------------------------------------------- */
1063 /** \name Average UV Islands Scale Operator
1064 * \{ */
1065
average_islands_scale_exec(bContext * C,wmOperator * UNUSED (op))1066 static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
1067 {
1068 const Scene *scene = CTX_data_scene(C);
1069 ViewLayer *view_layer = CTX_data_view_layer(C);
1070 ToolSettings *ts = scene->toolsettings;
1071 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1072
1073 const UnwrapOptions options = {
1074 .topology_from_uvs = true,
1075 .only_selected_faces = true,
1076 .only_selected_uvs = true,
1077 .fill_holes = false,
1078 .correct_aspect = true,
1079 };
1080
1081 uint objects_len = 0;
1082 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1083 view_layer, CTX_wm_view3d(C), &objects_len);
1084
1085 if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
1086 MEM_freeN(objects);
1087 return OPERATOR_CANCELLED;
1088 }
1089
1090 ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
1091 param_average(handle, false);
1092 param_flush(handle);
1093 param_delete(handle);
1094
1095 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1096 Object *obedit = objects[ob_index];
1097 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1098
1099 if (synced_selection && (em->bm->totvertsel == 0)) {
1100 continue;
1101 }
1102
1103 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
1104 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1105 }
1106 MEM_freeN(objects);
1107 return OPERATOR_FINISHED;
1108 }
1109
UV_OT_average_islands_scale(wmOperatorType * ot)1110 void UV_OT_average_islands_scale(wmOperatorType *ot)
1111 {
1112 /* identifiers */
1113 ot->name = "Average Islands Scale";
1114 ot->idname = "UV_OT_average_islands_scale";
1115 ot->description = "Average the size of separate UV islands, based on their area in 3D space";
1116
1117 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1118
1119 /* api callbacks */
1120 ot->exec = average_islands_scale_exec;
1121 ot->poll = ED_operator_uvedit;
1122 }
1123
1124 /** \} */
1125
1126 /* -------------------------------------------------------------------- */
1127 /** \name Live UV Unwrap
1128 * \{ */
1129
1130 static struct {
1131 ParamHandle **handles;
1132 uint len, len_alloc;
1133 } g_live_unwrap = {NULL};
1134
ED_uvedit_live_unwrap_begin(Scene * scene,Object * obedit)1135 void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
1136 {
1137 ParamHandle *handle = NULL;
1138 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1139 const bool abf = (scene->toolsettings->unwrapper == 0);
1140 bool use_subsurf;
1141
1142 modifier_unwrap_state(obedit, scene, &use_subsurf);
1143
1144 if (!ED_uvedit_test(obedit)) {
1145 return;
1146 }
1147
1148 const UnwrapOptions options = {
1149 .topology_from_uvs = false,
1150 .only_selected_faces = false,
1151 .only_selected_uvs = true,
1152 .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0,
1153 .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0,
1154 };
1155
1156 if (use_subsurf) {
1157 handle = construct_param_handle_subsurfed(scene, obedit, em, &options);
1158 }
1159 else {
1160 handle = construct_param_handle(scene, obedit, em->bm, &options);
1161 }
1162
1163 param_lscm_begin(handle, PARAM_TRUE, abf);
1164
1165 /* Create or increase size of g_live_unwrap.handles array */
1166 if (g_live_unwrap.handles == NULL) {
1167 g_live_unwrap.len_alloc = 32;
1168 g_live_unwrap.handles = MEM_mallocN(sizeof(ParamHandle *) * g_live_unwrap.len_alloc,
1169 "uvedit_live_unwrap_liveHandles");
1170 g_live_unwrap.len = 0;
1171 }
1172 if (g_live_unwrap.len >= g_live_unwrap.len_alloc) {
1173 g_live_unwrap.len_alloc *= 2;
1174 g_live_unwrap.handles = MEM_reallocN(g_live_unwrap.handles,
1175 sizeof(ParamHandle *) * g_live_unwrap.len_alloc);
1176 }
1177 g_live_unwrap.handles[g_live_unwrap.len] = handle;
1178 g_live_unwrap.len++;
1179 }
1180
ED_uvedit_live_unwrap_re_solve(void)1181 void ED_uvedit_live_unwrap_re_solve(void)
1182 {
1183 if (g_live_unwrap.handles) {
1184 for (int i = 0; i < g_live_unwrap.len; i++) {
1185 param_lscm_solve(g_live_unwrap.handles[i]);
1186 param_flush(g_live_unwrap.handles[i]);
1187 }
1188 }
1189 }
1190
ED_uvedit_live_unwrap_end(short cancel)1191 void ED_uvedit_live_unwrap_end(short cancel)
1192 {
1193 if (g_live_unwrap.handles) {
1194 for (int i = 0; i < g_live_unwrap.len; i++) {
1195 param_lscm_end(g_live_unwrap.handles[i]);
1196 if (cancel) {
1197 param_flush_restore(g_live_unwrap.handles[i]);
1198 }
1199 param_delete(g_live_unwrap.handles[i]);
1200 }
1201 MEM_freeN(g_live_unwrap.handles);
1202 g_live_unwrap.handles = NULL;
1203 g_live_unwrap.len = 0;
1204 g_live_unwrap.len_alloc = 0;
1205 }
1206 }
1207
1208 /** \} */
1209
1210 /* -------------------------------------------------------------------- */
1211 /** \name UV Map Common Transforms
1212 * \{ */
1213
1214 #define VIEW_ON_EQUATOR 0
1215 #define VIEW_ON_POLES 1
1216 #define ALIGN_TO_OBJECT 2
1217
1218 #define POLAR_ZX 0
1219 #define POLAR_ZY 1
1220
uv_map_transform_calc_bounds(BMEditMesh * em,float r_min[3],float r_max[3])1221 static void uv_map_transform_calc_bounds(BMEditMesh *em, float r_min[3], float r_max[3])
1222 {
1223 BMFace *efa;
1224 BMIter iter;
1225 INIT_MINMAX(r_min, r_max);
1226 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1227 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1228 BM_face_calc_bounds_expand(efa, r_min, r_max);
1229 }
1230 }
1231 }
1232
uv_map_transform_calc_center_median(BMEditMesh * em,float r_center[3])1233 static void uv_map_transform_calc_center_median(BMEditMesh *em, float r_center[3])
1234 {
1235 BMFace *efa;
1236 BMIter iter;
1237 uint center_accum_num = 0;
1238 zero_v3(r_center);
1239 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1240 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1241 float center[3];
1242 BM_face_calc_center_median(efa, center);
1243 add_v3_v3(r_center, center);
1244 center_accum_num += 1;
1245 }
1246 }
1247 mul_v3_fl(r_center, 1.0f / (float)center_accum_num);
1248 }
1249
uv_map_transform_center(const Scene * scene,View3D * v3d,Object * ob,BMEditMesh * em,float r_center[3],float r_bounds[2][3])1250 static void uv_map_transform_center(const Scene *scene,
1251 View3D *v3d,
1252 Object *ob,
1253 BMEditMesh *em,
1254 float r_center[3],
1255 float r_bounds[2][3])
1256 {
1257 /* only operates on the edit object - this is all that's needed now */
1258 const int around = (v3d) ? scene->toolsettings->transform_pivot_point : V3D_AROUND_CENTER_BOUNDS;
1259
1260 float bounds[2][3];
1261 INIT_MINMAX(bounds[0], bounds[1]);
1262 bool is_minmax_set = false;
1263
1264 switch (around) {
1265 case V3D_AROUND_CENTER_BOUNDS: /* bounding box center */
1266 {
1267 uv_map_transform_calc_bounds(em, bounds[0], bounds[1]);
1268 is_minmax_set = true;
1269 mid_v3_v3v3(r_center, bounds[0], bounds[1]);
1270 break;
1271 }
1272 case V3D_AROUND_CENTER_MEDIAN: {
1273 uv_map_transform_calc_center_median(em, r_center);
1274 break;
1275 }
1276 case V3D_AROUND_CURSOR: /* cursor center */
1277 {
1278 invert_m4_m4(ob->imat, ob->obmat);
1279 mul_v3_m4v3(r_center, ob->imat, scene->cursor.location);
1280 break;
1281 }
1282 case V3D_AROUND_ACTIVE: {
1283 BMEditSelection ese;
1284 if (BM_select_history_active_get(em->bm, &ese)) {
1285 BM_editselection_center(&ese, r_center);
1286 break;
1287 }
1288 ATTR_FALLTHROUGH;
1289 }
1290 case V3D_AROUND_LOCAL_ORIGINS: /* object center */
1291 default:
1292 zero_v3(r_center);
1293 break;
1294 }
1295
1296 /* if this is passed, always set! */
1297 if (r_bounds) {
1298 if (!is_minmax_set) {
1299 uv_map_transform_calc_bounds(em, bounds[0], bounds[1]);
1300 }
1301 copy_v3_v3(r_bounds[0], bounds[0]);
1302 copy_v3_v3(r_bounds[1], bounds[1]);
1303 }
1304 }
1305
uv_map_rotation_matrix_ex(float result[4][4],RegionView3D * rv3d,Object * ob,float upangledeg,float sideangledeg,float radius,const float offset[4])1306 static void uv_map_rotation_matrix_ex(float result[4][4],
1307 RegionView3D *rv3d,
1308 Object *ob,
1309 float upangledeg,
1310 float sideangledeg,
1311 float radius,
1312 const float offset[4])
1313 {
1314 float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
1315 float sideangle = 0.0f, upangle = 0.0f;
1316
1317 /* get rotation of the current view matrix */
1318 if (rv3d) {
1319 copy_m4_m4(viewmatrix, rv3d->viewmat);
1320 }
1321 else {
1322 unit_m4(viewmatrix);
1323 }
1324
1325 /* but shifting */
1326 zero_v3(viewmatrix[3]);
1327
1328 /* get rotation of the current object matrix */
1329 copy_m4_m4(rotobj, ob->obmat);
1330 zero_v3(rotobj[3]);
1331
1332 /* but shifting */
1333 add_v4_v4(rotobj[3], offset);
1334 rotobj[3][3] = 0.0f;
1335
1336 zero_m4(rotup);
1337 zero_m4(rotside);
1338
1339 /* compensate front/side.. against opengl x,y,z world definition */
1340 /* this is "kanonen gegen spatzen", a few plus minus 1 will do here */
1341 /* i wanted to keep the reason here, so we're rotating*/
1342 sideangle = (float)M_PI * (sideangledeg + 180.0f) / 180.0f;
1343 rotside[0][0] = cosf(sideangle);
1344 rotside[0][1] = -sinf(sideangle);
1345 rotside[1][0] = sinf(sideangle);
1346 rotside[1][1] = cosf(sideangle);
1347 rotside[2][2] = 1.0f;
1348
1349 upangle = (float)M_PI * upangledeg / 180.0f;
1350 rotup[1][1] = cosf(upangle) / radius;
1351 rotup[1][2] = -sinf(upangle) / radius;
1352 rotup[2][1] = sinf(upangle) / radius;
1353 rotup[2][2] = cosf(upangle) / radius;
1354 rotup[0][0] = 1.0f / radius;
1355
1356 /* calculate transforms*/
1357 mul_m4_series(result, rotup, rotside, viewmatrix, rotobj);
1358 }
1359
uv_map_rotation_matrix(float result[4][4],RegionView3D * rv3d,Object * ob,float upangledeg,float sideangledeg,float radius)1360 static void uv_map_rotation_matrix(float result[4][4],
1361 RegionView3D *rv3d,
1362 Object *ob,
1363 float upangledeg,
1364 float sideangledeg,
1365 float radius)
1366 {
1367 const float offset[4] = {0};
1368 uv_map_rotation_matrix_ex(result, rv3d, ob, upangledeg, sideangledeg, radius, offset);
1369 }
1370
uv_map_transform(bContext * C,wmOperator * op,float rotmat[4][4])1371 static void uv_map_transform(bContext *C, wmOperator *op, float rotmat[4][4])
1372 {
1373 /* context checks are messy here, making it work in both 3d view and uv editor */
1374 Object *obedit = CTX_data_edit_object(C);
1375 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1376 /* common operator properties */
1377 int align = RNA_enum_get(op->ptr, "align");
1378 int direction = RNA_enum_get(op->ptr, "direction");
1379 float radius = RNA_struct_find_property(op->ptr, "radius") ? RNA_float_get(op->ptr, "radius") :
1380 1.0f;
1381 float upangledeg, sideangledeg;
1382
1383 if (direction == VIEW_ON_EQUATOR) {
1384 upangledeg = 90.0f;
1385 sideangledeg = 0.0f;
1386 }
1387 else {
1388 upangledeg = 0.0f;
1389 if (align == POLAR_ZY) {
1390 sideangledeg = 0.0f;
1391 }
1392 else {
1393 sideangledeg = 90.0f;
1394 }
1395 }
1396
1397 /* be compatible to the "old" sphere/cylinder mode */
1398 if (direction == ALIGN_TO_OBJECT) {
1399 unit_m4(rotmat);
1400 }
1401 else {
1402 uv_map_rotation_matrix(rotmat, rv3d, obedit, upangledeg, sideangledeg, radius);
1403 }
1404 }
1405
uv_transform_properties(wmOperatorType * ot,int radius)1406 static void uv_transform_properties(wmOperatorType *ot, int radius)
1407 {
1408 static const EnumPropertyItem direction_items[] = {
1409 {VIEW_ON_EQUATOR, "VIEW_ON_EQUATOR", 0, "View on Equator", "3D view is on the equator"},
1410 {VIEW_ON_POLES, "VIEW_ON_POLES", 0, "View on Poles", "3D view is on the poles"},
1411 {ALIGN_TO_OBJECT,
1412 "ALIGN_TO_OBJECT",
1413 0,
1414 "Align to Object",
1415 "Align according to object transform"},
1416 {0, NULL, 0, NULL, NULL},
1417 };
1418 static const EnumPropertyItem align_items[] = {
1419 {POLAR_ZX, "POLAR_ZX", 0, "Polar ZX", "Polar 0 is X"},
1420 {POLAR_ZY, "POLAR_ZY", 0, "Polar ZY", "Polar 0 is Y"},
1421 {0, NULL, 0, NULL, NULL},
1422 };
1423
1424 RNA_def_enum(ot->srna,
1425 "direction",
1426 direction_items,
1427 VIEW_ON_EQUATOR,
1428 "Direction",
1429 "Direction of the sphere or cylinder");
1430 RNA_def_enum(ot->srna,
1431 "align",
1432 align_items,
1433 VIEW_ON_EQUATOR,
1434 "Align",
1435 "How to determine rotation around the pole");
1436 if (radius) {
1437 RNA_def_float(ot->srna,
1438 "radius",
1439 1.0f,
1440 0.0f,
1441 FLT_MAX,
1442 "Radius",
1443 "Radius of the sphere or cylinder",
1444 0.0001f,
1445 100.0f);
1446 }
1447 }
1448
correct_uv_aspect(Object * ob,BMEditMesh * em)1449 static void correct_uv_aspect(Object *ob, BMEditMesh *em)
1450 {
1451 BMLoop *l;
1452 BMIter iter, liter;
1453 MLoopUV *luv;
1454 BMFace *efa;
1455 float scale, aspx, aspy;
1456
1457 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1458
1459 ED_uvedit_get_aspect(ob, &aspx, &aspy);
1460
1461 if (aspx == aspy) {
1462 return;
1463 }
1464
1465 if (aspx > aspy) {
1466 scale = aspy / aspx;
1467
1468 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1469 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1470 continue;
1471 }
1472
1473 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1474 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1475 luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
1476 }
1477 }
1478 }
1479 else {
1480 scale = aspx / aspy;
1481
1482 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1483 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1484 continue;
1485 }
1486
1487 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1488 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1489 luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
1490 }
1491 }
1492 }
1493 }
1494
1495 #undef VIEW_ON_EQUATOR
1496 #undef VIEW_ON_POLES
1497 #undef ALIGN_TO_OBJECT
1498
1499 #undef POLAR_ZX
1500 #undef POLAR_ZY
1501
1502 /** \} */
1503
1504 /* -------------------------------------------------------------------- */
1505 /** \name UV Map Clip & Correct
1506 * \{ */
1507
uv_map_clip_correct_properties_ex(wmOperatorType * ot,bool clip_to_bounds)1508 static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds)
1509 {
1510 RNA_def_boolean(ot->srna,
1511 "correct_aspect",
1512 1,
1513 "Correct Aspect",
1514 "Map UVs taking image aspect ratio into account");
1515 /* Optional, since not all unwrapping types need to be clipped. */
1516 if (clip_to_bounds) {
1517 RNA_def_boolean(ot->srna,
1518 "clip_to_bounds",
1519 0,
1520 "Clip to Bounds",
1521 "Clip UV coordinates to bounds after unwrapping");
1522 }
1523 RNA_def_boolean(ot->srna,
1524 "scale_to_bounds",
1525 0,
1526 "Scale to Bounds",
1527 "Scale UV coordinates to bounds after unwrapping");
1528 }
1529
uv_map_clip_correct_properties(wmOperatorType * ot)1530 static void uv_map_clip_correct_properties(wmOperatorType *ot)
1531 {
1532 uv_map_clip_correct_properties_ex(ot, true);
1533 }
1534
uv_map_clip_correct_multi(Object ** objects,uint objects_len,wmOperator * op)1535 static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
1536 {
1537 BMFace *efa;
1538 BMLoop *l;
1539 BMIter iter, liter;
1540 MLoopUV *luv;
1541 float dx, dy, min[2], max[2];
1542 const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
1543 const bool clip_to_bounds = (RNA_struct_find_property(op->ptr, "clip_to_bounds") &&
1544 RNA_boolean_get(op->ptr, "clip_to_bounds"));
1545 const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds");
1546
1547 INIT_MINMAX2(min, max);
1548
1549 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1550 Object *ob = objects[ob_index];
1551
1552 BMEditMesh *em = BKE_editmesh_from_object(ob);
1553 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1554
1555 /* correct for image aspect ratio */
1556 if (correct_aspect) {
1557 correct_uv_aspect(ob, em);
1558 }
1559
1560 if (scale_to_bounds) {
1561 /* find uv limits */
1562 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1563 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1564 continue;
1565 }
1566
1567 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1568 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1569 minmax_v2v2_v2(min, max, luv->uv);
1570 }
1571 }
1572 }
1573 else if (clip_to_bounds) {
1574 /* clipping and wrapping */
1575 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1576 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1577 continue;
1578 }
1579
1580 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1581 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1582 clamp_v2(luv->uv, 0.0f, 1.0f);
1583 }
1584 }
1585 }
1586 }
1587
1588 if (scale_to_bounds) {
1589 /* rescale UV to be in 1/1 */
1590 dx = (max[0] - min[0]);
1591 dy = (max[1] - min[1]);
1592
1593 if (dx > 0.0f) {
1594 dx = 1.0f / dx;
1595 }
1596 if (dy > 0.0f) {
1597 dy = 1.0f / dy;
1598 }
1599
1600 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1601 Object *ob = objects[ob_index];
1602
1603 BMEditMesh *em = BKE_editmesh_from_object(ob);
1604 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1605
1606 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1607 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1608 continue;
1609 }
1610
1611 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1612 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1613
1614 luv->uv[0] = (luv->uv[0] - min[0]) * dx;
1615 luv->uv[1] = (luv->uv[1] - min[1]) * dy;
1616 }
1617 }
1618 }
1619 }
1620 }
1621
uv_map_clip_correct(Object * ob,wmOperator * op)1622 static void uv_map_clip_correct(Object *ob, wmOperator *op)
1623 {
1624 uv_map_clip_correct_multi(&ob, 1, op);
1625 }
1626
1627 /** \} */
1628
1629 /* -------------------------------------------------------------------- */
1630 /** \name UV Unwrap Operator
1631 * \{ */
1632
1633 /* Assumes UV Map exists, doesn't run update funcs. */
uvedit_unwrap(const Scene * scene,Object * obedit,const UnwrapOptions * options)1634 static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOptions *options)
1635 {
1636 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1637 if (!CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) {
1638 return;
1639 }
1640
1641 bool use_subsurf;
1642 modifier_unwrap_state(obedit, scene, &use_subsurf);
1643
1644 ParamHandle *handle;
1645 if (use_subsurf) {
1646 handle = construct_param_handle_subsurfed(scene, obedit, em, options);
1647 }
1648 else {
1649 handle = construct_param_handle(scene, obedit, em->bm, options);
1650 }
1651
1652 param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
1653 param_lscm_solve(handle);
1654 param_lscm_end(handle);
1655
1656 param_average(handle, true);
1657
1658 param_flush(handle);
1659
1660 param_delete(handle);
1661 }
1662
uvedit_unwrap_multi(const Scene * scene,Object ** objects,const int objects_len,const UnwrapOptions * options)1663 static void uvedit_unwrap_multi(const Scene *scene,
1664 Object **objects,
1665 const int objects_len,
1666 const UnwrapOptions *options)
1667 {
1668 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1669 Object *obedit = objects[ob_index];
1670 uvedit_unwrap(scene, obedit, options);
1671 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
1672 WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
1673 }
1674 }
1675
ED_uvedit_live_unwrap(const Scene * scene,Object ** objects,int objects_len)1676 void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len)
1677 {
1678 if (scene->toolsettings->edge_mode_live_unwrap) {
1679 const UnwrapOptions options = {
1680 .topology_from_uvs = false,
1681 .only_selected_faces = false,
1682 .only_selected_uvs = true,
1683 .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0,
1684 .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0,
1685 };
1686
1687 bool rotate = true;
1688 bool ignore_pinned = true;
1689
1690 uvedit_unwrap_multi(scene, objects, objects_len, &options);
1691 uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned);
1692 }
1693 }
1694
1695 enum {
1696 UNWRAP_ERROR_NONUNIFORM = (1 << 0),
1697 UNWRAP_ERROR_NEGATIVE = (1 << 1),
1698 };
1699
unwrap_exec(bContext * C,wmOperator * op)1700 static int unwrap_exec(bContext *C, wmOperator *op)
1701 {
1702 ViewLayer *view_layer = CTX_data_view_layer(C);
1703 const Scene *scene = CTX_data_scene(C);
1704 int method = RNA_enum_get(op->ptr, "method");
1705 const bool use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data");
1706 int reported_errors = 0;
1707 /* We will report an error unless at least one object
1708 * has the subsurf modifier in the right place. */
1709 bool subsurf_error = use_subsurf;
1710
1711 uint objects_len = 0;
1712 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
1713 view_layer, CTX_wm_view3d(C), &objects_len);
1714
1715 const UnwrapOptions options = {
1716 .topology_from_uvs = false,
1717 .only_selected_faces = true,
1718 .only_selected_uvs = true,
1719 .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"),
1720 .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"),
1721 };
1722
1723 bool rotate = true;
1724 bool ignore_pinned = true;
1725
1726 if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
1727 MEM_freeN(objects);
1728 return OPERATOR_CANCELLED;
1729 }
1730
1731 /* add uvs if they don't exist yet */
1732 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1733 Object *obedit = objects[ob_index];
1734 float obsize[3];
1735 bool use_subsurf_final;
1736
1737 if (!ED_uvedit_ensure_uvs(obedit)) {
1738 continue;
1739 }
1740
1741 if (subsurf_error) {
1742 /* Double up the check here but better keep uvedit_unwrap interface simple and not
1743 * pass operator for warning append. */
1744 modifier_unwrap_state(obedit, scene, &use_subsurf_final);
1745 if (use_subsurf_final) {
1746 subsurf_error = false;
1747 }
1748 }
1749
1750 if (reported_errors & (UNWRAP_ERROR_NONUNIFORM | UNWRAP_ERROR_NEGATIVE)) {
1751 continue;
1752 }
1753
1754 mat4_to_size(obsize, obedit->obmat);
1755 if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f)) {
1756 if ((reported_errors & UNWRAP_ERROR_NONUNIFORM) == 0) {
1757 BKE_report(op->reports,
1758 RPT_INFO,
1759 "Object has non-uniform scale, unwrap will operate on a non-scaled version of "
1760 "the mesh");
1761 reported_errors |= UNWRAP_ERROR_NONUNIFORM;
1762 }
1763 }
1764 else if (is_negative_m4(obedit->obmat)) {
1765 if ((reported_errors & UNWRAP_ERROR_NEGATIVE) == 0) {
1766 BKE_report(
1767 op->reports,
1768 RPT_INFO,
1769 "Object has negative scale, unwrap will operate on a non-flipped version of the mesh");
1770 reported_errors |= UNWRAP_ERROR_NEGATIVE;
1771 }
1772 }
1773 }
1774
1775 if (subsurf_error) {
1776 BKE_report(op->reports,
1777 RPT_INFO,
1778 "Subdivision Surface modifier needs to be first to work with unwrap");
1779 }
1780
1781 /* remember last method for live unwrap */
1782 if (RNA_struct_property_is_set(op->ptr, "method")) {
1783 scene->toolsettings->unwrapper = method;
1784 }
1785 else {
1786 RNA_enum_set(op->ptr, "method", scene->toolsettings->unwrapper);
1787 }
1788
1789 /* remember packing margin */
1790 if (RNA_struct_property_is_set(op->ptr, "margin")) {
1791 scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
1792 }
1793 else {
1794 RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
1795 }
1796
1797 if (options.fill_holes) {
1798 scene->toolsettings->uvcalc_flag |= UVCALC_FILLHOLES;
1799 }
1800 else {
1801 scene->toolsettings->uvcalc_flag &= ~UVCALC_FILLHOLES;
1802 }
1803
1804 if (options.correct_aspect) {
1805 scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT;
1806 }
1807 else {
1808 scene->toolsettings->uvcalc_flag |= UVCALC_NO_ASPECT_CORRECT;
1809 }
1810
1811 if (use_subsurf) {
1812 scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF;
1813 }
1814 else {
1815 scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF;
1816 }
1817
1818 /* execute unwrap */
1819 uvedit_unwrap_multi(scene, objects, objects_len, &options);
1820 uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned);
1821
1822 MEM_freeN(objects);
1823
1824 return OPERATOR_FINISHED;
1825 }
1826
UV_OT_unwrap(wmOperatorType * ot)1827 void UV_OT_unwrap(wmOperatorType *ot)
1828 {
1829 static const EnumPropertyItem method_items[] = {
1830 {0, "ANGLE_BASED", 0, "Angle Based", ""},
1831 {1, "CONFORMAL", 0, "Conformal", ""},
1832 {0, NULL, 0, NULL, NULL},
1833 };
1834
1835 /* identifiers */
1836 ot->name = "Unwrap";
1837 ot->description = "Unwrap the mesh of the object being edited";
1838 ot->idname = "UV_OT_unwrap";
1839 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1840
1841 /* api callbacks */
1842 ot->exec = unwrap_exec;
1843 ot->poll = ED_operator_uvmap;
1844
1845 /* properties */
1846 RNA_def_enum(ot->srna,
1847 "method",
1848 method_items,
1849 0,
1850 "Method",
1851 "Unwrapping method (Angle Based usually gives better results than Conformal, while "
1852 "being somewhat slower)");
1853 RNA_def_boolean(ot->srna,
1854 "fill_holes",
1855 1,
1856 "Fill Holes",
1857 "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and "
1858 "preserve symmetry");
1859 RNA_def_boolean(ot->srna,
1860 "correct_aspect",
1861 1,
1862 "Correct Aspect",
1863 "Map UVs taking image aspect ratio into account");
1864 RNA_def_boolean(
1865 ot->srna,
1866 "use_subsurf_data",
1867 0,
1868 "Use Subdivision Surface",
1869 "Map UVs taking vertex position after Subdivision Surface modifier has been applied");
1870 RNA_def_float_factor(
1871 ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
1872 }
1873
1874 /** \} */
1875
1876 /* -------------------------------------------------------------------- */
1877 /** \name Smart UV Project Operator
1878 * \{ */
1879
1880 /* Ignore all areas below this, as the UV's get zeroed. */
1881 static const float smart_uv_project_area_ignore = 1e-12f;
1882
1883 typedef struct ThickFace {
1884 float area;
1885 BMFace *efa;
1886 } ThickFace;
1887
smart_uv_project_thickface_area_cmp_fn(const void * tf_a_p,const void * tf_b_p)1888 static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p)
1889 {
1890
1891 const ThickFace *tf_a = (ThickFace *)tf_a_p;
1892 const ThickFace *tf_b = (ThickFace *)tf_b_p;
1893
1894 /* Ignore the area of small faces.
1895 * Also, order checks so `!isfinite(...)` values are counted as zero area. */
1896 if (!((tf_a->area > smart_uv_project_area_ignore) ||
1897 (tf_b->area > smart_uv_project_area_ignore))) {
1898 return 0;
1899 }
1900
1901 if (tf_a->area < tf_b->area) {
1902 return 1;
1903 }
1904 if (tf_a->area > tf_b->area) {
1905 return -1;
1906 }
1907 return 0;
1908 }
1909
smart_uv_project_calculate_project_normals(const ThickFace * thick_faces,const uint thick_faces_len,BMesh * bm,const float project_angle_limit_half_cos,const float project_angle_limit_cos,const float area_weight,float (** r_project_normal_array)[3])1910 static uint smart_uv_project_calculate_project_normals(const ThickFace *thick_faces,
1911 const uint thick_faces_len,
1912 BMesh *bm,
1913 const float project_angle_limit_half_cos,
1914 const float project_angle_limit_cos,
1915 const float area_weight,
1916 float (**r_project_normal_array)[3])
1917 {
1918 if (UNLIKELY(thick_faces_len == 0)) {
1919 *r_project_normal_array = NULL;
1920 return 0;
1921 }
1922
1923 const float *project_normal = thick_faces[0].efa->no;
1924
1925 const ThickFace **project_thick_faces = NULL;
1926 BLI_array_declare(project_thick_faces);
1927
1928 float(*project_normal_array)[3] = NULL;
1929 BLI_array_declare(project_normal_array);
1930
1931 BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
1932
1933 while (true) {
1934 for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
1935 if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) {
1936 continue;
1937 }
1938
1939 if (dot_v3v3(thick_faces[f_index].efa->no, project_normal) > project_angle_limit_half_cos) {
1940 BLI_array_append(project_thick_faces, &thick_faces[f_index]);
1941 BM_elem_flag_set(thick_faces[f_index].efa, BM_ELEM_TAG, true);
1942 }
1943 }
1944
1945 float average_normal[3] = {0.0f, 0.0f, 0.0f};
1946
1947 if (area_weight <= 0.0f) {
1948 for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces);
1949 f_proj_index++) {
1950 const ThickFace *tf = project_thick_faces[f_proj_index];
1951 add_v3_v3(average_normal, tf->efa->no);
1952 }
1953 }
1954 else if (area_weight >= 1.0f) {
1955 for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces);
1956 f_proj_index++) {
1957 const ThickFace *tf = project_thick_faces[f_proj_index];
1958 madd_v3_v3fl(average_normal, tf->efa->no, tf->area);
1959 }
1960 }
1961 else {
1962 for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces);
1963 f_proj_index++) {
1964 const ThickFace *tf = project_thick_faces[f_proj_index];
1965 const float area_blend = (tf->area * area_weight) + (1.0f - area_weight);
1966 madd_v3_v3fl(average_normal, tf->efa->no, area_blend);
1967 }
1968 }
1969
1970 /* Avoid NAN. */
1971 if (normalize_v3(average_normal) != 0.0f) {
1972 float(*normal)[3] = BLI_array_append_ret(project_normal_array);
1973 copy_v3_v3(*normal, average_normal);
1974 }
1975
1976 /* Find the most unique angle that points away from other normals. */
1977 float anble_best = 1.0f;
1978 uint angle_best_index = 0;
1979
1980 for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
1981 if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) {
1982 continue;
1983 }
1984
1985 float angle_test = -1.0f;
1986 for (int p_index = 0; p_index < BLI_array_len(project_normal_array); p_index++) {
1987 angle_test = max_ff(angle_test,
1988 dot_v3v3(project_normal_array[p_index], thick_faces[f_index].efa->no));
1989 }
1990
1991 if (angle_test < anble_best) {
1992 anble_best = angle_test;
1993 angle_best_index = f_index;
1994 }
1995 }
1996
1997 if (anble_best < project_angle_limit_cos) {
1998 project_normal = thick_faces[angle_best_index].efa->no;
1999 BLI_array_clear(project_thick_faces);
2000 BLI_array_append(project_thick_faces, &thick_faces[angle_best_index]);
2001 BM_elem_flag_enable(thick_faces[angle_best_index].efa, BM_ELEM_TAG);
2002 }
2003 else {
2004 if (BLI_array_len(project_normal_array) >= 1) {
2005 break;
2006 }
2007 }
2008 }
2009
2010 BLI_array_free(project_thick_faces);
2011 BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
2012
2013 *r_project_normal_array = project_normal_array;
2014 return BLI_array_len(project_normal_array);
2015 }
2016
smart_project_exec(bContext * C,wmOperator * op)2017 static int smart_project_exec(bContext *C, wmOperator *op)
2018 {
2019 Scene *scene = CTX_data_scene(C);
2020 ViewLayer *view_layer = CTX_data_view_layer(C);
2021
2022 /* May be NULL. */
2023 View3D *v3d = CTX_wm_view3d(C);
2024
2025 const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit");
2026 const float island_margin = RNA_float_get(op->ptr, "island_margin");
2027 const float area_weight = RNA_float_get(op->ptr, "area_weight");
2028
2029 const float project_angle_limit_cos = cosf(project_angle_limit);
2030 const float project_angle_limit_half_cos = cosf(project_angle_limit / 2);
2031
2032 /* Memory arena for list links (cleared for each object). */
2033 MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
2034
2035 uint objects_len = 0;
2036 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
2037 view_layer, v3d, &objects_len);
2038
2039 Object **objects_changed = MEM_mallocN(sizeof(*objects_changed) * objects_len, __func__);
2040 uint object_changed_len = 0;
2041
2042 BMFace *efa;
2043 BMIter iter;
2044 uint ob_index;
2045
2046 for (ob_index = 0; ob_index < objects_len; ob_index++) {
2047 Object *obedit = objects[ob_index];
2048 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2049 bool changed = false;
2050
2051 if (!ED_uvedit_ensure_uvs(obedit)) {
2052 continue;
2053 }
2054
2055 const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2056 ThickFace *thick_faces = MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__);
2057
2058 uint thick_faces_len = 0;
2059 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2060 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2061 continue;
2062 }
2063 thick_faces[thick_faces_len].area = BM_face_calc_area(efa);
2064 thick_faces[thick_faces_len].efa = efa;
2065 thick_faces_len++;
2066 }
2067
2068 qsort(thick_faces, thick_faces_len, sizeof(ThickFace), smart_uv_project_thickface_area_cmp_fn);
2069
2070 /* Remove all zero area faces. */
2071 while ((thick_faces_len > 0) &&
2072 !(thick_faces[thick_faces_len - 1].area > smart_uv_project_area_ignore)) {
2073
2074 /* Zero UV's so they don't overlap with other faces being unwrapped. */
2075 BMIter liter;
2076 BMLoop *l;
2077 BM_ITER_ELEM (l, &liter, thick_faces[thick_faces_len - 1].efa, BM_LOOPS_OF_FACE) {
2078 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2079 zero_v2(luv->uv);
2080 changed = true;
2081 }
2082
2083 thick_faces_len -= 1;
2084 }
2085
2086 float(*project_normal_array)[3] = NULL;
2087 int project_normals_len = smart_uv_project_calculate_project_normals(
2088 thick_faces,
2089 thick_faces_len,
2090 em->bm,
2091 project_angle_limit_half_cos,
2092 project_angle_limit_cos,
2093 area_weight,
2094 &project_normal_array);
2095
2096 if (project_normals_len == 0) {
2097 MEM_freeN(thick_faces);
2098 BLI_assert(project_normal_array == NULL);
2099 continue;
2100 }
2101
2102 /* After finding projection vectors, we find the uv positions. */
2103 LinkNode **thickface_project_groups = MEM_callocN(
2104 sizeof(*thickface_project_groups) * project_normals_len, __func__);
2105
2106 BLI_memarena_clear(arena);
2107
2108 for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
2109 const float *f_normal = thick_faces[f_index].efa->no;
2110
2111 float angle_best = dot_v3v3(f_normal, project_normal_array[0]);
2112 uint angle_best_index = 0;
2113
2114 for (int p_index = 1; p_index < project_normals_len; p_index++) {
2115 const float angle_test = dot_v3v3(f_normal, project_normal_array[p_index]);
2116 if (angle_test > angle_best) {
2117 angle_best = angle_test;
2118 angle_best_index = p_index;
2119 }
2120 }
2121
2122 BLI_linklist_prepend_arena(
2123 &thickface_project_groups[angle_best_index], &thick_faces[f_index], arena);
2124 }
2125
2126 for (int p_index = 0; p_index < project_normals_len; p_index++) {
2127 if (thickface_project_groups[p_index] == NULL) {
2128 continue;
2129 }
2130
2131 float axis_mat[3][3];
2132 axis_dominant_v3_to_m3_negate(axis_mat, project_normal_array[p_index]);
2133
2134 for (LinkNode *list = thickface_project_groups[p_index]; list; list = list->next) {
2135 ThickFace *tf = list->link;
2136 BMIter liter;
2137 BMLoop *l;
2138 BM_ITER_ELEM (l, &liter, tf->efa, BM_LOOPS_OF_FACE) {
2139 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2140 mul_v2_m3v3(luv->uv, axis_mat, l->v->co);
2141 }
2142 changed = true;
2143 }
2144 }
2145
2146 MEM_freeN(thick_faces);
2147 MEM_freeN(project_normal_array);
2148
2149 /* No need to free the lists in 'thickface_project_groups' values as the 'arena' is used. */
2150 MEM_freeN(thickface_project_groups);
2151
2152 if (changed) {
2153 objects_changed[object_changed_len] = objects[ob_index];
2154 object_changed_len += 1;
2155 }
2156 }
2157
2158 BLI_memarena_free(arena);
2159
2160 MEM_freeN(objects);
2161
2162 /* Pack islands & Stretch to UV bounds */
2163 if (object_changed_len > 0) {
2164
2165 scene->toolsettings->uvcalc_margin = island_margin;
2166
2167 /* Depsgraph refresh functions are called here. */
2168 ED_uvedit_pack_islands_multi(scene,
2169 objects_changed,
2170 object_changed_len,
2171 &(struct UVPackIsland_Params){
2172 .rotate = true,
2173 /* We could make this optional. */
2174 .rotate_align_axis = 1,
2175 .only_selected_faces = true,
2176 .correct_aspect = true,
2177 .use_seams = true,
2178 });
2179
2180 uv_map_clip_correct_multi(objects_changed, object_changed_len, op);
2181 }
2182
2183 MEM_freeN(objects_changed);
2184
2185 return OPERATOR_FINISHED;
2186 }
2187
UV_OT_smart_project(wmOperatorType * ot)2188 void UV_OT_smart_project(wmOperatorType *ot)
2189 {
2190 PropertyRNA *prop;
2191
2192 /* identifiers */
2193 ot->name = "Smart UV Project";
2194 ot->idname = "UV_OT_smart_project";
2195 ot->description = "Projection unwraps the selected faces of mesh objects";
2196
2197 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2198
2199 /* api callbacks */
2200 ot->exec = smart_project_exec;
2201 ot->poll = ED_operator_uvmap;
2202 ot->invoke = WM_operator_props_popup_confirm;
2203
2204 /* properties */
2205 prop = RNA_def_float_rotation(ot->srna,
2206 "angle_limit",
2207 0,
2208 NULL,
2209 DEG2RADF(0.0f),
2210 DEG2RADF(90.0f),
2211 "Angle Limit",
2212 "Lower for more projection groups, higher for less distortion",
2213 DEG2RADF(0.0f),
2214 DEG2RADF(89.0f));
2215 RNA_def_property_float_default(prop, DEG2RADF(66.0f));
2216
2217 RNA_def_float(ot->srna,
2218 "island_margin",
2219 0.0f,
2220 0.0f,
2221 1.0f,
2222 "Island Margin",
2223 "Margin to reduce bleed from adjacent islands",
2224 0.0f,
2225 1.0f);
2226 RNA_def_float(ot->srna,
2227 "area_weight",
2228 0.0f,
2229 0.0f,
2230 1.0f,
2231 "Area Weight",
2232 "Weight projections vector by faces with larger areas",
2233 0.0f,
2234 1.0f);
2235
2236 uv_map_clip_correct_properties_ex(ot, false);
2237 }
2238
2239 /** \} */
2240
2241 /* -------------------------------------------------------------------- */
2242 /** \name Project UV From View Operator
2243 * \{ */
2244
2245 static int uv_from_view_exec(bContext *C, wmOperator *op);
2246
uv_from_view_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))2247 static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2248 {
2249 View3D *v3d = CTX_wm_view3d(C);
2250 RegionView3D *rv3d = CTX_wm_region_view3d(C);
2251 Camera *camera = ED_view3d_camera_data_get(v3d, rv3d);
2252 PropertyRNA *prop;
2253
2254 prop = RNA_struct_find_property(op->ptr, "camera_bounds");
2255 if (!RNA_property_is_set(op->ptr, prop)) {
2256 RNA_property_boolean_set(op->ptr, prop, (camera != NULL));
2257 }
2258 prop = RNA_struct_find_property(op->ptr, "correct_aspect");
2259 if (!RNA_property_is_set(op->ptr, prop)) {
2260 RNA_property_boolean_set(op->ptr, prop, (camera == NULL));
2261 }
2262
2263 return uv_from_view_exec(C, op);
2264 }
2265
uv_from_view_exec(bContext * C,wmOperator * op)2266 static int uv_from_view_exec(bContext *C, wmOperator *op)
2267 {
2268 ViewLayer *view_layer = CTX_data_view_layer(C);
2269 const Scene *scene = CTX_data_scene(C);
2270 ARegion *region = CTX_wm_region(C);
2271 View3D *v3d = CTX_wm_view3d(C);
2272 RegionView3D *rv3d = CTX_wm_region_view3d(C);
2273 Camera *camera = ED_view3d_camera_data_get(v3d, rv3d);
2274 BMFace *efa;
2275 BMLoop *l;
2276 BMIter iter, liter;
2277 MLoopUV *luv;
2278 float rotmat[4][4];
2279 float objects_pos_offset[4];
2280 bool changed_multi = false;
2281
2282 const bool use_orthographic = RNA_boolean_get(op->ptr, "orthographic");
2283
2284 /* Note: objects that aren't touched are set to NULL (to skip clipping). */
2285 uint objects_len = 0;
2286 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
2287 view_layer, v3d, &objects_len);
2288
2289 if (use_orthographic) {
2290 /* Calculate average object position. */
2291 float objects_pos_avg[4] = {0};
2292
2293 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2294 add_v4_v4(objects_pos_avg, objects[ob_index]->obmat[3]);
2295 }
2296
2297 mul_v4_fl(objects_pos_avg, 1.0f / objects_len);
2298 negate_v4_v4(objects_pos_offset, objects_pos_avg);
2299 }
2300
2301 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2302 Object *obedit = objects[ob_index];
2303 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2304 bool changed = false;
2305
2306 /* add uvs if they don't exist yet */
2307 if (!ED_uvedit_ensure_uvs(obedit)) {
2308 continue;
2309 }
2310
2311 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2312
2313 if (use_orthographic) {
2314 uv_map_rotation_matrix_ex(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f, objects_pos_offset);
2315
2316 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2317 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2318 continue;
2319 }
2320
2321 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2322 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2323 BLI_uvproject_from_view_ortho(luv->uv, l->v->co, rotmat);
2324 }
2325 changed = true;
2326 }
2327 }
2328 else if (camera) {
2329 const bool camera_bounds = RNA_boolean_get(op->ptr, "camera_bounds");
2330 struct ProjCameraInfo *uci = BLI_uvproject_camera_info(
2331 v3d->camera,
2332 obedit->obmat,
2333 camera_bounds ? (scene->r.xsch * scene->r.xasp) : 1.0f,
2334 camera_bounds ? (scene->r.ysch * scene->r.yasp) : 1.0f);
2335
2336 if (uci) {
2337 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2338 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2339 continue;
2340 }
2341
2342 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2343 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2344 BLI_uvproject_from_camera(luv->uv, l->v->co, uci);
2345 }
2346 changed = true;
2347 }
2348
2349 MEM_freeN(uci);
2350 }
2351 }
2352 else {
2353 copy_m4_m4(rotmat, obedit->obmat);
2354
2355 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2356 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2357 continue;
2358 }
2359
2360 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2361 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2362 BLI_uvproject_from_view(
2363 luv->uv, l->v->co, rv3d->persmat, rotmat, region->winx, region->winy);
2364 }
2365 changed = true;
2366 }
2367 }
2368
2369 if (changed) {
2370 changed_multi = true;
2371 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
2372 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2373 }
2374 else {
2375 ARRAY_DELETE_REORDER_LAST(objects, ob_index, 1, objects_len);
2376 objects_len -= 1;
2377 ob_index -= 1;
2378 }
2379 }
2380
2381 if (changed_multi) {
2382 uv_map_clip_correct_multi(objects, objects_len, op);
2383 }
2384
2385 MEM_freeN(objects);
2386
2387 if (changed_multi) {
2388 return OPERATOR_FINISHED;
2389 }
2390 return OPERATOR_CANCELLED;
2391 }
2392
uv_from_view_poll(bContext * C)2393 static bool uv_from_view_poll(bContext *C)
2394 {
2395 RegionView3D *rv3d = CTX_wm_region_view3d(C);
2396
2397 if (!ED_operator_uvmap(C)) {
2398 return 0;
2399 }
2400
2401 return (rv3d != NULL);
2402 }
2403
UV_OT_project_from_view(wmOperatorType * ot)2404 void UV_OT_project_from_view(wmOperatorType *ot)
2405 {
2406 /* identifiers */
2407 ot->name = "Project from View";
2408 ot->idname = "UV_OT_project_from_view";
2409 ot->description = "Project the UV vertices of the mesh as seen in current 3D view";
2410
2411 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2412
2413 /* api callbacks */
2414 ot->invoke = uv_from_view_invoke;
2415 ot->exec = uv_from_view_exec;
2416 ot->poll = uv_from_view_poll;
2417
2418 /* properties */
2419 RNA_def_boolean(ot->srna, "orthographic", 0, "Orthographic", "Use orthographic projection");
2420 RNA_def_boolean(ot->srna,
2421 "camera_bounds",
2422 1,
2423 "Camera Bounds",
2424 "Map UVs to the camera region taking resolution and aspect into account");
2425 uv_map_clip_correct_properties(ot);
2426 }
2427
2428 /** \} */
2429
2430 /* -------------------------------------------------------------------- */
2431 /** \name Reset UV Operator
2432 * \{ */
2433
reset_exec(bContext * C,wmOperator * UNUSED (op))2434 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
2435 {
2436 ViewLayer *view_layer = CTX_data_view_layer(C);
2437 View3D *v3d = CTX_wm_view3d(C);
2438
2439 uint objects_len = 0;
2440 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
2441 view_layer, v3d, &objects_len);
2442 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2443 Object *obedit = objects[ob_index];
2444 Mesh *me = (Mesh *)obedit->data;
2445 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2446
2447 if (em->bm->totfacesel == 0) {
2448 continue;
2449 }
2450
2451 /* add uvs if they don't exist yet */
2452 if (!ED_uvedit_ensure_uvs(obedit)) {
2453 continue;
2454 }
2455
2456 ED_mesh_uv_loop_reset(C, me);
2457
2458 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
2459 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2460 }
2461 MEM_freeN(objects);
2462
2463 return OPERATOR_FINISHED;
2464 }
2465
UV_OT_reset(wmOperatorType * ot)2466 void UV_OT_reset(wmOperatorType *ot)
2467 {
2468 /* identifiers */
2469 ot->name = "Reset";
2470 ot->idname = "UV_OT_reset";
2471 ot->description = "Reset UV projection";
2472
2473 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2474
2475 /* api callbacks */
2476 ot->exec = reset_exec;
2477 ot->poll = ED_operator_uvmap;
2478 }
2479
2480 /** \} */
2481
2482 /* -------------------------------------------------------------------- */
2483 /** \name Sphere UV Project Operator
2484 * \{ */
2485
uv_sphere_project(float target[2],const float source[3],const float center[3],const float rotmat[4][4])2486 static void uv_sphere_project(float target[2],
2487 const float source[3],
2488 const float center[3],
2489 const float rotmat[4][4])
2490 {
2491 float pv[3];
2492
2493 sub_v3_v3v3(pv, source, center);
2494 mul_m4_v3(rotmat, pv);
2495
2496 map_to_sphere(&target[0], &target[1], pv[0], pv[1], pv[2]);
2497
2498 /* split line is always zero */
2499 if (target[0] >= 1.0f) {
2500 target[0] -= 1.0f;
2501 }
2502 }
2503
uv_map_mirror(BMEditMesh * em,BMFace * efa)2504 static void uv_map_mirror(BMEditMesh *em, BMFace *efa)
2505 {
2506 BMLoop *l;
2507 BMIter liter;
2508 MLoopUV *luv;
2509 float **uvs = BLI_array_alloca(uvs, efa->len);
2510 float dx;
2511 int i, mi;
2512
2513 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2514
2515 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
2516 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2517 uvs[i] = luv->uv;
2518 }
2519
2520 mi = 0;
2521 for (i = 1; i < efa->len; i++) {
2522 if (uvs[i][0] > uvs[mi][0]) {
2523 mi = i;
2524 }
2525 }
2526
2527 for (i = 0; i < efa->len; i++) {
2528 if (i != mi) {
2529 dx = uvs[mi][0] - uvs[i][0];
2530 if (dx > 0.5f) {
2531 uvs[i][0] += 1.0f;
2532 }
2533 }
2534 }
2535 }
2536
sphere_project_exec(bContext * C,wmOperator * op)2537 static int sphere_project_exec(bContext *C, wmOperator *op)
2538 {
2539 const Scene *scene = CTX_data_scene(C);
2540 View3D *v3d = CTX_wm_view3d(C);
2541
2542 ViewLayer *view_layer = CTX_data_view_layer(C);
2543 uint objects_len = 0;
2544 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
2545 view_layer, v3d, &objects_len);
2546 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2547 Object *obedit = objects[ob_index];
2548 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2549 BMFace *efa;
2550 BMLoop *l;
2551 BMIter iter, liter;
2552 MLoopUV *luv;
2553
2554 if (em->bm->totfacesel == 0) {
2555 continue;
2556 }
2557
2558 /* add uvs if they don't exist yet */
2559 if (!ED_uvedit_ensure_uvs(obedit)) {
2560 continue;
2561 }
2562
2563 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2564 float center[3], rotmat[4][4];
2565
2566 uv_map_transform(C, op, rotmat);
2567 uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
2568
2569 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2570 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2571 continue;
2572 }
2573
2574 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2575 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2576
2577 uv_sphere_project(luv->uv, l->v->co, center, rotmat);
2578 }
2579
2580 uv_map_mirror(em, efa);
2581 }
2582
2583 uv_map_clip_correct(obedit, op);
2584
2585 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
2586 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2587 }
2588 MEM_freeN(objects);
2589
2590 return OPERATOR_FINISHED;
2591 }
2592
UV_OT_sphere_project(wmOperatorType * ot)2593 void UV_OT_sphere_project(wmOperatorType *ot)
2594 {
2595 /* identifiers */
2596 ot->name = "Sphere Projection";
2597 ot->idname = "UV_OT_sphere_project";
2598 ot->description = "Project the UV vertices of the mesh over the curved surface of a sphere";
2599
2600 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2601
2602 /* api callbacks */
2603 ot->exec = sphere_project_exec;
2604 ot->poll = ED_operator_uvmap;
2605
2606 /* properties */
2607 uv_transform_properties(ot, 0);
2608 uv_map_clip_correct_properties(ot);
2609 }
2610
2611 /** \} */
2612
2613 /* -------------------------------------------------------------------- */
2614 /** \name Cylinder UV Project Operator
2615 * \{ */
2616
uv_cylinder_project(float target[2],const float source[3],const float center[3],const float rotmat[4][4])2617 static void uv_cylinder_project(float target[2],
2618 const float source[3],
2619 const float center[3],
2620 const float rotmat[4][4])
2621 {
2622 float pv[3];
2623
2624 sub_v3_v3v3(pv, source, center);
2625 mul_m4_v3(rotmat, pv);
2626
2627 map_to_tube(&target[0], &target[1], pv[0], pv[1], pv[2]);
2628
2629 /* split line is always zero */
2630 if (target[0] >= 1.0f) {
2631 target[0] -= 1.0f;
2632 }
2633 }
2634
cylinder_project_exec(bContext * C,wmOperator * op)2635 static int cylinder_project_exec(bContext *C, wmOperator *op)
2636 {
2637 const Scene *scene = CTX_data_scene(C);
2638 View3D *v3d = CTX_wm_view3d(C);
2639
2640 ViewLayer *view_layer = CTX_data_view_layer(C);
2641 uint objects_len = 0;
2642 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
2643 view_layer, v3d, &objects_len);
2644 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2645 Object *obedit = objects[ob_index];
2646 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2647 BMFace *efa;
2648 BMLoop *l;
2649 BMIter iter, liter;
2650 MLoopUV *luv;
2651
2652 if (em->bm->totfacesel == 0) {
2653 continue;
2654 }
2655
2656 /* add uvs if they don't exist yet */
2657 if (!ED_uvedit_ensure_uvs(obedit)) {
2658 continue;
2659 }
2660
2661 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2662 float center[3], rotmat[4][4];
2663
2664 uv_map_transform(C, op, rotmat);
2665 uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
2666
2667 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2668 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2669 continue;
2670 }
2671
2672 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2673 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2674
2675 uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
2676 }
2677
2678 uv_map_mirror(em, efa);
2679 }
2680
2681 uv_map_clip_correct(obedit, op);
2682
2683 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
2684 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2685 }
2686 MEM_freeN(objects);
2687
2688 return OPERATOR_FINISHED;
2689 }
2690
UV_OT_cylinder_project(wmOperatorType * ot)2691 void UV_OT_cylinder_project(wmOperatorType *ot)
2692 {
2693 /* identifiers */
2694 ot->name = "Cylinder Projection";
2695 ot->idname = "UV_OT_cylinder_project";
2696 ot->description = "Project the UV vertices of the mesh over the curved wall of a cylinder";
2697
2698 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2699
2700 /* api callbacks */
2701 ot->exec = cylinder_project_exec;
2702 ot->poll = ED_operator_uvmap;
2703
2704 /* properties */
2705 uv_transform_properties(ot, 1);
2706 uv_map_clip_correct_properties(ot);
2707 }
2708
2709 /* -------------------------------------------------------------------- */
2710 /** \name Cube UV Project Operator
2711 * \{ */
2712
uvedit_unwrap_cube_project(BMesh * bm,float cube_size,bool use_select,const float center[3])2713 static void uvedit_unwrap_cube_project(BMesh *bm,
2714 float cube_size,
2715 bool use_select,
2716 const float center[3])
2717 {
2718 BMFace *efa;
2719 BMLoop *l;
2720 BMIter iter, liter;
2721 MLoopUV *luv;
2722 float loc[3];
2723 int cox, coy;
2724
2725 int cd_loop_uv_offset;
2726
2727 cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
2728
2729 if (center) {
2730 copy_v3_v3(loc, center);
2731 }
2732 else {
2733 zero_v3(loc);
2734 }
2735
2736 /* choose x,y,z axis for projection depending on the largest normal
2737 * component, but clusters all together around the center of map. */
2738
2739 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2740 if (use_select && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2741 continue;
2742 }
2743
2744 axis_dominant_v3(&cox, &coy, efa->no);
2745
2746 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2747 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2748 luv->uv[0] = 0.5f + 0.5f * cube_size * (l->v->co[cox] - loc[cox]);
2749 luv->uv[1] = 0.5f + 0.5f * cube_size * (l->v->co[coy] - loc[coy]);
2750 }
2751 }
2752 }
2753
cube_project_exec(bContext * C,wmOperator * op)2754 static int cube_project_exec(bContext *C, wmOperator *op)
2755 {
2756 const Scene *scene = CTX_data_scene(C);
2757 View3D *v3d = CTX_wm_view3d(C);
2758
2759 PropertyRNA *prop_cube_size = RNA_struct_find_property(op->ptr, "cube_size");
2760 const float cube_size_init = RNA_property_float_get(op->ptr, prop_cube_size);
2761
2762 ViewLayer *view_layer = CTX_data_view_layer(C);
2763 uint objects_len = 0;
2764 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
2765 view_layer, v3d, &objects_len);
2766 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2767 Object *obedit = objects[ob_index];
2768 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2769
2770 if (em->bm->totfacesel == 0) {
2771 continue;
2772 }
2773
2774 /* add uvs if they don't exist yet */
2775 if (!ED_uvedit_ensure_uvs(obedit)) {
2776 continue;
2777 }
2778
2779 float bounds[2][3];
2780 float(*bounds_buf)[3] = NULL;
2781
2782 if (!RNA_property_is_set(op->ptr, prop_cube_size)) {
2783 bounds_buf = bounds;
2784 }
2785
2786 float center[3];
2787 uv_map_transform_center(scene, v3d, obedit, em, center, bounds_buf);
2788
2789 /* calculate based on bounds */
2790 float cube_size = cube_size_init;
2791 if (bounds_buf) {
2792 float dims[3];
2793 sub_v3_v3v3(dims, bounds[1], bounds[0]);
2794 cube_size = max_fff(UNPACK3(dims));
2795 cube_size = cube_size ? 2.0f / cube_size : 1.0f;
2796 if (ob_index == 0) {
2797 /* This doesn't fit well with, multiple objects. */
2798 RNA_property_float_set(op->ptr, prop_cube_size, cube_size);
2799 }
2800 }
2801
2802 uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
2803
2804 uv_map_clip_correct(obedit, op);
2805
2806 DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
2807 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2808 }
2809 MEM_freeN(objects);
2810
2811 return OPERATOR_FINISHED;
2812 }
2813
UV_OT_cube_project(wmOperatorType * ot)2814 void UV_OT_cube_project(wmOperatorType *ot)
2815 {
2816 /* identifiers */
2817 ot->name = "Cube Projection";
2818 ot->idname = "UV_OT_cube_project";
2819 ot->description = "Project the UV vertices of the mesh over the six faces of a cube";
2820
2821 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2822
2823 /* api callbacks */
2824 ot->exec = cube_project_exec;
2825 ot->poll = ED_operator_uvmap;
2826
2827 /* properties */
2828 RNA_def_float(ot->srna,
2829 "cube_size",
2830 1.0f,
2831 0.0f,
2832 FLT_MAX,
2833 "Cube Size",
2834 "Size of the cube to project on",
2835 0.001f,
2836 100.0f);
2837 uv_map_clip_correct_properties(ot);
2838 }
2839
2840 /** \} */
2841
2842 /* -------------------------------------------------------------------- */
2843 /** \name Simple UVs for Texture Painting
2844 * \{ */
2845
ED_uvedit_add_simple_uvs(Main * bmain,const Scene * scene,Object * ob)2846 void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
2847 {
2848 Mesh *me = ob->data;
2849 bool sync_selection = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
2850
2851 BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default,
2852 &((struct BMeshCreateParams){
2853 .use_toolflags = false,
2854 }));
2855
2856 /* turn sync selection off,
2857 * since we are not in edit mode we need to ensure only the uv flags are tested */
2858 scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION;
2859
2860 ED_mesh_uv_texture_ensure(me, NULL);
2861
2862 BM_mesh_bm_from_me(bm,
2863 me,
2864 (&(struct BMeshFromMeshParams){
2865 .calc_face_normal = true,
2866 }));
2867 /* select all uv loops first - pack parameters needs this to make sure charts are registered */
2868 ED_uvedit_select_all(bm);
2869 uvedit_unwrap_cube_project(bm, 1.0, false, NULL);
2870 /* set the margin really quickly before the packing operation*/
2871 scene->toolsettings->uvcalc_margin = 0.001f;
2872 uvedit_pack_islands(scene, ob, bm);
2873 BM_mesh_bm_to_me(bmain, bm, me, (&(struct BMeshToMeshParams){0}));
2874 BM_mesh_free(bm);
2875
2876 if (sync_selection) {
2877 scene->toolsettings->uv_flag |= UV_SYNC_SELECTION;
2878 }
2879 }
2880
2881 /** \} */
2882