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
8
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/* -------------------------------------------------------------------- */
88
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
128
129/* -------------------------------------------------------------------- */
132
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}
159
160
162{
163 if (stop) {
164 return *stop;
165 }
166 return false;
167}
168
169/* -------------------------------------------------------------------- */
172
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
596 vkeys[i] = blender::geometry::uv_find_pin_index(handle, BM_elem_index_get(l->v), luv);
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];
656 vkeys[0] = blender::geometry::uv_find_pin_index(handle, BM_elem_index_get(l->v), luv);
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
1027
1028/* -------------------------------------------------------------------- */
1031
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);
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);
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 */
1261 ot->exec = minimize_stretch_exec;
1262 ot->invoke = minimize_stretch_invoke;
1263 ot->modal = minimize_stretch_modal;
1264 ot->cancel = minimize_stretch_cancel;
1265 ot->poll = ED_operator_uvedit;
1266
1267 /* properties */
1268 RNA_def_boolean(ot->srna,
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);
1283 RNA_def_int(ot->srna,
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
1295
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
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/* -------------------------------------------------------------------- */
1630
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);
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
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
1929 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1930#else
1931 /* The operator will handle undo, so the job system can push() it after the job completes. */
1932 ot->flag = OPTYPE_REGISTER;
1933#endif
1934
1935 /* api callbacks */
1936 ot->exec = pack_islands_exec;
1937
1938#ifdef USE_INTERACTIVE_PACK
1940#else
1941 ot->invoke = uv_pack_islands_invoke;
1942#endif
1943 ot->ui = uv_pack_islands_ui;
1944 ot->poll = ED_operator_uvedit;
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");
1949 RNA_def_enum(ot->srna,
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");
1958 RNA_def_enum(ot->srna,
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);
1966 RNA_def_boolean(ot->srna,
1967 "pin",
1968 false,
1969 "Lock Pinned Islands",
1970 "Constrain islands containing any pinned UV's");
1971 RNA_def_enum(ot->srna,
1972 "pin_method",
1975 "Pin Method",
1976 "");
1977 RNA_def_enum(ot->srna,
1978 "shape_method",
1981 "Shape Method",
1982 "");
1983}
1984
1986
1987/* -------------------------------------------------------------------- */
1990
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
2041 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2042
2043 /* api callbacks */
2045 ot->poll = ED_operator_uvedit;
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
2053
2054/* -------------------------------------------------------------------- */
2057
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;
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
2192
2193/* -------------------------------------------------------------------- */
2196
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 {
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) {
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
2406 RNA_def_enum(ot->srna,
2407 "direction",
2408 direction_items,
2410 "Direction",
2411 "Direction of the sphere or cylinder");
2412 RNA_def_enum(ot->srna,
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");
2419 RNA_def_boolean(ot->srna,
2420 "seam",
2421 false,
2422 "Preserve Seams",
2423 "Separate projections by islands isolated by seams");
2424
2425 if (radius) {
2426 RNA_def_float(ot->srna,
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
2528
2529/* -------------------------------------------------------------------- */
2532
2533static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds)
2534{
2535 RNA_def_boolean(ot->srna,
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) {
2542 RNA_def_boolean(ot->srna,
2543 "clip_to_bounds",
2544 false,
2545 "Clip to Bounds",
2546 "Clip UV coordinates to bounds after unwrapping");
2547 }
2548 RNA_def_boolean(ot->srna,
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
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
2676
2677/* -------------------------------------------------------------------- */
2680
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
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";
2926 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2927
2928 /* api callbacks */
2929 ot->exec = unwrap_exec;
2930 ot->poll = ED_operator_uvmap;
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)");
2944 RNA_def_boolean(ot->srna,
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");
2950 RNA_def_boolean(ot->srna,
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");
2961 RNA_def_enum(ot->srna,
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 */
2971 RNA_def_boolean(ot->srna,
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
2978 RNA_def_int(ot->srna,
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
2988 RNA_def_boolean(ot->srna,
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");
2993 RNA_def_string(ot->srna,
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
3012
3013/* -------------------------------------------------------------------- */
3016
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
3338 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3339
3340 /* api callbacks */
3341 ot->exec = smart_project_exec;
3342 ot->poll = ED_operator_uvmap;
3343 ot->invoke = smart_project_invoke;
3344
3345 /* properties */
3346 prop = RNA_def_float_rotation(ot->srna,
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
3358 RNA_def_enum(ot->srna,
3359 "margin_method",
3362 "Margin Method",
3363 "");
3364 RNA_def_enum(ot->srna,
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 "");
3372 RNA_def_float(ot->srna,
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);
3381 RNA_def_float(ot->srna,
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
3395
3396/* -------------------------------------------------------------------- */
3399
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
3557 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3558
3559 /* api callbacks */
3560 ot->invoke = uv_from_view_invoke;
3561 ot->exec = uv_from_view_exec;
3562 ot->poll = uv_from_view_poll;
3563
3564 /* properties */
3565 RNA_def_boolean(ot->srna, "orthographic", false, "Orthographic", "Use orthographic projection");
3566 RNA_def_boolean(ot->srna,
3567 "camera_bounds",
3568 true,
3569 "Camera Bounds",
3570 "Map UVs to the camera region taking resolution and aspect into account");
3572}
3573
3575
3576/* -------------------------------------------------------------------- */
3579
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
3617 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3618
3619 /* api callbacks */
3620 ot->exec = reset_exec;
3621 ot->poll = ED_operator_uvmap;
3622}
3623
3625
3626/* -------------------------------------------------------------------- */
3629
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
3908 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3909
3910 /* api callbacks */
3911 ot->exec = sphere_project_exec;
3912 ot->poll = ED_operator_uvmap;
3913
3914 /* properties */
3917}
3918
3920
3921/* -------------------------------------------------------------------- */
3924
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
4096 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4097
4098 /* api callbacks */
4099 ot->exec = cylinder_project_exec;
4100 ot->poll = ED_operator_uvmap;
4101
4102 /* properties */
4105}
4106
4108
4109/* -------------------------------------------------------------------- */
4112
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
4232 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4233
4234 /* api callbacks */
4235 ot->exec = cube_project_exec;
4236 ot->poll = ED_operator_uvmap;
4237
4238 /* properties */
4239 RNA_def_float(ot->srna,
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
4252
4253/* -------------------------------------------------------------------- */
4256
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 */
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) {
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_METHOD_MINIMUM_STRETCH
@ UVCALC_UNWRAP_METHOD_CONFORMAL
@ UVCALC_UNWRAP_METHOD_ANGLE
@ UVCALC_UNWRAP_NO_FLIP
@ UVCALC_UNWRAP_USE_WEIGHTS
@ UVCALC_USESUBSURF
@ UVCALC_NO_ASPECT_CORRECT
@ UVCALC_FILLHOLES
@ UV_SYNC_SELECTION
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ V3D_AROUND_LOCAL_ORIGINS
struct wmOperator wmOperator
@ 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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between camera
#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
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ 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 ND_SPACE_IMAGE
Definition WM_types.hh:488
@ KM_PRESS
Definition WM_types.hh:284
#define NC_SPACE
Definition WM_types.hh:359
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#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 btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
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)
input_tx image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "preview_img") .compute_source("compositor_compute_preview.glsl") .do_static_compilation(true)
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)
VecBase< int32_t, 2 > int2
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]
int totfacesel
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]
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
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
char weight_group[MAX_VGROUP_NAME]
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
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
float max
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)
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
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)
@ UNWRAP_ERROR_NEGATIVE
@ UNWRAP_ERROR_NONUNIFORM
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 struct @362000021215203251150140160056175275164161262155 g_live_unwrap
@ PACK_UDIM_SRC_CLOSEST
@ PACK_ORIGINAL_AABB
@ PACK_UDIM_SRC_ACTIVE
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 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