Blender V4.3
uvedit_unwrap_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_defaults.h"
16#include "DNA_meshdata_types.h"
17#include "DNA_modifier_types.h"
18#include "DNA_object_types.h"
19#include "DNA_scene_defaults.h"
20#include "DNA_scene_types.h"
21
22#include "BKE_global.hh"
23
24#include "BLI_array.hh"
25#include "BLI_linklist.h"
26#include "BLI_listbase.h"
27#include "BLI_math_geom.h"
28#include "BLI_math_matrix.h"
29#include "BLI_math_rotation.h"
30#include "BLI_math_vector.h"
31#include "BLI_memarena.h"
32#include "BLI_string.h"
33#include "BLI_time.h"
34#include "BLI_utildefines.h"
35#include "BLI_uvproject.h"
36#include "BLI_vector.hh"
37
38#include "BLT_translation.hh"
39
40#include "BKE_context.hh"
41#include "BKE_customdata.hh"
42#include "BKE_deform.hh"
43#include "BKE_editmesh.hh"
44#include "BKE_global.hh"
45#include "BKE_image.hh"
46#include "BKE_layer.hh"
47#include "BKE_lib_id.hh"
48#include "BKE_main.hh"
49#include "BKE_mesh.hh"
50#include "BKE_object_types.hh"
51#include "BKE_report.hh"
52#include "BKE_subdiv.hh"
53#include "BKE_subdiv_mesh.hh"
55
56#include "DEG_depsgraph.hh"
57
58#include "GEO_uv_pack.hh"
60
61#include "UI_interface.hh"
62#include "UI_resources.hh"
63
64#include "ED_image.hh"
65#include "ED_mesh.hh"
66#include "ED_screen.hh"
67#include "ED_undo.hh"
68#include "ED_uvedit.hh"
69#include "ED_view3d.hh"
70
71#include "RNA_access.hh"
72#include "RNA_define.hh"
73
74#include "WM_api.hh"
75#include "WM_types.hh"
76
77#include "uvedit_intern.hh"
78
79using blender::Span;
80using blender::Vector;
84
85/* -------------------------------------------------------------------- */
89static bool ED_uvedit_ensure_uvs(Object *obedit)
90{
91 if (ED_uvedit_test(obedit)) {
92 return true;
93 }
94
96 BMFace *efa;
97 BMIter iter;
98
99 if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_PROP_FLOAT2)) {
100 ED_mesh_uv_add(static_cast<Mesh *>(obedit->data), nullptr, true, true, nullptr);
101 }
102
103 /* Happens when there are no faces. */
104 if (!ED_uvedit_test(obedit)) {
105 return false;
106 }
107
108 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
109 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
110 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
111 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
112
113 /* select new UVs (ignore UV_SYNC_SELECTION in this case) */
114 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
115 BMIter liter;
116 BMLoop *l;
117
118 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
119 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
120 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
121 }
122 }
123
124 return true;
125}
126
129/* -------------------------------------------------------------------- */
134{
135 if (!sima) {
136 return; /* Nothing to do. */
137 }
138
139 /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for
140 * the tiled image is considered. */
141 const Image *image = sima->image;
142 if (image && image->source == IMA_SRC_TILED) {
143 ImageTile *active_tile = static_cast<ImageTile *>(
144 BLI_findlink(&image->tiles, image->active_tile_index));
145 if (active_tile) {
146 udim_base_offset[0] = (active_tile->tile_number - 1001) % 10;
147 udim_base_offset[1] = (active_tile->tile_number - 1001) / 10;
148 }
149 return;
150 }
151
152 /* TODO: Support storing an active UDIM when there are no tiles present.
153 * Until then, use 2D cursor to find the active tile index for the UDIM grid. */
154 if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, sima->cursor)) {
155 udim_base_offset[0] = floorf(sima->cursor[0]);
156 udim_base_offset[1] = floorf(sima->cursor[1]);
157 }
158}
162{
163 if (stop) {
164 return *stop;
165 }
166 return false;
167}
168
169/* -------------------------------------------------------------------- */
202
204{
205 only_selected_uvs = options.only_selected_uvs;
206 only_selected_faces = options.only_selected_faces;
207 use_seams = !options.topology_from_uvs || options.topology_from_uvs_use_seams;
208 correct_aspect = options.correct_aspect;
209 pin_unselected = options.pin_unselected;
210}
211
212static void modifier_unwrap_state(Object *obedit,
213 const UnwrapOptions *options,
214 bool *r_use_subsurf)
215{
216 ModifierData *md;
217 bool subsurf = options->use_subsurf;
218
219 md = static_cast<ModifierData *>(obedit->modifiers.first);
220
221 /* Subdivision-surface will take the modifier settings
222 * only if modifier is first or right after mirror. */
223 if (subsurf) {
224 if (md && md->type == eModifierType_Subsurf) {
225 const SubsurfModifierData &smd = *reinterpret_cast<const SubsurfModifierData *>(md);
226 if (smd.levels > 0) {
227 /* Skip all calculation for zero subdivision levels, similar to the way the modifier is
228 * disabled in that case. */
229 subsurf = true;
230 }
231 else {
232 subsurf = false;
233 }
234 }
235 else {
236 subsurf = false;
237 }
238 }
239
240 *r_use_subsurf = subsurf;
241}
242
244{
246
247 /* To be set by the upper layer */
248 options.topology_from_uvs = false;
249 options.topology_from_uvs_use_seams = false;
250 options.only_selected_faces = false;
251 options.only_selected_uvs = false;
252 options.pin_unselected = false;
253
254 options.slim.skip_init = false;
255
256 if (ts) {
257 options.method = ts->unwrapper;
258 options.correct_aspect = (ts->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0;
259 options.fill_holes = (ts->uvcalc_flag & UVCALC_FILLHOLES) != 0;
260 options.use_subsurf = (ts->uvcalc_flag & UVCALC_USESUBSURF) != 0;
261
263 STRNCPY(options.weight_group, ts->uvcalc_weight_group);
264 options.slim.weight_influence = ts->uvcalc_weight_factor;
265
266 options.slim.iterations = ts->uvcalc_iterations;
267 options.slim.no_flip = ts->uvcalc_flag & UVCALC_UNWRAP_NO_FLIP;
268 }
269 else {
270 options.method = RNA_enum_get(op->ptr, "method");
271 options.correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
272 options.fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
273 options.use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data");
274
275 options.use_weights = RNA_boolean_get(op->ptr, "use_weights");
276 RNA_string_get(op->ptr, "weight_group", options.weight_group);
277 options.slim.weight_influence = RNA_float_get(op->ptr, "weight_factor");
278
279 options.slim.iterations = RNA_int_get(op->ptr, "iterations");
280 options.slim.no_flip = RNA_boolean_get(op->ptr, "no_flip");
281 }
282
283#ifndef WITH_UV_SLIM
286 if (op) {
287 BKE_report(op->reports, RPT_WARNING, "Built without SLIM, falling back to conformal method");
288 }
289 }
290#endif /* !WITH_UV_SLIM */
291
292 if (options.weight_group[0] == '\0' || options.use_weights == false) {
293 options.slim.weight_influence = 0.0f;
294 }
295
296 options.use_abf = options.method == UVCALC_UNWRAP_METHOD_ANGLE;
298
299 /* SLIM requires hole filling */
300 if (options.use_slim) {
301 options.fill_holes = true;
302 }
303
304 if (ob) {
305 bool use_subsurf_final;
306 modifier_unwrap_state(ob, &options, &use_subsurf_final);
307 options.use_subsurf = use_subsurf_final;
308 }
309
310 return options;
311}
312
313/* Generic sync functions
314 *
315 * NOTE: these could be moved to a generic API.
316 */
317
319 PointerRNA *ptr, const char *prop_name, char flag, bool flipped, char *value_p)
320{
321 if (PropertyRNA *prop = RNA_struct_find_property(ptr, prop_name)) {
322 if (RNA_property_is_set(ptr, prop)) {
323 if (RNA_property_boolean_get(ptr, prop) ^ flipped) {
324 *value_p |= flag;
325 }
326 else {
327 *value_p &= ~flag;
328 }
329 return true;
330 }
331 RNA_property_boolean_set(ptr, prop, ((*value_p & flag) > 0) ^ flipped);
332 return false;
333 }
335 return false;
336}
337
338static bool rna_property_sync_enum(PointerRNA *ptr, const char *prop_name, int *value_p)
339{
340 if (PropertyRNA *prop = RNA_struct_find_property(ptr, prop_name)) {
341 if (RNA_property_is_set(ptr, prop)) {
342 *value_p = RNA_property_enum_get(ptr, prop);
343 return true;
344 }
345 RNA_property_enum_set(ptr, prop, *value_p);
346 return false;
347 }
349 return false;
350}
351
352static bool rna_property_sync_enum_char(PointerRNA *ptr, const char *prop_name, char *value_p)
353{
354 int value_i = *value_p;
355 if (rna_property_sync_enum(ptr, prop_name, &value_i)) {
356 *value_p = value_i;
357 return true;
358 }
359 return false;
360}
361
362static bool rna_property_sync_int(PointerRNA *ptr, const char *prop_name, int *value_p)
363{
364 if (PropertyRNA *prop = RNA_struct_find_property(ptr, prop_name)) {
365 if (RNA_property_is_set(ptr, prop)) {
366 *value_p = RNA_property_int_get(ptr, prop);
367 return true;
368 }
369 RNA_property_int_set(ptr, prop, *value_p);
370 return false;
371 }
373 return false;
374}
375
376static bool rna_property_sync_float(PointerRNA *ptr, const char *prop_name, float *value_p)
377{
378 if (PropertyRNA *prop = RNA_struct_find_property(ptr, prop_name)) {
379 if (RNA_property_is_set(ptr, prop)) {
380 *value_p = RNA_property_float_get(ptr, prop);
381 return true;
382 }
383 RNA_property_float_set(ptr, prop, *value_p);
384 return false;
385 }
387 return false;
388}
389
390static bool rna_property_sync_string(PointerRNA *ptr, const char *prop_name, char value_p[])
391{
392 if (PropertyRNA *prop = RNA_struct_find_property(ptr, prop_name)) {
393 if (RNA_property_is_set(ptr, prop)) {
394 RNA_property_string_get(ptr, prop, value_p);
395 return true;
396 }
397 RNA_property_string_set(ptr, prop, value_p);
398 return false;
399 }
401 return false;
402}
403
405{
406 /* Remember last method for live unwrap. */
407 rna_property_sync_enum_char(op->ptr, "method", &ts->unwrapper);
408
409 /* Remember packing margin. */
410 rna_property_sync_float(op->ptr, "margin", &ts->uvcalc_margin);
411
412 rna_property_sync_int(op->ptr, "iterations", &ts->uvcalc_iterations);
413
414 rna_property_sync_float(op->ptr, "weight_factor", &ts->uvcalc_weight_factor);
415
416 rna_property_sync_string(op->ptr, "weight_group", ts->uvcalc_weight_group);
417
418 rna_property_sync_flag(op->ptr, "fill_holes", UVCALC_FILLHOLES, false, &ts->uvcalc_flag);
420 op->ptr, "correct_aspect", UVCALC_NO_ASPECT_CORRECT, true, &ts->uvcalc_flag);
421 rna_property_sync_flag(op->ptr, "use_subsurf_data", UVCALC_USESUBSURF, false, &ts->uvcalc_flag);
422 rna_property_sync_flag(op->ptr, "no_flip", UVCALC_UNWRAP_NO_FLIP, false, &ts->uvcalc_flag);
423
425 op->ptr, "use_weights", UVCALC_UNWRAP_USE_WEIGHTS, false, &ts->uvcalc_flag);
426}
427
428static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
429{
430 BMFace *efa;
431 BMLoop *l;
432 BMIter iter, liter;
433 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
434
435 if (offsets.uv == -1) {
436 return (em->bm->totfacesel != 0);
437 }
438
439 /* verify if we have any selected uv's before unwrapping,
440 * so we can cancel the operator early */
441 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
442 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
444 continue;
445 }
446 }
447 else if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
448 continue;
449 }
450
451 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
452 if (uvedit_uv_select_test(scene, l, offsets)) {
453 break;
454 }
455 }
456
457 if (options->only_selected_uvs && !l) {
458 continue;
459 }
460
461 return true;
462 }
463
464 return false;
465}
466
467static bool uvedit_have_selection_multi(const Scene *scene,
468 const Span<Object *> objects,
469 const UnwrapOptions *options)
470{
471 bool have_select = false;
472 for (Object *obedit : objects) {
474 if (uvedit_have_selection(scene, em, options)) {
475 have_select = true;
476 break;
477 }
478 }
479 return have_select;
480}
481
483 const int material_index,
484 float *r_aspx,
485 float *r_aspy)
486{
487 if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) {
488 *r_aspx = 1.0f;
489 *r_aspy = 1.0f;
490 return;
491 }
492 Image *ima;
493 ED_object_get_active_image(ob, material_index + 1, &ima, nullptr, nullptr, nullptr);
494 ED_image_get_uv_aspect(ima, nullptr, r_aspx, r_aspy);
495}
496
497void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
498{
500 BLI_assert(em != nullptr);
501 bool sloppy = true;
502 bool selected = false;
503 BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
504 if (!efa) {
505 *r_aspx = 1.0f;
506 *r_aspy = 1.0f;
507 return;
508 }
509
510 ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy);
511}
512
514{
515 float aspect[2];
516 ED_uvedit_get_aspect(ob, &aspect[0], &aspect[1]);
517 return aspect[0] / aspect[1];
518}
519
520static bool uvedit_is_face_affected(const Scene *scene,
521 BMFace *efa,
522 const UnwrapOptions *options,
523 const BMUVOffsets offsets)
524{
526 return false;
527 }
528
529 if (options->only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
530 return false;
531 }
532
533 if (options->only_selected_uvs) {
534 BMLoop *l;
535 BMIter iter;
536 BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
537 if (uvedit_uv_select_test(scene, l, offsets)) {
538 return true;
539 }
540 }
541 return false;
542 }
543
544 return true;
545}
546
547/* Prepare unique indices for each unique pinned UV, even if it shares a BMVert.
548 */
550 const Scene *scene,
551 BMFace *efa,
552 const UnwrapOptions *options,
553 const BMUVOffsets offsets)
554{
555 BMIter liter;
556 BMLoop *l;
557 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
558 bool pin = BM_ELEM_CD_GET_BOOL(l, offsets.pin);
559 if (options->pin_unselected && !pin) {
560 pin = !uvedit_uv_select_test(scene, l, offsets);
561 }
562 if (pin) {
563 int bmvertindex = BM_elem_index_get(l->v);
564 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
565 blender::geometry::uv_prepare_pin_index(handle, bmvertindex, luv);
566 }
567 }
568}
569
571 const Scene *scene,
572 BMFace *efa,
574 const UnwrapOptions *options,
575 const BMUVOffsets offsets,
576 const int cd_weight_offset,
577 const int cd_weight_index)
578{
585
586 int i;
587
588 BMIter liter;
589 BMLoop *l;
590
591 /* let parametrizer split the ngon, it can make better decisions
592 * about which split is best for unwrapping than poly-fill. */
593 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
594 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
595
597 co[i] = l->v->co;
598 uv[i] = luv;
599 pin[i] = BM_ELEM_CD_GET_BOOL(l, offsets.pin);
600 select[i] = uvedit_uv_select_test(scene, l, offsets);
601 if (options->pin_unselected && !select[i]) {
602 pin[i] = true;
603 }
604
605 /* Optional vertex group weighting. */
606 if (cd_weight_offset >= 0 && cd_weight_index >= 0) {
607 MDeformVert *dv = (MDeformVert *)BM_ELEM_CD_GET_VOID_P(l->v, cd_weight_offset);
608 weight[i] = BKE_defvert_find_weight(dv, cd_weight_index);
609 }
610 else {
611 weight[i] = 1.0f;
612 }
613 }
614
616 face_index,
617 i,
618 vkeys.data(),
619 co.data(),
620 uv.data(),
621 weight.data(),
622 pin.data(),
623 select.data());
624}
625
626/* Set seams on UV Parametrizer based on options. */
628 BMesh *bm,
629 const UnwrapOptions *options)
630{
631 if (options->topology_from_uvs && !options->topology_from_uvs_use_seams) {
632 return; /* Seams are not required with these options. */
633 }
634
635 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
636 if (offsets.uv == -1) {
637 return; /* UVs aren't present on BMesh. Nothing to do. */
638 }
639
640 BMEdge *edge;
641 BMIter iter;
642 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
643 if (!BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
644 continue; /* No seam on this edge, nothing to do. */
645 }
646
647 /* Pinned vertices might have more than one ParamKey per BMVert.
648 * Check all the BM_LOOPS_OF_EDGE to find all the ParamKeys.
649 */
650 BMLoop *l;
651 BMIter liter;
652 BM_ITER_ELEM (l, &liter, edge, BM_LOOPS_OF_EDGE) {
653 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
654 float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, offsets.uv);
655 ParamKey vkeys[2];
658 handle, BM_elem_index_get(l->next->v), luv_next);
659
660 /* Set the seam. */
662 }
663 }
664}
665
666/*
667 * Version of #construct_param_handle_multi with a separate BMesh parameter.
668 */
670 Object *ob,
671 BMesh *bm,
672 const UnwrapOptions *options,
673 int *r_count_failed = nullptr)
674{
675 BMFace *efa;
676 BMIter iter;
677 int i;
678
680
681 if (options->correct_aspect) {
683 }
684
685 /* we need the vert indices */
687
688 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
689 const int cd_weight_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
690 const int cd_weight_index = BKE_object_defgroup_name_index(ob, options->weight_group);
691
692 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
693 if (uvedit_is_face_affected(scene, efa, options, offsets)) {
694 uvedit_prepare_pinned_indices(handle, scene, efa, options, offsets);
695 }
696 }
697
698 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
699 if (uvedit_is_face_affected(scene, efa, options, offsets)) {
701 handle, scene, efa, i, options, offsets, cd_weight_offset, cd_weight_index);
702 }
703 }
704
706
708 handle, options->fill_holes, options->topology_from_uvs, r_count_failed);
709
710 return handle;
711}
712
717 const Span<Object *> objects,
718 const UnwrapOptions *options)
719{
720 BMFace *efa;
721 BMIter iter;
722 int i;
723
725
726 if (options->correct_aspect) {
727 Object *ob = objects[0];
729 }
730
731 /* we need the vert indices */
733
734 int offset = 0;
735
736 for (Object *obedit : objects) {
738 BMesh *bm = em->bm;
739
740 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
741
742 if (offsets.uv == -1) {
743 continue;
744 }
745
746 const int cd_weight_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
747 const int cd_weight_index = BKE_object_defgroup_name_index(obedit, options->weight_group);
748
749 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
750 if (uvedit_is_face_affected(scene, efa, options, offsets)) {
751 uvedit_prepare_pinned_indices(handle, scene, efa, options, offsets);
752 }
753 }
754
755 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
756 if (uvedit_is_face_affected(scene, efa, options, offsets)) {
758 handle, scene, efa, i + offset, options, offsets, cd_weight_offset, cd_weight_index);
759 }
760 }
761
763
764 offset += bm->totface;
765 }
766
768 handle, options->fill_holes, options->topology_from_uvs, nullptr);
769
770 return handle;
771}
772
773static void texface_from_original_index(const Scene *scene,
774 const BMUVOffsets offsets,
775 BMFace *efa,
776 int index,
777 float **r_uv,
778 bool *r_pin,
779 bool *r_select)
780{
781 BMLoop *l;
782 BMIter liter;
783
784 *r_uv = nullptr;
785 *r_pin = false;
786 *r_select = true;
787
788 if (index == ORIGINDEX_NONE) {
789 return;
790 }
791
792 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
793 if (BM_elem_index_get(l->v) == index) {
794 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
795 *r_uv = luv;
796 *r_pin = BM_ELEM_CD_GET_BOOL(l, offsets.pin);
797 *r_select = uvedit_uv_select_test(scene, l, offsets);
798 break;
799 }
800 }
801}
802
803static Mesh *subdivide_edit_mesh(const Object *object,
804 const BMEditMesh *em,
805 const SubsurfModifierData *smd)
806{
807 using namespace blender;
809 em->bm, nullptr, static_cast<const Mesh *>(object->data));
811
813 /* A zero level must be prevented by #modifier_unwrap_state
814 * since necessary data won't be available, see: #128958. */
815 BLI_assert(settings.level > 0);
816
817 /* Level 1 causes disconnected triangles, force level 2 to prevent this, see: #129503. */
818 if (settings.level == 1) {
819 settings.level = 2;
820 }
821
822 bke::subdiv::ToMeshSettings mesh_settings;
823 mesh_settings.resolution = (1 << smd->levels) + 1;
825
826 bke::subdiv::Subdiv *subdiv = bke::subdiv::new_from_mesh(&settings, me_from_em);
827 if (!subdiv) {
828 return nullptr;
829 }
830 Mesh *result = bke::subdiv::subdiv_to_mesh(subdiv, &mesh_settings, me_from_em);
831 BKE_id_free(nullptr, me_from_em);
832 bke::subdiv::free(subdiv);
833 return result;
834}
835
842 Object *ob,
843 BMEditMesh *em,
844 const UnwrapOptions *options,
845 int *r_count_failed = nullptr)
846{
847 /* pointers to modifier data for unwrap control */
848 SubsurfModifierData *smd_real;
849 /* Modifier initialization data, will control what type of subdivision will happen. */
850 SubsurfModifierData smd = {{nullptr}};
851
852 /* Holds a map to edit-faces for every subdivision-surface face.
853 * These will be used to get hidden/ selected flags etc. */
854 BMFace **faceMap;
855 /* Similar to the above, we need a way to map edges to their original ones. */
856 BMEdge **edgeMap;
857
858 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
859 const int cd_weight_index = BKE_object_defgroup_name_index(ob, options->weight_group);
860
862
863 if (options->correct_aspect) {
865 }
866
867 /* number of subdivisions to perform */
868 ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first);
869 smd_real = (SubsurfModifierData *)md;
870
871 smd.levels = smd_real->levels;
872 smd.subdivType = smd_real->subdivType;
873 smd.flags = smd_real->flags;
874 smd.quality = smd_real->quality;
875
876 Mesh *subdiv_mesh = subdivide_edit_mesh(ob, em, &smd);
877
878 const blender::Span<blender::float3> subsurf_positions = subdiv_mesh->vert_positions();
879 const blender::Span<blender::int2> subsurf_edges = subdiv_mesh->edges();
880 const blender::OffsetIndices subsurf_facess = subdiv_mesh->faces();
881 const blender::Span<int> subsurf_corner_verts = subdiv_mesh->corner_verts();
882 const blender::Span<MDeformVert> subsurf_deform_verts = subdiv_mesh->deform_verts();
883
884 const int *origVertIndices = static_cast<const int *>(
886 const int *origEdgeIndices = static_cast<const int *>(
888 const int *origPolyIndices = static_cast<const int *>(
890
891 faceMap = static_cast<BMFace **>(
892 MEM_mallocN(subdiv_mesh->faces_num * sizeof(BMFace *), "unwrap_edit_face_map"));
893
896
897 /* map subsurfed faces to original editFaces */
898 for (int i = 0; i < subdiv_mesh->faces_num; i++) {
899 faceMap[i] = BM_face_at_index(em->bm, origPolyIndices[i]);
900 }
901
902 edgeMap = static_cast<BMEdge **>(
903 MEM_mallocN(subdiv_mesh->edges_num * sizeof(BMEdge *), "unwrap_edit_edge_map"));
904
905 /* map subsurfed edges to original editEdges */
906 for (int i = 0; i < subdiv_mesh->edges_num; i++) {
907 /* not all edges correspond to an old edge */
908 edgeMap[i] = (origEdgeIndices[i] != ORIGINDEX_NONE) ?
909 BM_edge_at_index(em->bm, origEdgeIndices[i]) :
910 nullptr;
911 }
912
913 /* Prepare and feed faces to the solver. */
914 for (const int i : subsurf_facess.index_range()) {
915 ParamKey key, vkeys[4];
916 bool pin[4], select[4];
917 const float *co[4];
918 float *uv[4];
919 float weight[4];
920 BMFace *origFace = faceMap[i];
921
922 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
923 if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN)) {
924 continue;
925 }
926 }
927 else {
928 if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) ||
929 (options->only_selected_faces && !BM_elem_flag_test(origFace, BM_ELEM_SELECT)))
930 {
931 continue;
932 }
933 }
934
935 const blender::Span<int> poly_corner_verts = subsurf_corner_verts.slice(subsurf_facess[i]);
936
937 /* We will not check for v4 here. Sub-surface faces always have 4 vertices. */
938 BLI_assert(poly_corner_verts.size() == 4);
939 key = (ParamKey)i;
940 vkeys[0] = (ParamKey)poly_corner_verts[0];
941 vkeys[1] = (ParamKey)poly_corner_verts[1];
942 vkeys[2] = (ParamKey)poly_corner_verts[2];
943 vkeys[3] = (ParamKey)poly_corner_verts[3];
944
945 co[0] = subsurf_positions[poly_corner_verts[0]];
946 co[1] = subsurf_positions[poly_corner_verts[1]];
947 co[2] = subsurf_positions[poly_corner_verts[2]];
948 co[3] = subsurf_positions[poly_corner_verts[3]];
949
950 /* Optional vertex group weights. */
951 if (cd_weight_index >= 0) {
952 weight[0] = BKE_defvert_find_weight(&subsurf_deform_verts[poly_corner_verts[0]],
953 cd_weight_index);
954 weight[1] = BKE_defvert_find_weight(&subsurf_deform_verts[poly_corner_verts[1]],
955 cd_weight_index);
956 weight[2] = BKE_defvert_find_weight(&subsurf_deform_verts[poly_corner_verts[2]],
957 cd_weight_index);
958 weight[3] = BKE_defvert_find_weight(&subsurf_deform_verts[poly_corner_verts[3]],
959 cd_weight_index);
960 }
961 else {
962 weight[0] = 1.0f;
963 weight[1] = 1.0f;
964 weight[2] = 1.0f;
965 weight[3] = 1.0f;
966 }
967
968 /* This is where all the magic is done.
969 * If the vertex exists in the, we pass the original uv pointer to the solver, thus
970 * flushing the solution to the edit mesh. */
972 offsets,
973 origFace,
974 origVertIndices[poly_corner_verts[0]],
975 &uv[0],
976 &pin[0],
977 &select[0]);
979 offsets,
980 origFace,
981 origVertIndices[poly_corner_verts[1]],
982 &uv[1],
983 &pin[1],
984 &select[1]);
986 offsets,
987 origFace,
988 origVertIndices[poly_corner_verts[2]],
989 &uv[2],
990 &pin[2],
991 &select[2]);
993 offsets,
994 origFace,
995 origVertIndices[poly_corner_verts[3]],
996 &uv[3],
997 &pin[3],
998 &select[3]);
999
1001 handle, key, 4, vkeys, co, uv, weight, pin, select);
1002 }
1003
1004 /* These are calculated from original mesh too. */
1005 for (const int64_t i : subsurf_edges.index_range()) {
1006 if ((edgeMap[i] != nullptr) && BM_elem_flag_test(edgeMap[i], BM_ELEM_SEAM)) {
1007 const blender::int2 &edge = subsurf_edges[i];
1008 ParamKey vkeys[2];
1009 vkeys[0] = (ParamKey)edge[0];
1010 vkeys[1] = (ParamKey)edge[1];
1012 }
1013 }
1014
1016 handle, options->fill_holes, options->topology_from_uvs, r_count_failed);
1017
1018 /* cleanup */
1020 MEM_freeN(edgeMap);
1021 BKE_id_free(nullptr, subdiv_mesh);
1022
1023 return handle;
1024}
1025
1028/* -------------------------------------------------------------------- */
1041
1043{
1044 const Scene *scene = CTX_data_scene(C);
1045 ViewLayer *view_layer = CTX_data_view_layer(C);
1046
1048 options.topology_from_uvs = true;
1049 options.fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
1050 options.only_selected_faces = true;
1051 options.only_selected_uvs = true;
1052 options.correct_aspect = true;
1053
1055 scene, view_layer, CTX_wm_view3d(C));
1056
1057 if (!uvedit_have_selection_multi(scene, objects, &options)) {
1058 return false;
1059 }
1060
1061 MinStretch *ms = MEM_new<MinStretch>(__func__);
1062 ms->scene = scene;
1063 ms->objects_edit = objects;
1064 ms->blend = RNA_float_get(op->ptr, "blend");
1065 ms->iterations = RNA_int_get(op->ptr, "iterations");
1066 ms->i = 0;
1067 ms->handle = construct_param_handle_multi(scene, objects, &options);
1069
1071 if (ms->blend != 0.0f) {
1073 }
1074
1075 op->customdata = ms;
1076
1077 return true;
1078}
1079
1080static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interactive)
1081{
1082 MinStretch *ms = static_cast<MinStretch *>(op->customdata);
1083 ScrArea *area = CTX_wm_area(C);
1084 const Scene *scene = CTX_data_scene(C);
1085 ToolSettings *ts = scene->toolsettings;
1086 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1087
1090
1091 ms->i++;
1092 RNA_int_set(op->ptr, "iterations", ms->i);
1093
1094 if (interactive && (BLI_time_now_seconds() - ms->lasttime > 0.5)) {
1095 char str[UI_MAX_DRAW_STR];
1096
1098
1099 if (area) {
1100 SNPRINTF(str, IFACE_("Minimize Stretch. Blend %.2f"), ms->blend);
1101 ED_area_status_text(area, str);
1102 ED_workspace_status_text(C, IFACE_("Press + and -, or scroll wheel to set blending"));
1103 }
1104
1106
1107 for (Object *obedit : ms->objects_edit) {
1109
1110 if (synced_selection && (em->bm->totfacesel == 0)) {
1111 continue;
1112 }
1113
1114 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
1115 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1116 }
1117 }
1118}
1119
1120static void minimize_stretch_exit(bContext *C, wmOperator *op, bool cancel)
1121{
1122 MinStretch *ms = static_cast<MinStretch *>(op->customdata);
1123 ScrArea *area = CTX_wm_area(C);
1124 const Scene *scene = CTX_data_scene(C);
1125 ToolSettings *ts = scene->toolsettings;
1126 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1127
1128 ED_area_status_text(area, nullptr);
1129 ED_workspace_status_text(C, nullptr);
1130
1131 if (ms->timer) {
1133 }
1134
1135 if (cancel) {
1137 }
1138 else {
1140 }
1141
1143 delete (ms->handle);
1144
1145 for (Object *obedit : ms->objects_edit) {
1147
1148 if (synced_selection && (em->bm->totfacesel == 0)) {
1149 continue;
1150 }
1151
1152 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
1153 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1154 }
1155
1156 MEM_delete(ms);
1157 op->customdata = nullptr;
1158}
1159
1161{
1162 int i, iterations;
1163
1164 if (!minimize_stretch_init(C, op)) {
1165 return OPERATOR_CANCELLED;
1166 }
1167
1168 iterations = RNA_int_get(op->ptr, "iterations");
1169 for (i = 0; i < iterations; i++) {
1170 minimize_stretch_iteration(C, op, false);
1171 }
1172 minimize_stretch_exit(C, op, false);
1173
1174 return OPERATOR_FINISHED;
1175}
1176
1177static int minimize_stretch_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1178{
1179 if (!minimize_stretch_init(C, op)) {
1180 return OPERATOR_CANCELLED;
1181 }
1182
1183 minimize_stretch_iteration(C, op, true);
1184
1185 MinStretch *ms = static_cast<MinStretch *>(op->customdata);
1188
1190}
1191
1192static int minimize_stretch_modal(bContext *C, wmOperator *op, const wmEvent *event)
1193{
1194 MinStretch *ms = static_cast<MinStretch *>(op->customdata);
1195
1196 switch (event->type) {
1197 case EVT_ESCKEY:
1198 case RIGHTMOUSE:
1199 minimize_stretch_exit(C, op, true);
1200 return OPERATOR_CANCELLED;
1201 case EVT_RETKEY:
1202 case EVT_PADENTER:
1203 case LEFTMOUSE:
1204 minimize_stretch_exit(C, op, false);
1205 return OPERATOR_FINISHED;
1206 case EVT_PADPLUSKEY:
1207 case WHEELUPMOUSE:
1208 if (event->val == KM_PRESS) {
1209 if (ms->blend < 0.95f) {
1210 ms->blend += 0.1f;
1211 ms->lasttime = 0.0f;
1212 RNA_float_set(op->ptr, "blend", ms->blend);
1213 minimize_stretch_iteration(C, op, true);
1214 }
1215 }
1216 break;
1217 case EVT_PADMINUS:
1218 case WHEELDOWNMOUSE:
1219 if (event->val == KM_PRESS) {
1220 if (ms->blend > 0.05f) {
1221 ms->blend -= 0.1f;
1222 ms->lasttime = 0.0f;
1223 RNA_float_set(op->ptr, "blend", ms->blend);
1224 minimize_stretch_iteration(C, op, true);
1225 }
1226 }
1227 break;
1228 case TIMER:
1229 if (ms->timer == event->customdata) {
1230 double start = BLI_time_now_seconds();
1231
1232 do {
1233 minimize_stretch_iteration(C, op, true);
1234 } while (BLI_time_now_seconds() - start < 0.01);
1235 }
1236 break;
1237 }
1238
1239 if (ms->iterations && ms->i >= ms->iterations) {
1240 minimize_stretch_exit(C, op, false);
1241 return OPERATOR_FINISHED;
1242 }
1243
1245}
1246
1248{
1249 minimize_stretch_exit(C, op, true);
1250}
1251
1253{
1254 /* identifiers */
1255 ot->name = "Minimize Stretch";
1256 ot->idname = "UV_OT_minimize_stretch";
1258 ot->description = "Reduce UV stretching by relaxing angles";
1259
1260 /* api callbacks */
1266
1267 /* properties */
1269 "fill_holes",
1270 true,
1271 "Fill Holes",
1272 "Virtually fill holes in mesh before unwrapping, to better avoid overlaps and "
1273 "preserve symmetry");
1275 "blend",
1276 0.0f,
1277 0.0f,
1278 1.0f,
1279 "Blend",
1280 "Blend factor between stretch minimized and original",
1281 0.0f,
1282 1.0f);
1284 "iterations",
1285 0,
1286 0,
1287 INT_MAX,
1288 "Iterations",
1289 "Number of iterations to run, 0 is unlimited when run interactively",
1290 0,
1291 100);
1292}
1293
1297 const float matrix[2][2], /* Scale and rotation. */
1298 const float pre_translate[2] /* (pre) Translation. */
1299)
1300{
1301 /* Use a pre-transform to compute `A * (x+b)`
1302 *
1303 * \note Ordinarily, we'd use a post_transform like `A * x + b`
1304 * In general, post-transforms are easier to work with when using homogenous co-ordinates.
1305 *
1306 * When UV mapping into the unit square, post-transforms can lose precision on small islands.
1307 * Instead we're using a pre-transform to maintain precision.
1308 *
1309 * To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b`
1310 */
1311
1312 const int cd_loop_uv_offset = island->offsets.uv;
1313 const int faces_len = island->faces_len;
1314 for (int i = 0; i < faces_len; i++) {
1315 BMFace *f = island->faces[i];
1316 BMLoop *l;
1317 BMIter iter;
1318 BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
1319 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1320 blender::geometry::mul_v2_m2_add_v2v2(luv, matrix, luv, pre_translate);
1321 }
1322 }
1323}
1324
1328static float uv_nearest_image_tile_distance(const Image *image,
1329 const float coords[2],
1330 float nearest_tile_co[2])
1331{
1332 BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co);
1333
1334 /* Add 0.5 to get tile center coordinates. */
1335 float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
1336 add_v2_fl(nearest_tile_center_co, 0.5f);
1337
1338 return len_squared_v2v2(coords, nearest_tile_center_co);
1339}
1340
1344static float uv_nearest_grid_tile_distance(const int udim_grid[2],
1345 const float coords[2],
1346 float nearest_tile_co[2])
1347{
1348 const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
1349
1350 if (coords[0] > udim_grid[0]) {
1351 nearest_tile_co[0] = udim_grid[0] - 1;
1352 }
1353 else if (coords[0] < 0) {
1354 nearest_tile_co[0] = 0;
1355 }
1356 else {
1357 nearest_tile_co[0] = coords_floor[0];
1358 }
1359
1360 if (coords[1] > udim_grid[1]) {
1361 nearest_tile_co[1] = udim_grid[1] - 1;
1362 }
1363 else if (coords[1] < 0) {
1364 nearest_tile_co[1] = 0;
1365 }
1366 else {
1367 nearest_tile_co[1] = coords_floor[1];
1368 }
1369
1370 /* Add 0.5 to get tile center coordinates. */
1371 float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
1372 add_v2_fl(nearest_tile_center_co, 0.5f);
1373
1374 return len_squared_v2v2(coords, nearest_tile_center_co);
1375}
1376
1377static bool island_has_pins(const Scene *scene,
1378 FaceIsland *island,
1380{
1381 const bool pin_unselected = params->pin_unselected;
1382 const bool only_selected_faces = params->only_selected_faces;
1383 BMLoop *l;
1384 BMIter iter;
1385 const int pin_offset = island->offsets.pin;
1386 for (int i = 0; i < island->faces_len; i++) {
1387 BMFace *efa = island->faces[i];
1388 if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1389 return true;
1390 }
1391 BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) {
1392 if (BM_ELEM_CD_GET_BOOL(l, pin_offset)) {
1393 return true;
1394 }
1395 if (pin_unselected && !uvedit_uv_select_test(scene, l, island->offsets)) {
1396 return true;
1397 }
1398 }
1399 }
1400 return false;
1401}
1402
1417static void uvedit_pack_islands_multi(const Scene *scene,
1418 const Span<Object *> objects,
1419 BMesh **bmesh_override,
1420 const SpaceImage *udim_source_closest,
1421 const bool original_selection,
1422 const bool notify_wm,
1424{
1425 blender::Vector<FaceIsland *> island_vector;
1426 blender::Vector<bool> pinned_vector;
1427
1428 for (const int ob_index : objects.index_range()) {
1429 Object *obedit = objects[ob_index];
1430 BMesh *bm = nullptr;
1431 if (bmesh_override) {
1432 /* NOTE: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */
1433 bm = bmesh_override[ob_index];
1434 }
1435 else {
1437 bm = em->bm;
1438 }
1439 BLI_assert(bm);
1440
1441 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
1442 if (offsets.uv == -1) {
1443 continue;
1444 }
1445
1446 const float aspect_y = params->correct_aspect ? ED_uvedit_get_aspect_y(obedit) : 1.0f;
1447
1448 bool only_selected_faces = params->only_selected_faces;
1449 bool only_selected_uvs = params->only_selected_uvs;
1450 const bool ignore_pinned = params->pin_method == ED_UVPACK_PIN_IGNORE;
1451 if (ignore_pinned && params->pin_unselected) {
1452 only_selected_faces = false;
1453 only_selected_uvs = false;
1454 }
1455 ListBase island_list = {nullptr};
1457 bm,
1458 &island_list,
1459 only_selected_faces,
1460 only_selected_uvs,
1461 params->use_seams,
1462 aspect_y,
1463 offsets);
1464
1465 /* Remove from linked list and append to blender::Vector. */
1466 LISTBASE_FOREACH_MUTABLE (FaceIsland *, island, &island_list) {
1467 BLI_remlink(&island_list, island);
1468 const bool pinned = island_has_pins(scene, island, params);
1469 if (ignore_pinned && pinned) {
1470 MEM_freeN(island->faces);
1471 MEM_freeN(island);
1472 continue;
1473 }
1474 island_vector.append(island);
1475 pinned_vector.append(pinned);
1476 }
1477 }
1478
1479 if (island_vector.is_empty()) {
1480 return;
1481 }
1482
1483 /* Coordinates of bounding box containing all selected UVs. */
1484 float selection_min_co[2], selection_max_co[2];
1485 INIT_MINMAX2(selection_min_co, selection_max_co);
1486
1487 for (int index = 0; index < island_vector.size(); index++) {
1488 FaceIsland *island = island_vector[index];
1489
1490 for (int i = 0; i < island->faces_len; i++) {
1491 BMFace *f = island->faces[i];
1492 BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv);
1493 }
1494 }
1495
1496 /* Center of bounding box containing all selected UVs. */
1497 float selection_center[2];
1498 mid_v2_v2v2(selection_center, selection_min_co, selection_max_co);
1499
1500 if (original_selection) {
1501 /* Protect against degenerate source AABB. */
1502 if ((selection_max_co[0] - selection_min_co[0]) * (selection_max_co[1] - selection_min_co[1]) >
1503 1e-40f)
1504 {
1505 copy_v2_v2(params->udim_base_offset, selection_min_co);
1506 params->target_extent = selection_max_co[1] - selection_min_co[1];
1507 params->target_aspect_y = (selection_max_co[0] - selection_min_co[0]) /
1508 (selection_max_co[1] - selection_min_co[1]);
1509 }
1510 }
1511
1513 Heap *heap = BLI_heap_new();
1514
1516 for (int i = 0; i < island_vector.size(); i++) {
1517 FaceIsland *face_island = island_vector[i];
1519 pack_island->caller_index = i;
1520 pack_island->aspect_y = face_island->aspect_y;
1521 pack_island->pinned = pinned_vector[i];
1522 pack_island_vector.append(pack_island);
1523
1524 for (int i = 0; i < face_island->faces_len; i++) {
1525 BMFace *f = face_island->faces[i];
1526
1527 /* Storage. */
1529
1530 /* Obtain UVs of face. */
1531 BMLoop *l;
1532 BMIter iter;
1533 int j;
1534 BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, j) {
1535 copy_v2_v2(uvs[j], BM_ELEM_CD_GET_FLOAT_P(l, face_island->offsets.uv));
1536 }
1537
1538 pack_island->add_polygon(uvs, arena, heap);
1539
1540 BLI_memarena_clear(arena);
1541 }
1542 }
1543 BLI_heap_free(heap, nullptr);
1544 BLI_memarena_free(arena);
1545
1546 const float scale = pack_islands(pack_island_vector, *params);
1547 const bool is_cancelled = params->isCancelled();
1548
1549 float base_offset[2] = {0.0f, 0.0f};
1550 copy_v2_v2(base_offset, params->udim_base_offset);
1551
1552 if (udim_source_closest) {
1553 const Image *image = udim_source_closest->image;
1554 const int *udim_grid = udim_source_closest->tile_grid_shape;
1555 /* Check if selection lies on a valid UDIM grid tile. */
1556 bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center);
1557 if (is_valid_udim) {
1558 base_offset[0] = floorf(selection_center[0]);
1559 base_offset[1] = floorf(selection_center[1]);
1560 }
1561 /* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */
1562 else {
1563 float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX};
1564 float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX;
1565 if (image) {
1566 nearest_image_tile_dist = uv_nearest_image_tile_distance(
1567 image, selection_center, nearest_image_tile_co);
1568 }
1569
1570 float nearest_grid_tile_co[2] = {0.0f, 0.0f};
1571 nearest_grid_tile_dist = uv_nearest_grid_tile_distance(
1572 udim_grid, selection_center, nearest_grid_tile_co);
1573
1574 base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
1575 nearest_image_tile_co[0] :
1576 nearest_grid_tile_co[0];
1577 base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
1578 nearest_image_tile_co[1] :
1579 nearest_grid_tile_co[1];
1580 }
1581 }
1582
1583 float matrix[2][2];
1584 float matrix_inverse[2][2];
1585 float pre_translate[2];
1586 for (const int64_t i : pack_island_vector.index_range()) {
1587 if (is_cancelled) {
1588 continue;
1589 }
1590 blender::geometry::PackIsland *pack_island = pack_island_vector[i];
1591 FaceIsland *island = island_vector[pack_island->caller_index];
1592 const float island_scale = pack_island->can_scale_(*params) ? scale : 1.0f;
1593 pack_island->build_transformation(island_scale, pack_island->angle, matrix);
1594 invert_m2_m2(matrix_inverse, matrix);
1595
1596 /* Add base_offset, post transform. */
1597 mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
1598
1599 /* Add pre-translation from #pack_islands. */
1600 pre_translate[0] += pack_island->pre_translate.x;
1601 pre_translate[1] += pack_island->pre_translate.y;
1602
1603 /* Perform the transformation. */
1604 island_uv_transform(island, matrix, pre_translate);
1605 }
1606
1607 for (const int64_t i : pack_island_vector.index_range()) {
1608 blender::geometry::PackIsland *pack_island = pack_island_vector[i];
1609 /* Cleanup memory. */
1610 pack_island_vector[i] = nullptr;
1611 delete pack_island;
1612 }
1613
1614 if (notify_wm && !is_cancelled) {
1615 for (Object *obedit : objects) {
1616 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
1617 WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
1618 }
1619 }
1620
1621 for (FaceIsland *island : island_vector) {
1622 MEM_freeN(island->faces);
1623 MEM_freeN(island);
1624 }
1625}
1626
1627/* -------------------------------------------------------------------- */
1631/* TODO: support this, interaction with the job-system needs to be handled carefully. */
1632// #define USE_INTERACTIVE_PACK
1633
1634/* Packing targets. */
1635enum {
1639};
1640
1656
1657static void pack_islands_startjob(void *pidv, wmJobWorkerStatus *worker_status)
1658{
1659 worker_status->progress = 0.02f;
1660
1661 UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(pidv);
1662
1663 pid->pack_island_params.stop = &worker_status->stop;
1664 pid->pack_island_params.do_update = &worker_status->do_update;
1665 pid->pack_island_params.progress = &worker_status->progress;
1666
1668 pid->objects,
1669 nullptr,
1670 (pid->udim_source == PACK_UDIM_SRC_CLOSEST) ? pid->sima : nullptr,
1672 !pid->use_job,
1673 &pid->pack_island_params);
1674
1675 worker_status->progress = 0.99f;
1676 worker_status->do_update = true;
1677}
1678
1679static void pack_islands_endjob(void *pidv)
1680{
1681 UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(pidv);
1682 for (Object *obedit : pid->objects) {
1683 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
1684 WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
1685 }
1687
1688 if (pid->undo_str) {
1690 }
1691}
1692
1693static void pack_islands_freejob(void *pidv)
1694{
1695 WM_cursor_wait(false);
1696 UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(pidv);
1697 WM_set_locked_interface(pid->wm, false);
1698 MEM_delete(pid);
1699}
1700
1702{
1703 ViewLayer *view_layer = CTX_data_view_layer(C);
1704 const Scene *scene = CTX_data_scene(C);
1705 const SpaceImage *sima = CTX_wm_space_image(C);
1706
1707 UnwrapOptions options = unwrap_options_get(op, nullptr, scene->toolsettings);
1708 options.topology_from_uvs = true;
1709 options.only_selected_faces = true;
1710 options.only_selected_uvs = true;
1711 options.fill_holes = false;
1712 options.correct_aspect = true;
1713
1715 scene, view_layer, CTX_wm_view3d(C));
1716
1717 /* Early exit in case no UVs are selected. */
1718 if (!uvedit_have_selection_multi(scene, objects, &options)) {
1719 return OPERATOR_CANCELLED;
1720 }
1721
1722 /* RNA props */
1723 const int udim_source = RNA_enum_get(op->ptr, "udim_source");
1724 if (RNA_struct_property_is_set(op->ptr, "margin")) {
1725 scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
1726 }
1727 else {
1728 RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
1729 }
1730
1731 UVPackIslandsData *pid = MEM_new<UVPackIslandsData>(__func__);
1732 pid->use_job = op->flag & OP_IS_INVOKE;
1733 pid->scene = scene;
1734 pid->objects = std::move(objects);
1735 pid->sima = sima;
1736 pid->udim_source = udim_source;
1737 pid->wm = CTX_wm_manager(C);
1738
1740 {
1741 /* Call default constructor and copy the defaults. */
1743 pack_island_params = default_params;
1744 }
1745
1746 pack_island_params.setFromUnwrapOptions(options);
1747 if (RNA_boolean_get(op->ptr, "rotate")) {
1748 pack_island_params.rotate_method = eUVPackIsland_RotationMethod(
1749 RNA_enum_get(op->ptr, "rotate_method"));
1750 }
1751 else {
1752 pack_island_params.rotate_method = ED_UVPACK_ROTATION_NONE;
1753 }
1754 pack_island_params.scale_to_fit = RNA_boolean_get(op->ptr, "scale");
1755 pack_island_params.merge_overlap = RNA_boolean_get(op->ptr, "merge_overlap");
1756
1757 if (RNA_boolean_get(op->ptr, "pin")) {
1758 pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin_method"));
1759 }
1760 else {
1761 pack_island_params.pin_method = ED_UVPACK_PIN_NONE;
1762 }
1763
1764 pack_island_params.margin_method = eUVPackIsland_MarginMethod(
1765 RNA_enum_get(op->ptr, "margin_method"));
1766 pack_island_params.margin = RNA_float_get(op->ptr, "margin");
1767 pack_island_params.shape_method = eUVPackIsland_ShapeMethod(
1768 RNA_enum_get(op->ptr, "shape_method"));
1769
1770 if (udim_source == PACK_UDIM_SRC_ACTIVE) {
1771 pack_island_params.setUDIMOffsetFromSpaceImage(sima);
1772 }
1773
1774 if (pid->use_job) {
1775 /* Setup job. */
1776 if (pid->wm->op_undo_depth == 0) {
1777 /* The job must do its own undo push. */
1778 pid->undo_context = C;
1779 pid->undo_str = op->type->name;
1780 }
1781
1782 wmJob *wm_job = WM_jobs_get(
1783 pid->wm, CTX_wm_window(C), scene, "Packing UVs", WM_JOB_PROGRESS, WM_JOB_TYPE_UV_PACK);
1785 WM_jobs_timer(wm_job, 0.1, 0, 0);
1786 WM_set_locked_interface(pid->wm, true);
1788
1789 WM_cursor_wait(true);
1790 G.is_break = false;
1791 WM_jobs_start(CTX_wm_manager(C), wm_job);
1792 return OPERATOR_FINISHED;
1793 }
1794
1795 wmJobWorkerStatus worker_status = {};
1796 pack_islands_startjob(pid, &worker_status);
1799
1800 return OPERATOR_FINISHED;
1801}
1802
1805 "SCALED",
1806 0,
1807 "Scaled",
1808 "Use scale of existing UVs to multiply margin"},
1809 {ED_UVPACK_MARGIN_ADD, "ADD", 0, "Add", "Just add the margin, ignoring any UV scale"},
1811 "FRACTION",
1812 0,
1813 "Fraction",
1814 "Specify a precise fraction of final UV output"},
1815 {0, nullptr, 0, nullptr, nullptr},
1816};
1817
1819 {ED_UVPACK_ROTATION_ANY, "ANY", 0, "Any", "Any angle is allowed for rotation"},
1821 "CARDINAL",
1822 0,
1823 "Cardinal",
1824 "Only 90 degree rotations are allowed"},
1826
1827#define PACK_ROTATE_METHOD_AXIS_ALIGNED_OFFSET 3
1829 "AXIS_ALIGNED",
1830 0,
1831 "Axis-aligned",
1832 "Rotated to a minimal rectangle, either vertical or horizontal"},
1834 "AXIS_ALIGNED_X",
1835 0,
1836 "Axis-aligned (Horizontal)",
1837 "Rotate islands to be aligned horizontally"},
1839 "AXIS_ALIGNED_Y",
1840 0,
1841 "Axis-aligned (Vertical)",
1842 "Rotate islands to be aligned vertically"},
1843 {0, nullptr, 0, nullptr, nullptr},
1844};
1845
1847 {ED_UVPACK_SHAPE_CONCAVE, "CONCAVE", 0, "Exact Shape (Concave)", "Uses exact geometry"},
1848 {ED_UVPACK_SHAPE_CONVEX, "CONVEX", 0, "Boundary Shape (Convex)", "Uses convex hull"},
1850 {ED_UVPACK_SHAPE_AABB, "AABB", 0, "Bounding Box", "Uses bounding boxes"},
1851 {0, nullptr, 0, nullptr, nullptr},
1852};
1853
1861 {ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Scale", "Pinned islands won't rescale"},
1862 {ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Rotation", "Pinned islands won't rotate"},
1864 "ROTATION_SCALE",
1865 0,
1866 "Rotation and Scale",
1867 "Pinned islands will translate only"},
1868 {ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "All", "Pinned islands are locked in place"},
1869 {0, nullptr, 0, nullptr, nullptr},
1870};
1871
1872static void uv_pack_islands_ui(bContext * /*C*/, wmOperator *op)
1873{
1874 uiLayout *layout = op->layout;
1875 uiLayoutSetPropSep(layout, true);
1876 uiLayoutSetPropDecorate(layout, false);
1877 uiItemR(layout, op->ptr, "shape_method", UI_ITEM_NONE, nullptr, ICON_NONE);
1878 uiItemR(layout, op->ptr, "scale", UI_ITEM_NONE, nullptr, ICON_NONE);
1879 {
1880 uiItemR(layout, op->ptr, "rotate", UI_ITEM_NONE, nullptr, ICON_NONE);
1881 uiLayout *sub = uiLayoutRow(layout, true);
1882 uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "rotate"));
1883 uiItemR(sub, op->ptr, "rotate_method", UI_ITEM_NONE, nullptr, ICON_NONE);
1884 uiItemS(layout);
1885 }
1886 uiItemR(layout, op->ptr, "margin_method", UI_ITEM_NONE, nullptr, ICON_NONE);
1887 uiItemR(layout, op->ptr, "margin", UI_ITEM_NONE, nullptr, ICON_NONE);
1888 uiItemS(layout);
1889 {
1890 uiItemR(layout, op->ptr, "pin", UI_ITEM_NONE, nullptr, ICON_NONE);
1891 uiLayout *sub = uiLayoutRow(layout, true);
1892 uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "pin"));
1893 uiItemR(sub, op->ptr, "pin_method", UI_ITEM_NONE, IFACE_("Lock Method"), ICON_NONE);
1894 uiItemS(layout);
1895 }
1896 uiItemR(layout, op->ptr, "merge_overlap", UI_ITEM_NONE, nullptr, ICON_NONE);
1897 uiItemR(layout, op->ptr, "udim_source", UI_ITEM_NONE, nullptr, ICON_NONE);
1898 uiItemS(layout);
1899}
1900
1901static int uv_pack_islands_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1902{
1903 return WM_operator_props_popup_confirm_ex(C, op, event, IFACE_("Pack Islands"), IFACE_("Pack"));
1904}
1905
1907{
1908 static const EnumPropertyItem pack_target[] = {
1909 {PACK_UDIM_SRC_CLOSEST, "CLOSEST_UDIM", 0, "Closest UDIM", "Pack islands to closest UDIM"},
1911 "ACTIVE_UDIM",
1912 0,
1913 "Active UDIM",
1914 "Pack islands to active UDIM image tile or UDIM grid tile where 2D cursor is located"},
1916 "ORIGINAL_AABB",
1917 0,
1918 "Original bounding box",
1919 "Pack to starting bounding box of islands"},
1920 {0, nullptr, 0, nullptr, nullptr},
1921 };
1922 /* identifiers */
1923 ot->name = "Pack Islands";
1924 ot->idname = "UV_OT_pack_islands";
1925 ot->description =
1926 "Transform all islands so that they fill up the UV/UDIM space as much as possible";
1927
1928#ifdef USE_INTERACTIVE_PACK
1930#else
1931 /* The operator will handle undo, so the job system can push() it after the job completes. */
1933#endif
1934
1935 /* api callbacks */
1937
1938#ifdef USE_INTERACTIVE_PACK
1940#else
1942#endif
1945
1946 /* properties */
1947 RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", "");
1948 RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands to improve layout");
1950 "rotate_method",
1953 "Rotation Method",
1954 "");
1955 RNA_def_boolean(ot->srna, "scale", true, "Scale", "Scale islands to fill unit square");
1957 ot->srna, "merge_overlap", false, "Merge Overlapping", "Overlapping islands stick together");
1959 "margin_method",
1962 "Margin Method",
1963 "");
1965 ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
1967 "pin",
1968 false,
1969 "Lock Pinned Islands",
1970 "Constrain islands containing any pinned UV's");
1972 "pin_method",
1975 "Pin Method",
1976 "");
1978 "shape_method",
1981 "Shape Method",
1982 "");
1983}
1984
1987/* -------------------------------------------------------------------- */
1992{
1993 const Scene *scene = CTX_data_scene(C);
1994 ViewLayer *view_layer = CTX_data_view_layer(C);
1995 ToolSettings *ts = scene->toolsettings;
1996 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1997
1998 UnwrapOptions options = unwrap_options_get(nullptr, nullptr, ts);
1999 options.topology_from_uvs = true;
2000 options.only_selected_faces = true;
2001 options.only_selected_uvs = true;
2002 options.fill_holes = false;
2003 options.correct_aspect = true;
2004
2006 scene, view_layer, CTX_wm_view3d(C));
2007
2008 if (!uvedit_have_selection_multi(scene, objects, &options)) {
2009 return OPERATOR_CANCELLED;
2010 }
2011
2012 /* RNA props */
2013 const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv");
2014 const bool shear = RNA_boolean_get(op->ptr, "shear");
2015
2016 ParamHandle *handle = construct_param_handle_multi(scene, objects, &options);
2017 blender::geometry::uv_parametrizer_average(handle, false, scale_uv, shear);
2019 delete (handle);
2020
2021 for (Object *obedit : objects) {
2023
2024 if (synced_selection && (em->bm->totvertsel == 0)) {
2025 continue;
2026 }
2027
2028 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
2029 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2030 }
2031 return OPERATOR_FINISHED;
2032}
2033
2035{
2036 /* identifiers */
2037 ot->name = "Average Islands Scale";
2038 ot->idname = "UV_OT_average_islands_scale";
2039 ot->description = "Average the size of separate UV islands, based on their area in 3D space";
2040
2042
2043 /* api callbacks */
2046
2047 /* properties */
2048 RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently");
2049 RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands");
2050}
2051
2054/* -------------------------------------------------------------------- */
2058static struct {
2062} g_live_unwrap = {nullptr};
2063
2065{
2066 /* NOTE: don't validate the timer, assume the timer passed in is valid. */
2067 return g_live_unwrap.timer == timer;
2068}
2069
2077{
2078 if (g_live_unwrap.timer == nullptr) {
2079 return false;
2080 }
2081 if (BLI_findindex(&wm->timers, g_live_unwrap.timer) != -1) {
2082 return false;
2083 }
2084 g_live_unwrap.timer = nullptr;
2085 return true;
2086}
2087
2088void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, wmWindow *win_modal)
2089{
2090 ParamHandle *handle = nullptr;
2092
2093 if (!ED_uvedit_test(obedit)) {
2094 return;
2095 }
2096
2097 UnwrapOptions options = unwrap_options_get(nullptr, obedit, scene->toolsettings);
2098 options.topology_from_uvs = false;
2099 options.only_selected_faces = false;
2100 options.only_selected_uvs = false;
2101
2102 if (options.use_subsurf) {
2103 handle = construct_param_handle_subsurfed(scene, obedit, em, &options, nullptr);
2104 }
2105 else {
2106 handle = construct_param_handle(scene, obedit, em->bm, &options, nullptr);
2107 }
2108
2109 if (options.use_slim) {
2110 options.slim.no_flip = false;
2111 options.slim.skip_init = true;
2113
2114 if (win_modal) {
2115 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
2116 /* Clear in the unlikely event this is still set. */
2118 BLI_assert(!g_live_unwrap.timer);
2119 g_live_unwrap.timer = WM_event_timer_add(wm, win_modal, TIMER, 0.01f);
2120 }
2121 }
2122 else {
2124 }
2125
2126 /* Create or increase size of g_live_unwrap.handles array */
2127 if (g_live_unwrap.handles == nullptr) {
2128 g_live_unwrap.len_alloc = 32;
2129 g_live_unwrap.handles = static_cast<ParamHandle **>(MEM_mallocN(
2130 sizeof(ParamHandle *) * g_live_unwrap.len_alloc, "uvedit_live_unwrap_liveHandles"));
2131 g_live_unwrap.len = 0;
2132 }
2133 if (g_live_unwrap.len >= g_live_unwrap.len_alloc) {
2134 g_live_unwrap.len_alloc *= 2;
2135 g_live_unwrap.handles = static_cast<ParamHandle **>(
2136 MEM_reallocN(g_live_unwrap.handles, sizeof(ParamHandle *) * g_live_unwrap.len_alloc));
2137 }
2138 g_live_unwrap.handles[g_live_unwrap.len] = handle;
2139 g_live_unwrap.len++;
2140}
2141
2143{
2144 if (g_live_unwrap.handles) {
2145 for (int i = 0; i < g_live_unwrap.len; i++) {
2146 if (uv_parametrizer_is_slim(g_live_unwrap.handles[i])) {
2148 }
2149 else {
2151 }
2152
2154 }
2155 }
2156}
2157
2158void ED_uvedit_live_unwrap_end(const bool cancel)
2159{
2160 if (g_live_unwrap.timer) {
2161 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
2163 if (g_live_unwrap.timer) {
2164 wmWindow *win = g_live_unwrap.timer->win;
2165 WM_event_timer_remove(wm, win, g_live_unwrap.timer);
2166 g_live_unwrap.timer = nullptr;
2167 }
2168 }
2169
2170 if (g_live_unwrap.handles) {
2171 for (int i = 0; i < g_live_unwrap.len; i++) {
2172 if (uv_parametrizer_is_slim(g_live_unwrap.handles[i])) {
2174 }
2175 else {
2177 }
2178
2179 if (cancel) {
2181 }
2182 delete (g_live_unwrap.handles[i]);
2183 }
2184 MEM_freeN(g_live_unwrap.handles);
2185 g_live_unwrap.handles = nullptr;
2186 g_live_unwrap.len = 0;
2187 g_live_unwrap.len_alloc = 0;
2188 }
2189}
2190
2193/* -------------------------------------------------------------------- */
2197#define VIEW_ON_EQUATOR 0
2198#define VIEW_ON_POLES 1
2199#define ALIGN_TO_OBJECT 2
2200
2201#define POLAR_ZX 0
2202#define POLAR_ZY 1
2203
2204enum {
2206 FAN = 1,
2207};
2208
2209static void uv_map_transform_calc_bounds(BMEditMesh *em, float r_min[3], float r_max[3])
2210{
2211 BMFace *efa;
2212 BMIter iter;
2213 INIT_MINMAX(r_min, r_max);
2214 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2216 BM_face_calc_bounds_expand(efa, r_min, r_max);
2217 }
2218 }
2219}
2220
2221static void uv_map_transform_calc_center_median(BMEditMesh *em, float r_center[3])
2222{
2223 BMFace *efa;
2224 BMIter iter;
2225 uint center_accum_num = 0;
2226 zero_v3(r_center);
2227 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2229 float center[3];
2230 BM_face_calc_center_median(efa, center);
2231 add_v3_v3(r_center, center);
2232 center_accum_num += 1;
2233 }
2234 }
2235 mul_v3_fl(r_center, 1.0f / float(center_accum_num));
2236}
2237
2238static void uv_map_transform_center(const Scene *scene,
2239 View3D *v3d,
2240 Object *ob,
2241 BMEditMesh *em,
2242 float r_center[3],
2243 float r_bounds[2][3])
2244{
2245 /* only operates on the edit object - this is all that's needed now */
2246 const int around = (v3d) ? scene->toolsettings->transform_pivot_point :
2248
2249 float bounds[2][3];
2250 INIT_MINMAX(bounds[0], bounds[1]);
2251 bool is_minmax_set = false;
2252
2253 switch (around) {
2254 case V3D_AROUND_CENTER_BOUNDS: /* bounding box center */
2255 {
2256 uv_map_transform_calc_bounds(em, bounds[0], bounds[1]);
2257 is_minmax_set = true;
2258 mid_v3_v3v3(r_center, bounds[0], bounds[1]);
2259 break;
2260 }
2263 break;
2264 }
2265 case V3D_AROUND_CURSOR: /* cursor center */
2266 {
2267 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
2268 mul_v3_m4v3(r_center, ob->world_to_object().ptr(), scene->cursor.location);
2269 break;
2270 }
2271 case V3D_AROUND_ACTIVE: {
2272 BMEditSelection ese;
2273 if (BM_select_history_active_get(em->bm, &ese)) {
2274 BM_editselection_center(&ese, r_center);
2275 break;
2276 }
2278 }
2279 case V3D_AROUND_LOCAL_ORIGINS: /* object center */
2280 default:
2281 zero_v3(r_center);
2282 break;
2283 }
2284
2285 /* if this is passed, always set! */
2286 if (r_bounds) {
2287 if (!is_minmax_set) {
2288 uv_map_transform_calc_bounds(em, bounds[0], bounds[1]);
2289 }
2290 copy_v3_v3(r_bounds[0], bounds[0]);
2291 copy_v3_v3(r_bounds[1], bounds[1]);
2292 }
2293}
2294
2295static void uv_map_rotation_matrix_ex(float result[4][4],
2296 RegionView3D *rv3d,
2297 Object *ob,
2298 float upangledeg,
2299 float sideangledeg,
2300 float radius,
2301 const float offset[4])
2302{
2303 float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
2304 float sideangle = 0.0f, upangle = 0.0f;
2305
2306 /* get rotation of the current view matrix */
2307 if (rv3d) {
2308 copy_m4_m4(viewmatrix, rv3d->viewmat);
2309 }
2310 else {
2311 unit_m4(viewmatrix);
2312 }
2313
2314 /* but shifting */
2315 zero_v3(viewmatrix[3]);
2316
2317 /* get rotation of the current object matrix */
2318 copy_m4_m4(rotobj, ob->object_to_world().ptr());
2319 zero_v3(rotobj[3]);
2320
2321 /* but shifting */
2322 add_v4_v4(rotobj[3], offset);
2323 rotobj[3][3] = 0.0f;
2324
2325 zero_m4(rotup);
2326 zero_m4(rotside);
2327
2328 /* Compensate front/side.. against opengl x,y,z world definition.
2329 * This is "a sledgehammer to crack a nut" (overkill), a few plus minus 1 will do here.
2330 * I wanted to keep the reason here, so we're rotating. */
2331 sideangle = float(M_PI) * (sideangledeg + 180.0f) / 180.0f;
2332 rotside[0][0] = cosf(sideangle);
2333 rotside[0][1] = -sinf(sideangle);
2334 rotside[1][0] = sinf(sideangle);
2335 rotside[1][1] = cosf(sideangle);
2336 rotside[2][2] = 1.0f;
2337
2338 upangle = float(M_PI) * upangledeg / 180.0f;
2339 rotup[1][1] = cosf(upangle) / radius;
2340 rotup[1][2] = -sinf(upangle) / radius;
2341 rotup[2][1] = sinf(upangle) / radius;
2342 rotup[2][2] = cosf(upangle) / radius;
2343 rotup[0][0] = 1.0f / radius;
2344
2345 /* Calculate transforms. */
2346 mul_m4_series(result, rotup, rotside, viewmatrix, rotobj);
2347}
2348
2349static void uv_map_transform(bContext *C, wmOperator *op, float rotmat[3][3])
2350{
2351 Object *obedit = CTX_data_edit_object(C);
2353
2354 const int align = RNA_enum_get(op->ptr, "align");
2355 const int direction = RNA_enum_get(op->ptr, "direction");
2356 const float radius = RNA_struct_find_property(op->ptr, "radius") ?
2357 RNA_float_get(op->ptr, "radius") :
2358 1.0f;
2359
2360 /* Be compatible to the "old" sphere/cylinder mode. */
2361 if (direction == ALIGN_TO_OBJECT) {
2362 unit_m3(rotmat);
2363
2364 if (align == POLAR_ZY) {
2365 rotmat[0][0] = 0.0f;
2366 rotmat[0][1] = 1.0f;
2367 rotmat[1][0] = -1.0f;
2368 rotmat[1][1] = 0.0f;
2369 }
2370 return;
2371 }
2372
2373 const float up_angle_deg = (direction == VIEW_ON_EQUATOR) ? 90.0f : 0.0f;
2374 const float side_angle_deg = (align == POLAR_ZY) == (direction == VIEW_ON_EQUATOR) ? 90.0f :
2375 0.0f;
2376 const float offset[4] = {0};
2377 float rotmat4[4][4];
2378 uv_map_rotation_matrix_ex(rotmat4, rv3d, obedit, up_angle_deg, side_angle_deg, radius, offset);
2379 copy_m3_m4(rotmat, rotmat4);
2380}
2381
2383{
2384 static const EnumPropertyItem direction_items[] = {
2385 {VIEW_ON_EQUATOR, "VIEW_ON_EQUATOR", 0, "View on Equator", "3D view is on the equator"},
2386 {VIEW_ON_POLES, "VIEW_ON_POLES", 0, "View on Poles", "3D view is on the poles"},
2388 "ALIGN_TO_OBJECT",
2389 0,
2390 "Align to Object",
2391 "Align according to object transform"},
2392 {0, nullptr, 0, nullptr, nullptr},
2393 };
2394 static const EnumPropertyItem align_items[] = {
2395 {POLAR_ZX, "POLAR_ZX", 0, "Polar ZX", "Polar 0 is X"},
2396 {POLAR_ZY, "POLAR_ZY", 0, "Polar ZY", "Polar 0 is Y"},
2397 {0, nullptr, 0, nullptr, nullptr},
2398 };
2399
2400 static const EnumPropertyItem pole_items[] = {
2401 {PINCH, "PINCH", 0, "Pinch", "UVs are pinched at the poles"},
2402 {FAN, "FAN", 0, "Fan", "UVs are fanned at the poles"},
2403 {0, nullptr, 0, nullptr, nullptr},
2404 };
2405
2407 "direction",
2408 direction_items,
2410 "Direction",
2411 "Direction of the sphere or cylinder");
2413 "align",
2414 align_items,
2415 POLAR_ZX,
2416 "Align",
2417 "How to determine rotation around the pole");
2418 RNA_def_enum(ot->srna, "pole", pole_items, PINCH, "Pole", "How to handle faces at the poles");
2420 "seam",
2421 false,
2422 "Preserve Seams",
2423 "Separate projections by islands isolated by seams");
2424
2425 if (radius) {
2427 "radius",
2428 1.0f,
2429 0.0f,
2430 FLT_MAX,
2431 "Radius",
2432 "Radius of the sphere or cylinder",
2433 0.0001f,
2434 100.0f);
2435 }
2436}
2437
2439 const int cd_loop_uv_offset,
2440 const float aspect_y)
2441{
2442 BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */
2443 BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */
2444
2445 BMLoop *l;
2446 BMIter iter;
2447 BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
2448 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
2449 if (aspect_y > 1.0f) {
2450 /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */
2451 luv[0] = luv[0] / aspect_y + (0.5f - 0.5f / aspect_y);
2452 }
2453 else {
2454 /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */
2455 luv[1] = luv[1] * aspect_y + (0.5f - 0.5f * aspect_y);
2456 }
2457 }
2458}
2459
2461{
2462 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
2463 const float aspect_y = ED_uvedit_get_aspect_y(ob);
2464 if (aspect_y == 1.0f) {
2465 /* Scaling by 1.0 has no effect. */
2466 return;
2467 }
2468 BMFace *efa;
2469 BMIter iter;
2470 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2472 shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
2473 }
2474 }
2475}
2476
2478{
2479 const int materials_num = ob->totcol;
2480 if (materials_num == 0) {
2481 /* Without any materials, there is no aspect_y information and nothing to do. */
2482 return;
2483 }
2484
2485 blender::Array<float, 16> material_aspect_y(materials_num, -1);
2486 /* Lazily initialize aspect ratio for materials. */
2487
2488 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
2489
2490 BMFace *efa;
2491 BMIter iter;
2492 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2493 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2494 continue;
2495 }
2496
2497 const int material_index = efa->mat_nr;
2498 if (UNLIKELY(material_index < 0 || material_index >= materials_num)) {
2499 /* The index might be for a material slot which is not currently setup. */
2500 continue;
2501 }
2502
2503 float aspect_y = material_aspect_y[material_index];
2504 if (aspect_y == -1.0f) {
2505 /* Lazily initialize aspect ratio for materials. */
2506 float aspx, aspy;
2507 ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy);
2508 aspect_y = aspx / aspy;
2509 material_aspect_y[material_index] = aspect_y;
2510 }
2511
2512 if (aspect_y == 1.0f) {
2513 /* Scaling by 1.0 has no effect. */
2514 continue;
2515 }
2516 shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
2517 }
2518}
2519
2520#undef VIEW_ON_EQUATOR
2521#undef VIEW_ON_POLES
2522#undef ALIGN_TO_OBJECT
2523
2524#undef POLAR_ZX
2525#undef POLAR_ZY
2526
2529/* -------------------------------------------------------------------- */
2533static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds)
2534{
2536 "correct_aspect",
2537 true,
2538 "Correct Aspect",
2539 "Map UVs taking image aspect ratio into account");
2540 /* Optional, since not all unwrapping types need to be clipped. */
2541 if (clip_to_bounds) {
2543 "clip_to_bounds",
2544 false,
2545 "Clip to Bounds",
2546 "Clip UV coordinates to bounds after unwrapping");
2547 }
2549 "scale_to_bounds",
2550 false,
2551 "Scale to Bounds",
2552 "Scale UV coordinates to bounds after unwrapping");
2553}
2554
2559
2567static void uv_map_clip_correct(const Scene *scene,
2568 const Span<Object *> objects,
2569 wmOperator *op,
2570 bool per_face_aspect,
2571 bool only_selected_uvs)
2572{
2573 BMFace *efa;
2574 BMLoop *l;
2575 BMIter iter, liter;
2576 float dx, dy, min[2], max[2];
2577 const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
2578 const bool clip_to_bounds = (RNA_struct_find_property(op->ptr, "clip_to_bounds") &&
2579 RNA_boolean_get(op->ptr, "clip_to_bounds"));
2580 const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds");
2581
2582 INIT_MINMAX2(min, max);
2583
2584 for (Object *ob : objects) {
2586 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2587
2588 /* Correct for image aspect ratio. */
2589 if (correct_aspect) {
2590 if (per_face_aspect) {
2592 }
2593 else {
2594 correct_uv_aspect(ob, em);
2595 }
2596 }
2597
2598 if (scale_to_bounds) {
2599 /* find uv limits */
2600 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2601 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2602 continue;
2603 }
2604
2605 if (only_selected_uvs && !uvedit_face_select_test(scene, efa, offsets)) {
2606 continue;
2607 }
2608
2609 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2610 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
2611 minmax_v2v2_v2(min, max, luv);
2612 }
2613 }
2614 }
2615 else if (clip_to_bounds) {
2616 /* clipping and wrapping */
2617 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2618 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2619 continue;
2620 }
2621
2622 if (only_selected_uvs && !uvedit_face_select_test(scene, efa, offsets)) {
2623 continue;
2624 }
2625
2626 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2627 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
2628 clamp_v2(luv, 0.0f, 1.0f);
2629 }
2630 }
2631 }
2632 }
2633
2634 if (scale_to_bounds) {
2635 /* rescale UV to be in 1/1 */
2636 dx = (max[0] - min[0]);
2637 dy = (max[1] - min[1]);
2638
2639 if (dx > 0.0f) {
2640 dx = 1.0f / dx;
2641 }
2642 if (dy > 0.0f) {
2643 dy = 1.0f / dy;
2644 }
2645
2646 if (dx == 1.0f && dy == 1.0f && min[0] == 0.0f && min[1] == 0.0f) {
2647 /* Scaling by 1.0, without translating, has no effect. */
2648 return;
2649 }
2650
2651 for (Object *ob : objects) {
2653 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2654
2655 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2656 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2657 continue;
2658 }
2659
2660 if (only_selected_uvs && !uvedit_face_select_test(scene, efa, offsets)) {
2661 continue;
2662 }
2663
2664 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2665 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
2666
2667 luv[0] = (luv[0] - min[0]) * dx;
2668 luv[1] = (luv[1] - min[1]) * dy;
2669 }
2670 }
2671 }
2672 }
2673}
2674
2677/* -------------------------------------------------------------------- */
2681/* Assumes UV Map exists, doesn't run update functions. */
2682static void uvedit_unwrap(const Scene *scene,
2683 Object *obedit,
2684 const UnwrapOptions *options,
2685 int *r_count_changed,
2686 int *r_count_failed)
2687{
2690 return;
2691 }
2692
2693 bool use_subsurf;
2694 modifier_unwrap_state(obedit, options, &use_subsurf);
2695
2696 ParamHandle *handle;
2697 if (use_subsurf) {
2698 handle = construct_param_handle_subsurfed(scene, obedit, em, options, r_count_failed);
2699 }
2700 else {
2701 handle = construct_param_handle(scene, obedit, em->bm, options, r_count_failed);
2702 }
2703
2704 if (options->use_slim) {
2705 uv_parametrizer_slim_solve(handle, &options->slim, r_count_changed, r_count_failed);
2706 }
2707 else {
2709 blender::geometry::uv_parametrizer_lscm_solve(handle, r_count_changed, r_count_failed);
2711 }
2712
2713 blender::geometry::uv_parametrizer_average(handle, true, false, false);
2714
2716
2717 delete (handle);
2718}
2719
2720static void uvedit_unwrap_multi(const Scene *scene,
2721 const Span<Object *> objects,
2722 const UnwrapOptions *options,
2723 int *r_count_changed = nullptr,
2724 int *r_count_failed = nullptr)
2725{
2726 for (Object *obedit : objects) {
2727 uvedit_unwrap(scene, obedit, options, r_count_changed, r_count_failed);
2728 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
2729 WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
2730 }
2731}
2732
2733void ED_uvedit_live_unwrap(const Scene *scene, const Span<Object *> objects)
2734{
2735 if (scene->toolsettings->edge_mode_live_unwrap) {
2736 UnwrapOptions options = unwrap_options_get(nullptr, nullptr, scene->toolsettings);
2737 options.topology_from_uvs = false;
2738 options.only_selected_faces = false;
2739 options.only_selected_uvs = false;
2740
2741 uvedit_unwrap_multi(scene, objects, &options, nullptr);
2742
2743 blender::geometry::UVPackIsland_Params pack_island_params;
2744 pack_island_params.setFromUnwrapOptions(options);
2745 pack_island_params.rotate_method = ED_UVPACK_ROTATION_ANY;
2746 pack_island_params.pin_method = ED_UVPACK_PIN_IGNORE;
2747 pack_island_params.margin_method = ED_UVPACK_MARGIN_SCALED;
2748 pack_island_params.margin = scene->toolsettings->uvcalc_margin;
2749
2750 uvedit_pack_islands_multi(scene, objects, nullptr, nullptr, false, true, &pack_island_params);
2751 }
2752}
2753
2754enum {
2757};
2758
2760{
2761 ViewLayer *view_layer = CTX_data_view_layer(C);
2762 const Scene *scene = CTX_data_scene(C);
2763 int reported_errors = 0;
2764
2766 scene, view_layer, CTX_wm_view3d(C));
2767
2768 unwrap_options_sync_toolsettings(op, scene->toolsettings);
2769
2770 UnwrapOptions options = unwrap_options_get(op, nullptr, nullptr);
2771 options.topology_from_uvs = false;
2772 options.only_selected_faces = true;
2773 options.only_selected_uvs = false;
2774
2775 /* We will report an error unless at least one object
2776 * has the subsurf modifier in the right place. */
2777 bool subsurf_error = options.use_subsurf;
2778
2779 if (CTX_wm_space_image(C)) {
2780 /* Inside the UV Editor, only unwrap selected UVs. */
2781 options.only_selected_uvs = true;
2782 options.pin_unselected = true;
2783 }
2784
2785 if (!uvedit_have_selection_multi(scene, objects, &options)) {
2786 return OPERATOR_CANCELLED;
2787 }
2788
2789 /* add uvs if they don't exist yet */
2790 for (Object *obedit : objects) {
2791 float obsize[3];
2792 bool use_subsurf_final;
2793
2794 if (!ED_uvedit_ensure_uvs(obedit)) {
2795 continue;
2796 }
2797
2798 if (subsurf_error) {
2799 /* Double up the check here but better keep uvedit_unwrap interface simple and not
2800 * pass operator for warning append. */
2801 modifier_unwrap_state(obedit, &options, &use_subsurf_final);
2802 if (use_subsurf_final) {
2803 subsurf_error = false;
2804 }
2805 }
2806
2807 if (reported_errors & (UNWRAP_ERROR_NONUNIFORM | UNWRAP_ERROR_NEGATIVE)) {
2808 continue;
2809 }
2810
2811 mat4_to_size(obsize, obedit->object_to_world().ptr());
2812 if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f)) {
2813 if ((reported_errors & UNWRAP_ERROR_NONUNIFORM) == 0) {
2814 BKE_report(op->reports,
2815 RPT_INFO,
2816 "Object has non-uniform scale, unwrap will operate on a non-scaled version of "
2817 "the mesh");
2818 reported_errors |= UNWRAP_ERROR_NONUNIFORM;
2819 }
2820 }
2821 else if (is_negative_m4(obedit->object_to_world().ptr())) {
2822 if ((reported_errors & UNWRAP_ERROR_NEGATIVE) == 0) {
2823 BKE_report(
2824 op->reports,
2825 RPT_INFO,
2826 "Object has negative scale, unwrap will operate on a non-flipped version of the mesh");
2827 reported_errors |= UNWRAP_ERROR_NEGATIVE;
2828 }
2829 }
2830 }
2831
2832 if (subsurf_error) {
2833 BKE_report(op->reports,
2834 RPT_INFO,
2835 "Subdivision Surface modifier needs to be first to work with unwrap");
2836 }
2837
2838 /* execute unwrap */
2839 int count_changed = 0;
2840 int count_failed = 0;
2841 uvedit_unwrap_multi(scene, objects, &options, &count_changed, &count_failed);
2842
2843 blender::geometry::UVPackIsland_Params pack_island_params;
2844 pack_island_params.setFromUnwrapOptions(options);
2845 pack_island_params.rotate_method = ED_UVPACK_ROTATION_ANY;
2846 pack_island_params.pin_method = ED_UVPACK_PIN_IGNORE;
2847 pack_island_params.margin_method = eUVPackIsland_MarginMethod(
2848 RNA_enum_get(op->ptr, "margin_method"));
2849 pack_island_params.margin = RNA_float_get(op->ptr, "margin");
2850
2851 uvedit_pack_islands_multi(scene, objects, nullptr, nullptr, false, true, &pack_island_params);
2852
2853 if (count_failed == 0 && count_changed == 0) {
2854 BKE_report(op->reports,
2856 "Unwrap could not solve any island(s), edge seams may need to be added");
2857 }
2858 else if (count_failed) {
2859 BKE_reportf(op->reports,
2861 "Unwrap failed to solve %d of %d island(s), edge seams may need to be added",
2862 count_failed,
2863 count_changed + count_failed);
2864 }
2865
2866 return OPERATOR_FINISHED;
2867}
2868
2869static void unwrap_draw(bContext * /*C*/, wmOperator *op)
2870{
2871 uiLayout *layout = op->layout;
2872
2873 uiLayoutSetPropSep(layout, true);
2874 uiLayoutSetPropDecorate(layout, false);
2875
2876 /* Main draw call */
2877 PointerRNA ptr = RNA_pointer_create(nullptr, op->type->srna, op->properties);
2878
2879 uiLayout *col;
2880
2881 col = uiLayoutColumn(layout, true);
2882 uiItemR(col, &ptr, "method", UI_ITEM_NONE, nullptr, ICON_NONE);
2883 bool is_slim = RNA_enum_get(op->ptr, "method") == UVCALC_UNWRAP_METHOD_MINIMUM_STRETCH;
2884
2885 if (is_slim) {
2886 uiItemR(col, &ptr, "iterations", UI_ITEM_NONE, nullptr, ICON_NONE);
2887 uiItemR(col, &ptr, "no_flip", UI_ITEM_NONE, nullptr, ICON_NONE);
2888
2889 uiItemS(col);
2890 uiItemR(col, &ptr, "use_weights", UI_ITEM_NONE, nullptr, ICON_NONE);
2891
2892 if (RNA_boolean_get(op->ptr, "use_weights")) {
2893 col = uiLayoutColumn(layout, true);
2894 uiItemR(col, &ptr, "weight_group", UI_ITEM_NONE, nullptr, ICON_NONE);
2895 uiItemR(col, &ptr, "weight_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
2896 }
2897 }
2898 else {
2899 uiItemR(col, &ptr, "fill_holes", UI_ITEM_NONE, nullptr, ICON_NONE);
2900 }
2901
2902 uiItemS(col);
2903 uiItemR(col, &ptr, "use_subsurf_data", UI_ITEM_NONE, nullptr, ICON_NONE);
2904
2905 uiItemS(col);
2906 uiItemR(col, &ptr, "correct_aspect", UI_ITEM_NONE, nullptr, ICON_NONE);
2907 uiItemR(col, &ptr, "margin_method", UI_ITEM_NONE, nullptr, ICON_NONE);
2908 uiItemR(col, &ptr, "margin", UI_ITEM_NONE, nullptr, ICON_NONE);
2909}
2910
2912{
2913 const ToolSettings *tool_settings_default = DNA_struct_default_get(ToolSettings);
2914
2915 static const EnumPropertyItem method_items[] = {
2916 {UVCALC_UNWRAP_METHOD_ANGLE, "ANGLE_BASED", 0, "Angle Based", ""},
2917 {UVCALC_UNWRAP_METHOD_CONFORMAL, "CONFORMAL", 0, "Conformal", ""},
2918 {UVCALC_UNWRAP_METHOD_MINIMUM_STRETCH, "MINIMUM_STRETCH", 0, "Minimum Stretch", ""},
2919 {0, nullptr, 0, nullptr, nullptr},
2920 };
2921
2922 /* identifiers */
2923 ot->name = "Unwrap";
2924 ot->description = "Unwrap the mesh of the object being edited";
2925 ot->idname = "UV_OT_unwrap";
2927
2928 /* api callbacks */
2929 ot->exec = unwrap_exec;
2931
2932 /* Only draw relevant ui elements */
2933 ot->ui = unwrap_draw;
2934
2935 /* properties */
2936 ot->prop = RNA_def_enum(
2937 ot->srna,
2938 "method",
2939 method_items,
2940 tool_settings_default->unwrapper,
2941 "Method",
2942 "Unwrapping method (Angle Based usually gives better results than Conformal, while "
2943 "being somewhat slower)");
2945 "fill_holes",
2946 tool_settings_default->uvcalc_flag & UVCALC_FILLHOLES,
2947 "Fill Holes",
2948 "Virtually fill holes in mesh before unwrapping, to better avoid overlaps and "
2949 "preserve symmetry");
2951 "correct_aspect",
2952 !(tool_settings_default->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT),
2953 "Correct Aspect",
2954 "Map UVs taking image aspect ratio into account");
2956 ot->srna,
2957 "use_subsurf_data",
2958 false,
2959 "Use Subdivision Surface",
2960 "Map UVs taking vertex position after Subdivision Surface modifier has been applied");
2962 "margin_method",
2965 "Margin Method",
2966 "");
2968 ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
2969
2970 /* SLIM only */
2972 "no_flip",
2973 tool_settings_default->uvcalc_flag & UVCALC_UNWRAP_NO_FLIP,
2974 "No Flip",
2975 "Prevent flipping UV's, "
2976 "flipping may lower distortion depending on the position of pins");
2977
2979 "iterations",
2980 tool_settings_default->uvcalc_iterations,
2981 0,
2982 10000,
2983 "Iterations",
2984 "Number of iterations when \"Minimum Stretch\" method is used",
2985 1,
2986 30);
2987
2989 "use_weights",
2990 tool_settings_default->uvcalc_flag & UVCALC_UNWRAP_USE_WEIGHTS,
2991 "Importance Weights",
2992 "Whether to take into account per-vertex importance weights");
2994 "weight_group",
2995 tool_settings_default->uvcalc_weight_group,
2997 "Weight Group",
2998 "Vertex group name for importance weights (modulating the deform)");
3000 ot->srna,
3001 "weight_factor",
3002 tool_settings_default->uvcalc_weight_factor,
3003 -10000.0,
3004 10000.0,
3005 "Weight Factor",
3006 "How much influence the weightmap has for weighted parameterization, 0 being no influence",
3007 -10.0,
3008 10.0);
3009}
3010
3013/* -------------------------------------------------------------------- */
3017/* Ignore all areas below this, as the UVs get zeroed. */
3018static const float smart_uv_project_area_ignore = 1e-12f;
3019
3021 float area;
3023};
3024
3025static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p)
3026{
3027 const ThickFace *tf_a = (ThickFace *)tf_a_p;
3028 const ThickFace *tf_b = (ThickFace *)tf_b_p;
3029
3030 /* Ignore the area of small faces.
3031 * Also, order checks so `!isfinite(...)` values are counted as zero area. */
3032 if (!((tf_a->area > smart_uv_project_area_ignore) ||
3034 {
3035 return 0;
3036 }
3037
3038 if (tf_a->area < tf_b->area) {
3039 return 1;
3040 }
3041 if (tf_a->area > tf_b->area) {
3042 return -1;
3043 }
3044 return 0;
3045}
3046
3048 const ThickFace *thick_faces,
3049 const uint thick_faces_len,
3050 BMesh *bm,
3051 const float project_angle_limit_half_cos,
3052 const float project_angle_limit_cos,
3053 const float area_weight)
3054{
3055 if (UNLIKELY(thick_faces_len == 0)) {
3056 return {};
3057 }
3058
3059 const float *project_normal = thick_faces[0].efa->no;
3060
3061 blender::Vector<const ThickFace *> project_thick_faces;
3062 blender::Vector<blender::float3> project_normal_array;
3063
3065
3066 while (true) {
3067 for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
3068 if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) {
3069 continue;
3070 }
3071
3072 if (dot_v3v3(thick_faces[f_index].efa->no, project_normal) > project_angle_limit_half_cos) {
3073 project_thick_faces.append(&thick_faces[f_index]);
3074 BM_elem_flag_set(thick_faces[f_index].efa, BM_ELEM_TAG, true);
3075 }
3076 }
3077
3078 float average_normal[3] = {0.0f, 0.0f, 0.0f};
3079
3080 if (area_weight <= 0.0f) {
3081 for (int f_proj_index = 0; f_proj_index < project_thick_faces.size(); f_proj_index++) {
3082 const ThickFace *tf = project_thick_faces[f_proj_index];
3083 add_v3_v3(average_normal, tf->efa->no);
3084 }
3085 }
3086 else if (area_weight >= 1.0f) {
3087 for (int f_proj_index = 0; f_proj_index < project_thick_faces.size(); f_proj_index++) {
3088 const ThickFace *tf = project_thick_faces[f_proj_index];
3089 madd_v3_v3fl(average_normal, tf->efa->no, tf->area);
3090 }
3091 }
3092 else {
3093 for (int f_proj_index = 0; f_proj_index < project_thick_faces.size(); f_proj_index++) {
3094 const ThickFace *tf = project_thick_faces[f_proj_index];
3095 const float area_blend = (tf->area * area_weight) + (1.0f - area_weight);
3096 madd_v3_v3fl(average_normal, tf->efa->no, area_blend);
3097 }
3098 }
3099
3100 /* Avoid NAN. */
3101 if (normalize_v3(average_normal) != 0.0f) {
3102 project_normal_array.append(average_normal);
3103 }
3104
3105 /* Find the most unique angle that points away from other normals. */
3106 float anble_best = 1.0f;
3107 uint angle_best_index = 0;
3108
3109 for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
3110 if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) {
3111 continue;
3112 }
3113
3114 float angle_test = -1.0f;
3115 for (int p_index = 0; p_index < project_normal_array.size(); p_index++) {
3116 angle_test = max_ff(angle_test,
3117 dot_v3v3(project_normal_array[p_index], thick_faces[f_index].efa->no));
3118 }
3119
3120 if (angle_test < anble_best) {
3121 anble_best = angle_test;
3122 angle_best_index = f_index;
3123 }
3124 }
3125
3126 if (anble_best < project_angle_limit_cos) {
3127 project_normal = thick_faces[angle_best_index].efa->no;
3128 project_thick_faces.clear();
3129 project_thick_faces.append(&thick_faces[angle_best_index]);
3130 BM_elem_flag_enable(thick_faces[angle_best_index].efa, BM_ELEM_TAG);
3131 }
3132 else {
3133 if (project_normal_array.size() >= 1) {
3134 break;
3135 }
3136 }
3137 }
3138
3140
3141 return project_normal_array;
3142}
3143
3145{
3146 Scene *scene = CTX_data_scene(C);
3147 ViewLayer *view_layer = CTX_data_view_layer(C);
3148
3149 /* May be nullptr. */
3150 View3D *v3d = CTX_wm_view3d(C);
3151
3152 bool only_selected_uvs = false;
3153 if (CTX_wm_space_image(C)) {
3154 /* Inside the UV Editor, only project selected UVs. */
3155 only_selected_uvs = true;
3156 }
3157
3158 const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit");
3159 const float island_margin = RNA_float_get(op->ptr, "island_margin");
3160 const float area_weight = RNA_float_get(op->ptr, "area_weight");
3161
3162 const float project_angle_limit_cos = cosf(project_angle_limit);
3163 const float project_angle_limit_half_cos = cosf(project_angle_limit / 2);
3164
3165 /* Memory arena for list links (cleared for each object). */
3167
3169 scene, view_layer, v3d);
3170
3171 Vector<Object *> objects_changed;
3172
3173 BMFace *efa;
3174 BMIter iter;
3175
3176 for (const int ob_index : objects.index_range()) {
3177 Object *obedit = objects[ob_index];
3179 bool changed = false;
3180
3181 if (!ED_uvedit_ensure_uvs(obedit)) {
3182 continue;
3183 }
3184
3185 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3186 BLI_assert(offsets.uv >= 0);
3187 ThickFace *thick_faces = static_cast<ThickFace *>(
3188 MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__));
3189
3190 uint thick_faces_len = 0;
3191 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3192 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
3193 continue;
3194 }
3195
3196 if (only_selected_uvs) {
3197 if (!uvedit_face_select_test(scene, efa, offsets)) {
3198 uvedit_face_select_disable(scene, em->bm, efa, offsets);
3199 continue;
3200 }
3201 }
3202
3203 thick_faces[thick_faces_len].area = BM_face_calc_area(efa);
3204 thick_faces[thick_faces_len].efa = efa;
3205 thick_faces_len++;
3206 }
3207
3208 qsort(thick_faces, thick_faces_len, sizeof(ThickFace), smart_uv_project_thickface_area_cmp_fn);
3209
3210 /* Remove all zero area faces. */
3211 while ((thick_faces_len > 0) &&
3212 !(thick_faces[thick_faces_len - 1].area > smart_uv_project_area_ignore))
3213 {
3214
3215 /* Zero UVs so they don't overlap with other faces being unwrapped. */
3216 BMIter liter;
3217 BMLoop *l;
3218 BM_ITER_ELEM (l, &liter, thick_faces[thick_faces_len - 1].efa, BM_LOOPS_OF_FACE) {
3219 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3220 zero_v2(luv);
3221 changed = true;
3222 }
3223
3224 thick_faces_len -= 1;
3225 }
3226
3227 blender::Vector<blender::float3> project_normal_array =
3229 thick_faces_len,
3230 em->bm,
3231 project_angle_limit_half_cos,
3232 project_angle_limit_cos,
3233 area_weight);
3234
3235 if (project_normal_array.is_empty()) {
3236 MEM_freeN(thick_faces);
3237 continue;
3238 }
3239
3240 /* After finding projection vectors, we find the uv positions. */
3241 LinkNode **thickface_project_groups = static_cast<LinkNode **>(
3242 MEM_callocN(sizeof(*thickface_project_groups) * project_normal_array.size(), __func__));
3243
3244 BLI_memarena_clear(arena);
3245
3246 for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) {
3247 const float *f_normal = thick_faces[f_index].efa->no;
3248
3249 float angle_best = dot_v3v3(f_normal, project_normal_array[0]);
3250 uint angle_best_index = 0;
3251
3252 for (int p_index = 1; p_index < project_normal_array.size(); p_index++) {
3253 const float angle_test = dot_v3v3(f_normal, project_normal_array[p_index]);
3254 if (angle_test > angle_best) {
3255 angle_best = angle_test;
3256 angle_best_index = p_index;
3257 }
3258 }
3259
3261 &thickface_project_groups[angle_best_index], &thick_faces[f_index], arena);
3262 }
3263
3264 for (int p_index = 0; p_index < project_normal_array.size(); p_index++) {
3265 if (thickface_project_groups[p_index] == nullptr) {
3266 continue;
3267 }
3268
3269 float axis_mat[3][3];
3270 axis_dominant_v3_to_m3(axis_mat, project_normal_array[p_index]);
3271
3272 for (LinkNode *list = thickface_project_groups[p_index]; list; list = list->next) {
3273 ThickFace *tf = static_cast<ThickFace *>(list->link);
3274 BMIter liter;
3275 BMLoop *l;
3276 BM_ITER_ELEM (l, &liter, tf->efa, BM_LOOPS_OF_FACE) {
3277 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3278 mul_v2_m3v3(luv, axis_mat, l->v->co);
3279 }
3280 changed = true;
3281 }
3282 }
3283
3284 MEM_freeN(thick_faces);
3285
3286 /* No need to free the lists in 'thickface_project_groups' values as the 'arena' is used. */
3287 MEM_freeN(thickface_project_groups);
3288
3289 if (changed) {
3290 objects_changed.append(obedit);
3291 }
3292 }
3293
3294 BLI_memarena_free(arena);
3295
3296 /* Pack islands & Stretch to UV bounds */
3297 if (!objects_changed.is_empty()) {
3298
3299 scene->toolsettings->uvcalc_margin = island_margin;
3300
3301 /* Depsgraph refresh functions are called here. */
3302 const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
3303
3305 params.rotate_method = eUVPackIsland_RotationMethod(RNA_enum_get(op->ptr, "rotate_method"));
3306 params.only_selected_uvs = only_selected_uvs;
3307 params.only_selected_faces = true;
3308 params.correct_aspect = correct_aspect;
3309 params.use_seams = true;
3310 params.margin_method = eUVPackIsland_MarginMethod(RNA_enum_get(op->ptr, "margin_method"));
3311 params.margin = RNA_float_get(op->ptr, "island_margin");
3312
3313 uvedit_pack_islands_multi(scene, objects_changed, nullptr, nullptr, false, true, &params);
3314
3315 /* #uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
3316 const bool per_face_aspect = false;
3317 uv_map_clip_correct(scene, objects_changed, op, per_face_aspect, only_selected_uvs);
3318 }
3319
3320 return OPERATOR_FINISHED;
3321}
3322
3323static int smart_project_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3324{
3326 C, op, event, IFACE_("Smart UV Project"), IFACE_("Unwrap"));
3327}
3328
3330{
3331 PropertyRNA *prop;
3332
3333 /* identifiers */
3334 ot->name = "Smart UV Project";
3335 ot->idname = "UV_OT_smart_project";
3336 ot->description = "Projection unwraps the selected faces of mesh objects";
3337
3339
3340 /* api callbacks */
3344
3345 /* properties */
3347 "angle_limit",
3348 0,
3349 nullptr,
3350 DEG2RADF(0.0f),
3351 DEG2RADF(90.0f),
3352 "Angle Limit",
3353 "Lower for more projection groups, higher for less distortion",
3354 DEG2RADF(0.0f),
3355 DEG2RADF(89.0f));
3357
3359 "margin_method",
3362 "Margin Method",
3363 "");
3365 "rotate_method",
3366 /* Only show aligned options as the rotation from a projection
3367 * generated from a direction vector isn't meaningful. */
3370 "Rotation Method",
3371 "");
3373 "island_margin",
3374 0.0f,
3375 0.0f,
3376 1.0f,
3377 "Island Margin",
3378 "Margin to reduce bleed from adjacent islands",
3379 0.0f,
3380 1.0f);
3382 "area_weight",
3383 0.0f,
3384 0.0f,
3385 1.0f,
3386 "Area Weight",
3387 "Weight projection's vector by faces with larger areas",
3388 0.0f,
3389 1.0f);
3390
3392}
3393
3396/* -------------------------------------------------------------------- */
3400static int uv_from_view_exec(bContext *C, wmOperator *op);
3401
3402static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3403{
3404 View3D *v3d = CTX_wm_view3d(C);
3406 const Camera *camera = ED_view3d_camera_data_get(v3d, rv3d);
3407 PropertyRNA *prop;
3408
3409 prop = RNA_struct_find_property(op->ptr, "camera_bounds");
3410 if (!RNA_property_is_set(op->ptr, prop)) {
3411 RNA_property_boolean_set(op->ptr, prop, (camera != nullptr));
3412 }
3413 prop = RNA_struct_find_property(op->ptr, "correct_aspect");
3414 if (!RNA_property_is_set(op->ptr, prop)) {
3415 RNA_property_boolean_set(op->ptr, prop, (camera == nullptr));
3416 }
3417
3418 return uv_from_view_exec(C, op);
3419}
3420
3422{
3423 ViewLayer *view_layer = CTX_data_view_layer(C);
3424 const Scene *scene = CTX_data_scene(C);
3425 ARegion *region = CTX_wm_region(C);
3426 View3D *v3d = CTX_wm_view3d(C);
3428 const Camera *camera = ED_view3d_camera_data_get(v3d, rv3d);
3429 BMFace *efa;
3430 BMLoop *l;
3431 BMIter iter, liter;
3432 float rotmat[4][4];
3433 float objects_pos_offset[4];
3434
3435 const bool use_orthographic = RNA_boolean_get(op->ptr, "orthographic");
3436
3437 /* NOTE: objects that aren't touched are set to nullptr (to skip clipping). */
3439 scene, view_layer, v3d);
3440
3441 if (use_orthographic) {
3442 /* Calculate average object position. */
3443 float objects_pos_avg[4] = {0};
3444
3445 for (Object *object : objects) {
3446 add_v4_v4(objects_pos_avg, object->object_to_world().location());
3447 }
3448
3449 mul_v4_fl(objects_pos_avg, 1.0f / objects.size());
3450 negate_v4_v4(objects_pos_offset, objects_pos_avg);
3451 }
3452
3453 Vector<Object *> changed_objects;
3454
3455 for (Object *obedit : objects) {
3457 bool changed = false;
3458
3459 /* add uvs if they don't exist yet */
3460 if (!ED_uvedit_ensure_uvs(obedit)) {
3461 continue;
3462 }
3463
3464 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
3465
3466 if (use_orthographic) {
3467 uv_map_rotation_matrix_ex(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f, objects_pos_offset);
3468
3469 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3470 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
3471 continue;
3472 }
3473
3474 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3475 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
3476 BLI_uvproject_from_view_ortho(luv, l->v->co, rotmat);
3477 }
3478 changed = true;
3479 }
3480 }
3481 else if (camera) {
3482 const bool camera_bounds = RNA_boolean_get(op->ptr, "camera_bounds");
3484 v3d->camera,
3485 obedit->object_to_world().ptr(),
3486 camera_bounds ? (scene->r.xsch * scene->r.xasp) : 1.0f,
3487 camera_bounds ? (scene->r.ysch * scene->r.yasp) : 1.0f);
3488
3489 if (uci) {
3490 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3491 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
3492 continue;
3493 }
3494
3495 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3496 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
3497 BLI_uvproject_from_camera(luv, l->v->co, uci);
3498 }
3499 changed = true;
3500 }
3501
3502 MEM_freeN(uci);
3503 }
3504 }
3505 else {
3506 copy_m4_m4(rotmat, obedit->object_to_world().ptr());
3507
3508 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3509 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
3510 continue;
3511 }
3512
3513 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3514 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
3516 luv, l->v->co, rv3d->persmat, rotmat, region->winx, region->winy);
3517 }
3518 changed = true;
3519 }
3520 }
3521
3522 if (changed) {
3523 changed_objects.append(obedit);
3524 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
3525 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
3526 }
3527 }
3528
3529 if (changed_objects.is_empty()) {
3530 return OPERATOR_CANCELLED;
3531 }
3532
3533 const bool per_face_aspect = true;
3534 const bool only_selected_uvs = false;
3535 uv_map_clip_correct(scene, objects, op, per_face_aspect, only_selected_uvs);
3536 return OPERATOR_FINISHED;
3537}
3538
3540{
3542
3543 if (!ED_operator_uvmap(C)) {
3544 return false;
3545 }
3546
3547 return (rv3d != nullptr);
3548}
3549
3551{
3552 /* identifiers */
3553 ot->name = "Project from View";
3554 ot->idname = "UV_OT_project_from_view";
3555 ot->description = "Project the UV vertices of the mesh as seen in current 3D view";
3556
3558
3559 /* api callbacks */
3563
3564 /* properties */
3565 RNA_def_boolean(ot->srna, "orthographic", false, "Orthographic", "Use orthographic projection");
3567 "camera_bounds",
3568 true,
3569 "Camera Bounds",
3570 "Map UVs to the camera region taking resolution and aspect into account");
3572}
3573
3576/* -------------------------------------------------------------------- */
3580static int reset_exec(bContext *C, wmOperator * /*op*/)
3581{
3582 const Scene *scene = CTX_data_scene(C);
3583 ViewLayer *view_layer = CTX_data_view_layer(C);
3584 View3D *v3d = CTX_wm_view3d(C);
3585
3587 scene, view_layer, v3d);
3588 for (Object *obedit : objects) {
3589 Mesh *mesh = (Mesh *)obedit->data;
3591
3592 if (em->bm->totfacesel == 0) {
3593 continue;
3594 }
3595
3596 /* add uvs if they don't exist yet */
3597 if (!ED_uvedit_ensure_uvs(obedit)) {
3598 continue;
3599 }
3600
3601 ED_mesh_uv_loop_reset(C, mesh);
3602
3603 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
3604 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
3605 }
3606
3607 return OPERATOR_FINISHED;
3608}
3609
3611{
3612 /* identifiers */
3613 ot->name = "Reset";
3614 ot->idname = "UV_OT_reset";
3615 ot->description = "Reset UV projection";
3616
3618
3619 /* api callbacks */
3620 ot->exec = reset_exec;
3622}
3623
3626/* -------------------------------------------------------------------- */
3630static void uv_map_mirror(BMFace *efa,
3631 const bool *regular,
3632 const bool fan,
3633 const int cd_loop_uv_offset)
3634{
3635 /* A heuristic to improve alignment of faces near the seam.
3636 * In simple terms, we're looking for faces which span more
3637 * than 0.5 units in the *u* coordinate.
3638 * If we find such a face, we try and improve the unwrapping
3639 * by adding (1.0, 0.0) onto some of the face's UVs.
3640 *
3641 * Note that this is only a heuristic. The property we're
3642 * attempting to maintain is that the winding of the face
3643 * in UV space corresponds with the handedness of the face
3644 * in 3D space w.r.t to the unwrapping. Even for triangles,
3645 * that property is somewhat complicated to evaluate. */
3646
3647 float right_u = -1.0e30f;
3648 BMLoop *l;
3649 BMIter liter;
3651 int j;
3652 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, j) {
3653 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
3654 uvs[j] = luv;
3655 if (luv[0] >= 1.0f) {
3656 luv[0] -= 1.0f;
3657 }
3658 right_u = max_ff(right_u, luv[0]);
3659 }
3660
3661 float left_u = 1.0e30f;
3662 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3663 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
3664 if (right_u <= luv[0] + 0.5f) {
3665 left_u = min_ff(left_u, luv[0]);
3666 }
3667 }
3668
3669 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3670 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
3671 if (luv[0] + 0.5f < right_u) {
3672 if (2 * luv[0] + 1.0f < left_u + right_u) {
3673 luv[0] += 1.0f;
3674 }
3675 }
3676 }
3677 if (!fan) {
3678 return;
3679 }
3680
3681 /* Another heuristic, this time, we attempt to "fan"
3682 * the UVs of faces which pass through one of the poles
3683 * of the unwrapping. */
3684
3685 /* Need to recompute min and max. */
3686 float minmax_u[2] = {1.0e30f, -1.0e30f};
3687 int pole_count = 0;
3688 for (int i = 0; i < efa->len; i++) {
3689 if (regular[i]) {
3690 minmax_u[0] = min_ff(minmax_u[0], uvs[i][0]);
3691 minmax_u[1] = max_ff(minmax_u[1], uvs[i][0]);
3692 }
3693 else {
3694 pole_count++;
3695 }
3696 }
3697 if (ELEM(pole_count, 0, efa->len)) {
3698 return;
3699 }
3700 for (int i = 0; i < efa->len; i++) {
3701 if (regular[i]) {
3702 continue;
3703 }
3704 float u = 0.0f;
3705 float sum = 0.0f;
3706 const int i_plus = (i + 1) % efa->len;
3707 const int i_minus = (i + efa->len - 1) % efa->len;
3708 if (regular[i_plus]) {
3709 u += uvs[i_plus][0];
3710 sum += 1.0f;
3711 }
3712 if (regular[i_minus]) {
3713 u += uvs[i_minus][0];
3714 sum += 1.0f;
3715 }
3716 if (sum == 0) {
3717 u += minmax_u[0] + minmax_u[1];
3718 sum += 2.0f;
3719 }
3720 uvs[i][0] = u / sum;
3721 }
3722}
3723
3739 float branch;
3740};
3741
3752static float uv_sphere_project(const Scene *scene,
3753 BMesh *bm,
3754 BMFace *efa_init,
3755 const float center[3],
3756 const float rotmat[3][3],
3757 const bool fan,
3758 const BMUVOffsets offsets,
3759 const bool only_selected_uvs,
3760 const bool use_seams,
3761 const float branch_init)
3762{
3763 float max_u = 0.0f;
3764 if (use_seams && BM_elem_flag_test(efa_init, BM_ELEM_TAG)) {
3765 return max_u;
3766 }
3767
3768 /* Similar to #BM_mesh_calc_face_groups with added connectivity information. */
3770 stack.append({efa_init, branch_init});
3771
3772 while (stack.size()) {
3773 UV_FaceBranch face_branch = stack.pop_last();
3774 BMFace *efa = face_branch.efa;
3775
3776 if (use_seams) {
3777 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
3778 continue; /* Faces might be in the stack more than once. */
3779 }
3780
3781 BM_elem_flag_set(efa, BM_ELEM_TAG, true); /* Visited, don't consider again. */
3782 }
3783
3784 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
3785 continue; /* Unselected face, ignore. */
3786 }
3787
3788 if (only_selected_uvs) {
3789 if (!uvedit_face_select_test(scene, efa, offsets)) {
3790 uvedit_face_select_disable(scene, bm, efa, offsets);
3791 continue; /* Unselected UV, ignore. */
3792 }
3793 }
3794
3795 /* Remember which UVs are at the pole. */
3797
3798 int i;
3799 BMLoop *l;
3800 BMIter iter;
3801 BM_ITER_ELEM_INDEX (l, &iter, efa, BM_LOOPS_OF_FACE, i) {
3802 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3803 float pv[3];
3804 sub_v3_v3v3(pv, l->v->co, center);
3805 mul_m3_v3(rotmat, pv);
3806 regular[i] = map_to_sphere(&luv[0], &luv[1], pv[0], pv[1], pv[2]);
3807 if (!use_seams) {
3808 continue; /* Nothing more to do. */
3809 }
3810
3811 /* Move UV to correct branch. */
3812 luv[0] = luv[0] + ceilf(face_branch.branch - 0.5f - luv[0]);
3813 max_u = max_ff(max_u, luv[0]);
3814
3815 BMEdge *edge = l->e;
3816 if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
3817 continue; /* Stop flood fill at seams. */
3818 }
3819
3820 /* Extend flood fill by pushing to stack. */
3821 BMFace *efa2;
3822 BMIter iter2;
3823 BM_ITER_ELEM (efa2, &iter2, edge, BM_FACES_OF_EDGE) {
3824 if (!BM_elem_flag_test(efa2, BM_ELEM_TAG)) {
3825 stack.append({efa2, luv[0]});
3826 }
3827 }
3828 }
3829 uv_map_mirror(efa, regular.data(), fan, offsets.uv);
3830 }
3831
3832 return max_u;
3833}
3834
3836{
3837 const Scene *scene = CTX_data_scene(C);
3838 View3D *v3d = CTX_wm_view3d(C);
3839
3840 bool only_selected_uvs = false;
3841 if (CTX_wm_space_image(C)) {
3842 /* Inside the UV Editor, only project selected UVs. */
3843 only_selected_uvs = true;
3844 }
3845
3846 ViewLayer *view_layer = CTX_data_view_layer(C);
3848 scene, view_layer, v3d);
3849 for (Object *obedit : objects) {
3851 BMFace *efa;
3852 BMIter iter;
3853
3854 if (em->bm->totfacesel == 0) {
3855 continue;
3856 }
3857
3858 /* add uvs if they don't exist yet */
3859 if (!ED_uvedit_ensure_uvs(obedit)) {
3860 continue;
3861 }
3862
3863 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3864 float center[3], rotmat[3][3];
3865
3866 uv_map_transform(C, op, rotmat);
3867 uv_map_transform_center(scene, v3d, obedit, em, center, nullptr);
3868
3869 const bool fan = RNA_enum_get(op->ptr, "pole");
3870 const bool use_seams = RNA_boolean_get(op->ptr, "seam");
3871
3872 if (use_seams) {
3874 }
3875
3876 float island_offset = 0.0f;
3877 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3878 const float max_u = uv_sphere_project(scene,
3879 em->bm,
3880 efa,
3881 center,
3882 rotmat,
3883 fan,
3884 offsets,
3885 only_selected_uvs,
3886 use_seams,
3887 island_offset + 0.5f);
3888 island_offset = ceilf(max_ff(max_u, island_offset));
3889 }
3890
3891 const bool per_face_aspect = true;
3892 uv_map_clip_correct(scene, {obedit}, op, per_face_aspect, only_selected_uvs);
3893
3894 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
3895 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
3896 }
3897
3898 return OPERATOR_FINISHED;
3899}
3900
3902{
3903 /* identifiers */
3904 ot->name = "Sphere Projection";
3905 ot->idname = "UV_OT_sphere_project";
3906 ot->description = "Project the UV vertices of the mesh over the curved surface of a sphere";
3907
3909
3910 /* api callbacks */
3913
3914 /* properties */
3917}
3918
3921/* -------------------------------------------------------------------- */
3925/* See #uv_sphere_project for description of parameters. */
3926static float uv_cylinder_project(const Scene *scene,
3927 BMesh *bm,
3928 BMFace *efa_init,
3929 const float center[3],
3930 const float rotmat[3][3],
3931 const bool fan,
3932 const BMUVOffsets offsets,
3933 const bool only_selected_uvs,
3934 const bool use_seams,
3935 const float branch_init)
3936{
3937 float max_u = 0.0f;
3938 if (use_seams && BM_elem_flag_test(efa_init, BM_ELEM_TAG)) {
3939 return max_u;
3940 }
3941
3942 /* Similar to BM_mesh_calc_face_groups with added connectivity information. */
3943
3945
3946 stack.append({efa_init, branch_init});
3947
3948 while (stack.size()) {
3949 UV_FaceBranch face_branch = stack.pop_last();
3950 BMFace *efa = face_branch.efa;
3951
3952 if (use_seams) {
3953 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
3954 continue; /* Faces might be in the stack more than once. */
3955 }
3956
3957 BM_elem_flag_set(efa, BM_ELEM_TAG, true); /* Visited, don't consider again. */
3958 }
3959
3960 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
3961 continue; /* Unselected face, ignore. */
3962 }
3963
3964 if (only_selected_uvs) {
3965 if (!uvedit_face_select_test(scene, efa, offsets)) {
3966 uvedit_face_select_disable(scene, bm, efa, offsets);
3967 continue; /* Unselected UV, ignore. */
3968 }
3969 }
3970
3971 /* Remember which UVs are at the pole. */
3973
3974 int i;
3975 BMLoop *l;
3976 BMIter iter;
3977 BM_ITER_ELEM_INDEX (l, &iter, efa, BM_LOOPS_OF_FACE, i) {
3978 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3979 float pv[3];
3980 sub_v3_v3v3(pv, l->v->co, center);
3981 mul_m3_v3(rotmat, pv);
3982 regular[i] = map_to_tube(&luv[0], &luv[1], pv[0], pv[1], pv[2]);
3983
3984 if (!use_seams) {
3985 continue; /* Nothing more to do. */
3986 }
3987
3988 /* Move UV to correct branch. */
3989 luv[0] = luv[0] + ceilf(face_branch.branch - 0.5f - luv[0]);
3990 max_u = max_ff(max_u, luv[0]);
3991
3992 BMEdge *edge = l->e;
3993 if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
3994 continue; /* Stop flood fill at seams. */
3995 }
3996
3997 /* Extend flood fill by pushing to stack. */
3998 BMFace *efa2;
3999 BMIter iter2;
4000 BM_ITER_ELEM (efa2, &iter2, edge, BM_FACES_OF_EDGE) {
4001 if (!BM_elem_flag_test(efa2, BM_ELEM_TAG)) {
4002 stack.append({efa2, luv[0]});
4003 }
4004 }
4005 }
4006
4007 uv_map_mirror(efa, regular.data(), fan, offsets.uv);
4008 }
4009
4010 return max_u;
4011}
4012
4014{
4015 const Scene *scene = CTX_data_scene(C);
4016 View3D *v3d = CTX_wm_view3d(C);
4017
4018 bool only_selected_uvs = false;
4019 if (CTX_wm_space_image(C)) {
4020 /* Inside the UV Editor, only project selected UVs. */
4021 only_selected_uvs = true;
4022 }
4023
4024 ViewLayer *view_layer = CTX_data_view_layer(C);
4026 scene, view_layer, v3d);
4027 for (Object *obedit : objects) {
4029 BMFace *efa;
4030 BMIter iter;
4031
4032 if (em->bm->totfacesel == 0) {
4033 continue;
4034 }
4035
4036 /* add uvs if they don't exist yet */
4037 if (!ED_uvedit_ensure_uvs(obedit)) {
4038 continue;
4039 }
4040
4041 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
4042 float center[3], rotmat[3][3];
4043
4044 uv_map_transform(C, op, rotmat);
4045 uv_map_transform_center(scene, v3d, obedit, em, center, nullptr);
4046
4047 const bool fan = RNA_enum_get(op->ptr, "pole");
4048 const bool use_seams = RNA_boolean_get(op->ptr, "seam");
4049
4050 if (use_seams) {
4052 }
4053
4054 float island_offset = 0.0f;
4055
4056 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4057 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
4058 continue;
4059 }
4060
4061 if (only_selected_uvs && !uvedit_face_select_test(scene, efa, offsets)) {
4062 uvedit_face_select_disable(scene, em->bm, efa, offsets);
4063 continue;
4064 }
4065
4066 const float max_u = uv_cylinder_project(scene,
4067 em->bm,
4068 efa,
4069 center,
4070 rotmat,
4071 fan,
4072 offsets,
4073 only_selected_uvs,
4074 use_seams,
4075 island_offset + 0.5f);
4076 island_offset = ceilf(max_ff(max_u, island_offset));
4077 }
4078
4079 const bool per_face_aspect = true;
4080 uv_map_clip_correct(scene, {obedit}, op, per_face_aspect, only_selected_uvs);
4081
4082 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
4083 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
4084 }
4085
4086 return OPERATOR_FINISHED;
4087}
4088
4090{
4091 /* identifiers */
4092 ot->name = "Cylinder Projection";
4093 ot->idname = "UV_OT_cylinder_project";
4094 ot->description = "Project the UV vertices of the mesh over the curved wall of a cylinder";
4095
4097
4098 /* api callbacks */
4101
4102 /* properties */
4105}
4106
4109/* -------------------------------------------------------------------- */
4113static void uvedit_unwrap_cube_project(const Scene *scene,
4114 BMesh *bm,
4115 float cube_size,
4116 const bool use_select,
4117 const bool only_selected_uvs,
4118 const float center[3])
4119{
4120 BMFace *efa;
4121 BMLoop *l;
4122 BMIter iter, liter;
4123 float loc[3];
4124 int cox, coy;
4125
4126 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4127
4128 if (center) {
4129 copy_v3_v3(loc, center);
4130 }
4131 else {
4132 zero_v3(loc);
4133 }
4134
4135 if (UNLIKELY(cube_size == 0.0f)) {
4136 cube_size = 1.0f;
4137 }
4138
4139 /* choose x,y,z axis for projection depending on the largest normal
4140 * component, but clusters all together around the center of map. */
4141
4142 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4143 if (use_select && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
4144 continue;
4145 }
4146 if (only_selected_uvs && !uvedit_face_select_test(scene, efa, offsets)) {
4147 uvedit_face_select_disable(scene, bm, efa, offsets);
4148 continue;
4149 }
4150
4151 axis_dominant_v3(&cox, &coy, efa->no);
4152
4153 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4154 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4155 luv[0] = 0.5f + ((l->v->co[cox] - loc[cox]) / cube_size);
4156 luv[1] = 0.5f + ((l->v->co[coy] - loc[coy]) / cube_size);
4157 }
4158 }
4159}
4160
4162{
4163 const Scene *scene = CTX_data_scene(C);
4164 View3D *v3d = CTX_wm_view3d(C);
4165
4166 bool only_selected_uvs = false;
4167 if (CTX_wm_space_image(C)) {
4168 /* Inside the UV Editor, only cube project selected UVs. */
4169 only_selected_uvs = true;
4170 }
4171
4172 PropertyRNA *prop_cube_size = RNA_struct_find_property(op->ptr, "cube_size");
4173 const float cube_size_init = RNA_property_float_get(op->ptr, prop_cube_size);
4174
4175 ViewLayer *view_layer = CTX_data_view_layer(C);
4177 scene, view_layer, v3d);
4178 for (const int ob_index : objects.index_range()) {
4179 Object *obedit = objects[ob_index];
4181
4182 if (em->bm->totfacesel == 0) {
4183 continue;
4184 }
4185
4186 /* add uvs if they don't exist yet */
4187 if (!ED_uvedit_ensure_uvs(obedit)) {
4188 continue;
4189 }
4190
4191 float bounds[2][3];
4192 float(*bounds_buf)[3] = nullptr;
4193
4194 if (!RNA_property_is_set(op->ptr, prop_cube_size)) {
4195 bounds_buf = bounds;
4196 }
4197
4198 float center[3];
4199 uv_map_transform_center(scene, v3d, obedit, em, center, bounds_buf);
4200
4201 /* calculate based on bounds */
4202 float cube_size = cube_size_init;
4203 if (bounds_buf) {
4204 float dims[3];
4205 sub_v3_v3v3(dims, bounds[1], bounds[0]);
4206 cube_size = max_fff(UNPACK3(dims));
4207 if (ob_index == 0) {
4208 /* This doesn't fit well with, multiple objects. */
4209 RNA_property_float_set(op->ptr, prop_cube_size, cube_size);
4210 }
4211 }
4212
4213 uvedit_unwrap_cube_project(scene, em->bm, cube_size, true, only_selected_uvs, center);
4214
4215 const bool per_face_aspect = true;
4216 uv_map_clip_correct(scene, {obedit}, op, per_face_aspect, only_selected_uvs);
4217
4218 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
4220 }
4221
4222 return OPERATOR_FINISHED;
4223}
4224
4226{
4227 /* identifiers */
4228 ot->name = "Cube Projection";
4229 ot->idname = "UV_OT_cube_project";
4230 ot->description = "Project the UV vertices of the mesh over the six faces of a cube";
4231
4233
4234 /* api callbacks */
4237
4238 /* properties */
4240 "cube_size",
4241 1.0f,
4242 0.0f,
4243 FLT_MAX,
4244 "Cube Size",
4245 "Size of the cube to project on",
4246 0.001f,
4247 100.0f);
4249}
4250
4253/* -------------------------------------------------------------------- */
4257void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
4258{
4259 Mesh *mesh = static_cast<Mesh *>(ob->data);
4260 bool sync_selection = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
4261
4262 BMeshCreateParams create_params{};
4263 create_params.use_toolflags = false;
4264 BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4265
4266 /* turn sync selection off,
4267 * since we are not in edit mode we need to ensure only the uv flags are tested */
4268 scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION;
4269
4270 ED_mesh_uv_ensure(mesh, nullptr);
4271
4272 BMeshFromMeshParams bm_from_me_params{};
4273 bm_from_me_params.calc_face_normal = true;
4274 bm_from_me_params.calc_vert_normal = true;
4275 BM_mesh_bm_from_me(bm, mesh, &bm_from_me_params);
4276
4277 /* Select all UVs for cube_project. */
4279 /* A cube size of 2.0 maps [-1..1] vertex coords to [0.0..1.0] in UV coords. */
4280 uvedit_unwrap_cube_project(scene, bm, 2.0, false, false, nullptr);
4281
4282 /* Pack UVs. */
4284 params.rotate_method = ED_UVPACK_ROTATION_ANY;
4285 params.only_selected_uvs = false;
4286 params.only_selected_faces = false;
4287 params.correct_aspect = false;
4288 params.use_seams = true;
4289 params.margin_method = ED_UVPACK_MARGIN_SCALED;
4290 params.margin = 0.001f;
4291
4292 uvedit_pack_islands_multi(scene, {ob}, &bm, nullptr, false, true, &params);
4293
4294 /* Write back from BMesh to Mesh. */
4295 BMeshToMeshParams bm_to_me_params{};
4296 BM_mesh_bm_to_me(bmain, bm, mesh, &bm_to_me_params);
4298
4299 if (sync_selection) {
4300 scene->toolsettings->uv_flag |= UV_SYNC_SELECTION;
4301 }
4302}
4303
SpaceImage * CTX_wm_space_image(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
#define ORIGINDEX_NONE
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
support for deformation groups and hooks.
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:770
int BKE_object_defgroup_name_index(const Object *ob, blender::StringRef name)
Definition deform.cc:585
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
#define G_MAIN
int BKE_image_find_nearest_tile_with_offset(const Image *image, const float co[2], float r_uv_offset[2]) ATTR_NONNULL(2
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
blender::bke::subdiv::Settings BKE_subsurf_modifier_settings_init(const SubsurfModifierData *smd, bool use_render_params)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ATTR_FALLTHROUGH
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:192
Heap * BLI_heap_new(void) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.c:187
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float max_fff(float a, float b, float c)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
#define M_PI
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
bool map_to_sphere(float *r_u, float *r_v, float x, float y, float z)
bool map_to_tube(float *r_u, float *r_v, float x, float y, float z)
MINLINE void axis_dominant_v3(int *r_axis_a, int *r_axis_b, const float axis[3])
bool invert_m2_m2(float inverse[2][2], const float mat[2][2])
void mul_m3_v3(const float M[3][3], float r[3])
void zero_m4(float m[4][4])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
void unit_m4(float m[4][4])
Definition rct.c:1127
#define mul_m4_series(...)
void mul_v2_m2v2(float r[2], const float mat[2][2], const float vec[2])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool is_negative_m4(const float mat[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mat4_to_size(float size[3], const float M[4][4])
#define DEG2RADF(_deg)
MINLINE void mul_v4_fl(float r[4], float f)
MINLINE void add_v4_v4(float r[4], const float a[4])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_fl(float r[2], float f)
MINLINE void negate_v4_v4(float r[4], const float a[4])
MINLINE void clamp_v2(float vec[2], float min, float max)
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
#define BLI_MEMARENA_STD_BUFSIZE
void void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define INIT_MINMAX2(min, max)
#define INIT_MINMAX(min, max)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
void BLI_uvproject_from_view(float target[2], float source[3], float persmat[4][4], float rotmat[4][4], float winx, float winy)
Definition uvproject.cc:83
void BLI_uvproject_from_view_ortho(float target[2], float source[3], const float rotmat[4][4])
Definition uvproject.cc:176
void BLI_uvproject_from_camera(float target[2], float source[3], struct ProjCameraInfo *uci)
Definition uvproject.cc:31
struct ProjCameraInfo * BLI_uvproject_camera_info(const struct Object *ob, const float rotmat[4][4], float winx, float winy)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define MAX_ID_NAME
Definition DNA_ID.h:377
@ CD_MDEFORMVERT
@ CD_PROP_FLOAT2
#define DNA_struct_default_get(struct_name)
@ IMA_SRC_TILED
@ eModifierType_Subsurf
@ eSubsurfModifierFlag_ControlEdges
Object is a sort of wrapper for general info.
#define MAX_VGROUP_NAME
@ UVCALC_UNWRAP_NO_FLIP
@ UVCALC_UNWRAP_USE_WEIGHTS
@ UVCALC_USESUBSURF
@ UVCALC_NO_ASPECT_CORRECT
@ UVCALC_FILLHOLES
@ UVCALC_UNWRAP_METHOD_MINIMUM_STRETCH
@ UVCALC_UNWRAP_METHOD_CONFORMAL
@ UVCALC_UNWRAP_METHOD_ANGLE
@ UV_SYNC_SELECTION
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ V3D_AROUND_LOCAL_ORIGINS
@ OPERATOR_RUNNING_MODAL
void ED_image_get_uv_aspect(Image *ima, ImageUser *iuser, float *r_aspx, float *r_aspy)
void ED_mesh_uv_ensure(Mesh *mesh, const char *name)
Definition mesh_data.cc:363
int ED_mesh_uv_add(Mesh *mesh, const char *name, bool active_set, bool do_init, ReportList *reports)
Definition mesh_data.cc:221
void ED_mesh_uv_loop_reset(bContext *C, Mesh *mesh)
Definition mesh_data.cc:211
void EDBM_mesh_elem_index_ensure_multi(blender::Span< Object * > objects, char htype)
bool ED_operator_uvmap(bContext *C)
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:803
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
bool ED_operator_uvedit(bContext *C)
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
bool ED_object_get_active_image(Object *ob, int mat_nr, Image **r_ima, ImageUser **r_iuser, const bNode **r_node, const bNodeTree **r_ntree)
void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, struct wmWindow *win_modal)
bool uvedit_face_select_test(const Scene *scene, BMFace *efa, BMUVOffsets offsets)
float ED_uvedit_get_aspect_y(Object *obedit)
void ED_uvedit_get_aspect_from_material(Object *ob, const int material_index, float *r_aspx, float *r_aspy)
bool ED_uvedit_live_unwrap_timer_check(const wmTimer *timer)
bool ED_uvedit_test(Object *obedit)
Definition uvedit_ops.cc:69
void ED_uvedit_select_all(BMesh *bm)
void ED_uvedit_live_unwrap_re_solve()
int bm_mesh_calc_uv_islands(const Scene *scene, BMesh *bm, ListBase *island_list, const bool only_selected_faces, const bool only_selected_uvs, const bool use_seams, const float aspect_y, BMUVOffsets offsets)
void ED_uvedit_get_aspect(Object *obedit, float *r_aspx, float *r_aspy)
void uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa, BMUVOffsets offsets)
void ED_uvedit_live_unwrap_end(bool cancel)
bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const float coords[2])
void ED_uvedit_live_unwrap(const Scene *scene, blender::Span< Object * > objects)
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
Camera * ED_view3d_camera_data_get(View3D *v3d, RegionView3D *rv3d)
eUVPackIsland_MarginMethod
@ ED_UVPACK_MARGIN_FRACTION
@ ED_UVPACK_MARGIN_SCALED
@ ED_UVPACK_MARGIN_ADD
eUVPackIsland_ShapeMethod
@ ED_UVPACK_SHAPE_AABB
@ ED_UVPACK_SHAPE_CONCAVE
@ ED_UVPACK_SHAPE_CONVEX
eUVPackIsland_PinMethod
@ ED_UVPACK_PIN_NONE
@ ED_UVPACK_PIN_LOCK_ROTATION_SCALE
@ ED_UVPACK_PIN_LOCK_SCALE
@ ED_UVPACK_PIN_LOCK_ROTATION
@ ED_UVPACK_PIN_IGNORE
@ ED_UVPACK_PIN_LOCK_ALL
eUVPackIsland_RotationMethod
@ ED_UVPACK_ROTATION_ANY
@ ED_UVPACK_ROTATION_AXIS_ALIGNED_X
@ ED_UVPACK_ROTATION_AXIS_ALIGNED
@ ED_UVPACK_ROTATION_CARDINAL
@ ED_UVPACK_ROTATION_NONE
@ ED_UVPACK_ROTATION_AXIS_ALIGNED_Y
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
#define RNA_ENUM_ITEM_SEPR
Definition RNA_types.hh:528
#define C
Definition RandGen.cpp:29
void uiLayoutSetActive(uiLayout *layout, bool active)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
#define UI_ITEM_NONE
#define UI_MAX_DRAW_STR
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ WM_JOB_TYPE_UV_PACK
Definition WM_api.hh:1611
@ WM_JOB_PROGRESS
Definition WM_api.hh:1566
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_GRAB_CURSOR_XY
Definition WM_types.hh:168
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ KM_PRESS
Definition WM_types.hh:284
#define ND_SPACE_IMAGE
Definition WM_types.hh:488
#define NC_SPACE
Definition WM_types.hh:359
#define BM_ELEM_CD_GET_BOOL(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
#define BM_elem_index_get(ele)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_uv_map_ensure_vert_select_attr(BMesh *bm, const char *uv_map_name)
void BM_uv_map_ensure_edge_select_attr(BMesh *bm, const char *uv_map_name)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_FACES_OF_EDGE
@ BM_EDGES_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_EDGE
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
void BM_editselection_center(BMEditSelection *ese, float r_center[3])
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
bool BM_select_history_active_get(BMesh *bm, BMEditSelection *ese)
const BMAllocTemplate bm_mesh_allocsize_default
Definition bmesh_mesh.cc:29
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
BLI_INLINE BMEdge * BM_edge_at_index(BMesh *bm, const int index)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_face_calc_bounds_expand(const BMFace *f, float min[3], float max[3])
float BM_face_calc_area(const BMFace *f)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
ATTR_WARN_UNUSED_RESULT const BMLoop * l
BMUVOffsets BM_uv_map_get_offsets(const BMesh *bm)
void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], const int cd_loop_uv_offset)
static T sum(const btAlignedObjectArray< T > &items)
const T * data() const
Definition BLI_array.hh:301
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
bool can_scale_(const UVPackIsland_Params &params) const
Definition uv_pack.cc:2428
void add_polygon(Span< float2 > uvs, MemArena *arena, Heap *heap)
Definition uv_pack.cc:164
void build_transformation(float scale, double rotation, float r_matrix[2][2]) const
Definition uv_pack.cc:2351
eUVPackIsland_RotationMethod rotate_method
eUVPackIsland_MarginMethod margin_method
eUVPackIsland_ShapeMethod shape_method
void setUDIMOffsetFromSpaceImage(const SpaceImage *sima)
eUVPackIsland_PinMethod pin_method
void setFromUnwrapOptions(const UnwrapOptions &options)
CCL_NAMESPACE_BEGIN struct Options options
#define sinf(x)
#define cosf(x)
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
uint col
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
#define G(x, y, z)
void free(Subdiv *subdiv)
Definition subdiv.cc:192
Subdiv * new_from_mesh(const Settings *settings, const Mesh *mesh)
Definition subdiv.cc:133
Mesh * subdiv_to_mesh(Subdiv *subdiv, const ToMeshSettings *settings, const Mesh *coarse_mesh)
bool uv_parametrizer_is_slim(const ParamHandle *phandle)
void uv_parametrizer_construct_end(ParamHandle *phandle, bool fill_holes, bool topology_from_uvs, int *r_count_failed=nullptr)
void uv_parametrizer_edge_set_seam(ParamHandle *phandle, const ParamKey *vkeys)
void uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned, bool scale_uv, bool shear)
void uv_parametrizer_flush(ParamHandle *handle)
void uv_parametrizer_slim_live_begin(ParamHandle *phandle, const ParamSlimOptions *slim_options)
void uv_parametrizer_slim_live_solve_iteration(ParamHandle *phandle)
ParamKey uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2])
void uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2])
void uv_parametrizer_slim_live_end(ParamHandle *phandle)
void uv_parametrizer_stretch_blend(ParamHandle *handle, float blend)
void uv_parametrizer_slim_solve(ParamHandle *phandle, const ParamSlimOptions *slim_options, int *count_changed, int *count_failed)
void mul_v2_m2_add_v2v2(float r[2], const float mat[2][2], const float a[2], const float b[2])
Definition uv_pack.cc:55
void uv_parametrizer_lscm_end(ParamHandle *handle)
void uv_parametrizer_aspect_ratio(ParamHandle *handle, float aspect_y)
void uv_parametrizer_stretch_begin(ParamHandle *handle)
void uv_parametrizer_flush_restore(ParamHandle *handle)
void uv_parametrizer_lscm_begin(ParamHandle *handle, bool live, bool abf)
float pack_islands(Span< PackIsland * > islands, const UVPackIsland_Params &params)
void uv_parametrizer_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed)
void uv_parametrizer_stretch_iter(ParamHandle *handle)
void uv_parametrizer_stretch_end(ParamHandle *handle)
void uv_parametrizer_face_add(ParamHandle *handle, const ParamKey key, const int nverts, const ParamKey *vkeys, const float **co, float **uv, const float *weight, const bool *pin, const bool *select)
const int faceMap[6][4]
Definition octree.cpp:2829
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
std::string RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
PropertyRNA * RNA_def_float_factor(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
void RNA_def_property_float_default(PropertyRNA *prop, float value)
PropertyRNA * RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
__int64 int64_t
Definition stdint.h:89
short mat_nr
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * next
float co[3]
int totfacesel
CustomData vdata
int totvertsel
CustomData ldata
int totface
float aspect_y
Definition ED_uvedit.hh:297
BMFace ** faces
Definition ED_uvedit.hh:290
BMUVOffsets offsets
Definition ED_uvedit.hh:296
Definition DNA_ID.h:413
struct LinkNode * next
void * first
CustomData edge_data
int edges_num
CustomData face_data
CustomData vert_data
int faces_num
const Scene * scene
Vector< Object * > objects_edit
ParamHandle * handle
ObjectRuntimeHandle * runtime
ListBase modifiers
float persmat[4][4]
float viewmat[4][4]
int tile_grid_shape[2]
struct Image * image
char uvcalc_weight_group[64]
wmWindowManager * wm
Vector< Object * > objects
const SpaceImage * sima
blender::geometry::UVPackIsland_Params pack_island_params
ParamSlimOptions slim
struct Object * camera
short val
Definition WM_types.hh:724
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
void UV_OT_cylinder_project(wmOperatorType *ot)
void UV_OT_project_from_view(wmOperatorType *ot)
void UV_OT_smart_project(wmOperatorType *ot)
void UV_OT_unwrap(wmOperatorType *ot)
void UV_OT_sphere_project(wmOperatorType *ot)
void UV_OT_cube_project(wmOperatorType *ot)
void UV_OT_average_islands_scale(wmOperatorType *ot)
void UV_OT_reset(wmOperatorType *ot)
void UV_OT_minimize_stretch(wmOperatorType *ot)
void UV_OT_pack_islands(wmOperatorType *ot)
static int uv_from_view_exec(bContext *C, wmOperator *op)
static void uv_map_transform_center(const Scene *scene, View3D *v3d, Object *ob, BMEditMesh *em, float r_center[3], float r_bounds[2][3])
static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int cube_project_exec(bContext *C, wmOperator *op)
static bool uv_from_view_poll(bContext *C)
static void uv_map_clip_correct(const Scene *scene, const Span< Object * > objects, wmOperator *op, bool per_face_aspect, bool only_selected_uvs)
static int cylinder_project_exec(bContext *C, wmOperator *op)
static void minimize_stretch_exit(bContext *C, wmOperator *op, bool cancel)
static void pack_islands_freejob(void *pidv)
@ UNWRAP_ERROR_NEGATIVE
@ UNWRAP_ERROR_NONUNIFORM
static bool island_has_pins(const Scene *scene, FaceIsland *island, const blender::geometry::UVPackIsland_Params *params)
static void construct_param_edge_set_seams(ParamHandle *handle, BMesh *bm, const UnwrapOptions *options)
static void uv_map_mirror(BMFace *efa, const bool *regular, const bool fan, const int cd_loop_uv_offset)
static void uvedit_unwrap_cube_project(const Scene *scene, BMesh *bm, float cube_size, const bool use_select, const bool only_selected_uvs, const float center[3])
static const EnumPropertyItem pack_margin_method_items[]
static Mesh * subdivide_edit_mesh(const Object *object, const BMEditMesh *em, const SubsurfModifierData *smd)
#define POLAR_ZX
@ PACK_UDIM_SRC_CLOSEST
@ PACK_ORIGINAL_AABB
@ PACK_UDIM_SRC_ACTIVE
static float uv_cylinder_project(const Scene *scene, BMesh *bm, BMFace *efa_init, const float center[3], const float rotmat[3][3], const bool fan, const BMUVOffsets offsets, const bool only_selected_uvs, const bool use_seams, const float branch_init)
static void minimize_stretch_cancel(bContext *C, wmOperator *op)
static float uv_nearest_grid_tile_distance(const int udim_grid[2], const float coords[2], float nearest_tile_co[2])
static void uv_transform_properties(wmOperatorType *ot, int radius)
static ParamHandle * construct_param_handle_subsurfed(const Scene *scene, Object *ob, BMEditMesh *em, const UnwrapOptions *options, int *r_count_failed=nullptr)
static void construct_param_handle_face_add(ParamHandle *handle, const Scene *scene, BMFace *efa, blender::geometry::ParamKey face_index, const UnwrapOptions *options, const BMUVOffsets offsets, const int cd_weight_offset, const int cd_weight_index)
static int minimize_stretch_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOptions *options, int *r_count_changed, int *r_count_failed)
static void pack_islands_startjob(void *pidv, wmJobWorkerStatus *worker_status)
static int minimize_stretch_exec(bContext *C, wmOperator *op)
static void uv_pack_islands_ui(bContext *, wmOperator *op)
static const EnumPropertyItem pack_shape_method_items[]
static bool rna_property_sync_string(PointerRNA *ptr, const char *prop_name, char value_p[])
static const float smart_uv_project_area_ignore
static int unwrap_exec(bContext *C, wmOperator *op)
static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p)
static UnwrapOptions unwrap_options_get(wmOperator *op, Object *ob, const ToolSettings *ts)
static void correct_uv_aspect(Object *ob, BMEditMesh *em)
static void pack_islands_endjob(void *pidv)
static bool uvedit_live_unwrap_timer_validate(const wmWindowManager *wm)
wmTimer * timer
static int pack_islands_exec(bContext *C, wmOperator *op)
static void uv_map_transform(bContext *C, wmOperator *op, float rotmat[3][3])
static void uv_map_transform_calc_center_median(BMEditMesh *em, float r_center[3])
static void uv_map_clip_correct_properties(wmOperatorType *ot)
static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds)
uint len_alloc
static ParamHandle * construct_param_handle_multi(const Scene *scene, const Span< Object * > objects, const UnwrapOptions *options)
static const EnumPropertyItem pack_rotate_method_items[]
static void uv_map_rotation_matrix_ex(float result[4][4], RegionView3D *rv3d, Object *ob, float upangledeg, float sideangledeg, float radius, const float offset[4])
static void unwrap_options_sync_toolsettings(wmOperator *op, ToolSettings *ts)
static bool rna_property_sync_enum(PointerRNA *ptr, const char *prop_name, int *value_p)
#define VIEW_ON_POLES
static bool rna_property_sync_enum_char(PointerRNA *ptr, const char *prop_name, char *value_p)
static float uv_nearest_image_tile_distance(const Image *image, const float coords[2], float nearest_tile_co[2])
static int average_islands_scale_exec(bContext *C, wmOperator *op)
static int sphere_project_exec(bContext *C, wmOperator *op)
static void uvedit_prepare_pinned_indices(ParamHandle *handle, const Scene *scene, BMFace *efa, const UnwrapOptions *options, const BMUVOffsets offsets)
static bool rna_property_sync_float(PointerRNA *ptr, const char *prop_name, float *value_p)
static bool rna_property_sync_int(PointerRNA *ptr, const char *prop_name, int *value_p)
static int minimize_stretch_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void uvedit_pack_islands_multi(const Scene *scene, const Span< Object * > objects, BMesh **bmesh_override, const SpaceImage *udim_source_closest, const bool original_selection, const bool notify_wm, blender::geometry::UVPackIsland_Params *params)
static int smart_project_exec(bContext *C, wmOperator *op)
static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interactive)
static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
static bool uvedit_is_face_affected(const Scene *scene, BMFace *efa, const UnwrapOptions *options, const BMUVOffsets offsets)
#define PACK_ROTATE_METHOD_AXIS_ALIGNED_OFFSET
static bool minimize_stretch_init(bContext *C, wmOperator *op)
static void uv_map_transform_calc_bounds(BMEditMesh *em, float r_min[3], float r_max[3])
static int smart_project_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void unwrap_draw(bContext *, wmOperator *op)
static bool rna_property_sync_flag(PointerRNA *ptr, const char *prop_name, char flag, bool flipped, char *value_p)
static bool ED_uvedit_ensure_uvs(Object *obedit)
static void uvedit_unwrap_multi(const Scene *scene, const Span< Object * > objects, const UnwrapOptions *options, int *r_count_changed=nullptr, int *r_count_failed=nullptr)
static void shrink_loop_uv_by_aspect_ratio(BMFace *efa, const int cd_loop_uv_offset, const float aspect_y)
static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em)
#define ALIGN_TO_OBJECT
#define POLAR_ZY
static ParamHandle * construct_param_handle(const Scene *scene, Object *ob, BMesh *bm, const UnwrapOptions *options, int *r_count_failed=nullptr)
ParamHandle ** handles
static void island_uv_transform(FaceIsland *island, const float matrix[2][2], const float pre_translate[2])
#define VIEW_ON_EQUATOR
static float uv_sphere_project(const Scene *scene, BMesh *bm, BMFace *efa_init, const float center[3], const float rotmat[3][3], const bool fan, const BMUVOffsets offsets, const bool only_selected_uvs, const bool use_seams, const float branch_init)
static const EnumPropertyItem pinned_islands_method_items[]
static struct @598 g_live_unwrap
static void modifier_unwrap_state(Object *obedit, const UnwrapOptions *options, bool *r_use_subsurf)
static void texface_from_original_index(const Scene *scene, const BMUVOffsets offsets, BMFace *efa, int index, float **r_uv, bool *r_pin, bool *r_select)
static blender::Vector< blender::float3 > 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)
static int uv_pack_islands_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool uvedit_have_selection_multi(const Scene *scene, const Span< Object * > objects, const UnwrapOptions *options)
static int reset_exec(bContext *C, wmOperator *)
void WM_cursor_wait(bool val)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
@ RIGHTMOUSE
@ TIMER
@ WHEELUPMOUSE
@ EVT_PADENTER
@ WHEELDOWNMOUSE
@ EVT_PADMINUS
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_PADPLUSKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:352
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:455
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:189
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:364
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:336
int WM_operator_props_popup_call(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)
uint8_t flag
Definition wm_window.cc:138