diff options
Diffstat (limited to 'source/blender/editors/uvedit')
-rw-r--r-- | source/blender/editors/uvedit/uvedit_intern.h | 14 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_ops.c | 148 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_path.c | 22 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_rip.c | 25 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_select.c | 1159 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_smart_stitch.c | 2 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_unwrap_ops.c | 2 |
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(®ion->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); } } |