Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/uvedit')
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h14
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c148
-rw-r--r--source/blender/editors/uvedit/uvedit_path.c22
-rw-r--r--source/blender/editors/uvedit/uvedit_rip.c25
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c1159
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c2
7 files changed, 1126 insertions, 246 deletions
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 42f0a32ea6f..b2a411147a5 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -70,11 +70,13 @@ bool uv_find_nearest_vert_multi(struct Scene *scene,
bool uv_find_nearest_edge(struct Scene *scene,
struct Object *obedit,
const float co[2],
+ float penalty,
struct UvNearestHit *hit);
bool uv_find_nearest_edge_multi(struct Scene *scene,
struct Object **objects,
uint objects_len,
const float co[2],
+ float penalty,
struct UvNearestHit *hit);
/**
@@ -116,6 +118,16 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
struct BMEdge *e,
const float co[2]);
+bool uvedit_vert_is_edge_select_any_other(const struct Scene *scene,
+ struct BMLoop *l,
+ const int cd_loop_uv_offset);
+bool uvedit_vert_is_face_select_any_other(const struct Scene *scene,
+ struct BMLoop *l,
+ const int cd_loop_uv_offset);
+bool uvedit_vert_is_all_other_faces_selected(const struct Scene *scene,
+ struct BMLoop *l,
+ const int cd_loop_uv_offset);
+
/* utility tool functions */
void uvedit_live_unwrap_update(struct SpaceImage *sima,
@@ -169,3 +181,5 @@ void UV_OT_select_circle(struct wmOperatorType *ot);
void UV_OT_select_more(struct wmOperatorType *ot);
void UV_OT_select_less(struct wmOperatorType *ot);
void UV_OT_select_overlap(struct wmOperatorType *ot);
+/* Used only when UV sync select is disabled. */
+void UV_OT_select_mode(struct wmOperatorType *ot);
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 0742b612b97..cbf0522c328 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -249,7 +249,7 @@ void ED_uvedit_select_all(BMesh *bm)
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
}
}
}
@@ -1406,12 +1406,15 @@ static void UV_OT_pin(wmOperatorType *ot)
/** \name Hide Operator
* \{ */
-/* check if we are selected or unselected based on 'bool_test' arg,
- * needed for select swap support */
-#define UV_SEL_TEST(luv, bool_test) \
+/* Check if vertex/edge is selected or unselected based on #bool_test arg. Needed for select swap
+ * support */
+#define UV_VERT_SEL_TEST(luv, bool_test) \
((((luv)->flag & MLOOPUV_VERTSEL) == MLOOPUV_VERTSEL) == bool_test)
-/* is every UV vert selected or unselected depending on bool_test */
+#define UV_EDGE_SEL_TEST(luv, bool_test) \
+ ((((luv)->flag & MLOOPUV_EDGESEL) == MLOOPUV_EDGESEL) == bool_test)
+
+/* Is the specified UV face, selected or unselected depending on bool_test. */
static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const int cd_loop_uv_offset)
{
BMLoop *l_iter;
@@ -1420,7 +1423,7 @@ static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const int cd_loop
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
- if (!UV_SEL_TEST(luv, select_test)) {
+ if (!UV_EDGE_SEL_TEST(luv, select_test)) {
return false;
}
} while ((l_iter = l_iter->next) != l_first);
@@ -1472,19 +1475,17 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (UV_SEL_TEST(luv, !swap)) {
+ if (UV_VERT_SEL_TEST(luv, !swap) || UV_EDGE_SEL_TEST(luv, !swap)) {
hide = 1;
break;
}
}
if (hide) {
- /* NOTE: a special case for edges could be used,
- * for now edges act like verts and get flushed */
if (use_face_center) {
if (em->selectmode == SCE_SELECT_FACE) {
- /* check that every UV is selected */
- if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
+ /* Deselect BMesh face if UV face is (de)selected depending on #swap. */
+ if (bm_face_is_all_uv_sel(efa, !swap, cd_loop_uv_offset)) {
BM_face_select_set(em->bm, efa, false);
}
uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
@@ -1493,7 +1494,12 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (UV_SEL_TEST(luv, !swap)) {
+ /* For both cases rely on edge sel tests, since all vert sel tests are invalid in
+ * case of sticky selections. */
+ if (UV_EDGE_SEL_TEST(luv, !swap) && (em->selectmode == SCE_SELECT_EDGE)) {
+ BM_edge_select_set(em->bm, l->e, false);
+ }
+ else if (UV_EDGE_SEL_TEST(luv, !swap) && (em->selectmode == SCE_SELECT_VERTEX)) {
BM_vert_select_set(em->bm, l->v, false);
}
}
@@ -1504,29 +1510,57 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
}
}
else if (em->selectmode == SCE_SELECT_FACE) {
- /* check if a UV is de-selected */
- if (bm_face_is_all_uv_sel(efa, false, cd_loop_uv_offset) != !swap) {
- BM_face_select_set(em->bm, efa, false);
- uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ /* Deselect BMesh face depending on the type of UV selectmode and the type of UV element
+ * being considered. */
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (UV_EDGE_SEL_TEST(luv, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
+ BM_face_select_set(em->bm, efa, false);
+ break;
+ }
+ else if (UV_VERT_SEL_TEST(luv, !swap) && (ts->uv_selectmode == UV_SELECT_VERTEX)) {
+ BM_face_select_set(em->bm, efa, false);
+ break;
+ }
+ else if (ts->uv_selectmode == UV_SELECT_ISLAND) {
+ BM_face_select_set(em->bm, efa, false);
+ break;
+ }
}
+ uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
}
else {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (UV_SEL_TEST(luv, !swap)) {
- BM_vert_select_set(em->bm, l->v, false);
- if (!swap) {
- luv->flag &= ~MLOOPUV_VERTSEL;
+ if (UV_EDGE_SEL_TEST(luv, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
+ if (em->selectmode == SCE_SELECT_EDGE)
+ BM_edge_select_set(em->bm, l->e, false);
+ else {
+ BM_vert_select_set(em->bm, l->v, false);
+ BM_vert_select_set(em->bm, l->next->v, false);
}
}
+ else if (UV_VERT_SEL_TEST(luv, !swap) && (ts->uv_selectmode != UV_SELECT_EDGE)) {
+ if (em->selectmode == SCE_SELECT_EDGE)
+ BM_edge_select_set(em->bm, l->e, false);
+ else
+ BM_vert_select_set(em->bm, l->v, false);
+ }
+ }
+ if (!swap) {
+ uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
}
}
}
}
- /* flush vertex selection changes */
+ /* Flush editmesh selections to ensure valid selection states. */
if (em->selectmode != SCE_SELECT_FACE) {
- EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX | SCE_SELECT_EDGE);
+ /* NOTE: Make sure correct flags are used. Previously this was done by passing
+ * (SCE_SELECT_VERTEX | SCE_SELECT_EDGE), which doesn't work now that we support proper UV
+ * edge selection. */
+
+ BM_mesh_select_mode_flush(em->bm);
}
BM_select_history_validate(em->bm);
@@ -1540,7 +1574,8 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-#undef UV_SEL_TEST
+#undef UV_VERT_SEL_TEST
+#undef UV_EDGE_SEL_TEST
static void UV_OT_hide(wmOperatorType *ot)
{
@@ -1567,12 +1602,10 @@ static void UV_OT_hide(wmOperatorType *ot)
static int uv_reveal_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
- SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
const ToolSettings *ts = scene->toolsettings;
const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
- const bool stickymode = sima ? (ts->uv_sticky != SI_STICKY_DISABLE) : 1;
const bool select = RNA_boolean_get(op->ptr, "select");
uint objects_len = 0;
@@ -1589,8 +1622,10 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- /* note on tagging, selecting faces needs to be delayed so it doesn't select the verts and
- * confuse our checks on selected verts. */
+ /* NOTE: Selecting faces is delayed so that it doesn't select verts/edges and confuse certain
+ * UV selection checks.
+ * This creates a temporary state which breaks certain UV selection functions that do face
+ * visibilty checks internally. Current implementation handles each case separately. */
/* call the mesh function if we are in mesh sync sel */
if (ts->uv_flag & UV_SYNC_SELECTION) {
@@ -1604,6 +1639,11 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
}
continue;
}
+
+ /* NOTE(@sidd017): Supporting selections in all cases is quite difficult considering there are
+ * at least 12 cases to look into (3 mesh selectmodes + 4 uv selectmodes + sticky modes).
+ * For now we select all UV faces as sticky disabled to ensure proper UV selection states (vert
+ * + edge flags) */
if (use_face_center) {
if (em->selectmode == SCE_SELECT_FACE) {
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -1611,7 +1651,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
+ SET_FLAG_FROM_TEST(luv->flag, select, (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL));
}
/* BM_face_select_set(em->bm, efa, true); */
BM_elem_flag_enable(efa, BM_ELEM_TAG);
@@ -1619,42 +1659,27 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
}
}
else {
- /* enable adjacent faces to have disconnected UV selections if sticky is disabled */
- if (!stickymode) {
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- BM_elem_flag_disable(efa, BM_ELEM_TAG);
- if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
- !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- int totsel = 0;
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_disable(efa, BM_ELEM_TAG);
+ if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ int totsel = 0;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (em->selectmode == SCE_SELECT_VERTEX) {
totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT);
}
-
- if (!totsel) {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
- }
- /* BM_face_select_set(em->bm, efa, true); */
- BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ else if (em->selectmode == SCE_SELECT_EDGE) {
+ totsel += BM_elem_flag_test(l->e, BM_ELEM_SELECT);
}
}
- }
- }
- else {
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- BM_elem_flag_disable(efa, BM_ELEM_TAG);
- if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
- !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+
+ if (!totsel) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
- }
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ SET_FLAG_FROM_TEST(luv->flag, select, (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL));
}
- /* BM_face_select_set(em->bm, efa, true); */
- BM_elem_flag_enable(efa, BM_ELEM_TAG);
}
+ /* BM_face_select_set(em->bm, efa, true); */
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
}
}
}
@@ -1665,7 +1690,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
+ SET_FLAG_FROM_TEST(luv->flag, select, (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL));
}
/* BM_face_select_set(em->bm, efa, true); */
BM_elem_flag_enable(efa, BM_ELEM_TAG);
@@ -1677,10 +1702,8 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
BM_elem_flag_disable(efa, BM_ELEM_TAG);
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
- }
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ SET_FLAG_FROM_TEST(luv->flag, select, (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL));
}
/* BM_face_select_set(em->bm, efa, true); */
BM_elem_flag_enable(efa, BM_ELEM_TAG);
@@ -2022,6 +2045,7 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_select_more);
WM_operatortype_append(UV_OT_select_less);
WM_operatortype_append(UV_OT_select_overlap);
+ WM_operatortype_append(UV_OT_select_mode);
WM_operatortype_append(UV_OT_snap_cursor);
WM_operatortype_append(UV_OT_snap_selected);
diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c
index 33621c1f0b6..7c6960a634a 100644
--- a/source/blender/editors/uvedit/uvedit_path.c
+++ b/source/blender/editors/uvedit/uvedit_path.c
@@ -234,6 +234,9 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
const int cd_loop_uv_offset)
{
const char uv_selectmode = ED_uvedit_select_mode_get(scene);
+ /* TODO(@sidd017): Implement logic to calculate shortest path for UV edges, since we now support
+ * proper edge selection for UVs (D12028).
+ * Till then continue using vertex path to fake shortest path calculation for edges. */
const bool use_fake_edge_select = (uv_selectmode & UV_SELECT_EDGE);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
@@ -377,7 +380,7 @@ static bool facetag_test_cb(BMFace *f, void *user_data_v)
BMIter iter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
- if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) {
+ if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) {
return false;
}
}
@@ -531,8 +534,21 @@ static bool uv_shortest_path_pick_ex(Scene *scene,
* flush the selection from the vertices. */
BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX, BM_SELECT_LEN_FLUSH_RECALC_ALL);
}
+ ED_uvedit_select_sync_flush(scene->toolsettings, em, select);
+ }
+ else {
+ if (uv_selectmode & UV_SELECT_EDGE) {
+ /* TODO(@sidd017): Remove this case when adding proper uv edge support for this operator.
+ * In the meantime, this case helps ensures proper UV selection states for edge mode. */
+ if (select) {
+ uvedit_select_flush(scene, em);
+ }
+ else {
+ uvedit_deselect_flush(scene, em);
+ }
+ }
+ ED_uvedit_selectmode_flush(scene, em);
}
- ED_uvedit_select_sync_flush(scene->toolsettings, em, select);
}
if (ts->uv_flag & UV_SYNC_SELECTION) {
@@ -603,7 +619,7 @@ static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEve
else if (uv_selectmode & UV_SELECT_EDGE) {
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
- if (!uv_find_nearest_edge(scene, obedit, co, &hit)) {
+ if (!uv_find_nearest_edge(scene, obedit, co, 0.0f, &hit)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c
index 272540d61c7..545cc57e3c4 100644
--- a/source/blender/editors/uvedit/uvedit_rip.c
+++ b/source/blender/editors/uvedit/uvedit_rip.c
@@ -52,7 +52,7 @@
/** Unordered loop data, stored in #BMLoop.head.index. */
typedef struct ULData {
- /** When this UV is selected as well as the next UV. */
+ /** When the specified UV edge is selected. */
uint is_select_edge : 1;
/**
* When only this UV is selected and none of the other UV's
@@ -762,15 +762,17 @@ static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const
const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (luv->flag & MLOOPUV_VERTSEL) {
const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
- const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
- if (luv_next->flag & MLOOPUV_VERTSEL) {
+ if (luv->flag & MLOOPUV_EDGESEL) {
UL(l)->is_select_edge = true;
}
+ else if ((luv_prev->flag & MLOOPUV_EDGESEL) == 0) {
+ /* #bm_loop_uv_select_single_vert_validate validates below. */
+ UL(l)->is_select_vert_single = true;
+ is_all = false;
+ }
else {
- if ((luv_prev->flag & MLOOPUV_VERTSEL) == 0) {
- /* #bm_loop_uv_select_single_vert_validate validates below. */
- UL(l)->is_select_vert_single = true;
- }
+ /* Cases where all vertices of a face are selected but not all edges are selected. */
+ is_all = false;
}
}
else {
@@ -797,7 +799,7 @@ static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const
}
}
- /* Special case: if we have selected faces, isolated them.
+ /* Special case: if we have selected faces, isolate them.
* This isn't a rip, however it's useful for users as a quick way
* to detach the selection.
*
@@ -812,6 +814,10 @@ static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const
luv->flag &= ~MLOOPUV_VERTSEL;
changed = true;
}
+ if (luv->flag & MLOOPUV_EDGESEL) {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ changed = true;
+ }
}
}
}
@@ -871,6 +877,9 @@ static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const
}
}
}
+ if (changed) {
+ uvedit_deselect_flush(scene, em);
+ }
return changed;
}
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 4454a8414b5..ca66981cd3a 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -46,6 +46,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -65,6 +66,8 @@ static void uv_select_all_perform_multi(Scene *scene,
static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bool select);
static void uv_select_flush_from_tag_loop(Scene *scene, Object *obedit, const bool select);
+static void uv_select_flush_from_loop_edge_flag(Scene *scene, BMEditMesh *em);
+
static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
const ToolSettings *ts,
Object *obedit);
@@ -217,8 +220,15 @@ bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int c
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (!(luv->flag & MLOOPUV_VERTSEL)) {
- return false;
+ if (ts->uv_selectmode & UV_SELECT_VERTEX) {
+ if ((luv->flag & MLOOPUV_VERTSEL) == 0) {
+ return false;
+ }
+ }
+ else {
+ if ((luv->flag & MLOOPUV_EDGESEL) == 0) {
+ return false;
+ }
}
}
return true;
@@ -236,21 +246,58 @@ void uvedit_face_select_set_with_sticky(const Scene *scene,
const int cd_loop_uv_offset)
{
const ToolSettings *ts = scene->toolsettings;
+ const char sticky = ts->uv_sticky;
if (ts->uv_flag & UV_SYNC_SELECTION) {
uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
return;
}
+ if (!uvedit_face_visible_test(scene, efa)) {
+ return;
+ }
+ /* NOTE: Previously face selections done in sticky vertex mode selected stray UV vertices
+ * (not part of any face selections). This now uses the sticky location mode logic instead. */
+ switch (sticky) {
+ case SI_STICKY_DISABLE: {
+ uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
+ break;
+ }
+ default: {
+ /* SI_STICKY_LOC and SI_STICKY_VERTEX modes. */
+ uvedit_face_select_shared_vert(scene, em, efa, select, do_history, cd_loop_uv_offset);
+ }
+ }
+}
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- uvedit_uv_select_set_with_sticky(scene, em, l_iter, select, do_history, cd_loop_uv_offset);
- } while ((l_iter = l_iter->next) != l_first);
+void uvedit_face_select_shared_vert(const Scene *scene,
+ BMEditMesh *em,
+ BMFace *efa,
+ const bool select,
+ const bool do_history,
+ const int cd_loop_uv_offset)
+{
+ BMLoop *l;
+ BMIter liter;
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (select) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ uvedit_uv_select_shared_vert(
+ scene, em, l, select, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ }
+ else {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ if (!uvedit_vert_is_face_select_any_other(scene, l, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_vert(
+ scene, em, l, select, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ }
+ }
+ }
}
-void uvedit_face_select_set(const struct Scene *scene,
- struct BMEditMesh *em,
- struct BMFace *efa,
+void uvedit_face_select_set(const Scene *scene,
+ BMEditMesh *em,
+ BMFace *efa,
const bool select,
const bool do_history,
const int cd_loop_uv_offset)
@@ -284,7 +331,7 @@ void uvedit_face_select_enable(const Scene *scene,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
}
}
}
@@ -306,7 +353,7 @@ void uvedit_face_select_disable(const Scene *scene,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~(MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
}
}
}
@@ -324,13 +371,15 @@ bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_
BM_elem_flag_test(l->next->v, BM_ELEM_SELECT);
}
- MLoopUV *luv1, *luv2;
-
- luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
+ if (ts->uv_selectmode & UV_SELECT_VERTEX) {
+ MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ return (luv->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL);
+ }
+ return (luv->flag & MLOOPUV_EDGESEL);
}
+
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
{
return uvedit_edge_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset);
@@ -349,8 +398,89 @@ void uvedit_edge_select_set_with_sticky(const Scene *scene,
return;
}
- uvedit_uv_select_set_with_sticky(scene, em, l, select, do_history, cd_loop_uv_offset);
- uvedit_uv_select_set_with_sticky(scene, em, l->next, select, do_history, cd_loop_uv_offset);
+ const int sticky = ts->uv_sticky;
+ switch (sticky) {
+ case SI_STICKY_DISABLE: {
+ if (uvedit_face_visible_test(scene, l->f)) {
+ uvedit_edge_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ }
+ break;
+ }
+ case SI_STICKY_VERTEX: {
+ uvedit_edge_select_shared_vert(
+ scene, em, l, select, SI_STICKY_VERTEX, do_history, cd_loop_uv_offset);
+ break;
+ }
+ default: {
+ /* SI_STICKY_LOC (Fallback) */
+ uvedit_edge_select_shared_vert(
+ scene, em, l, select, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ break;
+ }
+ }
+}
+
+/**
+ * Selects UV edges and shared vertices according to sticky_flag.
+ *
+ * \param sticky_flag:
+ * - SI_STICKY_LOC: selects all UV edges that share the same mesh vertices and UV coordinates.
+ * - SI_STICKY_VERTEX: selects all UV edges sharing the same mesh vertices.
+ */
+void uvedit_edge_select_shared_vert(const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l,
+ const bool select,
+ const int sticky_flag,
+ const bool do_history,
+ const int cd_loop_uv_offset)
+{
+ BLI_assert((sticky_flag == SI_STICKY_LOC) || (sticky_flag == SI_STICKY_VERTEX));
+ /* Set edge flags. Rely on this for face visibility checks */
+ uvedit_edge_select_set_noflush(scene, l, select, sticky_flag, cd_loop_uv_offset);
+
+ /* Vert selections. */
+ BMLoop *l_iter = l;
+ do {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if (select && (luv->flag & MLOOPUV_EDGESEL)) {
+ uvedit_uv_select_shared_vert(
+ scene, em, l_iter, true, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_shared_vert(
+ scene, em, l_iter->next, true, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ }
+
+ else if (!select && !(luv->flag & MLOOPUV_EDGESEL)) {
+ if (!uvedit_vert_is_edge_select_any_other(scene, l, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_vert(
+ scene, em, l_iter, false, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ }
+ if (!uvedit_vert_is_edge_select_any_other(scene, l->next, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_vert(
+ scene, em, l_iter->next, false, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ }
+ }
+ } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != SI_STICKY_LOC));
+}
+
+/* Set edge flags for required UV edges. */
+void uvedit_edge_select_set_noflush(const Scene *scene,
+ BMLoop *l,
+ const bool select,
+ const int sticky_flag,
+ const int cd_loop_uv_offset)
+{
+ MLoopUV *luv;
+ BMLoop *l_iter = l;
+ do {
+ if (uvedit_face_visible_test(scene, l_iter->f)) {
+ if ((sticky_flag == SI_STICKY_VERTEX) ||
+ BM_loop_uv_share_edge_check(l, l_iter, cd_loop_uv_offset)) {
+ luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_EDGESEL);
+ }
+ }
+ } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != SI_STICKY_DISABLE));
}
void uvedit_edge_select_set(const Scene *scene,
@@ -395,13 +525,12 @@ void uvedit_edge_select_enable(const Scene *scene,
}
}
else {
- MLoopUV *luv1, *luv2;
-
- luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ MLoopUV *luv, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
- luv1->flag |= MLOOPUV_VERTSEL;
- luv2->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
+ luv_next->flag |= MLOOPUV_VERTSEL;
}
}
@@ -426,13 +555,25 @@ void uvedit_edge_select_disable(const Scene *scene,
}
}
else {
- MLoopUV *luv1, *luv2;
-
- luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
-
- luv1->flag &= ~MLOOPUV_VERTSEL;
- luv2->flag &= ~MLOOPUV_VERTSEL;
+ MLoopUV *luv, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ if ((ts->uv_selectmode & UV_SELECT_VERTEX) == 0) {
+ /* Deselect UV vertex if not part of another edge selection */
+ MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ if (!(luv_next->flag & MLOOPUV_EDGESEL)) {
+ luv_next->flag &= ~MLOOPUV_VERTSEL;
+ }
+ if (!(luv_prev->flag & MLOOPUV_EDGESEL)) {
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ }
+ }
+ else {
+ luv_next->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ }
}
}
@@ -469,45 +610,66 @@ void uvedit_uv_select_set_with_sticky(const Scene *scene,
const int sticky = ts->uv_sticky;
switch (sticky) {
case SI_STICKY_DISABLE: {
- uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ if (uvedit_face_visible_test(scene, l->f)) {
+ uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ }
+ break;
+ }
+ case SI_STICKY_VERTEX: {
+ uvedit_uv_select_shared_vert(
+ scene, em, l, select, SI_STICKY_VERTEX, do_history, cd_loop_uv_offset);
break;
}
default: {
- /* #SI_STICKY_VERTEX or #SI_STICKY_LOC. */
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- BMEdge *e_first, *e_iter;
- e_first = e_iter = l->e;
- do {
- if (e_iter->l) {
- BMLoop *l_radial_iter = e_iter->l;
- do {
- if (l_radial_iter->v == l->v) {
- if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
- bool do_select = false;
- if (sticky == SI_STICKY_VERTEX) {
- do_select = true;
- }
- else {
- const MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_radial_iter,
- cd_loop_uv_offset);
- if (equals_v2v2(luv_other->uv, luv->uv)) {
- do_select = true;
- }
- }
-
- if (do_select) {
- uvedit_uv_select_set(
- scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
- }
- }
- }
- } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
- }
- } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
+ /* SI_STICKY_LOC. */
+ uvedit_uv_select_shared_vert(
+ scene, em, l, select, SI_STICKY_LOC, do_history, cd_loop_uv_offset);
+ break;
}
}
}
+/**
+ * Selects shared UVs based on #sticky_flag.
+ *
+ * \param sticky_flag: Type of sticky selection :
+ * - SI_STICKY_LOC: selects all UVs sharing same mesh vertex and UV coordinates.
+ * - SI_STICKY_VERTEX: selects all UVs sharing same mesh vertex.
+ */
+void uvedit_uv_select_shared_vert(const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l,
+ const bool select,
+ const int sticky_flag,
+ const bool do_history,
+ const int cd_loop_uv_offset)
+{
+ BLI_assert((sticky_flag == SI_STICKY_LOC) || (sticky_flag == SI_STICKY_VERTEX));
+
+ BMEdge *e_first, *e_iter;
+ e_first = e_iter = l->e;
+ do {
+ BMLoop *l_radial_iter = e_iter->l;
+ do {
+ if (l_radial_iter->v == l->v) {
+ if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
+ bool do_select = false;
+ if (sticky_flag == SI_STICKY_VERTEX) {
+ do_select = true;
+ }
+ else if (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset)) {
+ do_select = true;
+ }
+
+ if (do_select) {
+ uvedit_uv_select_set(scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
+ }
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
+}
+
void uvedit_uv_select_set(const Scene *scene,
BMEditMesh *em,
BMLoop *l,
@@ -630,7 +792,8 @@ static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scen
/** \name Find Nearest Elements
* \{ */
-bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
+bool uv_find_nearest_edge(
+ Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
{
BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -659,8 +822,13 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea
sub_v2_v2(delta, co);
mul_v2_v2(delta, hit->scale);
- const float dist_test_sq = len_squared_v2(delta);
+ float dist_test_sq = len_squared_v2(delta);
+ /* Ensures that successive selection attempts will select other edges sharing the same
+ * UV coordinates as the previous selection. */
+ if ((penalty != 0.0f) && uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) {
+ dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty);
+ }
if (dist_test_sq < hit->dist_sq) {
hit->ob = obedit;
hit->efa = efa;
@@ -675,13 +843,17 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea
return found;
}
-bool uv_find_nearest_edge_multi(
- Scene *scene, Object **objects, const uint objects_len, const float co[2], UvNearestHit *hit)
+bool uv_find_nearest_edge_multi(Scene *scene,
+ Object **objects,
+ const uint objects_len,
+ const float co[2],
+ const float penalty,
+ UvNearestHit *hit)
{
bool found = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- if (uv_find_nearest_edge(scene, obedit, co, hit)) {
+ if (uv_find_nearest_edge(scene, obedit, co, penalty, hit)) {
found = true;
}
}
@@ -801,6 +973,8 @@ bool uv_find_nearest_vert(
float dist_test_sq = len_squared_v2(delta);
+ /* Ensures that successive selection attempts will select other vertices sharing the same
+ * UV coordinates */
if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty_dist);
}
@@ -961,6 +1135,191 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Helper functions for UV selection.
+ * \{ */
+
+bool uvedit_vert_is_edge_select_any_other(const Scene *scene,
+ BMLoop *l,
+ const int cd_loop_uv_offset)
+{
+ BMEdge *e_iter = l->e;
+ do {
+ BMLoop *l_radial_iter = e_iter->l, *l_other;
+ do {
+ if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
+ /* Use #l_other to check if the uvs are connected (share the same uv coordinates)
+ * and #l_radial_iter for the actual edge selection test. */
+ l_other = (l_radial_iter->v != l->v) ? l_radial_iter->next : l_radial_iter;
+ if (BM_loop_uv_share_vert_check(l, l_other, cd_loop_uv_offset) &&
+ uvedit_edge_select_test(scene, l_radial_iter, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != l->e);
+
+ return false;
+}
+
+bool uvedit_vert_is_face_select_any_other(const Scene *scene,
+ BMLoop *l,
+ const int cd_loop_uv_offset)
+{
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
+ if (!uvedit_face_visible_test(scene, l_iter->f) || (l_iter->f == l->f)) {
+ continue;
+ }
+ if (BM_loop_uv_share_vert_check(l, l_iter, cd_loop_uv_offset) &&
+ uvedit_face_select_test(scene, l_iter->f, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool uvedit_vert_is_all_other_faces_selected(const Scene *scene,
+ BMLoop *l,
+ const int cd_loop_uv_offset)
+{
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
+ if (!uvedit_face_visible_test(scene, l_iter->f) || (l_iter->f == l->f)) {
+ continue;
+ }
+ if (BM_loop_uv_share_vert_check(l, l_iter, cd_loop_uv_offset) &&
+ !uvedit_face_select_test(scene, l_iter->f, cd_loop_uv_offset)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Clear specified UV flag (vert/edge/pinned).
+ */
+static void bm_uv_flag_clear(Scene *scene, BMesh *bm, const int flag, const int cd_loop_uv_offset)
+{
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv->flag &= ~flag;
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Select-Mode Flushing
+ *
+ * \{ */
+
+void ED_uvedit_selectmode_flush(Scene *scene, BMEditMesh *em)
+{
+ ToolSettings *ts = scene->toolsettings;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
+
+ /* Vertex Mode only. */
+ if (ts->uv_selectmode & UV_SELECT_VERTEX) {
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ if ((luv->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ }
+ else {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ }
+ }
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Flush selection (up/down)
+ * \{ */
+
+/* Careful when using this in face select mode.
+ * For face selections with sticky mode enabled, this can create invalid selection states. */
+void uvedit_select_flush(Scene *scene, BMEditMesh *em)
+{
+ ToolSettings *ts = scene->toolsettings;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
+
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ if ((luv->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ }
+ }
+ }
+}
+
+void uvedit_deselect_flush(Scene *scene, BMEditMesh *em)
+{
+ ToolSettings *ts = scene->toolsettings;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
+
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ if (luv->flag & MLOOPUV_EDGESEL) {
+ if (!(luv->flag & MLOOPUV_VERTSEL) || !(luv_next->flag & MLOOPUV_VERTSEL)) {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ }
+ }
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Edge Loop Select
* \{ */
@@ -1124,13 +1483,14 @@ static void uv_select_edgeloop_single_side_tag(const Scene *scene,
static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
{
+ const ToolSettings *ts = scene->toolsettings;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
bool select;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
if (extend) {
- select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ select = !(uvedit_edge_select_test(scene, hit->l, cd_loop_uv_offset));
}
else {
select = true;
@@ -1185,7 +1545,15 @@ static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, c
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
- uvedit_edge_select_set_with_sticky(scene, em, l_iter, select, false, cd_loop_uv_offset);
+ if (ts->uv_selectmode == UV_SELECT_VERTEX) {
+ uvedit_uv_select_set_with_sticky(scene, em, l_iter, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set_with_sticky(
+ scene, em, l_iter->next, select, false, cd_loop_uv_offset);
+ }
+ else {
+ uvedit_edge_select_set_with_sticky(
+ scene, em, l_iter, select, false, cd_loop_uv_offset);
+ }
}
}
}
@@ -1197,6 +1565,68 @@ static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, c
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Face Loop Select
+ * \{ */
+
+static int uv_select_faceloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ bool select;
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ if (!extend) {
+ uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ }
+
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false);
+
+ if (extend) {
+ select = !(uvedit_face_select_test(scene, hit->l->f, cd_loop_uv_offset));
+ }
+ else {
+ select = true;
+ }
+
+ BMLoop *l_pair[2] = {
+ hit->l,
+ uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset),
+ };
+
+ for (int side = 0; side < 2; side++) {
+ BMLoop *l_step = l_pair[side];
+ while (l_step) {
+ if (!uvedit_face_visible_test(scene, l_step->f)) {
+ break;
+ }
+
+ uvedit_face_select_set_with_sticky(scene, em, l_step->f, select, false, cd_loop_uv_offset);
+
+ BM_elem_flag_enable(l_step->f, BM_ELEM_TAG);
+ if (l_step->f->len == 4) {
+ BMLoop *l_step_opposite = l_step->next->next;
+ l_step = uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step_opposite, cd_loop_uv_offset);
+ }
+ else {
+ l_step = NULL;
+ }
+
+ /* Break iteration when `l_step`:
+ * - is the first loop where we started from.
+ * - tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration). */
+ if (l_step && BM_elem_flag_test(l_step->f, BM_ELEM_TAG)) {
+ break;
+ }
+ }
+ }
+
+ return (select) ? 1 : -1;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Edge Ring Select
* \{ */
@@ -1207,6 +1637,9 @@ static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, c
const bool use_face_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode & SCE_SELECT_FACE) :
(ts->uv_selectmode & UV_SELECT_FACE);
+ const bool use_vertex_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
+ (ts->selectmode & SCE_SELECT_VERTEX) :
+ (ts->uv_selectmode & UV_SELECT_VERTEX);
bool select;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
@@ -1218,7 +1651,7 @@ static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, c
BM_mesh_elem_hflag_disable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false);
if (extend) {
- select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ select = !(uvedit_edge_select_test(scene, hit->l, cd_loop_uv_offset));
}
else {
select = true;
@@ -1239,9 +1672,18 @@ static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, c
}
if (use_face_select) {
+ /* While selecting face loops is now done in a separate function #uv_select_faceloop(),
+ * this check is still kept for edge ring selection, to keep it consistent with how edge
+ * ring selection works in face mode in the 3D viewport. */
uvedit_face_select_set_with_sticky(scene, em, l_step->f, select, false, cd_loop_uv_offset);
}
+ else if (use_vertex_select) {
+ uvedit_uv_select_set_with_sticky(scene, em, l_step, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set_with_sticky(
+ scene, em, l_step->next, select, false, cd_loop_uv_offset);
+ }
else {
+ /* Edge select mode */
uvedit_edge_select_set_with_sticky(scene, em, l_step, select, false, cd_loop_uv_offset);
}
@@ -1259,8 +1701,19 @@ static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, c
l_step = NULL;
}
+ /* Break iteration when `l_step`:
+ * - Is the first loop where we started from.
+ * - Tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration).
+ * - Has its corresponding UV edge selected/unselected based on #select. */
if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
- break;
+ /* Previously this check was not done and this resulted in the final edge in the edge ring
+ * cycle to be skipped during selection (caused by old sticky selection behavior). */
+ if (select && uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) {
+ break;
+ }
+ else if (!select && !uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) {
+ break;
+ }
}
}
}
@@ -1570,30 +2023,50 @@ static int uv_select_more_less(bContext *C, const bool select)
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, efa)) {
-#define IS_SEL 1
-#define IS_UNSEL 2
+ if (select) {
+#define NEIGHBORING_FACE_IS_SEL 1
+#define CURR_FACE_IS_UNSEL 2
- int sel_state = 0;
+ int sel_state = 0;
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (luv->flag & MLOOPUV_VERTSEL) {
- sel_state |= IS_SEL;
- }
- else {
- sel_state |= IS_UNSEL;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ sel_state |= NEIGHBORING_FACE_IS_SEL;
+ }
+ else {
+ sel_state |= CURR_FACE_IS_UNSEL;
+ }
+
+ if (!(luv->flag & MLOOPUV_EDGESEL)) {
+ sel_state |= CURR_FACE_IS_UNSEL;
+ }
+
+ /* If the current face is not selected and at least one neighboring face is
+ * selected, then tag the current face to grow selection.*/
+ if (sel_state == (NEIGHBORING_FACE_IS_SEL | CURR_FACE_IS_UNSEL)) {
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ changed = true;
+ break;
+ }
}
- /* if we have a mixed selection, tag to grow it */
- if (sel_state == (IS_SEL | IS_UNSEL)) {
- BM_elem_flag_enable(efa, BM_ELEM_TAG);
- changed = true;
- break;
+#undef NEIGHBORING_FACE_IS_SEL
+#undef CURR_FACE_IS_UNSEL
+ }
+ else {
+ if (!uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ /* Deselect face when at least one of the surrounding faces is not selected */
+ if (!uvedit_vert_is_all_other_faces_selected(scene, l, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ changed = true;
+ break;
+ }
}
}
-
-#undef IS_SEL
-#undef IS_UNSEL
}
}
}
@@ -1631,6 +2104,13 @@ static int uv_select_more_less(bContext *C, const bool select)
else {
/* Select tagged loops. */
uv_select_flush_from_tag_loop(scene, obedit, select);
+ /* Set/unset edge flags based on selected verts. */
+ if (select) {
+ uvedit_select_flush(scene, em);
+ }
+ else {
+ uvedit_deselect_flush(scene, em);
+ }
}
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
@@ -1724,16 +2204,68 @@ bool uvedit_select_is_any_selected_multi(Scene *scene, Object **objects, const u
return found;
}
-static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
+static void uv_select_all(Scene *scene, BMEditMesh *em, bool select_all)
{
- const ToolSettings *ts = scene->toolsettings;
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa;
BMLoop *l;
BMIter iter, liter;
MLoopUV *luv;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ const int uv_select_flags = (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ SET_FLAG_FROM_TEST(luv->flag, select_all, uv_select_flags);
+ }
+ }
+}
+
+static void uv_select_invert(Scene *scene, BMEditMesh *em)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ MLoopUV *luv;
+ char uv_selectmode = ts->uv_selectmode;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if ((uv_selectmode == UV_SELECT_EDGE) || (uv_selectmode == UV_SELECT_FACE)) {
+ /* Use #MLOOPUV_EDGESEL to flag edges that must be selected. */
+ luv->flag ^= MLOOPUV_EDGESEL;
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ }
+ /* Use #MLOOPUV_VERTSEL to flag verts that must be selected. */
+ else if ((uv_selectmode == UV_SELECT_VERTEX) || (uv_selectmode == UV_SELECT_ISLAND)) {
+ luv->flag ^= MLOOPUV_VERTSEL;
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ }
+ }
+ }
+
+ /* Flush based on uv vert/edge flags and current UV select mode */
+ if ((uv_selectmode == UV_SELECT_EDGE) || (uv_selectmode == UV_SELECT_FACE)) {
+ uv_select_flush_from_loop_edge_flag(scene, em);
+ }
+ else if ((uv_selectmode == UV_SELECT_VERTEX) || (uv_selectmode == UV_SELECT_ISLAND)) {
+ uvedit_select_flush(scene, em);
+ }
+}
+
+static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (action == SEL_TOGGLE) {
action = uvedit_select_is_any_selected(scene, obedit) ? SEL_DESELECT : SEL_SELECT;
@@ -1757,26 +2289,16 @@ static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
}
}
else {
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
- }
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-
- switch (action) {
- case SEL_SELECT:
- luv->flag |= MLOOPUV_VERTSEL;
- break;
- case SEL_DESELECT:
- luv->flag &= ~MLOOPUV_VERTSEL;
- break;
- case SEL_INVERT:
- luv->flag ^= MLOOPUV_VERTSEL;
- break;
- }
- }
+ switch (action) {
+ case SEL_SELECT:
+ uv_select_all(scene, em, true);
+ break;
+ case SEL_DESELECT:
+ uv_select_all(scene, em, false);
+ break;
+ case SEL_INVERT:
+ uv_select_invert(scene, em);
+ break;
}
}
}
@@ -1905,7 +2427,7 @@ static int uv_mouse_select_multi(bContext *C,
}
else if (selectmode == UV_SELECT_EDGE) {
/* find edge */
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, penalty_dist, &hit);
if (found_item) {
if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
@@ -1931,7 +2453,7 @@ static int uv_mouse_select_multi(bContext *C,
}
}
else if (selectmode == UV_SELECT_ISLAND) {
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit);
if (!found_item) {
/* Without this, we can be within the face of an island but too far from an edge,
@@ -2028,6 +2550,10 @@ static int uv_mouse_select_multi(bContext *C,
EDBM_selectmode_flush(em);
}
}
+ /* #extend=false implies single vertex selection, which doesn't need to be flushed. */
+ else if (extend) {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obiter = objects[ob_index];
@@ -2144,7 +2670,7 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
int flush = 0;
/* Find edge. */
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit);
if (!found_item) {
return OPERATOR_CANCELLED;
}
@@ -2158,7 +2684,12 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
}
if (loop_type == UV_LOOP_SELECT) {
- flush = uv_select_edgeloop(scene, obedit, &hit, extend);
+ if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) {
+ flush = uv_select_faceloop(scene, obedit, &hit, extend);
+ }
+ else {
+ flush = uv_select_edgeloop(scene, obedit, &hit, extend);
+ }
}
else if (loop_type == UV_RING_SELECT) {
flush = uv_select_edgering(scene, obedit, &hit, extend);
@@ -2175,6 +2706,9 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
EDBM_deselect_flush(em);
}
}
+ else {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obiter = objects[ob_index];
@@ -2210,15 +2744,7 @@ static int uv_select_loop_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "location", co);
const bool extend = RNA_boolean_get(op->ptr, "extend");
- Scene *scene = CTX_data_scene(C);
- enum eUVLoopGenericType type = UV_LOOP_SELECT;
- if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) {
- /* For now ring-select and face-loop is the same thing,
- * if we support real edge selection this will no longer be the case. */
- type = UV_RING_SELECT;
- }
-
- return uv_mouse_select_loop_generic(C, co, extend, type);
+ return uv_mouse_select_loop_generic(C, co, extend, UV_LOOP_SELECT);
}
static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -2368,7 +2894,7 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent
RNA_float_get_array(op->ptr, "location", co);
}
- if (!uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit)) {
+ if (!uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit)) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
}
@@ -2533,10 +3059,10 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (luv->flag & MLOOPUV_VERTSEL) {
+ if ((luv->flag & MLOOPUV_VERTSEL) || (luv->flag & MLOOPUV_EDGESEL)) {
is_sel = true;
}
- else {
+ if (!(luv->flag & MLOOPUV_VERTSEL) || !(luv->flag & MLOOPUV_EDGESEL)) {
is_unsel = true;
}
@@ -2549,7 +3075,7 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
if (is_sel && is_unsel) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~(MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
}
changed = true;
@@ -2658,7 +3184,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene,
/**
* Flush the selection from face tags based on sticky and selection modes.
*
- * needed because settings the selection a face is done in a number of places but it also
+ * needed because setting the selection of a face is done in a number of places but it also
* needs to respect the sticky modes for the UV verts, so dealing with the sticky modes
* is best done in a separate function.
*
@@ -2680,31 +3206,9 @@ static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bo
BMIter iter, liter;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && ts->uv_sticky == SI_STICKY_VERTEX) {
- /* Tag all verts as untouched, then touch the ones that have a face center
- * in the loop and select all MLoopUV's that use a touched vert. */
- BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
-
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- BM_elem_flag_enable(l->v, BM_ELEM_TAG);
- }
- }
- }
-
- /* now select tagged verts */
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 &&
+ ((ts->uv_sticky == SI_STICKY_VERTEX) || (ts->uv_sticky == SI_STICKY_LOC))) {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
- }
- }
- }
- }
- else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && ts->uv_sticky == SI_STICKY_LOC) {
struct UvVertMap *vmap;
uint efa_index;
@@ -2719,8 +3223,19 @@ static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bo
/* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- uv_select_flush_from_tag_sticky_loc_internal(
- scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (select) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
+ }
+ else {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ if (!uvedit_vert_is_face_select_any_other(scene, l, cd_loop_uv_offset)) {
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
+ }
+ }
}
}
}
@@ -2738,11 +3253,11 @@ static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bo
/**
* Flush the selection from loop tags based on sticky and selection modes.
*
- * needed because settings the selection a face is done in a number of places but it also needs
+ * needed because setting the selection of a face is done in a number of places but it also needs
* to respect the sticky modes for the UV verts, so dealing with the sticky modes is best done
* in a separate function.
*
- * \note This function is very similar to #uv_select_flush_from_tag_loop,
+ * \note This function is very similar to #uv_select_flush_from_tag_face,
* be sure to update both upon changing.
*/
static void uv_select_flush_from_tag_loop(Scene *scene, Object *obedit, const bool select)
@@ -2818,6 +3333,77 @@ static void uv_select_flush_from_tag_loop(Scene *scene, Object *obedit, const bo
}
}
+/**
+ * Flush the selection from UV edge flags based on sticky modes.
+ *
+ * Useful when performing edge selections in different sticky modes, since setting the required
+ * edge flags (#MLOOPUV_EDGESEL) is done manually or using #uvedit_edge_select_set_noflush,
+ * but dealing with sticky modes for vertex selections is best done in a separate function.
+ *
+ * \note Current behavior is selecting only; deselecting can be added but the behavior isn't
+ * required anywhere.*/
+static void uv_select_flush_from_loop_edge_flag(Scene *scene, BMEditMesh *em)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 &&
+ ((ts->uv_sticky == SI_STICKY_LOC) || (ts->uv_sticky == SI_STICKY_VERTEX))) {
+ /* Use the #MLOOPUV_EDGESEL flag to identify which verts must to be selected */
+ struct UvVertMap *vmap;
+ uint efa_index;
+ /* Clear UV vert flags */
+ bm_uv_flag_clear(scene, em->bm, MLOOPUV_VERTSEL, cd_loop_uv_offset);
+
+ BM_mesh_elem_table_ensure(em->bm, BM_FACE);
+ vmap = BM_uv_vert_map_create(em->bm, false, false);
+ if (vmap == NULL) {
+ return;
+ }
+ BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ /* This visibility check could be removed? Simply relying on edge flags to ensure
+ * visibility might be sufficient. */
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ /* Select verts based on UV edge flag. */
+ if (luv->flag & MLOOPUV_EDGESEL) {
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l, true, cd_loop_uv_offset);
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l->next, true, cd_loop_uv_offset);
+ }
+ }
+ }
+ BM_uv_vert_map_free(vmap);
+ }
+ else {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv, *luv_next, *luv_prev;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+
+ if (luv->flag & MLOOPUV_EDGESEL) {
+ luv->flag |= MLOOPUV_VERTSEL;
+ luv_next->flag |= MLOOPUV_VERTSEL;
+ }
+ else if (!(luv_prev->flag & MLOOPUV_EDGESEL)) {
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ }
+ }
+ }
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -2964,8 +3550,12 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
if (changed || use_pre_deselect) {
changed_multi = true;
-
- ED_uvedit_select_sync_flush(ts, em, select);
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ ED_uvedit_select_sync_flush(ts, em, select);
+ }
+ else {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
@@ -3173,8 +3763,12 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
if (changed || use_pre_deselect) {
changed_multi = true;
-
- ED_uvedit_select_sync_flush(ts, em, select);
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ ED_uvedit_select_sync_flush(ts, em, select);
+ }
+ else {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
@@ -3355,8 +3949,12 @@ static bool do_lasso_select_mesh_uv(bContext *C,
if (changed || use_pre_deselect) {
changed_multi = true;
-
- ED_uvedit_select_sync_flush(ts, em, select);
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ ED_uvedit_select_sync_flush(ts, em, select);
+ }
+ else {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
@@ -3407,11 +4005,21 @@ void UV_OT_select_lasso(wmOperatorType *ot)
/** \name Select Pinned UV's Operator
* \{ */
-static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op))
+static int uv_select_pinned_exec(bContext *C, wmOperator *op)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
const ToolSettings *ts = scene->toolsettings;
+
+ /* Use this operator only in vertex mode, since it is not guaranteed that pinned vertices may
+ * form higher selection states (like edges/faces/islands) in other modes. */
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
+ if (ts->uv_selectmode != UV_SELECT_VERTEX) {
+ BKE_report(op->reports, RPT_ERROR, "Pinned vertices can be selected in Vertex Mode only");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
BMFace *efa;
BMLoop *l;
@@ -3443,6 +4051,9 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op))
}
}
}
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
+ ED_uvedit_selectmode_flush(scene, em);
+ }
if (changed) {
uv_select_tag_update_for_object(depsgraph, ts, obedit);
@@ -3797,9 +4408,7 @@ BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_e
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
- const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
- const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset);
- if ((luv_curr->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
+ if (uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) {
BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
edges[edges_len++] = l_iter;
@@ -3887,3 +4496,211 @@ finally:
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select Mode UV Vert/Edge/Face/Island Operator
+ * \{ */
+
+/** Deselects UVs that are not part of a complete island selection.
+ *
+ * Use only when sync select disabled.
+ */
+static void uv_isolate_selected_islands(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset)
+{
+ BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0);
+ BMFace *efa;
+ BMIter iter, liter;
+ UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, true, false, false, true);
+ if (elementmap == NULL) {
+ return;
+ }
+
+ int num_islands = elementmap->totalIslands;
+ /* Boolean array that tells if island with index i is completely selected or not. */
+ bool *is_island_not_selected = MEM_callocN(sizeof(bool) * (num_islands), __func__);
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BMLoop *l;
+ if (!uvedit_face_visible_test(scene, efa)) {
+ BM_elem_flag_disable(efa, BM_ELEM_TAG);
+ continue;
+ }
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (!uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) {
+ UvElement *element = BM_uv_element_get(elementmap, efa, l);
+ is_island_not_selected[element->island] = true;
+ }
+ }
+ }
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BMLoop *l;
+ if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ UvElement *element = BM_uv_element_get(elementmap, efa, l);
+ /* Deselect all elements of islands which are not completely selected. */
+ if (is_island_not_selected[element->island] == true) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv->flag &= ~(MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
+ }
+ }
+ }
+
+ BM_uv_element_map_free(elementmap);
+ MEM_freeN(is_island_not_selected);
+}
+
+void ED_uvedit_selectmode_clean(Scene *scene, Object *obedit)
+{
+ ToolSettings *ts = scene->toolsettings;
+ BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ char sticky = ts->uv_sticky;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+
+ if (ts->uv_selectmode == UV_SELECT_VERTEX) {
+ /* Vertex mode. */
+ if (sticky != SI_STICKY_DISABLE) {
+ bm_loop_tags_clear(em->bm);
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(l, BM_ELEM_TAG);
+ }
+ }
+ }
+ uv_select_flush_from_tag_loop(scene, obedit, true);
+ }
+ }
+
+ else if (ts->uv_selectmode == UV_SELECT_EDGE) {
+ /* Edge mode. */
+ if (sticky != SI_STICKY_DISABLE) {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) {
+ uvedit_edge_select_set_noflush(scene, l, true, sticky, cd_loop_uv_offset);
+ }
+ }
+ }
+ }
+ uv_select_flush_from_loop_edge_flag(scene, em);
+ }
+
+ else if (ts->uv_selectmode == UV_SELECT_FACE) {
+ /* Face mode. */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_disable(efa, BM_ELEM_TAG);
+ if (uvedit_face_visible_test(scene, efa)) {
+ if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ }
+ uvedit_face_select_set(scene, em, efa, false, false, cd_loop_uv_offset);
+ }
+ }
+ uv_select_flush_from_tag_face(scene, obedit, true);
+ }
+
+ else if (ts->uv_selectmode == UV_SELECT_ISLAND) {
+ /* Island mode. */
+ uv_isolate_selected_islands(scene, em, cd_loop_uv_offset);
+ }
+
+ ED_uvedit_selectmode_flush(scene, em);
+}
+
+void ED_uvedit_selectmode_clean_multi(bContext *C)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ ED_uvedit_selectmode_clean(scene, obedit);
+
+ uv_select_tag_update_for_object(depsgraph, scene->toolsettings, obedit);
+ }
+ MEM_freeN(objects);
+}
+
+static int uv_select_mode_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ const char new_uv_selectmode = RNA_enum_get(op->ptr, "type");
+
+ /* Early exit if no change in current selection mode */
+ if (new_uv_selectmode == ts->uv_selectmode) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Set new UV select mode. */
+ ts->uv_selectmode = new_uv_selectmode;
+
+ /* Handle UV selection states according to new select mode and sticky mode. */
+ ED_uvedit_selectmode_clean_multi(C);
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
+ WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int uv_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ const ToolSettings *ts = CTX_data_tool_settings(C);
+ const SpaceImage *sima = CTX_wm_space_image(C);
+
+ /* Could be removed? - Already done in poll callback. */
+ if ((!sima) || (sima->mode != SI_MODE_UV)) {
+ return OPERATOR_CANCELLED;
+ }
+ /* Pass through when UV sync selection is enabled.
+ * Allow for mesh select-mode key-map. */
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ return uv_select_mode_exec(C, op);
+}
+
+void UV_OT_select_mode(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "UV Select Mode";
+ ot->description = "Change UV selection mode";
+ ot->idname = "UV_OT_select_mode";
+
+ /* api callbacks */
+ ot->invoke = uv_select_mode_invoke;
+ ot->exec = uv_select_mode_exec;
+ ot->poll = ED_operator_uvedit_space_image;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* RNA props */
+ PropertyRNA *prop;
+ ot->prop = prop = RNA_def_enum(
+ ot->srna, "type", rna_enum_mesh_select_mode_uv_items, 0, "Type", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index c0d0fe95c8c..b104ebe8bf2 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -2545,7 +2545,7 @@ static StitchState *stitch_select(bContext *C,
return state;
}
}
- else if (uv_find_nearest_edge_multi(scene, ssc->objects, ssc->objects_len, co, &hit)) {
+ else if (uv_find_nearest_edge_multi(scene, ssc->objects, ssc->objects_len, co, 0.0f, &hit)) {
/* find StitchState from hit->ob */
StitchState *state = NULL;
for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index d6400e9340f..3ed99052753 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -119,7 +119,7 @@ static bool ED_uvedit_ensure_uvs(Object *obedit)
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= (MLOOPUV_VERTSEL | MLOOPUV_EDGESEL);
}
}