diff options
10 files changed, 874 insertions, 158 deletions
diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl index 4f8d553a220..2b4ddaa52f3 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl @@ -2,9 +2,10 @@ #pragma BLENDER_REQUIRE(common_overlay_lib.glsl) layout(lines) in; -layout(triangle_strip, max_vertices = 4) out; +layout(triangle_strip, max_vertices = 6) out; in float selectionFac[2]; +in float edgeSelectionFac[2]; flat in vec2 stippleStart[2]; noperspective in vec2 stipplePos[2]; @@ -35,6 +36,12 @@ void main() vec2 ss_pos[2]; vec4 pos0 = gl_in[0].gl_Position; vec4 pos1 = gl_in[1].gl_Position; + /* TODO(verify): Calculating edge-center in clip space might cause errors due to precision loss?? */ + vec4 edgeCenter = (pos0 + pos1) / 2.0; + bool is_edge_selected = edgeSelectionFac[0] == 1.0; + /* Depth value as specified in vertex shader */ + edgeCenter.z = is_edge_selected ? 0.25 : 0.35; + ss_pos[0] = pos0.xy / pos0.w; ss_pos[1] = pos1.xy / pos1.w; @@ -54,8 +61,14 @@ void main() vec2 line_perp = vec2(-line_dir.y, line_dir.x); vec2 edge_ofs = line_perp * sizeViewportInv * ceil(half_size); + vec2 stippleStartCenter, stipplePosCenter; + /* TODO(stipple pattern): Probable incorrect implementation - used as specified in vertex shader */ + stippleStartCenter = stipplePosCenter = 500.0 + 500.0 * (edgeCenter.xy / edgeCenter.w); + do_vertex(pos0, selectionFac[0], stippleStart[0], stipplePos[0], half_size, edge_ofs.xy); do_vertex(pos0, selectionFac[0], stippleStart[0], stipplePos[0], -half_size, -edge_ofs.xy); + do_vertex(edgeCenter, edgeSelectionFac[0], stippleStartCenter, stipplePosCenter, half_size, edge_ofs.xy); + do_vertex(edgeCenter, edgeSelectionFac[0], stippleStartCenter, stipplePosCenter, -half_size, -edge_ofs.xy); do_vertex(pos1, selectionFac[1], stippleStart[1], stipplePos[1], half_size, edge_ofs.xy); do_vertex(pos1, selectionFac[1], stippleStart[1], stipplePos[1], -half_size, -edge_ofs.xy); diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl index 7627a287a05..3b00fd7aafe 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl @@ -6,6 +6,7 @@ in vec2 au; in int flag; out float selectionFac; +out float edgeSelectionFac; noperspective out vec2 stipplePos; flat out vec2 stippleStart; @@ -20,7 +21,9 @@ void main() half_pixel_offset; bool is_select = (flag & VERT_UV_SELECT) != 0; + bool is_edge_selected = (flag & EDGE_UV_SELECT) != 0; selectionFac = is_select ? 1.0 : 0.0; + edgeSelectionFac = is_edge_selected ? 1.0 : 0.0; /* Move selected edges to the top * Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4 * actual pixels are at 0.75, 1.0 is used for the background. */ diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index f3aba12a924..884124396b9 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -145,6 +145,13 @@ void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima, const bool select, const bool do_history, const uint cd_loop_uv_offset); +void uvedit_edge_select_shared_location(const struct Scene *scene, + struct BMEditMesh *em, + struct BMLoop *l, + const bool select, + const bool use_mesh_location, + const bool do_history, + const uint cd_loop_uv_offset); void uvedit_edge_select_set(const struct Scene *scene, struct BMEditMesh *em, struct BMLoop *l, @@ -168,6 +175,13 @@ void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima, const bool select, const bool do_history, const uint cd_loop_uv_offset); +void uvedit_uv_select_shared_location(const struct Scene *scene, + struct BMEditMesh *em, + struct BMLoop *l, + const bool select, + const bool use_mesh_location, + const bool do_history, + const uint cd_loop_uv_offset); void uvedit_uv_select_set(const struct Scene *scene, struct BMEditMesh *em, struct BMLoop *l, diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index cd8fbd00316..9bc8a165b1f 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -86,11 +86,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], + const float penalty, struct UvNearestHit *hit); bool uv_find_nearest_edge_multi(struct Scene *scene, struct Object **objects, const uint objects_len, const float co[2], + const float penalty, struct UvNearestHit *hit); bool uv_find_nearest_face_ex(struct Scene *scene, @@ -123,6 +125,17 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene, struct BMEdge *e, const float co[2]); +/* flush uv selection */ +void uv_flush_vert_to_edge(struct Scene *scene, + struct BMEditMesh *em, + const int cd_loop_uv_offset); +void uv_flush_edge_to_vert(struct Scene *scene, + struct BMEditMesh *em, + const int cd_loop_uv_offset); +void uv_flush_edge_to_vert_with_sticky_loc(struct Scene *scene, + struct BMEditMesh *em, + const int cd_loop_uv_offset); + /* utility tool functions */ void uvedit_live_unwrap_update(struct SpaceImage *sima, diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c index 2613c5b23a0..1f3ba206103 100644 --- a/source/blender/editors/uvedit/uvedit_path.c +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -627,7 +627,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_select.c b/source/blender/editors/uvedit/uvedit_select.c index 86390882bed..83e8259f693 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -70,27 +70,45 @@ #include "uvedit_intern.h" -static void uv_select_all_perform(Scene *scene, Object *obedit, int action); +static void uv_select_all_perform(Scene *scene, SpaceImage *sima, Object *obedit, int action); -static void uv_select_all_perform_multi_ex( - Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude); -static void uv_select_all_perform_multi(Scene *scene, - Object **objects, - const uint objects_len, - int action); +static void uv_select_all_perform_multi_ex(Scene *scene, + SpaceImage *sima, + Object **objects, + const uint objects_len, + int action, + const Object *ob_exclude); +static void uv_select_all_perform_multi( + Scene *scene, SpaceImage *sima, Object **objects, const uint objects_len, int action); + +static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, + BMEditMesh *em, + UvVertMap *vmap, + const uint efa_index, + BMLoop *l, + const bool select, + const int cd_loop_uv_offset); static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, const bool select); -static void uv_select_flush_from_tag_loop(SpaceImage *sima, +static void uv_select_flush_from_tag_loop(const SpaceImage *sima, Scene *scene, - Object *obedit, + BMEditMesh *em, const bool select); static void uv_select_tag_update_for_object(Depsgraph *depsgraph, const ToolSettings *ts, Object *obedit); +static bool uvedit_vert_is_any_other_edge_selected(const Scene *scene, + BMLoop *l, + const int cd_loop_uv_offset); +static bool uvedit_vert_is_any_other_face_selected(const Scene *scene, + BMLoop *l, + BMVert *v, + const int cd_loop_uv_offset); + /* -------------------------------------------------------------------- */ /** \name Active Selection Tracking * @@ -246,6 +264,9 @@ bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int c if (!(luv->flag & MLOOPUV_VERTSEL)) { return false; } + if (!(luv->flag & MLOOPUV_EDGESEL)) { + return false; + } } return true; } @@ -268,12 +289,43 @@ void uvedit_face_select_set_with_sticky(const SpaceImage *sima, return; } - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - uvedit_uv_select_set_with_sticky( - sima, scene, em, l_iter, select, do_history, cd_loop_uv_offset); - } while ((l_iter = l_iter->next) != l_first); + const int sticky = sima->sticky; + switch (sticky) { + case SI_STICKY_DISABLE: { + if (uvedit_face_visible_test(scene, efa)) { + uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset); + } + break; + } + case SI_STICKY_LOC: + case SI_STICKY_VERTEX: + default: { + if (uvedit_face_visible_test(scene, efa)) { + /* Sticky location and vertex modes. */ + /* Fallback, in case sima->sticky is invalid */ + BMLoop *l; + MLoopUV *luv; + BMIter liter; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (select) { + luv->flag |= MLOOPUV_EDGESEL; + uvedit_uv_select_shared_location( + scene, em, l, select, false, do_history, cd_loop_uv_offset); + } + else { + luv->flag &= ~MLOOPUV_EDGESEL; + if (!uvedit_vert_is_any_other_face_selected(scene, l, l->v, cd_loop_uv_offset)) { + uvedit_uv_select_shared_location( + scene, em, l, select, false, do_history, cd_loop_uv_offset); + } + } + } + } + break; + } + } } void uvedit_face_select_set(const struct Scene *scene, @@ -313,6 +365,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_EDGESEL; } } } @@ -335,6 +388,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_EDGESEL; } } } @@ -357,7 +411,12 @@ bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_ 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); - return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL); + /* UV edge is selected only if : + * - first UV vertex in luv1 is selected + * - second UV vertex in luv2 is selected + * - MLOOPUV_EDGESEL flag is set for luv1 */ + return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL) && + (luv1->flag & MLOOPUV_EDGESEL); } bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) { @@ -378,9 +437,101 @@ void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima, return; } - uvedit_uv_select_set_with_sticky(sima, scene, em, l, select, do_history, cd_loop_uv_offset); - uvedit_uv_select_set_with_sticky( - sima, scene, em, l->next, select, do_history, cd_loop_uv_offset); + const int sticky = sima->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_location( + scene, em, l, select, true, do_history, cd_loop_uv_offset); + break; + } + default: { + /* SI_STICKY_LOC + * Fallback to SI_STICKY_LOC, in case sima->sticky is invalid */ + uvedit_edge_select_shared_location( + scene, em, l, select, false, do_history, cd_loop_uv_offset); + break; + } + } +} + +/* Selects UV edges sharing the same location as l->e + * + * If use_mesh_location is : + * - true :- selects all UV edges sharing the same location on the 3D mesh + * - false :- selects all UV edges sharing the same location in UV space */ +void uvedit_edge_select_shared_location(const Scene *scene, + BMEditMesh *em, + BMLoop *l, + const bool select, + const bool use_mesh_location, + const bool do_history, + const uint cd_loop_uv_offset) +{ + BMLoop *l_radial_iter = l; + do { + if (uvedit_face_visible_test(scene, l_radial_iter->f)) { + + if (use_mesh_location) { + MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial_iter, cd_loop_uv_offset); + if (select) { + luv_radial->flag |= MLOOPUV_EDGESEL; + uvedit_uv_select_shared_location( + scene, em, l_radial_iter, select, false, do_history, cd_loop_uv_offset); + uvedit_uv_select_shared_location( + scene, em, l_radial_iter->next, select, false, do_history, cd_loop_uv_offset); + } + else { + luv_radial->flag &= ~MLOOPUV_EDGESEL; + if (!uvedit_vert_is_any_other_edge_selected(scene, l_radial_iter, cd_loop_uv_offset)) { + uvedit_uv_select_shared_location( + scene, em, l_radial_iter, select, false, do_history, cd_loop_uv_offset); + } + if (!uvedit_vert_is_any_other_edge_selected( + scene, l_radial_iter->next, cd_loop_uv_offset)) { + uvedit_uv_select_shared_location( + scene, em, l_radial_iter->next, select, false, do_history, cd_loop_uv_offset); + } + } + } + else { + if (BM_loop_uv_share_edge_check(l, l_radial_iter, cd_loop_uv_offset)) { + MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial_iter, cd_loop_uv_offset); + if (select) { + luv_radial->flag |= MLOOPUV_EDGESEL; + } + else { + luv_radial->flag &= ~MLOOPUV_EDGESEL; + } + /* Handle vertex selections outside radial cycle loop to avoid redundant calls to + * uvedit_uv_select_shared_location() */ + } + } + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l); + + if (!use_mesh_location) { + if (select) { + uvedit_uv_select_shared_location(scene, em, l, select, false, do_history, cd_loop_uv_offset); + uvedit_uv_select_shared_location( + scene, em, l->next, select, false, do_history, cd_loop_uv_offset); + } + else { + if (!uvedit_vert_is_any_other_edge_selected(scene, l, cd_loop_uv_offset)) { + uvedit_uv_select_shared_location( + scene, em, l, select, false, do_history, cd_loop_uv_offset); + } + if (!uvedit_vert_is_any_other_edge_selected(scene, l->next, cd_loop_uv_offset)) { + uvedit_uv_select_shared_location( + scene, em, l->next, select, false, do_history, cd_loop_uv_offset); + } + } + } } void uvedit_edge_select_set(const Scene *scene, @@ -425,13 +576,15 @@ void uvedit_edge_select_enable(const Scene *scene, } } else { - MLoopUV *luv1, *luv2; + 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 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + luv->flag |= MLOOPUV_VERTSEL; + luv_next->flag |= MLOOPUV_VERTSEL; - luv1->flag |= MLOOPUV_VERTSEL; - luv2->flag |= MLOOPUV_VERTSEL; + luv->flag |= MLOOPUV_EDGESEL; } } @@ -456,13 +609,21 @@ void uvedit_edge_select_disable(const Scene *scene, } } else { - MLoopUV *luv1, *luv2; + MLoopUV *luv_prev, *luv, *luv_next; + + luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); + 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 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_EDGESEL; - luv1->flag &= ~MLOOPUV_VERTSEL; - luv2->flag &= ~MLOOPUV_VERTSEL; + /* Deselect UV vertex only if it is not part of another edge selection */ + if (!(luv_next->flag & MLOOPUV_EDGESEL)) { + luv_next->flag &= ~MLOOPUV_VERTSEL; + } + if (!(luv_prev->flag & MLOOPUV_EDGESEL)) { + luv->flag &= ~MLOOPUV_VERTSEL; + } } } @@ -503,40 +664,60 @@ void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima, uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset); break; } + case SI_STICKY_VERTEX: { + uvedit_uv_select_shared_location(scene, em, l, select, true, 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; + /* SI_STICKY_LOC + * Fallback to SI_STICKY_LOC, in case sima->sticky is invalid */ + uvedit_uv_select_shared_location(scene, em, l, select, false, do_history, cd_loop_uv_offset); + break; + } + } +} + +/* Selects UV vertices sharing the same location as l->v + * + * If use_mesh_location is : + * - true :- selects all UV vertices sharing the same location on the 3D mesh + * - false :- selects all UV vertices sharing the same location in UV space */ +void uvedit_uv_select_shared_location(const Scene *scene, + BMEditMesh *em, + BMLoop *l, + const bool select, + const bool use_mesh_location, + const bool do_history, + const uint cd_loop_uv_offset) +{ + 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 { + /* This check should be removed ?? */ + if (e_iter->l) { + BMLoop *l_radial_iter = e_iter->l; + 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 (l_radial_iter->v == l->v) { + if (uvedit_face_visible_test(scene, l_radial_iter->f)) { - if (do_select) { - uvedit_uv_select_set( - scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset); - } + if (use_mesh_location) { + uvedit_uv_select_set( + scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset); + } + 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)) { + 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); + } 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, @@ -577,6 +758,22 @@ void uvedit_uv_select_enable(const Scene *scene, else { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv->flag |= MLOOPUV_VERTSEL; + + /* Only allow the edges between two selected vertices to be selected when in vertex selection + * mode */ + if (ts->uv_selectmode & UV_SELECT_VERTEX) { + MLoopUV *luv_next, *luv_prev; + 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 previous UV vertex selected then mark corresponding edge as selected */ + if (luv_prev->flag & MLOOPUV_VERTSEL) { + luv_prev->flag |= MLOOPUV_EDGESEL; + } + /* If next UV vertex selected then mark the edge for current MLoopUV as selected */ + if (luv_next->flag & MLOOPUV_VERTSEL) { + luv->flag |= MLOOPUV_EDGESEL; + } + } } } @@ -596,8 +793,19 @@ void uvedit_uv_select_disable(const Scene *scene, } } else { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + MLoopUV *luv, *luv_prev, *luv_next; + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); + luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_VERTSEL; + + if ((luv_prev->flag & MLOOPUV_VERTSEL) && (luv_prev->flag & MLOOPUV_EDGESEL)) { + luv_prev->flag &= ~MLOOPUV_EDGESEL; + } + if ((luv_next->flag & MLOOPUV_VERTSEL) && (luv->flag & MLOOPUV_EDGESEL)) { + luv->flag &= ~MLOOPUV_EDGESEL; + } } } @@ -661,7 +869,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); @@ -690,8 +899,22 @@ 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); + /* Sometimes an incorrect edge is chosen as the nearest edge and even in successive + * selection attempts, the same edge will be selected + * Proof: Disable sticky selection and use edge selection mode to select an UV edge that is + * shared between 2 UV faces. Sometimes the edge that is sharing the correct location but is + * part of a different face (not the face denoted by co[2]) is selected. + * + * The penalty used below ensures that the correct edge will be selected in the next + * attempts. + * TODO: Add a check to ensure the correct edge is returned in the first attempt itself */ + + /* Select the other edge sharing the same position as the previous selcted edge */ + 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; @@ -706,13 +929,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; } } @@ -841,6 +1068,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); } @@ -934,6 +1163,229 @@ bool ED_uvedit_nearest_uv_multi(const Scene *scene, /** \} */ +/** + * For a specified UV vertex and edge, check if any edge connected to it is selected or not + */ +static bool uvedit_vert_is_any_other_edge_selected(const Scene *scene, + BMLoop *l, + const int cd_loop_uv_offset) +{ + 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 (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset) && + uvedit_face_visible_test(scene, l_radial_iter->f)) { + + if (uvedit_edge_select_test(scene, l_radial_iter, cd_loop_uv_offset)) { + return true; + } + } + } + else { + if (BM_loop_uv_share_vert_check(l, l_radial_iter->next, cd_loop_uv_offset) && + uvedit_face_visible_test(scene, l_radial_iter->f)) { + + if (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)) != e_first); + + return false; +} + +/** + * For a specified UV vertexand face, check if any other face connected to it is selected or not */ +static bool uvedit_vert_is_any_other_face_selected(const Scene *scene, + BMLoop *l, + BMVert *v, + const int cd_loop_uv_offset) +{ + BMEdge *e_first, *e_iter; + e_first = e_iter = l->e; + do { + BMLoop *l_radial_iter = e_iter->l; + do { + if ((l_radial_iter->f != l->f) && uvedit_face_visible_test(scene, l_radial_iter->f) && + (l->v == l_radial_iter->v)) { + + if (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset) && + uvedit_face_select_test(scene, l_radial_iter->f, 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, v)) != e_first); + + return false; +} + +/* -------------------------------------------------------------------- */ +/** \name UV Flush selection + * + * Helper functions for flushing the UV selection state + * \{ */ + +/** + * Clear all selection flags + */ +static void uv_clear_selection_flag(Scene *scene, + BMEditMesh *em, + const int selection_flag, + const int cd_loop_uv_offset) +{ + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + 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); + luv->flag &= ~selection_flag; + } + } +} + +/** + * Flushes selection upwards (verts to edge) + */ +void uv_flush_vert_to_edge(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset) +{ + 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; + } + } + } +} + +/** + * Flushes selection downwards (edge to verts) + * Could be renamed to uv_validate_selection_state() + * + * UNUSUAL CASE : Flushing downwards + * Useful when the vertices of the edges that need to be selected are marked using MLOOPUV_EDGESEL + * flag + */ +void uv_flush_edge_to_vert(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset) +{ + 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_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; + } + } + } +} + +void uv_flush_edge_to_face(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset) +{ + 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; + } + + if (!uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv; + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_VERTSEL; + luv->flag &= ~MLOOPUV_EDGESEL; + } + } + } +} + +/** + * Select shared vertices (vertex selection with sticky location) for all edges that are marked + * using MLOOPUV_EDGESEL flag as a tag + */ +void uv_flush_edge_to_vert_with_sticky_loc(Scene *scene, + BMEditMesh *em, + const int cd_loop_uv_offset) +{ + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + struct UvVertMap *vmap; + uint efa_index; + uv_clear_selection_flag(scene, em, MLOOPUV_VERTSEL, cd_loop_uv_offset); + + 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)) { + 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); + + 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); +} + +/* TODO: (Siddhartha) Separate functions for flushing selections using sticky modes */ + +/** \} */ + +/** TODO: (Siddhartha) UV Select-Mode Flushing + * + * NOTE: An attempt at trying to implement UV selection mode flushing brought up a bunch of corner + * cases. These mostly revolved around how to handle sticky modes during flushing. */ /* -------------------------------------------------------------------- */ /** \name Find Nearest to Element * @@ -1171,7 +1623,7 @@ static int uv_select_edgeloop( 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; @@ -1214,7 +1666,7 @@ static int uv_select_edgeloop( /* Apply the selection. */ if (!extend) { - uv_select_all_perform(scene, obedit, SEL_DESELECT); + uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT); } /* Select all tagged loops. */ @@ -1239,6 +1691,70 @@ static int uv_select_edgeloop( /** \} */ /* -------------------------------------------------------------------- */ +/** \name Face Loop Select + * \{ */ + +static int uv_select_faceloop( + const SpaceImage *sima, 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, NULL, 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( + sima, 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 * \{ */ @@ -1250,18 +1766,21 @@ static int uv_select_edgering( 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); if (!extend) { - uv_select_all_perform(scene, obedit, SEL_DESELECT); + uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT); } 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; @@ -1282,10 +1801,20 @@ static int uv_select_edgering( } 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 the 3D viewport */ uvedit_face_select_set_with_sticky( sima, scene, em, l_step->f, select, false, cd_loop_uv_offset); } + else if (use_vertex_select) { + uvedit_uv_select_set_with_sticky( + sima, scene, em, l_step, select, false, cd_loop_uv_offset); + uvedit_uv_select_set_with_sticky( + sima, scene, em, l_step->next, select, false, cd_loop_uv_offset); + } else { + /* Edge select mode */ uvedit_edge_select_set_with_sticky( sima, scene, em, l_step, select, false, cd_loop_uv_offset); } @@ -1304,8 +1833,19 @@ static int uv_select_edgering( 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 (casued 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; + } } } } @@ -1351,9 +1891,9 @@ static void uv_select_linked_multi(Scene *scene, BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */ - /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see T44320 - * this made *every* projection split the island into front/back islands. - * Keep 'use_winding' to false, see: T50970. + /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see + * T44320 this made *every* projection split the island into front/back islands. Keep + * 'use_winding' to false, see: T50970. * * Better solve this by having a delimit option for select-linked operator, * keeping island-select working as is. */ @@ -1620,30 +2160,66 @@ 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)) { + if (select) { #define IS_SEL 1 #define 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 |= IS_SEL; + } + else { + sel_state |= IS_UNSEL; + } - /* 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; + /* 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 IS_SEL #undef IS_UNSEL + } + else { + if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + /* tag faces that must be deselected */ + + /* Deselect face only when any of the surrounding faces is not selected */ + bool deselect_face = false; + BMEdge *e_first, *e_iter; + e_first = e_iter = l->e; + do { + BMLoop *l_radial_iter = e_iter->l; + do { + if ((l_radial_iter->f != l->f) && + uvedit_face_visible_test(scene, l_radial_iter->f) && + (l->v == l_radial_iter->v)) { + + if (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset) && + !uvedit_face_select_test(scene, l_radial_iter->f, cd_loop_uv_offset)) { + BM_elem_flag_enable(efa, BM_ELEM_TAG); + changed = deselect_face = true; + break; + } + } + } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l); + if (deselect_face) + break; + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first); + + if (deselect_face) { + break; + } + } + } + } } } } @@ -1680,7 +2256,7 @@ static int uv_select_more_less(bContext *C, const bool select) } else { /* Select tagged loops. */ - uv_select_flush_from_tag_loop(sima, scene, obedit, select); + uv_select_flush_from_tag_loop(sima, scene, em, select); } DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); @@ -1774,7 +2350,7 @@ 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_perform(Scene *scene, SpaceImage *sima, Object *obedit, int action) { const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -1807,32 +2383,86 @@ 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; + if (action != SEL_INVERT) { + 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; + luv->flag |= MLOOPUV_EDGESEL; + break; + case SEL_DESELECT: + luv->flag &= ~MLOOPUV_VERTSEL; + luv->flag &= ~MLOOPUV_EDGESEL; + break; + } + } } + } + 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); + 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 (ts->uv_selectmode) { + /* Invert MLOOPUV_EDGESEL flag state for all uv elements if in edge/face selection + * mode. This will mark all edges that need to be selected */ + case UV_SELECT_EDGE: + case UV_SELECT_FACE: { + luv->flag ^= MLOOPUV_EDGESEL; + break; + } + /* Invert MLOOPUV_VERTSEL flag state for all uv elements if in vertex/island selection + * mode This will mark all vertices that need to be selected */ + case UV_SELECT_VERTEX: + case UV_SELECT_ISLAND: /* Fallback to vertex selection mode logic */ + default: { + luv->flag ^= MLOOPUV_VERTSEL; + break; + } + } + } + } + /* Flush based on selection flags and current UV selection mode */ + switch (ts->uv_selectmode) { + case UV_SELECT_EDGE: + case UV_SELECT_FACE: { + if (sima->sticky == SI_STICKY_DISABLE) { + uv_flush_edge_to_vert(scene, em, cd_loop_uv_offset); + } + else { + /* Special Handling for sticky modes. */ + uv_flush_edge_to_vert_with_sticky_loc(scene, em, cd_loop_uv_offset); + } + break; + } + case UV_SELECT_VERTEX: + case UV_SELECT_ISLAND: /* Fallback to vertex selection mode logic */ + default: { + uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset); + break; } } } } } -static void uv_select_all_perform_multi_ex( - Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude) +static void uv_select_all_perform_multi_ex(Scene *scene, + SpaceImage *sima, + Object **objects, + const uint objects_len, + int action, + const Object *ob_exclude) { if (action == SEL_TOGGLE) { action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT : @@ -1844,22 +2474,21 @@ static void uv_select_all_perform_multi_ex( if (ob_exclude && (obedit == ob_exclude)) { continue; } - uv_select_all_perform(scene, obedit, action); + uv_select_all_perform(scene, sima, obedit, action); } } -static void uv_select_all_perform_multi(Scene *scene, - Object **objects, - const uint objects_len, - int action) +static void uv_select_all_perform_multi( + Scene *scene, SpaceImage *sima, Object **objects, const uint objects_len, int action) { - uv_select_all_perform_multi_ex(scene, objects, objects_len, action, NULL); + uv_select_all_perform_multi_ex(scene, sima, objects, objects_len, action, NULL); } static int uv_select_all_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); + SpaceImage *sima = CTX_wm_space_image(C); const ToolSettings *ts = scene->toolsettings; ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1869,7 +2498,7 @@ static int uv_select_all_exec(bContext *C, wmOperator *op) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); - uv_select_all_perform_multi(scene, objects, objects_len, action); + uv_select_all_perform_multi(scene, sima, objects, objects_len, action); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; @@ -1956,7 +2585,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; @@ -1982,7 +2611,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, @@ -1994,7 +2623,7 @@ static int uv_mouse_select_multi(bContext *C, if (!found_item) { if (deselect_all) { - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; @@ -2013,7 +2642,7 @@ static int uv_mouse_select_multi(bContext *C, /* do selection */ if (selectmode == UV_SELECT_ISLAND) { if (!extend) { - uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); + uv_select_all_perform_multi_ex(scene, NULL, objects, objects_len, SEL_DESELECT, obedit); } /* Current behavior of 'extend' * is actually toggling, so pass extend flag as 'toggle' here */ @@ -2056,7 +2685,7 @@ static int uv_mouse_select_multi(bContext *C, else { const bool select = true; /* deselect all */ - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT); if (selectmode == UV_SELECT_VERTEX) { /* select vertex */ @@ -2198,7 +2827,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; } @@ -2208,11 +2837,16 @@ static int uv_mouse_select_loop_generic_multi(bContext *C, /* Do selection. */ if (!extend) { - uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); + uv_select_all_perform_multi_ex(scene, NULL, objects, objects_len, SEL_DESELECT, obedit); } if (loop_type == UV_LOOP_SELECT) { - flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend); + if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) { + flush = uv_select_faceloop(sima, scene, obedit, &hit, extend); + } + else { + flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend); + } } else if (loop_type == UV_RING_SELECT) { flush = uv_select_edgering(sima, scene, obedit, &hit, extend); @@ -2264,15 +2898,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) @@ -2422,14 +3048,14 @@ 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; } } if (!extend && !deselect) { - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT); } uv_select_linked_multi( @@ -2576,8 +3202,8 @@ static int uv_select_split_exec(bContext *C, wmOperator *op) const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - bool is_sel = false; - bool is_unsel = false; + /* Assume UV face is selected */ + bool uv_face_is_sel = true; if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -2587,23 +3213,20 @@ 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) { - is_sel = true; + if ((luv->flag & MLOOPUV_VERTSEL) && (luv->flag & MLOOPUV_EDGESEL)) { + continue; } else { - is_unsel = true; - } - - /* we have mixed selection, bail out */ - if (is_sel && is_unsel) { + uv_face_is_sel = false; break; } } - if (is_sel && is_unsel) { + if (!uv_face_is_sel) { 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_EDGESEL; } changed = true; @@ -2712,7 +3335,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. * @@ -2737,7 +3360,11 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima, 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 && sima->sticky == SI_STICKY_VERTEX) { + if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && + ((sima->sticky == SI_STICKY_VERTEX) || (sima->sticky == SI_STICKY_LOC))) { + /* TEMPORARY : Commenting out old code that was used for sticky vertex mode, since sticky + * vertex for face selection now fallbacks to sticky location */ +#if 0 /* 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); @@ -2761,7 +3388,7 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima, } } } - else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) { +#endif struct UvVertMap *vmap; uint efa_index; @@ -2776,8 +3403,19 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima, /* 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_any_other_face_selected(scene, l, l->v, cd_loop_uv_offset)) { + uv_select_flush_from_tag_sticky_loc_internal( + scene, em, vmap, efa_index, l, select, cd_loop_uv_offset); + } + } } } } @@ -2795,16 +3433,16 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima, /** * 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(SpaceImage *sima, +static void uv_select_flush_from_tag_loop(const SpaceImage *sima, Scene *scene, - Object *obedit, + BMEditMesh *em, const bool select) { /* Selecting UV Loops with some modes requires us to change @@ -2814,7 +3452,6 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima, * selection (so for sticky modes, vertex or location based). */ const ToolSettings *ts = scene->toolsettings; - BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; BMIter iter, liter; @@ -2844,6 +3481,13 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima, } } } + + /* If UV edge selection mode then flush selection upwards. Not required for vertex selection + * mode since edge selections for vertex selection mode is handled within + * uvedit_uv_select_set() itself */ + if (ts->uv_selectmode == UV_SELECT_EDGE) { + uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset); + } } else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) { struct UvVertMap *vmap; @@ -2866,6 +3510,13 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima, } } BM_uv_vert_map_free(vmap); + + /* If UV edge selection mode then flush selection upwards. Not required for vertex selection + * mode since edge selections for vertex selection mode is handled within + * uvedit_uv_select_set() itself */ + if (ts->uv_selectmode == UV_SELECT_EDGE) { + uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset); + } } else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { @@ -2875,6 +3526,12 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima, } } } + /* If UV edge selection mode then flush selection upwards. Not required for vertex selection + * mode since edge selections for vertex selection mode is handled within + * uvedit_uv_select_set() itself */ + if (ts->uv_selectmode == UV_SELECT_EDGE) { + uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset); + } } } @@ -2924,7 +3581,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) view_layer, ((View3D *)NULL), &objects_len); if (use_pre_deselect) { - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT); } /* don't indent to avoid diff noise! */ @@ -3147,7 +3804,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op) const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); if (use_pre_deselect) { - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT); } for (uint ob_index = 0; ob_index < objects_len; ob_index++) { @@ -3326,7 +3983,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, view_layer, ((View3D *)NULL), &objects_len); if (use_pre_deselect) { - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT); } for (uint ob_index = 0; ob_index < objects_len; ob_index++) { @@ -3506,6 +4163,12 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op)) } } + /* Flush selection + * REASON : uvedit_uv_select_enable() allows edge selection only in vertex select mode */ + if (ts->uv_selectmode != UV_SELECT_VERTEX) { + uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset); + } + if (changed) { uv_select_tag_update_for_object(depsgraph, ts, obedit); } @@ -3626,7 +4289,7 @@ static int uv_select_overlap(bContext *C, const bool extend) BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); if (!extend) { - uv_select_all_perform(scene, obedit, SEL_DESELECT); + uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT); } BMIter iter; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index c5485cc1495..ef451d3fdaf 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -2561,7 +2561,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/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index bc6b35c8e43..4bc1de3b78e 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -337,7 +337,7 @@ typedef struct MLoopUV { /** #MLoopUV.flag */ enum { - /* MLOOPUV_DEPRECATED = (1 << 0), MLOOPUV_EDGESEL removed */ + MLOOPUV_EDGESEL = (1 << 0), MLOOPUV_VERTSEL = (1 << 1), MLOOPUV_PINNED = (1 << 2), }; diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 5a937e6b06b..7a45cac94ee 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -2102,6 +2102,10 @@ static void rna_def_mloopuv(BlenderRNA *brna) prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MLOOPUV_VERTSEL); RNA_def_property_ui_text(prop, "UV Select", ""); + + prop = RNA_def_property(srna, "select_edge", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MLOOPUV_EDGESEL); + RNA_def_property_ui_text(prop, "UV Edge Select", ""); } static void rna_def_mloopcol(BlenderRNA *brna) diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c index d0c745e6a1d..ca5d408bfdb 100644 --- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c @@ -72,6 +72,7 @@ static int bpy_bmloopuv_uv_set(BPy_BMLoopUV *self, PyObject *value, void *UNUSED PyDoc_STRVAR(bpy_bmloopuv_flag__pin_uv_doc, "UV pin state.\n\n:type: boolean"); PyDoc_STRVAR(bpy_bmloopuv_flag__select_doc, "UV select state.\n\n:type: boolean"); +PyDoc_STRVAR(bpy_bmloopuv_flag__select_edge_doc, "UV edge select state.\n\n:type: boolean"); static PyObject *bpy_bmloopuv_flag_get(BPy_BMLoopUV *self, void *flag_p) { @@ -109,6 +110,11 @@ static PyGetSetDef bpy_bmloopuv_getseters[] = { (setter)bpy_bmloopuv_flag_set, bpy_bmloopuv_flag__select_doc, (void *)MLOOPUV_VERTSEL}, + {"select_edge", + (getter)bpy_bmloopuv_flag_get, + (setter)bpy_bmloopuv_flag_set, + bpy_bmloopuv_flag__select_edge_doc, + (void *)MLOOPUV_EDGESEL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; |