diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index e950f9bc679..30e93e71968 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -235,12 +235,17 @@ typedef enum { GHOST_kStandardCursorCrosshairA, GHOST_kStandardCursorCrosshairB, GHOST_kStandardCursorCrosshairC, + GHOST_kStandardCursorCrosshairD, + GHOST_kStandardCursorBox, + GHOST_kStandardCursorBoxDot, + GHOST_kStandardCursorBoxPointer, GHOST_kStandardCursorPencil, GHOST_kStandardCursorUpArrow, GHOST_kStandardCursorDownArrow, GHOST_kStandardCursorVerticalSplit, GHOST_kStandardCursorHorizontalSplit, GHOST_kStandardCursorEraser, + GHOST_kStandardCursorPointer, GHOST_kStandardCursorKnife, GHOST_kStandardCursorEyedropper, GHOST_kStandardCursorZoomIn, diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 3e82f55c583..506edbf022e 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -727,6 +727,9 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const case GHOST_kStandardCursorHorizontalSplit: cursor = ::LoadImage(module, "splith_cursor", IMAGE_CURSOR, cx, cy, flags); break; + case GHOST_kStandardCursorPointer: + cursor = ::LoadImage(module, "pointer_cursor", IMAGE_CURSOR, cx, cy, flags); + break; case GHOST_kStandardCursorKnife: cursor = ::LoadImage(module, "knife_cursor", IMAGE_CURSOR, cx, cy, flags); break; @@ -772,6 +775,18 @@ HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const case GHOST_kStandardCursorCrosshairC: cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags); break; /* Minimal Crosshair C */ + case GHOST_kStandardCursorCrosshairD: + cursor = ::LoadImage(module, "crossD_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Open Crosshair D */ + case GHOST_kStandardCursorBox: + cursor = ::LoadImage(module, "box_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Box */ + case GHOST_kStandardCursorBoxDot: + cursor = ::LoadImage(module, "boxdot_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Box Dot */ + case GHOST_kStandardCursorBoxPointer: + cursor = ::LoadImage(module, "boxpointer_cursor", IMAGE_CURSOR, cx, cy, flags); + break; /* Box Pointer */ case GHOST_kStandardCursorBottomSide: case GHOST_kStandardCursorUpDown: cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags); diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 9dc7b184b12..82ebc6f0ba3 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -7,7 +7,7 @@ inkscape:output_extension="org.inkscape.output.svg.inkscape" sodipodi:docname="blender_icons.svg" version="1.0" - inkscape:version="1.2 (dc2aeda, 2022-05-15)" + inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" sodipodi:version="0.32" id="svg2" height="640" @@ -42,19 +42,70 @@ guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:window-width="1728" - inkscape:window-height="1051" + inkscape:window-width="1920" + inkscape:window-height="1027" id="namedview34335" showgrid="false" - inkscape:zoom="1.2495612" - inkscape:cx="196.46897" - inkscape:cy="320.51252" - inkscape:window-x="767" - inkscape:window-y="120" - inkscape:window-maximized="0" + inkscape:zoom="22.627417" + inkscape:cx="423.44648" + inkscape:cy="332.84842" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" inkscape:current-layer="layer8" inkscape:showpageshadow="2" - inkscape:deskcolor="#808080" /> + inkscape:deskcolor="#808080" + showguides="true" + inkscape:guide-bbox="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) { - return true; - } - for (uint a = 0; a < mcoords_len - 1; a++) { - if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { + if (!fully_inside) { + /* check points in lasso */ + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) { return true; } + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { + return true; + } + + /* no points in lasso, so we have to intersect with lasso edge */ + + if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) { + return true; + } + for (unsigned int a = 0; a < mcoords_len - 1; a++) { + if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { + return true; + } + } } return false; diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index d19616223cd..4755ad7d625 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -183,6 +183,8 @@ static void blo_update_defaults_screen(bScreen *screen, v3d->overlay.vertex_paint_mode_opacity = 1.0f; /* Use dimmed selected edges. */ v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_EDGES; + /* Show xray facedots */ + v3d->overlay.edit_flag |= V3D_OVERLAY_EDIT_FACE_DOT_XRAY; /* grease pencil settings */ v3d->vertex_opacity = 1.0f; v3d->gp_flag |= V3D_GP_SHOW_EDIT_LINES; @@ -373,6 +375,25 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (idprop) { IDP_ClearProperty(idprop); } + + /* X-Ray. */ + ts->xray_button = true; + ts->auto_xray_object = true; + ts->auto_xray_edit = true; + ts->auto_xray_box = true; + ts->auto_xray_lasso = true; + ts->auto_xray_circle = true; + ts->select_through = true; + ts->select_through_object = true; + ts->select_through_box = true; + ts->select_through_lasso = true; + ts->select_through_circle = true; + + /* Viewport-Facing Select */ + ts->viewport_facing_select_mode = 1; + ts->viewport_facing_select_vert = 1; + ts->viewport_facing_select_edge = 1; + ts->viewport_facing_select_face = 1; } void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 4b2acfa7205..bdb327fc5f3 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -434,9 +434,8 @@ void blo_do_versions_userdef(UserDef *userdef) if (!USER_VERSION_ATLEAST(278, 6)) { /* Clear preference flags for re-use. */ - userdef->flag &= ~(USER_FLAG_NUMINPUT_ADVANCED | USER_FLAG_UNUSED_2 | USER_FLAG_UNUSED_3 | - USER_FLAG_UNUSED_6 | USER_FLAG_UNUSED_7 | USER_FLAG_UNUSED_9 | - USER_DEVELOPER_UI); + userdef->flag &= ~(USER_FLAG_NUMINPUT_ADVANCED | USER_FLAG_UNUSED_6 | USER_FLAG_UNUSED_7 | + USER_FLAG_UNUSED_9 | USER_DEVELOPER_UI); userdef->uiflag &= ~(USER_HEADER_BOTTOM); userdef->transopts &= ~(USER_TR_UNUSED_2 | USER_TR_UNUSED_3 | USER_TR_UNUSED_4 | USER_TR_UNUSED_6 | USER_TR_UNUSED_7); diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.cc b/source/blender/draw/engines/overlay/overlay_edit_mesh.cc index 256588e56f2..eff0b81a165 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.cc +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.cc @@ -59,8 +59,10 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) bool select_edge = pd->edit_mesh.select_edge = (tsettings->selectmode & SCE_SELECT_EDGE) != 0; bool do_occlude_wire = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_OCCLUDE_WIRE) != 0; - bool show_face_dots = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) != 0 || - pd->edit_mesh.do_zbufclip; + bool show_face_dots = !XRAY_FLAG_ENABLED(draw_ctx->v3d) && + v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT || + XRAY_FLAG_ENABLED(draw_ctx->v3d) && + v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT_XRAY; pd->edit_mesh.do_faces = true; pd->edit_mesh.do_edges = true; diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c index 03b189a3e5b..34615e47e16 100644 --- a/source/blender/draw/engines/select/select_draw_utils.c +++ b/source/blender/draw/engines/select/select_draw_utils.c @@ -72,7 +72,9 @@ static bool check_ob_drawface_dot(short select_mode, const View3D *v3d, eDrawTyp { if (select_mode & SCE_SELECT_FACE) { if ((dt < OB_SOLID) || XRAY_FLAG_ENABLED(v3d)) { - return true; + if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT_XRAY) { + return true; + } } if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) { return true; diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 35afb6e1ed4..2af85ee1d7b 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -688,7 +688,8 @@ bool select_lasso(const ViewContext &vc, int(pos1_proj.y), int(pos2_proj.x), int(pos2_proj.y), - IS_CLIPPED)) { + IS_CLIPPED, + false)) { apply_selection_operation_at_index(selection.span, curve_i, sel_op); changed = true; break; diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index d7fb108809f..b447630f98b 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -100,7 +100,10 @@ void ED_getTransformOrientationMatrix(const struct Scene *scene, struct Object *ob, struct Object *obedit, short around, - float r_orientation_mat[3][3]); + float r_orientation_mat[3][3], + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa); int BIF_countTransformOrientation(const struct bContext *C); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 9507ab4d476..f1dfc564208 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -391,11 +391,20 @@ void mesh_foreachScreenEdge_clip_bb_segment(struct ViewContext *vc, void *userData, eV3DProjTest clip_flag); -void mesh_foreachScreenFace( +void mesh_foreachScreenFaceCenter( struct ViewContext *vc, void (*func)(void *userData, struct BMFace *efa, const float screen_co[2], int index), void *userData, eV3DProjTest clip_flag); +void mesh_foreachScreenFaceVerts(struct ViewContext *vc, + void (*func)(void *userData, + struct BMFace *efa, + const float screen_co[][2], + int total_count, + struct rctf *poly_rect, + bool *face_hit), + void *userData, + const eV3DProjTest clip_flag); void nurbs_foreachScreenVert(struct ViewContext *vc, void (*func)(void *userData, struct Nurb *nu, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 6e0edeace6a..0d7b6ca733c 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -447,8 +447,8 @@ DEF_ICON_BLANK(705) DEF_ICON_BLANK(706) DEF_ICON_BLANK(707) DEF_ICON_BLANK(708) -DEF_ICON_BLANK(709) -DEF_ICON_BLANK(710) +DEF_ICON(AUTO_XRAY) +DEF_ICON(SELECT_THROUGH) DEF_ICON(SELECT_SET) DEF_ICON(SELECT_EXTEND) DEF_ICON(SELECT_SUBTRACT) diff --git a/source/blender/editors/interface/resources.cc b/source/blender/editors/interface/resources.cc index 5982f8ad1c9..c457aa38f45 100644 --- a/source/blender/editors/interface/resources.cc +++ b/source/blender/editors/interface/resources.cc @@ -229,7 +229,7 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_HEADER_ACTIVE: { cp = ts->header; - const int factor = 5; + const int factor = U.header_highlight; /* Lighten the header color when editor is active. */ header_active[0] = cp[0] > 245 ? cp[0] - factor : cp[0] + factor; header_active[1] = cp[1] > 245 ? cp[1] - factor : cp[1] + factor; diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index 94c2e3352b3..c8ec4ab78c2 100644 --- a/source/blender/editors/mesh/editmesh_select.cc +++ b/source/blender/editors/mesh/editmesh_select.cc @@ -233,7 +233,8 @@ static void findnearestvert__doClosest(void *userData, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT && U.flag & USER_SELECT_UNBIASED) && + data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -438,7 +439,8 @@ static void find_nearest_edge__doClosest( dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT && U.flag & USER_SELECT_UNBIASED) && + data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -670,7 +672,8 @@ static void findnearestface__doClosest(void *userData, dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT && U.flag & USER_SELECT_UNBIASED) && + data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { dist_test_bias += FIND_NEAR_SELECT_BIAS; } @@ -706,27 +709,26 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, uint base_index = 0; if (!XRAY_FLAG_ENABLED(vc->v3d)) { - float dist_test; + float dist_test = 0.0f; uint index; BMFace *efa; + uint dist_px_manhattan_test = 0; { - uint dist_px_manhattan_test = 0; - if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) { - dist_px_manhattan_test = uint( - ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p)); + if (U.flag & USER_ADJUSTABLE_CLICK_SELECT || + *dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) { + dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, + *dist_px_manhattan_p); } DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_FACE); - if (dist_px_manhattan_test == 0) { + if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT) && dist_px_manhattan_test == 0) { index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval); - dist_test = 0.0f; } else { index = DRW_select_buffer_find_nearest_to_point( vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test); - dist_test = dist_px_manhattan_test; } if (index) { @@ -752,7 +754,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace( + mesh_foreachScreenFaceCenter( vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); *r_dist_center = data.dist_px_manhattan; @@ -760,7 +762,14 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, /* end exception */ if (efa) { - if (dist_test < *dist_px_manhattan_p) { + if (U.flag & USER_ADJUSTABLE_CLICK_SELECT && dist_px_manhattan_test < *dist_px_manhattan_p) { + if (r_base_index) { + *r_base_index = base_index; + } + *dist_px_manhattan_p = dist_px_manhattan_test; + return efa; + } + else if (!(U.flag & USER_ADJUSTABLE_CLICK_SELECT) && dist_test < *dist_px_manhattan_p) { if (r_base_index) { *r_base_index = base_index; } @@ -804,7 +813,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc, *dist_px_manhattan_p; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag); + mesh_foreachScreenFaceCenter(vc, findnearestface__doClosest, &data, clip_flag); hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 80e5da43423..10dc4b90421 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3847,6 +3847,61 @@ static void SCREEN_OT_repeat_history(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Invoke Last Operator + * \{ */ + +static int invoke_last_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmOperator *lastop = wm->operators.last; + + /* Seek last registered operator */ + while (lastop) { + if (lastop->type->flag & OPTYPE_REGISTER) { + if (STREQ(lastop->idname, "MESH_OT_delete") || STREQ(lastop->idname, "MESH_OT_select_all") || + STREQ(lastop->idname, "MESH_OT_select_mode") || + STREQ(lastop->idname, "OBJECT_OT_editmode_toggle") || + STREQ(lastop->idname, "OBJECT_OT_select_all") || + STREQ(lastop->idname, "TRANSFORM_OT_resize") || + STREQ(lastop->idname, "TRANSFORM_OT_rotate") || + STREQ(lastop->idname, "TRANSFORM_OT_trackball") || + STREQ(lastop->idname, "TRANSFORM_OT_translate")) { + lastop = lastop->prev; + } + else { + //printf(lastop->idname, "\n"); + break; + } + } + else { + lastop = lastop->prev; + } + } + + if (lastop) { + WM_operator_free_all_after(wm, lastop); + WM_operator_invoke_last(C, lastop); + } + + return OPERATOR_CANCELLED; +} + +static void SCREEN_OT_invoke_last(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Invoke Last"; + ot->description = "Invoke last operation"; + ot->idname = "SCREEN_OT_invoke_last"; + + /* api callbacks */ + ot->exec = invoke_last_exec; + + ot->poll = ED_operator_screenactive; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Redo Operator * \{ */ @@ -5679,6 +5734,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_actionzone); WM_operatortype_append(SCREEN_OT_repeat_last); WM_operatortype_append(SCREEN_OT_repeat_history); + WM_operatortype_append(SCREEN_OT_invoke_last); WM_operatortype_append(SCREEN_OT_redo_last); /* Screen tools. */ diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d5b96e03f13..87fb6e6c77f 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -1235,3 +1235,242 @@ void VIEW3D_OT_toggle_xray(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Automatic XRay + * \{ */ + +static int toggle_auto_xray_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + const bool mode_match = obedit ? ts->auto_xray_edit : ts->auto_xray_object; + + if (STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box")) { + if (mode_match && ts->auto_xray && ts->auto_xray_box) { + ts->auto_xray ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->auto_xray_edit ^= true; + } + else { + ts->auto_xray_object ^= true; + } + } + + if (!ts->auto_xray) { + ts->auto_xray ^= true; + } + + if (!ts->auto_xray_box) { + ts->auto_xray_box ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso")) { + if (mode_match && ts->auto_xray && ts->auto_xray_lasso) { + ts->auto_xray ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->auto_xray_edit ^= true; + } + else { + ts->auto_xray_object ^= true; + } + } + + if (!ts->auto_xray) { + ts->auto_xray ^= true; + } + + if (!ts->auto_xray_lasso) { + ts->auto_xray_lasso ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle")) { + if (mode_match && ts->auto_xray && ts->auto_xray_circle) { + ts->auto_xray ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->auto_xray_edit ^= true; + } + else { + ts->auto_xray_object ^= true; + } + } + + if (!ts->auto_xray) { + ts->auto_xray ^= true; + } + + if (!ts->auto_xray_circle) { + ts->auto_xray_circle ^= true; + } + } + } + + ED_area_tag_redraw(area); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_auto_xray(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Automatic X-Ray"; + ot->idname = "VIEW3D_OT_toggle_auto_xray"; + ot->description = "Transparent scene display during box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_auto_xray_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Select Through + * \{ */ + +static int toggle_select_through_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *obedit = CTX_data_edit_object(C); + bToolRef *tref = area->runtime.tool; + const bool mode_match = obedit ? ts->select_through_edit : ts->select_through_object; + + if (STREQ(tref->idname, "builtin.select_box") || + STREQ(tref->idname_fallback, "builtin.select_box")) { + if (mode_match && ts->select_through && ts->select_through_box) { + ts->select_through ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->select_through_edit ^= true; + } + else { + ts->select_through_object ^= true; + } + } + + if (!ts->select_through) { + ts->select_through ^= true; + } + + if (!ts->select_through_box) { + ts->select_through_box ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_lasso") || + STREQ(tref->idname_fallback, "builtin.select_lasso")) { + if (mode_match && ts->select_through && ts->select_through_lasso) { + ts->select_through ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->select_through_edit ^= true; + } + else { + ts->select_through_object ^= true; + } + } + + if (!ts->select_through) { + ts->select_through ^= true; + } + + if (!ts->select_through_lasso) { + ts->select_through_lasso ^= true; + } + } + } + else if (STREQ(tref->idname, "builtin.select_circle") || + STREQ(tref->idname_fallback, "builtin.select_circle")) { + if (mode_match && ts->select_through && ts->select_through_circle) { + ts->select_through ^= true; + } + else { + if (!mode_match) { + if (obedit) { + ts->select_through_edit ^= true; + } + else { + ts->select_through_object ^= true; + } + } + + if (!ts->select_through) { + ts->select_through ^= true; + } + + if (!ts->select_through_circle) { + ts->select_through_circle ^= true; + } + } + } + + ED_area_tag_redraw(area); + + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_select_through(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Select Through"; + ot->idname = "VIEW3D_OT_toggle_select_through"; + ot->description = "Select occluded objects and mesh elements with box, lasso, and circle select"; + + /* api callbacks */ + ot->exec = toggle_select_through_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Facedots + * \{ */ + +static int toggle_facedots_exec(bContext *C, wmOperator *op) +{ + ScrArea *area = CTX_wm_area(C); + View3D *v3d = CTX_wm_view3d(C); + if (!XRAY_FLAG_ENABLED(v3d)) { + v3d->overlay.edit_flag ^= V3D_OVERLAY_EDIT_FACE_DOT; + } + else { + v3d->overlay.edit_flag ^= V3D_OVERLAY_EDIT_FACE_DOT_XRAY; + } + ED_area_tag_redraw(area); + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_facedots(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Facedots"; + ot->idname = "VIEW3D_OT_toggle_facedots"; + ot->description = "Show face center dots in the current shading mode"; + /* api callbacks */ + ot->exec = toggle_facedots_exec; + ot->poll = ED_operator_view3d_active; +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index b94833dd276..02d852e87ff 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -55,6 +55,9 @@ void VIEW3D_OT_render_border(struct wmOperatorType *ot); void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot); void VIEW3D_OT_toggle_shading(struct wmOperatorType *ot); void VIEW3D_OT_toggle_xray(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_auto_xray(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_select_through(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_facedots(struct wmOperatorType *ot); /* view3d_draw.c */ diff --git a/source/blender/editors/space_view3d/view3d_iterators.cc b/source/blender/editors/space_view3d/view3d_iterators.cc index e7e8eb5b275..d5e560331c4 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.cc +++ b/source/blender/editors/space_view3d/view3d_iterators.cc @@ -13,6 +13,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_bitmap.h" #include "BLI_math_geom.h" #include "BLI_rect.h" #include "BLI_utildefines.h" @@ -38,6 +39,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "MEM_guardedalloc.h" + /* -------------------------------------------------------------------- */ /** \name Internal Clipping Utilities * \{ */ @@ -238,7 +241,7 @@ struct foreachScreenEdge_userData { int content_planes_len; }; -struct foreachScreenFace_userData { +struct foreachScreenFaceCenter_userData { void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index); void *userData; ViewContext vc; @@ -536,12 +539,12 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, /** \name Edit-Mesh: For Each Screen Face Center * \{ */ -static void mesh_foreachScreenFace__mapFunc(void *userData, +static void mesh_foreachScreenFaceCenter__mapFunc(void *userData, int index, const float cent[3], const float /*no*/[3]) { - foreachScreenFace_userData *data = static_cast(userData); + foreachScreenFaceCenter_userData *data = static_cast(userData); BMFace *efa = BM_face_at_index(data->vc.em->bm, index); if (UNLIKELY(BM_elem_flag_test(efa, BM_ELEM_HIDDEN))) { return; @@ -556,14 +559,14 @@ static void mesh_foreachScreenFace__mapFunc(void *userData, data->func(data->userData, efa, screen_co, index); } -void mesh_foreachScreenFace( +void mesh_foreachScreenFaceCenter( ViewContext *vc, void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index), void *userData, const eV3DProjTest clip_flag) { BLI_assert((clip_flag & V3D_PROJ_TEST_CLIP_CONTENT) == 0); - foreachScreenFace_userData data; + foreachScreenFaceCenter_userData data; Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); @@ -580,16 +583,150 @@ void mesh_foreachScreenFace( const int face_dot_tags_num = me->runtime->subsurf_face_dot_tags.size(); if (face_dot_tags_num && (face_dot_tags_num != me->totvert)) { BKE_mesh_foreach_mapped_subdiv_face_center( - me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP); + me, mesh_foreachScreenFaceCenter__mapFunc, &data, MESH_FOREACH_NOP); } else { BKE_mesh_foreach_mapped_face_center( - me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP); + me, mesh_foreachScreenFaceCenter__mapFunc, &data, MESH_FOREACH_NOP); } } /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh: For Each Screen Face Verts + * \{ */ + +void mesh_foreachScreenFaceVerts(ViewContext *vc, + void (*func)(void *userData, + struct BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit), + void *userData, + const eV3DProjTest clip_flag) +{ + ED_view3d_check_mats_rv3d(vc->rv3d); + BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE); + + BMFace *efa; + const BMesh *bm = vc->em->bm; + + float temp_screen_co[2]; + int total_length = 0; + + float(*screen_coords)[2] = static_cast( + MEM_mallocN(sizeof(float) * 2 * bm->totvert, __func__)); + int face_screen_verts_size = 4; + float(*face_screen_verts)[2] = static_cast( + MEM_mallocN(sizeof(int) * 2 * face_screen_verts_size, __func__)); + + /* This makes only sense on subdivided meshes.*/ + BLI_bitmap *faces_visited; + int cage_index = BKE_modifiers_get_cage_index(vc->scene, vc->obedit, NULL, 1); + const bool cage_display = cage_index != -1; + if (cage_display) { + faces_visited = BLI_BITMAP_NEW((size_t)bm->totface, __func__); + } + + /* Transform and store all visible verts into screen coords. */ + for (int i = 0; i < bm->totvert; i++) { + BMVert *bmvert = BM_vert_at_index(vc->em->bm, i); + + if (BM_elem_flag_test_bool(bmvert, BM_ELEM_HIDDEN)) { + continue; + } + + if (ED_view3d_project_float_object(vc->region, bmvert->co, temp_screen_co, clip_flag) == + V3D_PROJ_RET_OK) { + screen_coords[i][0] = temp_screen_co[0]; + screen_coords[i][1] = temp_screen_co[1]; + } + else { + screen_coords[i][0] = 0.0f; + screen_coords[i][1] = 0.0f; + } + } + + const int *poly_index = static_cast(CustomData_get_layer(&bm->pdata, CD_ORIGINDEX)); + const bool use_original_index = poly_index != 0; + + rctf poly_rect_data; + rctf *poly_rect = &poly_rect_data; + bool face_hit = false; + + /* Collect polygon verts and send off per poly callback. */ + for (int i = 0; i < bm->totface; i++) { + int original_index = i; + if (use_original_index) { + original_index = *poly_index++; + if (original_index == ORIGINDEX_NONE) { + continue; + } + } + + if (cage_display && BLI_BITMAP_TEST(faces_visited, original_index)) { + continue; + } + + efa = BM_face_at_index(vc->em->bm, original_index); + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + + if (bm->totvert > face_screen_verts_size) { + face_screen_verts_size = bm->totvert; + MEM_freeN(face_screen_verts); + face_screen_verts = static_cast( + MEM_mallocN(sizeof(float) * 2 * face_screen_verts_size, __func__)); + } + + total_length = 0; + BLI_rctf_init_minmax(poly_rect); + + bool skip = false; + + BMLoop *l_first, *l_iter; + int j = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const int k = BM_elem_index_get(l_iter->v); + face_screen_verts[j][0] = screen_coords[k][0]; + face_screen_verts[j][1] = screen_coords[k][1]; + + /* Ignore polygons with invalid screen coords.*/ + if (face_screen_verts[j][0] == 0.0f && face_screen_verts[j][1] == 0.0f) { + skip = true; + break; + } + + total_length++, j++; + BLI_rctf_do_minmax_v(poly_rect, screen_coords[k]); + } while ((l_iter = l_iter->next) != l_first); + + if (skip) { + continue; + } + + face_hit = false; + + func( + userData, efa, (const float(*)[2])face_screen_verts, total_length, poly_rect, &face_hit); + + if (cage_display && face_hit) { + BLI_BITMAP_ENABLE(faces_visited, original_index); + } + } + } + + if (cage_display) { + MEM_freeN(faces_visited); + } + MEM_freeN(screen_coords); + MEM_freeN(face_screen_verts); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Edit-Nurbs: For Each Screen Vertex * \{ */ diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index d47f7325cc5..54ffc020477 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -99,6 +99,26 @@ void view3d_operator_properties_common(wmOperatorType *ot, const enum eV3D_OpPro if (flag & V3D_OP_PROP_USE_MOUSE_INIT) { WM_operator_properties_use_cursor_init(ot); } + + if (flag & V3D_OP_PROP_FRAME_SELECTED_DISTANCE) { + PropertyRNA *prop; + prop = RNA_def_int(ot->srna, + "frame_selected_distance", + 0, + 0, + 1000, + "Camera Offset", + "Distance to put camera from selected", + 0, + 1000); + + } + + if (flag & V3D_OP_PROP_ZOOM_SPEED) { + PropertyRNA *prop; + prop = RNA_def_float( + ot->srna, "zoom_speed", 1.2f, 1.001, 10, "Zoom Speed", "Speed to zoom camera", 1.001f, 10); + } } /** \} */ @@ -665,7 +685,8 @@ static void view3d_from_minmax(bContext *C, const float min[3], const float max[3], bool ok_dist, - const int smooth_viewtx) + const int smooth_viewtx, + const int view_dist) { RegionView3D *rv3d = region->regiondata; float afm[3]; @@ -708,7 +729,7 @@ static void view3d_from_minmax(bContext *C, v3d, region, depsgraph, persp, true, (size / 2) * VIEW3D_MARGIN); if (rv3d->is_persp) { /* don't zoom closer than the near clipping plane */ - new_dist = max_ff(new_dist, v3d->clip_start * 1.5f); + new_dist = max_ff(new_dist, v3d->clip_start * 1.5f + view_dist); } } } @@ -754,7 +775,8 @@ static void view3d_from_minmax_multi(bContext *C, const float min[3], const float max[3], const bool ok_dist, - const int smooth_viewtx) + const int smooth_viewtx, + const int view_dist) { ScrArea *area = CTX_wm_area(C); ARegion *region; @@ -764,7 +786,7 @@ static void view3d_from_minmax_multi(bContext *C, /* when using all regions, don't jump out of camera view, * but _do_ allow locked cameras to be moved */ if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx, view_dist); } } } @@ -844,10 +866,10 @@ static int view3d_all_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx); + view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx, 0); } else { - view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx, 0); } ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); @@ -906,6 +928,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) /* any one of the regions may be locked */ (use_all_regions && v3d->flag2 & V3D_LOCK_CAMERA)); const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + const int view_dist = RNA_int_get(op->ptr, "frame_selected_distance"); INIT_MINMAX(min, max); if (is_face_map) { @@ -1020,10 +1043,10 @@ static int viewselected_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_undo_begin(C, area); if (use_all_regions) { - view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx, view_dist); } else { - view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx); + view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx, view_dist); } ED_view3d_smooth_view_undo_end(C, area, op->type->name, false); @@ -1046,7 +1069,8 @@ void VIEW3D_OT_view_selected(wmOperatorType *ot) ot->flag = 0; /* properties */ - view3d_operator_properties_common(ot, V3D_OP_PROP_USE_ALL_REGIONS); + view3d_operator_properties_common( + ot, V3D_OP_PROP_USE_ALL_REGIONS | V3D_OP_PROP_FRAME_SELECTED_DISTANCE); } /** \} */ @@ -1205,7 +1229,7 @@ static int view_axis_exec(bContext *C, wmOperator *op) Object *obedit = CTX_data_edit_object(C); /* same as transform gizmo when normal is set */ ED_getTransformOrientationMatrix( - scene, view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat); + scene, view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat, NULL, NULL, NULL); align_quat = align_quat_buf; mat3_to_quat(align_quat, twmat); invert_qt_normalized(align_quat); diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h index 46dacbc606f..a641a3748fa 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.h +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -34,6 +34,8 @@ enum eV3D_OpPropFlag { V3D_OP_PROP_DELTA = (1 << 1), V3D_OP_PROP_USE_ALL_REGIONS = (1 << 2), V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3), + V3D_OP_PROP_FRAME_SELECTED_DISTANCE = (1 << 4), + V3D_OP_PROP_ZOOM_SPEED = (1 << 5), }; enum { diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c index 51aab367959..a5f8341dd27 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -506,9 +506,10 @@ static int viewzoom_exec(bContext *C, wmOperator *op) } ED_view3d_dist_range_get(v3d, dist_range); + const float zoom_speed = RNA_float_get(op->ptr, "zoom_speed"); if (delta < 0) { - const float step = 1.2f; + const float step = zoom_speed; if (use_cam_zoom) { view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); } @@ -519,7 +520,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) } } else { - const float step = 1.0f / 1.2f; + const float step = 1.0f / zoom_speed; if (use_cam_zoom) { view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy); } @@ -631,8 +632,9 @@ void VIEW3D_OT_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY; /* properties */ - view3d_operator_properties_common( - ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT); + view3d_operator_properties_common(ot, + V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | + V3D_OP_PROP_USE_MOUSE_INIT | V3D_OP_PROP_ZOOM_SPEED); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index ad12aef6d67..5551508fe8a 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -200,6 +200,9 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_toggle_shading); WM_operatortype_append(VIEW3D_OT_toggle_xray); WM_operatortype_append(VIEW3D_OT_toggle_matcap_flip); + WM_operatortype_append(VIEW3D_OT_toggle_auto_xray); + WM_operatortype_append(VIEW3D_OT_toggle_select_through); + WM_operatortype_append(VIEW3D_OT_toggle_facedots); WM_operatortype_append(VIEW3D_OT_ruler_add); WM_operatortype_append(VIEW3D_OT_ruler_remove); diff --git a/source/blender/editors/space_view3d/view3d_select.cc b/source/blender/editors/space_view3d/view3d_select.cc index 51c6517c1d1..f763166b90f 100644 --- a/source/blender/editors/space_view3d/view3d_select.cc +++ b/source/blender/editors/space_view3d/view3d_select.cc @@ -81,6 +81,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_select_utils.h" +#include "ED_transform.h" #include "UI_interface.h" #include "UI_resources.h" @@ -104,7 +105,12 @@ float ED_view3d_select_dist_px(void) { - return 75.0f * U.pixelsize; + if (U.flag & USER_ADJUSTABLE_CLICK_SELECT) { + return U.selection_radius * U.pixelsize; + } + else { + return 75.0f * U.pixelsize; + } } void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgraph) @@ -240,15 +246,339 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w /** \name Internal Edit-Mesh Utilities * \{ */ -static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, +struct BoxSelectUserData { + ViewContext *vc; + const rcti *rect; + const rctf *rect_fl; + rctf _rect_fl; + eSelectOp sel_op; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + + /* runtime */ + bool is_done; + bool is_changed; +}; + +struct LassoSelectUserData { + ViewContext *vc; + const rcti *rect; + const rctf *rect_fl; + rctf _rect_fl; + const int (*mcoords)[2]; + int mcoords_len; + eSelectOp sel_op; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + + /* runtime */ + int pass; + bool is_done; + bool is_changed; +}; + +struct CircleSelectUserData { + ViewContext *vc; + bool select; + int mval[2]; + float mval_fl[2]; + float radius; + float radius_squared; + eBezTriple_Flag select_flag; + int edge_style; + int face_style; + bool check_mesh_direction; + + /* runtime */ + bool is_changed; +}; + +bool edbm_normal_facing_viewport(ViewContext *vc, + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa, + bool use_direction) +{ + ToolSettings *ts = vc->scene->toolsettings; + float meshmat[3][3]; + bool backface = false; + int direction = 0; + if (eve != NULL) { + ED_getTransformOrientationMatrix(vc->scene, + vc->view_layer, + vc->v3d, + vc->obact, + vc->obedit, + V3D_AROUND_ACTIVE, + meshmat, + eve, + NULL, + NULL); + direction = ts->viewport_facing_select_vert; + if (use_direction && (direction == 4 || direction == 8)) { + backface = true; + } + } + else if (eed != NULL) { + ED_getTransformOrientationMatrix(vc->scene, + vc->view_layer, + vc->v3d, + vc->obact, + vc->obedit, + V3D_AROUND_ACTIVE, + meshmat, + NULL, + eed, + NULL); + direction = ts->viewport_facing_select_edge; + if (use_direction && (direction == 4 || direction == 8)) { + backface = true; + } + } + else if (efa != NULL) { + ED_getTransformOrientationMatrix(vc->scene, + vc->view_layer, + vc->v3d, + vc->obact, + vc->obedit, + V3D_AROUND_ACTIVE, + meshmat, + NULL, + NULL, + efa); + direction = ts->viewport_facing_select_face; + if (use_direction && (direction == 4 || direction == 8)) { + backface = true; + } + } + normalize_m3(meshmat); + invert_m3(meshmat); + float meshcol3[3] = {0, 0, 0}; + meshcol3[0] = meshmat[0][2]; + meshcol3[1] = meshmat[1][2]; + meshcol3[2] = meshmat[2][2]; + float viewcol3[3] = {0, 0, 0}; + viewcol3[0] = vc->rv3d->viewmat[0][2]; + viewcol3[1] = vc->rv3d->viewmat[1][2]; + viewcol3[2] = vc->rv3d->viewmat[2][2]; + bool mesh_facing; + if (backface) { + mesh_facing = dot_v3v3(meshcol3, viewcol3) < -ts->viewport_facing_select_threshold; + } + else { + mesh_facing = dot_v3v3(meshcol3, viewcol3) > ts->viewport_facing_select_threshold; + } + return mesh_facing; +} + +bool edbm_facing_viewport_precheck(ToolSettings *ts, int style, bool xray) +{ + if (!ts->viewport_facing_select) { + return false; + } + const bool mode_match = xray ? ts->viewport_facing_select_mode == 1 || + ts->viewport_facing_select_mode == 4 : + ts->viewport_facing_select_mode < 4; + const bool check_mesh_facing = mode_match && style > 0 && style < 16; + return check_mesh_facing; +} + +bool edbm_facing_viewport(ViewContext *vc, BMVert *eve, BMEdge *eed, BMFace *efa, int style) +{ + BMIter iter; + bool mesh_facing = false; + if (eve != NULL) { + /* viewport-facing or rear-facing vert */ + mesh_facing = edbm_normal_facing_viewport(vc, eve, NULL, NULL, true); + if (!mesh_facing && eve->e && eve->e->l && (style == 2 || style == 8)) { + BM_ITER_ELEM (efa, &iter, eve, BM_FACES_OF_VERT) { + const bool eve_efa_facing = edbm_normal_facing_viewport(vc, NULL, NULL, efa, false); + /* vert of a viewport-facing face */ + if (style == 2) { + if (eve_efa_facing) { + mesh_facing = true; + break; + } + } + /* vert of a rear-facing face */ + else if (!eve_efa_facing) { + mesh_facing = true; + break; + } + } + } + } + else if (eed != NULL) { + /* viewport-facing or rear-facing edge */ + mesh_facing = edbm_normal_facing_viewport(vc, NULL, eed, NULL, true); + if (!mesh_facing && eed->l && (style == 2 || style == 8)) { + BM_ITER_ELEM (efa, &iter, eed, BM_FACES_OF_EDGE) { + const bool eed_efa_facing = edbm_normal_facing_viewport(vc, NULL, NULL, efa, false); + /* edge of a viewport-facing face */ + if (style == 2) { + if (eed_efa_facing) { + mesh_facing = true; + break; + } + } + /* edge of a rear-facing face */ + else if (!eed_efa_facing) { + mesh_facing = true; + break; + } + } + } + } + else if (efa != NULL) { + /* viewport-facing or rear-facing face */ + mesh_facing = edbm_normal_facing_viewport(vc, NULL, NULL, efa, true); + if (!mesh_facing && (style == 2 || style == 8)) { + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + const bool efa_eve_facing = edbm_normal_facing_viewport(vc, eve, NULL, NULL, false); + /* face has a viewport-facing vert */ + if (style == 2) { + if (efa_eve_facing) { + mesh_facing = true; + break; + } + } + /* face has a rear-facing vert */ + else if (!efa_eve_facing) { + mesh_facing = true; + break; + } + } + } + } + return mesh_facing; +} + +bool edbm_circle_enclose_mesh(BMEdge *eed, BMFace *efa, struct CircleSelectUserData *data) +{ + BMVert *eve; + BMIter iter; + bool enclose = false; + + if (eed != NULL) { + BM_ITER_ELEM (eve, &iter, eed, BM_VERTS_OF_EDGE) { + float vertv3[3] = {eve->co[0], eve->co[1], eve->co[2]}; + float vertv2[2] = {0.0f, 0.0f}; + ED_view3d_project_float_object( + data->vc->region, vertv3, vertv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + enclose = len_squared_v2v2(data->mval_fl, vertv2) <= data->radius_squared; + if (!enclose) { + break; + } + } + } + else if (efa != NULL) { + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + float vertv3[3] = {eve->co[0], eve->co[1], eve->co[2]}; + float vertv2[2] = {0.0f, 0.0f}; + ED_view3d_project_float_object( + data->vc->region, vertv3, vertv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + enclose = len_squared_v2v2(data->mval_fl, vertv2) <= data->radius_squared; + if (!enclose) { + break; + } + } + } + return enclose; +} + +bool edbm_center_face(ViewContext *vc, + BMFace *efa, + const rctf *rect, + struct LassoSelectUserData *lassoData, + struct CircleSelectUserData *circleData) +{ + BMVert *eve; + BMIter iter; + float centerv3[3] = {0.0f, 0.0f, 0.0f}; + float centerv2[2] = {0.0f, 0.0f}; + bool center_face = false; + + /* tri */ + if (efa->len == 3) { + float tri_vco[3][3] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + int tri_index = 0; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + tri_vco[tri_index][0] = eve->co[0]; + tri_vco[tri_index][1] = eve->co[1]; + tri_vco[tri_index][2] = eve->co[2]; + tri_index++; + } + float triv1[3] = {tri_vco[0][0], tri_vco[0][1], tri_vco[0][2]}; + float triv2[3] = {tri_vco[1][0], tri_vco[1][1], tri_vco[1][2]}; + float triv3[3] = {tri_vco[2][0], tri_vco[2][1], tri_vco[2][2]}; + mid_v3_v3v3v3(centerv3, triv1, triv2, triv3); + } + /* quad */ + else if (efa->len == 4) { + float quad_vco[4][3] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + int quad_index = 0; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + quad_vco[quad_index][0] = eve->co[0]; + quad_vco[quad_index][1] = eve->co[1]; + quad_vco[quad_index][2] = eve->co[2]; + quad_index++; + } + float quadv1[3] = {quad_vco[0][0], quad_vco[0][1], quad_vco[0][2]}; + float quadv2[3] = {quad_vco[1][0], quad_vco[1][1], quad_vco[1][2]}; + float quadv3[3] = {quad_vco[2][0], quad_vco[2][1], quad_vco[2][2]}; + float quadv4[3] = {quad_vco[3][0], quad_vco[3][1], quad_vco[3][2]}; + mid_v3_v3v3v3v3(centerv3, quadv1, quadv2, quadv3, quadv4); + } + /* ngon */ + else { + const float w = 1.0f / (float)efa->len; + BM_ITER_ELEM (eve, &iter, efa, BM_VERTS_OF_FACE) { + madd_v3_v3fl(centerv3, eve->co, w); + } + } + ED_view3d_project_float_object( + vc->region, centerv3, centerv2, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + + /* lasso center */ + if (lassoData != NULL) { + center_face = BLI_rctf_isect_pt_v(rect, centerv2) && + BLI_lasso_is_point_inside(lassoData->mcoords, + lassoData->mcoords_len, + centerv2[0], + centerv2[1], + IS_CLIPPED); + } + /* circle center */ + else if (circleData != NULL) { + center_face = (len_squared_v2v2(circleData->mval_fl, centerv2) <= circleData->radius_squared); + } + /* box center */ + else { + center_face = BLI_rctf_isect_pt_v(rect, centerv2); + } + return center_face; +} + + + +static bool edbm_backbuf_check_and_select_verts(ViewContext *vc, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) { + ToolSettings *ts = vc->scene->toolsettings; BMVert *eve; BMIter iter; bool changed = false; + const int style = ts->viewport_facing_select_vert; + const bool check_mesh_facing = edbm_facing_viewport_precheck(ts, style, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); @@ -261,7 +591,12 @@ static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + bool mesh_facing = true; + if (is_inside && check_mesh_facing) { + mesh_facing = edbm_facing_viewport(vc, eve, NULL, NULL, style); + } + const int sel_op_result = ED_select_op_action_deselected( + sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_vert_select_set(em->bm, eve, sel_op_result); changed = true; @@ -272,15 +607,20 @@ static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, return changed; } -static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, +static bool edbm_backbuf_check_and_select_edges(void *userData, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op) { + CircleSelectUserData *data = static_cast(userData); + ToolSettings *ts = data->vc->scene->toolsettings; BMEdge *eed; BMIter iter; bool changed = false; + const int style = ts->viewport_facing_select_edge; + const bool check_mesh_facing = edbm_facing_viewport_precheck(ts, style, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_EDGE); @@ -293,7 +633,19 @@ static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + bool enclose_edge = true; + bool mesh_facing = true; + + if (data->edge_style == 4 && is_inside) { + enclose_edge = edbm_circle_enclose_mesh(eed, NULL, data); + } + + if (check_mesh_facing && is_inside) { + mesh_facing = edbm_facing_viewport(data->vc, NULL, eed, NULL, style); + } + + const int sel_op_result = ED_select_op_action_deselected( + sel_op, is_select, is_inside && enclose_edge && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(em->bm, eed, sel_op_result); changed = true; @@ -304,28 +656,74 @@ static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, return changed; } -static bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel, +static bool edbm_backbuf_check_and_select_faces(ViewContext *vc, + EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, - const eSelectOp sel_op) + const eSelectOp sel_op, + const rcti *rect, + const int face_style, + void *ldata, + void *cdata) { + ToolSettings *ts = vc->scene->toolsettings; + BMIter iter, viter; BMFace *efa; - BMIter iter; + BMVert *eve; + rctf rectf; bool changed = false; - + const int style = ts->viewport_facing_select_face; + const bool check_mesh_facing = edbm_facing_viewport_precheck(ts, style, false); const BLI_bitmap *select_bitmap = esel->select_bitmap; + LassoSelectUserData *lassoData = static_cast(ldata); + CircleSelectUserData *circleData = static_cast(cdata); uint index = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_FACE); - if (index == 0) { + uint vindex = DRW_select_buffer_context_offset_for_object_elem(depsgraph, ob, SCE_SELECT_VERTEX); + + if (rect != NULL) { + BLI_rctf_rcti_copy(&rectf, rect); + } + + if (index == 0 || vindex == 0) { return false; } index -= 1; + vindex -= 1; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + bool enclose_face = true; + bool center_face = true; + bool mesh_facing = true; + + if (face_style > 2 && is_inside) { + if (face_style == 4) { + if (circleData != NULL) { + enclose_face = edbm_circle_enclose_mesh(NULL, efa, circleData); + } + else { + BM_ITER_ELEM (eve, &viter, efa, BM_VERTS_OF_FACE) { + enclose_face = BLI_BITMAP_TEST_BOOL(select_bitmap, vindex + BM_elem_index_get(eve)); + if (!enclose_face) { + break; + } + } + } + } + else { + center_face = edbm_center_face(vc, efa, &rectf, lassoData, circleData); + } + } + + if (check_mesh_facing && is_inside) { + mesh_facing = edbm_facing_viewport(vc, NULL, NULL, efa, style); + } + + const int sel_op_result = ED_select_op_action_deselected( + sel_op, is_select, is_inside && enclose_face && center_face && mesh_facing); if (sel_op_result != -1) { BM_face_select_set(em->bm, efa, sel_op_result); changed = true; @@ -404,28 +802,14 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me, /** \name Lasso Select * \{ */ -struct LassoSelectUserData { - ViewContext *vc; - const rcti *rect; - const rctf *rect_fl; - rctf _rect_fl; - const int (*mcoords)[2]; - int mcoords_len; - eSelectOp sel_op; - eBezTriple_Flag select_flag; - - /* runtime */ - int pass; - bool is_done; - bool is_changed; -}; - static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, ViewContext *vc, const rcti *rect, const int (*mcoords)[2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + const int edge_style, + const int face_style) { r_data->vc = vc; @@ -438,6 +822,9 @@ static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, r_data->sel_op = sel_op; /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->pass = 0; @@ -527,8 +914,12 @@ static void do_lasso_select_pose__do_tag(void *userData, } if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) && - BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { + BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX, + false)) { pchan->bone->flag |= BONE_DONE; data->is_changed = true; } @@ -552,7 +943,7 @@ static void do_lasso_tag_pose(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); view3d_userdata_lassoselect_init( - &data, vc, &rect, mcoords, mcoords_len, static_cast(0)); + &data, vc, &rect, mcoords, mcoords_len, static_cast(0), 0, 0); ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d); @@ -563,42 +954,225 @@ static void do_lasso_tag_pose(ViewContext *vc, V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); } +/** + * Compare result of 'GPU_select': 'GPUSelectResult', + * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. + */ +static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) +{ + GPUSelectResult *a = (GPUSelectResult *)sel_a_p; + GPUSelectResult *b = (GPUSelectResult *)sel_b_p; + + if (a->depth < b->depth) { + return -1; + } + if (a->depth > b->depth) { + return 1; + } + + /* Depths match, sort by id. */ + uint sel_a = a->id; + uint sel_b = b->id; + +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&sel_a); + BLI_endian_switch_uint32(&sel_b); +#endif + + if (sel_a < sel_b) { + return -1; + } + if (sel_a > sel_b) { + return 1; + } + return 0; +} + static bool do_lasso_select_objects(ViewContext *vc, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op, + int circle_data[3]) { View3D *v3d = vc->v3d; + int totobj = MAXPICKELEMS; /* XXX solve later */ + /* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */ + GPUSelectResult *buffer = static_cast( + MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__)); + ToolSettings *ts = vc->scene->toolsettings; - bool changed = false; - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + const int select_through_int = RNA_enum_get(op->ptr, "select_through"); + const bool select_through = circle_data == NULL ? + ts->select_through && ts->select_through_object && + ts->select_through_lasso : + ts->select_through && ts->select_through_object && + ts->select_through_circle; + float region_co[2]; + float mval_fl[2]; + if (circle_data != NULL) { + mval_fl[0] = circle_data[0]; + mval_fl[1] = circle_data[1]; } - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { - if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */ - float region_co[2]; - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) == - V3D_PROJ_RET_OK) && - BLI_lasso_is_point_inside(mcoords, - mcoords_len, - int(region_co[0]), - int(region_co[1]), - /* Dummy value. */ - INT_MAX); - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - changed = true; + bool changed = false; + + if (select_through && circle_data == NULL) { + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + } + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */ + float region_co[2]; + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) == + V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(region_co[0]), + int(region_co[1]), + /* Dummy value. */ + INT_MAX); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } } } + + if (changed) { + DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } } - if (changed) { - DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); - WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + else { + int hits = 0; + rcti rect_data; + rcti *rect = &rect_data; + blender::Vector bases; + + if (circle_data != NULL) { + int point[4][2] = {}; + + for (int i = 0; i < 4; i++) { + float angle = 6.28f * (i + 1) / 4; + point[i][0] = circle_data[0] + circle_data[2] * cosf(angle); + point[i][1] = circle_data[1] + circle_data[2] * sinf(angle); + } + rect->xmin = rect->xmax = point[0][0]; + rect->ymin = rect->ymax = point[0][1]; + uint a; + for (a = 1; a < 4; a++) { + if (point[a][0] < rect->xmin) { + rect->xmin = point[a][0]; + } + else if (point[a][0] > rect->xmax) { + rect->xmax = point[a][0]; + } + if (point[a][1] < rect->ymin) { + rect->ymin = point[a][1]; + } + else if (point[a][1] > rect->ymax) { + rect->ymax = point[a][1]; + } + } + } + else { + BLI_lasso_boundbox(rect, mcoords, mcoords_len); + } + const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, + vc->obact); + if (XRAY_FLAG_ENABLED(vc->v3d) || select_through) { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + } + else { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } + BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); + LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d); + } + + ListBase *object_bases = BKE_view_layer_object_bases_get(vc->view_layer); + if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) { + goto finally; + } + + LISTBASE_FOREACH (Base *, base, object_bases) { + if (BASE_SELECTABLE(v3d, base)) { + if ((base->object->runtime.select_id & 0x0000FFFF) != 0) { + bases.append(base); + } + } + } + + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + qsort(buffer, hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp); + + for (const GPUSelectResult *buf_iter = buffer, *buf_end = buf_iter + hits; buf_iter < buf_end; + buf_iter++) { + bPoseChannel *pchan_dummy; + Base *base = ED_armature_base_and_pchan_from_select_buffer( + bases.data(), bases.size(), buf_iter->id, &pchan_dummy); + if (base != nullptr) { + base->object->id.tag |= LIB_TAG_DOIT; + } + } + + for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { + if (BASE_SELECTABLE(v3d, base)) { + const bool is_select = base->flag & BASE_SELECTED; + bool is_inside = false; + + if (circle_data == NULL) { + is_inside = base->object->id.tag & LIB_TAG_DOIT && + (ED_view3d_project_base(vc->region, base, region_co) == V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcoords, + mcoords_len, + int(region_co[0]), + int(region_co[1]), + /* Dummy value. */ + INT_MAX); + } + else { + is_inside = base->object->id.tag & LIB_TAG_DOIT ? + RNA_boolean_get(op->ptr, "select_origin_circle") ? + (ED_view3d_project_float_global(vc->region, + base->object->object_to_world[3], + region_co, + V3D_PROJ_TEST_CLIP_DEFAULT) == + V3D_PROJ_RET_OK) ? + len_squared_v2v2(mval_fl, region_co) <= circle_data[2] * circle_data[2] : + false : + true : + false; + } + + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + + finally: + + MEM_freeN(buffer); + + if (changed) { + DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(vc->C, NC_SCENE | ND_OB_SELECT, vc->scene); + } } + return changed; } @@ -712,7 +1286,15 @@ static void do_lasso_select_mesh__doSelectVert(void *userData, (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && BLI_lasso_is_point_inside( data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + bool mesh_facing = true; + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, eve, NULL, NULL, data->vc->scene->toolsettings->viewport_facing_select_vert); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); data->is_changed = true; @@ -733,6 +1315,7 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, user_data); LassoSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -745,7 +1328,14 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), IS_CLIPPED) && BLI_lasso_is_point_inside( data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_done = true; @@ -762,6 +1352,7 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, user_data); LassoSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -772,15 +1363,23 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), - IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + IS_CLIPPED, + false)); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_changed = true; } } -static void do_lasso_select_mesh__doSelectFace(void *userData, +static void do_lasso_select_mesh__doSelectFaceCenter(void *userData, BMFace *efa, const float screen_co[2], int /*index*/) @@ -791,7 +1390,86 @@ static void do_lasso_select_mesh__doSelectFace(void *userData, (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && BLI_lasso_is_point_inside( data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + bool mesh_facing = true; + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); + if (sel_op_result != -1) { + BM_face_select_set(data->vc->em->bm, efa, sel_op_result); + data->is_changed = true; + } +} + +static void do_lasso_select_mesh__doSelectFace(void *user_data, + BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit) +{ + LassoSelectUserData *data = static_cast(user_data); + int style = data->face_style; + + if (!BLI_rctf_isect(data->rect_fl, screen_rect, NULL)) + return; + + bool inside = false; + bool mesh_facing = true; + for (int i = 0; i < total_count; i++) { + + int a = i; + int b = (i + 1) % total_count; + + /* enclose */ + if (style == 4) { + inside = BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co[a]), + UNPACK2(screen_co[b]), + IS_CLIPPED, + true); + if (!inside) { + break; + } + } + /* touch */ + else { + inside = BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co[a]), + UNPACK2(screen_co[b]), + IS_CLIPPED, + false); + if (inside) { + break; + } + } + } + + /* touch interior of face */ + if (style == 2) { + if (!inside) { + float point[2] = {static_cast(data->mcoords[0][0]), static_cast(data->mcoords[0][1])}; + inside = isect_point_poly_v2(point, screen_co, total_count, true); + } + } + + *face_hit = inside; + + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + + if (inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, inside && mesh_facing); + if (sel_op_result != -1) { BM_face_select_set(data->vc->em->bm, efa, sel_op_result); data->is_changed = true; @@ -802,7 +1480,8 @@ static bool do_lasso_select_mesh(ViewContext *vc, wmGenericUserData *wm_userdata, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { LassoSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; @@ -813,7 +1492,8 @@ static bool do_lasso_select_mesh(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init( + &data, vc, &rect, mcoords, mcoords_len, sel_op, RNA_enum_get(op->ptr, "edge_type"), RNA_enum_get(op->ptr, "face_type")); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -827,12 +1507,22 @@ static bool do_lasso_select_mesh(ViewContext *vc, GPU_matrix_set(vc->rv3d->viewmat); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const bool select_through = ts->select_through && ts->select_through_edit && + ts->select_through_lasso; + const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !select_through; EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); if (use_zbuf) { if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + /* for near enclose face */ + if (data.face_style == 4 && ts->selectmode & SCE_SELECT_FACE && + !(ts->selectmode & SCE_SELECT_VERTEX)) { + editselect_buf_cache_init_with_generic_userdata( + wm_userdata, vc, ts->selectmode | SCE_SELECT_VERTEX); + } + else { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + } esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, nullptr); @@ -842,9 +1532,12 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_vert, true); + mesh_foreachScreenVert( vc, do_lasso_select_mesh__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } @@ -860,12 +1553,20 @@ static bool do_lasso_select_mesh(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB); - /* Fully inside. */ - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); - if (data.is_done == false) { - /* Fall back to partially inside. - * Clip content to account for edges partially behind the view. */ + + data.check_mesh_direction = use_zbuf ? + edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, false) : + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, true); + + /* Fully inside, hybrid and enclose edge */ + if (data.edge_style != 2) { + mesh_foreachScreenEdge_clip_bb_segment( + vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag); + } + /* Partially inside, hybrid and touch edge */ + if (data.edge_style == 2 || data.edge_style == 1 && data.is_done == false) { mesh_foreachScreenEdge_clip_bb_segment(vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, @@ -875,12 +1576,33 @@ static bool do_lasso_select_mesh(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - data.is_changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + sel_op, + &rect, + data.face_style, + &data, + NULL); } else { - mesh_foreachScreenFace( - vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_face, true); + + /* xray center and auto face with fallback for touch and enclose intersect */ + if (data.face_style == 1 || data.face_style == 8 || SEL_OP_USE_OUTSIDE(sel_op)) { + mesh_foreachScreenFaceCenter( + vc, do_lasso_select_mesh__doSelectFaceCenter, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* xray touch and enclose face - doesn't work for intersect */ + else { + mesh_foreachScreenFaceVerts(vc, + do_lasso_select_mesh__doSelectFace, + &data, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + } } } @@ -944,7 +1666,7 @@ static bool do_lasso_select_curve(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -993,7 +1715,7 @@ static bool do_lasso_select_lattice(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -1022,7 +1744,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (screen_co_a[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX), + false) { is_inside_flag |= BONESEL_ROOT; } } @@ -1033,7 +1756,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (screen_co_b[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && BLI_lasso_is_point_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX), + false) { is_inside_flag |= BONESEL_TIP; } } @@ -1047,7 +1771,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), - INT_MAX)) { + INT_MAX, + false)) { is_inside_flag |= BONESEL_BONE; } } @@ -1076,7 +1801,7 @@ static void do_lasso_select_armature__doSelectBone_clip_content(void *userData, } if (BLI_lasso_is_edge_inside( - data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX, false)) { is_inside_flag |= BONESEL_BONE; } @@ -1093,7 +1818,7 @@ static bool do_lasso_select_armature(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -1153,7 +1878,7 @@ static bool do_lasso_select_meta(ViewContext *vc, BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(mb); @@ -1237,7 +1962,8 @@ static bool do_lasso_select_paintvert(ViewContext *vc, LassoSelectUserData_ForMeshVert data; data.select_vert = select_vert.span; - view3d_userdata_lassoselect_init(&data.lasso_data, vc, &rect, mcoords, mcoords_len, sel_op); + view3d_userdata_lassoselect_init( + &data.lasso_data, vc, &rect, mcoords, mcoords_len, sel_op, 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -1302,7 +2028,8 @@ static bool view3d_lasso_select(bContext *C, ViewContext *vc, const int mcoords[][2], const int mcoords_len, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { using namespace blender; Object *ob = CTX_data_active_object(C); @@ -1332,7 +2059,7 @@ static bool view3d_lasso_select(bContext *C, } } else { - changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op); + changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op, op, NULL); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -1346,7 +2073,7 @@ static bool view3d_lasso_select(bContext *C, switch (vc->obedit->type) { case OB_MESH: - changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op); + changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op, op); break; case OB_CURVES_LEGACY: case OB_SURF: @@ -1417,7 +2144,7 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init(C, &vc, depsgraph); eSelectOp sel_op = static_cast(RNA_enum_get(op->ptr, "mode")); - bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op); + bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op, op); MEM_freeN((void *)mcoords); @@ -2140,40 +2867,6 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, return hits; } -/** - * Compare result of 'GPU_select': 'GPUSelectResult', - * Needed for stable sorting, so cycling through all items near the cursor behaves predictably. - */ -static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p) -{ - GPUSelectResult *a = (GPUSelectResult *)sel_a_p; - GPUSelectResult *b = (GPUSelectResult *)sel_b_p; - - if (a->depth < b->depth) { - return -1; - } - if (a->depth > b->depth) { - return 1; - } - - /* Depths match, sort by id. */ - uint sel_a = a->id; - uint sel_b = b->id; - -#ifdef __BIG_ENDIAN__ - BLI_endian_switch_uint32(&sel_a); - BLI_endian_switch_uint32(&sel_b); -#endif - - if (sel_a < sel_b) { - return -1; - } - if (sel_a > sel_b) { - return 1; - } - return 0; -} - /** * \param has_bones: When true, skip non-bone hits, also allow bases to be used * that are visible but not select-able, @@ -3277,23 +3970,12 @@ void VIEW3D_OT_select(wmOperatorType *ot) /** \name Box Select * \{ */ -struct BoxSelectUserData { - ViewContext *vc; - const rcti *rect; - const rctf *rect_fl; - rctf _rect_fl; - eSelectOp sel_op; - eBezTriple_Flag select_flag; - - /* runtime */ - bool is_done; - bool is_changed; -}; - static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, ViewContext *vc, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + const int edge_style, + const int face_style) { r_data->vc = vc; @@ -3304,6 +3986,9 @@ static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, r_data->sel_op = sel_op; /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->is_done = false; @@ -3379,7 +4064,7 @@ static bool do_paintvert_box_select(ViewContext *vc, BoxSelectUserData_ForMeshVert data; data.select_vert = select_vert.span; - view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op, 0, 0); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -3485,7 +4170,7 @@ static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel const bool deselect_all = (sel_op == SEL_OP_SET); BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -3524,7 +4209,7 @@ static bool do_lattice_box_select(ViewContext *vc, rcti *rect, const eSelectOp s { BoxSelectUserData data; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, vc, rect, sel_op, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -3545,7 +4230,15 @@ static void do_mesh_box_select__doSelectVert(void *userData, BoxSelectUserData *data = static_cast(userData); const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + bool mesh_facing = true; + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, eve, NULL, NULL, data->vc->scene->toolsettings->viewport_facing_select_vert); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); data->is_changed = true; @@ -3566,6 +4259,7 @@ static void do_mesh_box_select__doSelectEdge_pass0( userData); BoxSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -3574,7 +4268,14 @@ static void do_mesh_box_select__doSelectEdge_pass0( const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_done = true; @@ -3591,6 +4292,7 @@ static void do_mesh_box_select__doSelectEdge_pass1( userData); BoxSelectUserData *data = data_for_edge->data; bool is_visible = true; + bool mesh_facing = true; if (data_for_edge->backbuf_offset) { uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1; is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx); @@ -3598,13 +4300,79 @@ static void do_mesh_box_select__doSelectEdge_pass1( const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); data->is_changed = true; } } + static void do_mesh_box_select__doSelectFace(void *userData, + BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit) +{ + BoxSelectUserData *data = static_cast(userData); + + if (!BLI_rctf_isect(data->rect_fl, screen_rect, NULL)) + return; + + bool inside = false; + bool mesh_facing = true; + for (int i = 0; i < total_count; i++) { + + int a = i; + int b = (i + 1) % total_count; + + /* enclose */ + if (data->face_style == 4) { + inside = edge_fully_inside_rect(data->rect_fl, screen_co[a], screen_co[b]); + if (!inside) { + break; + } + } + /* touch */ + else { + inside = edge_inside_rect(data->rect_fl, screen_co[a], screen_co[b]); + if (inside) { + break; + } + } + } + + /* touch interior of face */ + if (data->face_style == 2 && !inside) { + float point[2] = {data->rect_fl->xmax, data->rect_fl->ymax}; + inside = isect_point_poly_v2(point, screen_co, total_count, true); + } + + *face_hit = inside; + + const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); + + if (inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, inside && mesh_facing); + + if (sel_op_result != -1) { + BM_face_select_set(data->vc->em->bm, efa, sel_op_result); + data->is_changed = true; + } +} + +static void do_mesh_box_select__doSelectFaceCenter(void *userData, BMFace *efa, const float screen_co[2], int /*index*/) @@ -3612,7 +4380,15 @@ static void do_mesh_box_select__doSelectFace(void *userData, BoxSelectUserData *data = static_cast(userData); const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); + bool mesh_facing = true; + + if (is_inside && data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + const int sel_op_result = ED_select_op_action_deselected( + data->sel_op, is_select, is_inside && mesh_facing); if (sel_op_result != -1) { BM_face_select_set(data->vc->em->bm, efa, sel_op_result); data->is_changed = true; @@ -3621,12 +4397,18 @@ static void do_mesh_box_select__doSelectFace(void *userData, static bool do_mesh_box_select(ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, - const eSelectOp sel_op) + const eSelectOp sel_op, + wmOperator *op) { BoxSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; - view3d_userdata_boxselect_init(&data, vc, rect, sel_op); + view3d_userdata_boxselect_init(&data, + vc, + rect, + sel_op, + RNA_enum_get(op->ptr, "edge_type"), + RNA_enum_get(op->ptr, "face_type")); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -3640,12 +4422,22 @@ static bool do_mesh_box_select(ViewContext *vc, GPU_matrix_set(vc->rv3d->viewmat); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const bool select_through = ts->select_through && ts->select_through_edit && + ts->select_through_box; + const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !select_through; EditSelectBuf_Cache *esel = static_cast(wm_userdata->data); if (use_zbuf) { if (wm_userdata->data == nullptr) { - editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + /* for near enclose face */ + if (data.face_style == 4 && ts->selectmode & SCE_SELECT_FACE && + !(ts->selectmode & SCE_SELECT_VERTEX)) { + editselect_buf_cache_init_with_generic_userdata( + wm_userdata, vc, ts->selectmode | SCE_SELECT_VERTEX); + } + else { + editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); + } esel = static_cast(wm_userdata->data); esel->select_bitmap = DRW_select_buffer_bitmap_from_rect( vc->depsgraph, vc->region, vc->v3d, rect, nullptr); @@ -3655,9 +4447,12 @@ static bool do_mesh_box_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_VERTEX) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, esel, vc->depsgraph, vc->obedit, vc->em, sel_op); } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_vert, true); + mesh_foreachScreenVert( vc, do_mesh_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } @@ -3674,12 +4469,19 @@ static bool do_mesh_box_select(ViewContext *vc, const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR | (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB); - /* Fully inside. */ - mesh_foreachScreenEdge_clip_bb_segment( - vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); - if (data.is_done == false) { - /* Fall back to partially inside. - * Clip content to account for edges partially behind the view. */ + + data.check_mesh_direction = use_zbuf ? + edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, false) : + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, true); + /* Fully inside, hybrid and enclose edge */ + if (data.edge_style != 2) { + mesh_foreachScreenEdge_clip_bb_segment( + vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag); + } + /* Partially inside, hybrid and touch edge */ + if (data.edge_style == 2 || data.edge_style == 1 && data.is_done == false) { mesh_foreachScreenEdge_clip_bb_segment(vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, @@ -3690,11 +4492,24 @@ static bool do_mesh_box_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { data.is_changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, sel_op); + vc, esel, vc->depsgraph, vc->obedit, vc->em, sel_op, rect, data.face_style, NULL, NULL); } else { - mesh_foreachScreenFace( - vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_face, true); + + /* xray center and auto face with fallback for touch and enclose intersect */ + if (data.face_style == 1 || data.face_style == 8 || SEL_OP_USE_OUTSIDE(sel_op)) { + mesh_foreachScreenFaceCenter( + vc, do_mesh_box_select__doSelectFaceCenter, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* xray touch and enclose face - doesn't work with intersect */ + else { + mesh_foreachScreenFaceVerts(vc, + do_mesh_box_select__doSelectFace, + &data, + V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + } } } @@ -3855,18 +4670,34 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_ return 0; } -static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) +static bool do_object_box_select( + bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op, wmOperator *op) { View3D *v3d = vc->v3d; int totobj = MAXPICKELEMS; /* XXX solve later */ + ToolSettings *ts = vc->scene->toolsettings; + rctf rect_f_data; + rctf *rect_f = &rect_f_data; + BLI_rctf_rcti_copy(rect_f, rect); + float region_co[2]; /* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */ GPUSelectResult *buffer = static_cast( MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__)); const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene, vc->obact); - const int hits = view3d_opengl_select( - vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + const bool select_origin = RNA_boolean_get(op->ptr, "select_origin_box"); + const bool select_through = ts->select_through && ts->select_through_object && + ts->select_through_box; + int hits = 0; + if (XRAY_FLAG_ENABLED(vc->v3d) || select_through) { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); + } + else { + hits = view3d_opengl_select( + vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_PICK_NEAREST, select_filter); + } BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) { base->object->id.tag &= ~LIB_TAG_DOIT; @@ -3908,7 +4739,13 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const for (Base *base = static_cast(object_bases->first); base && hits; base = base->next) { if (BASE_SELECTABLE(v3d, base)) { const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = base->object->id.tag & LIB_TAG_DOIT; + const bool is_inside = base->object->id.tag & LIB_TAG_DOIT ? + select_origin ? + (ED_view3d_project_base(vc->region, base, region_co) == + V3D_PROJ_RET_OK) && + BLI_rctf_isect_pt_v(rect_f, region_co) : + true : + false; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); @@ -4034,7 +4871,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) switch (vc.obedit->type) { case OB_MESH: vc.em = BKE_editmesh_from_object(vc.obedit); - changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op); + changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op, op); if (changed) { DEG_id_tag_update(static_cast(vc.obedit->data), ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); @@ -4108,7 +4945,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } } else { /* object mode with none active */ - changed_multi = do_object_box_select(C, &vc, &rect, sel_op); + changed_multi = do_object_box_select(C, &vc, &rect, sel_op, op); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -4151,24 +4988,13 @@ void VIEW3D_OT_select_box(wmOperatorType *ot) /** \name Circle Select * \{ */ -struct CircleSelectUserData { - ViewContext *vc; - bool select; - int mval[2]; - float mval_fl[2]; - float radius; - float radius_squared; - eBezTriple_Flag select_flag; - - /* runtime */ - bool is_changed; -}; - static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, ViewContext *vc, const bool select, const int mval[2], - const float rad) + const float rad, + const int edge_style, + const int face_style) { r_data->vc = vc; r_data->select = select; @@ -4181,6 +5007,9 @@ static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, /* SELECT by default, but can be changed if needed (only few cases use and respect this). */ r_data->select_flag = (eBezTriple_Flag)SELECT; + r_data->edge_style = edge_style; + r_data->face_style = face_style; + r_data->check_mesh_direction = false; /* runtime */ r_data->is_changed = false; @@ -4194,8 +5023,16 @@ static void mesh_circle_doSelectVert(void *userData, CircleSelectUserData *data = static_cast(userData); if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { - BM_vert_select_set(data->vc->em->bm, eve, data->select); + bool mesh_facing = true; data->is_changed = true; + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, eve, NULL, NULL, data->vc->scene->toolsettings->viewport_facing_select_vert); + } + if (mesh_facing) { + BM_vert_select_set(data->vc->em->bm, eve, data->select); + data->is_changed = true; + } } } static void mesh_circle_doSelectEdge(void *userData, @@ -4205,22 +5042,91 @@ static void mesh_circle_doSelectEdge(void *userData, int /*index*/) { CircleSelectUserData *data = static_cast(userData); - + + bool enclose_edge = true; + bool mesh_facing = true; if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { - BM_edge_select_set(data->vc->em->bm, eed, data->select); + if (data->edge_style == 4) { + enclose_edge = edbm_circle_enclose_mesh(eed, NULL, data); + } + + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, eed, NULL, data->vc->scene->toolsettings->viewport_facing_select_edge); + } + + if (enclose_edge && mesh_facing) { + BM_edge_select_set(data->vc->em->bm, eed, data->select); + data->is_changed = true; + } + } +} + +static void mesh_circle_doSelectFace(void *userData, + BMFace *efa, + const float screen_co[][2], + int total_count, + rctf *screen_rect, + bool *face_hit) +{ + CircleSelectUserData *data = static_cast(userData); + + if (!BLI_rctf_isect_circle(screen_rect, data->mval_fl, data->radius)) { + return; + } + + bool inside = false; + bool enclose_face = true; + bool mesh_facing = true; + for (int i = 0; i < total_count; i++) { + + int a = i; + int b = (i + 1) % total_count; + + inside = edge_inside_circle(data->mval_fl, data->radius, screen_co[a], screen_co[b]); + if (inside) + break; + } + + if (!inside) { + inside = isect_point_poly_v2(data->mval_fl, screen_co, total_count, true); + } + + *face_hit = inside; + + if (data->face_style == 4 && inside) { + enclose_face = edbm_circle_enclose_mesh(NULL, efa, data); + } + + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + if (inside && enclose_face && mesh_facing) { + BM_face_select_set(data->vc->em->bm, efa, data->select); data->is_changed = true; } } -static void mesh_circle_doSelectFace(void *userData, + +static void mesh_circle_doSelectFaceCenter(void *userData, BMFace *efa, const float screen_co[2], int /*index*/) { CircleSelectUserData *data = static_cast(userData); + bool mesh_facing = true; if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { - BM_face_select_set(data->vc->em->bm, efa, data->select); - data->is_changed = true; + if (data->check_mesh_direction) { + mesh_facing = edbm_facing_viewport( + data->vc, NULL, NULL, efa, data->vc->scene->toolsettings->viewport_facing_select_face); + } + + if (mesh_facing) { + BM_face_select_set(data->vc->em->bm, efa, data->select); + data->is_changed = true; + } } } @@ -4228,7 +5134,8 @@ static bool mesh_circle_select(ViewContext *vc, wmGenericUserData *wm_userdata, eSelectOp sel_op, const int mval[2], - float rad) + float rad, + wmOperator *op) { ToolSettings *ts = vc->scene->toolsettings; CircleSelectUserData data; @@ -4248,9 +5155,17 @@ static bool mesh_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, + vc, + select, + mval, + rad, + RNA_enum_get(op->ptr, "edge_type"), + RNA_enum_get(op->ptr, "face_type")); - const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d); + const bool select_through = ts->select_through && ts->select_through_edit && + ts->select_through_circle; + const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !select_through; if (use_zbuf) { if (wm_userdata->data == nullptr) { @@ -4270,10 +5185,13 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != nullptr) { changed |= edbm_backbuf_check_and_select_verts( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + vc, esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_vert, true); + mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } } @@ -4282,10 +5200,13 @@ static bool mesh_circle_select(ViewContext *vc, if (use_zbuf) { if (esel->select_bitmap != nullptr) { changed |= edbm_backbuf_check_and_select_edges( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + &data, esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } } else { + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_edge, true); + mesh_foreachScreenEdge_clip_bb_segment( vc, mesh_circle_doSelectEdge, @@ -4296,13 +5217,33 @@ static bool mesh_circle_select(ViewContext *vc, if (ts->selectmode & SCE_SELECT_FACE) { if (use_zbuf) { - if (esel->select_bitmap != nullptr) { - changed |= edbm_backbuf_check_and_select_faces( - esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + if (esel->select_bitmap != NULL) { + changed |= edbm_backbuf_check_and_select_faces(vc, + esel, + vc->depsgraph, + vc->obedit, + vc->em, + select ? SEL_OP_ADD : SEL_OP_SUB, + NULL, + data.face_style, + NULL, + &data); } } else { - mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + data.check_mesh_direction = edbm_facing_viewport_precheck( + ts, ts->viewport_facing_select_face, true); + + /* xray center and auto face */ + if (data.face_style == 1 || data.face_style == 8) { + mesh_foreachScreenFaceCenter( + vc, mesh_circle_doSelectFaceCenter, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + } + /* xray touch and enclose face */ + else { + mesh_foreachScreenFaceVerts( + vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_BB); + } } } @@ -4416,7 +5357,7 @@ static bool paint_vertsel_circle_select(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */ - view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad, 0, 0); meshobject_foreachScreenVert( vc, paint_vertsel_circle_select_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); changed |= data.circle_data.is_changed; @@ -4470,7 +5411,7 @@ static bool nurbscurve_circle_select(ViewContext *vc, const bool deselect_all = (sel_op == SEL_OP_SET); CircleSelectUserData data; - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); Curve *curve = (Curve *)vc->obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(curve); @@ -4511,7 +5452,7 @@ static bool lattice_circle_select(ViewContext *vc, CircleSelectUserData data; const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -4602,7 +5543,7 @@ static bool pose_circle_select(ViewContext *vc, CircleSelectUserData data; const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_pose_deselect_all(vc->obact, SEL_DESELECT, false); @@ -4739,7 +5680,7 @@ static bool armature_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -4792,7 +5733,7 @@ static bool mball_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); - view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + view3d_userdata_circleselect_init(&data, vc, select, mval, rad, 0, 0); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(static_cast(vc->obedit->data)); @@ -4813,14 +5754,15 @@ static bool obedit_circle_select(bContext *C, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], - float rad) + float rad, + wmOperator *op) { using namespace blender; bool changed = false; BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); switch (vc->obedit->type) { case OB_MESH: - changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad); + changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad, op); break; case OB_CURVES_LEGACY: case OB_SURF: @@ -4961,7 +5903,6 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = ED_select_op_modal( static_cast(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture)); - ED_view3d_viewcontext_init(C, &vc, depsgraph); Object *obact = vc.obact; @@ -4981,7 +5922,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) obedit = vc.obedit; if (obedit) { - obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius)); + obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius), op); } else if (BKE_paint_select_face_test(obact)) { paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius)); @@ -5009,11 +5950,22 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else { - if (object_circle_select(&vc, sel_op, mval, float(radius))) { - DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); + ToolSettings *ts = vc.scene->toolsettings; + const bool default_object_select = RNA_boolean_get(op->ptr, "select_origin_circle") && + ts->select_through && ts->select_through_object && + ts->select_through_circle; + if (default_object_select) { + if (object_circle_select(&vc, sel_op, mval, float(radius))) { + DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); - ED_outliner_select_sync_from_object_tag(C); + ED_outliner_select_sync_from_object_tag(C); + } + } + else { + BKE_object_update_select_id(CTX_data_main(C)); + int circle_data[3] = {mval[0], mval[1], radius}; + do_lasso_select_objects(&vc, NULL, NULL, sel_op, op, circle_data); } } diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 0cf9d5fa309..5a067f0f7f9 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -516,7 +516,8 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene, } case V3D_ORIENT_NORMAL: { if (obedit || (ob && ob->mode & OB_MODE_POSE)) { - ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat); + ED_getTransformOrientationMatrix( + scene, view_layer, v3d, ob, obedit, pivot_point, r_mat, NULL, NULL, NULL); break; } /* No break we define 'normal' as 'local' in Object mode. */ @@ -529,7 +530,8 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene, * use the active pones axis for display #33575, this works as expected on a single * bone and users who select many bones will understand what's going on and what local * means when they start transforming. */ - ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat); + ED_getTransformOrientationMatrix( + scene, view_layer, v3d, ob, obedit, pivot_point, r_mat, NULL, NULL, NULL); } else { transform_orientations_create_from_axis(r_mat, UNPACK3(ob->object_to_world)); @@ -752,13 +754,18 @@ int getTransformOrientation_ex(const Scene *scene, struct Object *obedit, float normal[3], float plane[3], - const short around) + const short around, + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa) { int result = ORIENTATION_NONE; const bool activeOnly = (around == V3D_AROUND_ACTIVE); - zero_v3(normal); - zero_v3(plane); + if (efa == NULL && eed == NULL) { + zero_v3(normal); + zero_v3(plane); + } if (obedit) { float imat[3][3], mat[3][3]; @@ -777,20 +784,72 @@ int getTransformOrientation_ex(const Scene *scene, float vec[3] = {0, 0, 0}; /* USE LAST SELECTED WITH ACTIVE */ - if (activeOnly && BM_select_history_active_get(em->bm, &ese)) { - BM_editselection_normal(&ese, normal); - BM_editselection_plane(&ese, plane); + if (efa != NULL || eed != NULL || eve != NULL || + activeOnly && BM_select_history_active_get(em->bm, &ese)) { + if (efa != NULL) { + copy_v3_v3(normal, efa->no); + BM_face_calc_tangent_auto(efa, plane); + result = ORIENTATION_FACE; + } + else if (eed != NULL) { + float eed_plane[3]; + float vec[3]; + add_v3_v3v3(normal, eed->v1->no, eed->v2->no); + sub_v3_v3v3(eed_plane, eed->v2->co, eed->v1->co); + cross_v3_v3v3(vec, normal, eed_plane); + cross_v3_v3v3(normal, eed_plane, vec); + normalize_v3(normal); - switch (ese.htype) { - case BM_VERT: - result = ORIENTATION_VERT; - break; - case BM_EDGE: - result = ORIENTATION_EDGE; - break; - case BM_FACE: - result = ORIENTATION_FACE; - break; + if (BM_edge_is_boundary(eed)) { + sub_v3_v3v3(plane, eed->l->v->co, eed->l->next->v->co); + } + else { + if (eed->v2->co[1] > eed->v1->co[1]) { + sub_v3_v3v3(plane, eed->v2->co, eed->v1->co); + } + else { + sub_v3_v3v3(plane, eed->v1->co, eed->v2->co); + } + } + normalize_v3(plane); + result = ORIENTATION_EDGE; + } + else if (eve != NULL) { + copy_v3_v3(normal, eve->no); + if (eve->e) { + float vert1v3[3] = {eve->e->v1->co[0], eve->e->v1->co[1], eve->e->v1->co[2]}; + float vert2v3[3] = {eve->e->v2->co[0], eve->e->v2->co[1], eve->e->v2->co[2]}; + sub_v3_v3v3(plane, vert2v3, vert1v3); + } + else { + if (eve->no[0] < 0.5f) { + vec[0] = 1.0f; + } + else if (eve->no[1] < 0.5f) { + vec[1] = 1.0f; + } + else { + vec[2] = 1.0f; + } + cross_v3_v3v3(plane, eve->no, vec); + } + normalize_v3(plane); + result = ORIENTATION_VERT; + } + else { + BM_editselection_normal(&ese, normal); + BM_editselection_plane(&ese, plane); + switch (ese.htype) { + case BM_VERT: + result = ORIENTATION_VERT; + break; + case BM_EDGE: + result = ORIENTATION_EDGE; + break; + case BM_FACE: + result = ORIENTATION_FACE; + break; + } } } else { @@ -1289,7 +1348,8 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3]) ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); - return getTransformOrientation_ex(scene, view_layer, v3d, obact, obedit, normal, plane, around); + return getTransformOrientation_ex( + scene, view_layer, v3d, obact, obedit, normal, plane, around, NULL, NULL, NULL); } void ED_getTransformOrientationMatrix(const Scene *scene, @@ -1298,14 +1358,32 @@ void ED_getTransformOrientationMatrix(const Scene *scene, Object *ob, Object *obedit, const short around, - float r_orientation_mat[3][3]) + float r_orientation_mat[3][3], + struct BMVert *eve, + struct BMEdge *eed, + struct BMFace *efa) { float normal[3] = {0.0, 0.0, 0.0}; float plane[3] = {0.0, 0.0, 0.0}; int type; - type = getTransformOrientation_ex(scene, view_layer, v3d, ob, obedit, normal, plane, around); + if (efa != NULL) { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, NULL, NULL, efa); + } + else if (eed != NULL) { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, NULL, eed, NULL); + } + else if (eve != NULL) { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, eve, NULL, NULL); + } + else { + type = getTransformOrientation_ex( + scene, view_layer, v3d, ob, obedit, normal, plane, around, NULL, NULL, NULL); + } /* Fallback, when the plane can't be calculated. */ if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) { diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 6897b171d8b..b4951e7aab6 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3947,8 +3947,12 @@ static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, if (UI_view2d_view_to_region_segment_clip( ®ion->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && - BLI_lasso_is_edge_inside( - mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) { + BLI_lasso_is_edge_inside(mcoords, + mcoords_len, + UNPACK2(co_screen_a), + UNPACK2(co_screen_b), + V2D_IS_CLIPPED, + false)) { return true; } return false; diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 6cc01d254ce..c332d479da3 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -366,6 +366,25 @@ /* UV painting */ \ .uv_sculpt_settings = 0, \ .uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN, \ + \ + /* X-Ray */ \ + .xray_button = true, \ + .auto_xray_object = true, \ + .auto_xray_edit = true, \ + .auto_xray_box = true, \ + .auto_xray_lasso = true, \ + .auto_xray_circle = true, \ + .select_through = true, \ + .select_through_object = true, \ + .select_through_box = true, \ + .select_through_lasso = true, \ + .select_through_circle = true, \ + \ + /* Viewport-Facing Select */ \ + .viewport_facing_select_mode = 1, \ + .viewport_facing_select_vert = 1, \ + .viewport_facing_select_edge = 1, \ + .viewport_facing_select_face = 1, \ } /* clang-format off */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 6fb38eae744..7164759a632 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1557,7 +1557,35 @@ typedef struct ToolSettings { char gpencil_v3d_align; /** General 2D Editor. */ char gpencil_v2d_align; - char _pad0[2]; + + /* Mesh Normal Direction Select */ + char viewport_facing_select; + char viewport_facing_select_mode; + char viewport_facing_select_vert; + float viewport_facing_select_threshold; + char viewport_facing_select_edge; + char viewport_facing_select_face; + + /* X-Ray Options */ + char xray_button; + char auto_xray; + char auto_xray_button; + char auto_xray_reset; + char auto_xray_object; + char auto_xray_edit; + char auto_xray_box; + char auto_xray_lasso; + char auto_xray_circle; + char select_through; + char select_through_button; + char select_through_object; + char select_through_edit; + char select_through_box; + char select_through_lasso; + char select_through_circle; + + /* Combine shading and xray header buttons */ + char shrink_shading_header; /* Annotations. */ /** Stroke placement settings - 3D View. */ @@ -2331,6 +2359,71 @@ typedef enum eSnapTransformMode { SCE_SNAP_TRANSFORM_MODE_SCALE = (1 << 2), } eSnapTransformMode; +/** #Viewport-facing select mode */ +enum { + VIEWPORT_FACING_SELECT_BOTH = (1 << 0), + VIEWPORT_FACING_SELECT_NEAR = (1 << 1), + VIEWPORT_FACING_SELECT_XRAY = (1 << 2), +}; + +/** #Viewport-facing select vert */ +enum { + VIEWPORT_FACING_SELECT_FRONT_VERTS = (1 << 0), + VIEWPORT_FACING_SELECT_FRONT_VERTS_FACE = (1 << 1), + VIEWPORT_FACING_SELECT_REAR_VERTS = (1 << 2), + VIEWPORT_FACING_SELECT_REAR_VERTS_FACE = (1 << 3), + VIEWPORT_FACING_SELECT_ALL_VERTS = (1 << 4), +}; + +/** #Viewport-facing select edge */ +enum { + VIEWPORT_FACING_SELECT_FRONT_EDGES = (1 << 0), + VIEWPORT_FACING_SELECT_FRONT_EDGES_FACE = (1 << 1), + VIEWPORT_FACING_SELECT_REAR_EDGES = (1 << 2), + VIEWPORT_FACING_SELECT_REAR_EDGES_FACE = (1 << 3), + VIEWPORT_FACING_SELECT_ALL_EDGES = (1 << 4), +}; + +/** #Viewport-facing select face */ +enum { + VIEWPORT_FACING_SELECT_FRONT_FACES = (1 << 0), + VIEWPORT_FACING_SELECT_FRONT_FACES_VERT = (1 << 1), + VIEWPORT_FACING_SELECT_REAR_FACES = (1 << 2), + VIEWPORT_FACING_SELECT_REAR_FACES_VERT = (1 << 3), + VIEWPORT_FACING_SELECT_ALL_FACES = (1 << 4), +}; + +/** #Face select style */ +enum { + FACE_DEFAULT = (1 << 0), + FACE_TOUCH = (1 << 1), + FACE_ENCLOSE = (1 << 2), + FACE_CENTER = (1 << 3), +}; + +/** #Edge select style */ +enum { + EDGE_DEFAULT = (1 << 0), + EDGE_TOUCH = (1 << 1), + EDGE_ENCLOSE = (1 << 2), +}; + +/** #Auto X-Ray mode */ +enum { + AUTO_XRAY_DISABLE = (1 << 0), + AUTO_XRAY_OBJECT = (1 << 1), + AUTO_XRAY_EDIT = (1 << 2), + AUTO_XRAY_BOTH = (1 << 3), +}; + +/** #Select through mode */ +enum { + SELECT_THROUGH_DISABLE = (1 << 0), + SELECT_THROUGH_OBJECT = (1 << 1), + SELECT_THROUGH_EDIT = (1 << 2), + SELECT_THROUGH_BOTH = (1 << 3), +}; + /** #ToolSettings.selectmode */ #define SCE_SELECT_VERTEX (1 << 0) /* for mesh */ #define SCE_SELECT_EDGE (1 << 1) diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 01b2c0464ce..b1709d9589d 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -800,7 +800,9 @@ typedef struct UserDef { char gizmo_size; /** Navigate gizmo size. */ char gizmo_size_navigate_v3d; - char _pad3[5]; + /** Header highlight */ + char header_highlight; + char _pad3[4]; short edit_studio_light; short lookdev_sphere_size; short vbotimeout, vbocollectrate; @@ -840,7 +842,9 @@ typedef struct UserDef { /** #eGPUBackendType */ short gpu_backend; - char _pad7[4]; + /** Keymap click-drag direction style. */ + char click_drag_direction; + char _pad7[3]; /** Private, defaults to 20 for 72 DPI setting. */ short widget_unit; @@ -900,6 +904,15 @@ typedef struct UserDef { char drag_threshold; char move_threshold; + /** Adjustable selection radius. */ + float selection_radius; + char adjustable_click_select; + char select_unbiased; + + /** Edit mode cursor. **/ + char edit_cursor; + char _pad9[1]; + char font_path_ui[1024]; char font_path_ui_mono[1024]; @@ -1002,8 +1015,8 @@ typedef enum eUserPref_SpaceData_Flag { typedef enum eUserPref_Flag { USER_AUTOSAVE = (1 << 0), USER_FLAG_NUMINPUT_ADVANCED = (1 << 1), - USER_FLAG_UNUSED_2 = (1 << 2), /* cleared */ - USER_FLAG_UNUSED_3 = (1 << 3), /* cleared */ + USER_ADJUSTABLE_CLICK_SELECT = (1 << 2), + USER_SELECT_UNBIASED = (1 << 3), USER_FLAG_UNUSED_4 = (1 << 4), /* cleared */ USER_TRACKBALL = (1 << 5), USER_FLAG_UNUSED_6 = (1 << 6), /* cleared */ @@ -1153,6 +1166,26 @@ typedef enum eUserpref_TableAPI { USER_TABLET_WINTAB = 2, } eUserpref_TabletAPI; +/** #UserDef.click_drag_direction_types */ +typedef enum eUserpref_Click_Drag_Direction { + USER_CLICK_DRAG_DIRECTION_EIGHT_WAY = 0, + USER_CLICK_DRAG_DIRECTION_LEFT_RIGHT = 1, + USER_CLICK_DRAG_DIRECTION_UP_DOWN = 2, +} eUserpref_Click_Drag_Direction; + +/** #UserDef.edit_cursor_types */ +typedef enum eUserpref_Edit_Cursor_Types { + USER_EDIT_CURSOR_CROSS = 0, + USER_EDIT_CURSOR_SYSTEM = 1, + USER_EDIT_CURSOR_POINTER = 2, + USER_EDIT_CURSOR_CROSS_OPEN = 3, + USER_EDIT_CURSOR_CROSS_DOT = 4, + USER_EDIT_CURSOR_CROSS_MIN = 5, + USER_EDIT_CURSOR_BOX = 6, + USER_EDIT_CURSOR_BOX_DOT = 7, + USER_EDIT_CURSOR_BOX_POINTER = 8, +} eUserpref_Edit_Cursor_Types; + /** #UserDef.app_flag */ typedef enum eUserpref_APP_Flag { USER_APP_LOCK_CORNER_SPLIT = (1 << 0), diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index fb70fe982ba..cb3ef48293f 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -588,6 +588,7 @@ enum { V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21), V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22), + V3D_OVERLAY_EDIT_FACE_DOT_XRAY = (1 << 23), }; /** #View3DOverlay.paint_flag */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 078f7389fbd..4b706461e6b 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3057,10 +3057,153 @@ static void rna_def_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem viewport_facing_select_mode_items[] = { + {VIEWPORT_FACING_SELECT_BOTH, + "BOTH", + 0, + "Near and X-Ray", + "Use viewport-facing selection in near select and X-Ray"}, + {VIEWPORT_FACING_SELECT_NEAR, + "NEAR", + 0, + "Near", + "Use viewport-facing selection in near select"}, + {VIEWPORT_FACING_SELECT_XRAY, + "XRAY", + 0, + "X-Ray", + "Use viewport-facing selection in X-Ray"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_vert_items[] = { + {VIEWPORT_FACING_SELECT_FRONT_VERTS, + "FRONT_VERTS", + 0, + "Front Verts", + "Select vertices with viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_FRONT_VERTS_FACE, + "FRONT_VERTS_FACE", + 0, + "Verts of Front Face", + "Select vertices if they are part of a face that has a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_REAR_VERTS, + "REAR_VERTS", + 0, + "Rear Verts", + "Select vertices without viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_REAR_VERTS_FACE, + "REAR_VERTS_FACE", + 0, + "Verts of Rear Face", + "Select vertices if they are part of a face that does not have a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_ALL_VERTS, + "ALL_VERTS", + 0, + "All Verts", + "Select vertices regarless of their normal direction"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_edge_items[] = { + {VIEWPORT_FACING_SELECT_FRONT_EDGES, + "FRONT_EDGES", + 0, + "Front Edges", + "Select edges with viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_FRONT_EDGES_FACE, + "FRONT_EDGES_FACE", + 0, + "Edges of Front Face", + "Select edges if they are part of a face that has a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_REAR_EDGES, + "REAR_EDGES", + 0, + "Rear Edges", + "Select edges without viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_REAR_EDGES_FACE, + "REAR_EDGES_FACE", + 0, + "Edges of Rear Face", + "Select edges if they are part of a face that does not have a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_ALL_EDGES, + "ALL_EDGES", + 0, + "All Edges", + "Select edges regarless of their normal direction"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem viewport_facing_select_face_items[] = { + {VIEWPORT_FACING_SELECT_FRONT_FACES, + "FRONT_FACES", + 0, + "Front Faces", + "Select faces with viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_FRONT_FACES_VERT, + "FRONT_FACES_VERT", + 0, + "Faces of Front Vert", + "Select faces if they have a vertex with a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_REAR_FACES, + "REAR_FACES", + 0, + "Rear Faces", + "Select faces without viewport-facing normals"}, + {VIEWPORT_FACING_SELECT_REAR_FACES_VERT, + "REAR_FACES_VERT", + 0, + "Faces of Rear Vert", + "Select faces if they have a vertex without a viewport-facing normal"}, + {VIEWPORT_FACING_SELECT_ALL_FACES, + "ALL_FACES", + 0, + "All Faces", + "Select faces regarless of their normal direction"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "ToolSettings", NULL); RNA_def_struct_path_func(srna, "rna_ToolSettings_path"); RNA_def_struct_ui_text(srna, "Tool Settings", ""); + /* Viewport-Facing Select */ + prop = RNA_def_property(srna, "viewport_facing_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "viewport_facing_select", 0); + RNA_def_property_ui_text( + prop, + "Viewport Facing Select", + "Filter box, lasso, and circle selection of mesh elements based on the direction of their " + "normals compared to the viewport"); + + prop = RNA_def_property(srna, "viewport_facing_select_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_mode"); + RNA_def_property_enum_items(prop, viewport_facing_select_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Which selection modes to use viewport-facing selection"); + + prop = RNA_def_property(srna, "viewport_facing_select_vert", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_vert"); + RNA_def_property_enum_items(prop, viewport_facing_select_vert_items); + RNA_def_property_ui_text(prop, "Vert", "Direction and mode for vertices"); + + prop = RNA_def_property(srna, "viewport_facing_select_edge", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_edge"); + RNA_def_property_enum_items(prop, viewport_facing_select_edge_items); + RNA_def_property_ui_text(prop, "Edge", "Direction and mode for edges"); + + prop = RNA_def_property(srna, "viewport_facing_select_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "viewport_facing_select_face"); + RNA_def_property_enum_items(prop, viewport_facing_select_face_items); + RNA_def_property_ui_text(prop, "Face", "Direction and mode for faces"); + + prop = RNA_def_property(srna, "viewport_facing_select_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1.0f, 2); + RNA_def_property_ui_text( + prop, + "Threshold", + "How close the angles of the viewport and mesh element need to be for selection to occur"); + prop = RNA_def_property(srna, "sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Sculpt"); RNA_def_property_ui_text(prop, "Sculpt", ""); @@ -3733,6 +3876,102 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Normal Vector", "Normal Vector used to copy, add or multiply"); RNA_def_property_ui_range(prop, -10000.0, 10000.0, 1, 3); + /* Shrink Shading Header */ + prop = RNA_def_property(srna, "shrink_shading_header", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrink_shading_header", 0); + RNA_def_property_ui_text( + prop, "Shrink Header", "Combine the four Shading Header buttons into one button that also toggles X-Ray"); + + /* X-Ray header button */ + prop = RNA_def_property(srna, "xray_button", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "xray_button", 0); + RNA_def_property_ui_text( + prop, "X-Ray", "Show button for X-Ray in viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* Auto X-Ray */ + prop = RNA_def_property(srna, "auto_xray", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray", 0); + RNA_def_property_ui_text(prop, "Enable", "Transparent scene display during drag select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_button", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_button", 0); + RNA_def_property_ui_text( + prop, "Auto X-Ray", "Show button for automatic X-Ray in viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_reset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_reset", 0); + RNA_def_property_ui_text(prop, "Auto X-Ray Reset", "Helper that turns xray off for autoxray"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_object", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_object", 0); + RNA_def_property_ui_text(prop, "Object", "Automatic X-Ray in object mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_edit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_edit", 0); + RNA_def_property_ui_text(prop, "Edit", "Automatic X-Ray in edit mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_box", 0); + RNA_def_property_ui_text(prop, "Box", "Transparent scene display during box select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_lasso", 0); + RNA_def_property_ui_text(prop, "Lasso", "Transparent scene display during lasso select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "auto_xray_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "auto_xray_circle", 0); + RNA_def_property_ui_text(prop, "Circle", "Transparent scene display during circle select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* Select Through */ + prop = RNA_def_property(srna, "select_through", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through", 0); + RNA_def_property_ui_text( + prop, "Enable", "Select occluded objects and mesh elements with drag select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_button", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_button", 0); + RNA_def_property_ui_text( + prop, "Select Through", "Show button for select through in viewport header"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_object", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_object", 0); + RNA_def_property_ui_text(prop, "Object", "Select through in object mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_edit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_edit", 0); + RNA_def_property_ui_text(prop, "Edit", "Select through in edit mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_box", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_box", 0); + RNA_def_property_ui_text( + prop, "Box", "Select occluded objects and mesh elements with box select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_lasso", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_lasso", 0); + RNA_def_property_ui_text( + prop, "Lasso", "Select occluded objects and mesh elements with lasso select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "select_through_circle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "select_through_circle", 0); + RNA_def_property_ui_text( + prop, "Circle", "Select occluded objects and mesh elements with circle select"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* Unified Paint Settings */ prop = RNA_def_property(srna, "unified_paint_settings", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8edeb6dd29d..caf122f3a8b 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4566,6 +4566,15 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) "Display face center when face selection is enabled in solid shading modes"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_face_center_xray", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.edit_flag", V3D_OVERLAY_EDIT_FACE_DOT_XRAY); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text( + prop, + "Display Face Center X-Ray", + "Display face center when face selection is enabled in X-Ray shading modes"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_edge_crease", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay.edit_flag", V3D_OVERLAY_EDIT_CREASES); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 46be0067071..f5388dd3710 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4999,6 +4999,19 @@ static void rna_def_userdef_edit(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem edit_cursor_items[] = { + {USER_EDIT_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {USER_EDIT_CURSOR_SYSTEM, "SYSTEM", 0, "System", ""}, + {USER_EDIT_CURSOR_POINTER, "POINTER", 0, "Pointer", ""}, + {USER_EDIT_CURSOR_CROSS_OPEN, "CROSS_OPEN", 0, "Cross Open", ""}, + {USER_EDIT_CURSOR_CROSS_DOT, "CROSS_DOT", 0, "Cross Dot", ""}, + {USER_EDIT_CURSOR_CROSS_MIN, "CROSS_MIN", 0, "Cross Minimal", ""}, + {USER_EDIT_CURSOR_BOX, "BOX", 0, "Box", ""}, + {USER_EDIT_CURSOR_BOX_DOT, "BOX_DOT", 0, "Box Dot", ""}, + {USER_EDIT_CURSOR_BOX_POINTER, "BOX_POINTER", 0, "Box Pointer", ""}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "PreferencesEdit", NULL); RNA_def_struct_sdna(srna, "UserDef"); RNA_def_struct_nested(brna, srna, "Preferences"); @@ -5310,6 +5323,21 @@ static void rna_def_userdef_edit(BlenderRNA *brna) prop, "Auto-offset Margin", "Minimum distance between nodes for Auto-offsetting nodes"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "adjustable_click_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_ADJUSTABLE_CLICK_SELECT); + RNA_def_property_ui_text( + prop, "Enable", "Use additional options for single-click select"); + + prop = RNA_def_property(srna, "select_unbiased", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_SELECT_UNBIASED); + RNA_def_property_ui_text( + prop, "Select Unbiased", "Click-select will not favor unselected mesh elements"); + + prop = RNA_def_property(srna, "selection_radius", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.1f, 150.0f); + RNA_def_property_ui_range(prop, 0.1f, 150.0f, 0.01f, 2); + RNA_def_property_ui_text(prop, "Radius", "Size of single-click selection radius"); + /* cursor */ prop = RNA_def_property(srna, "use_cursor_lock_adjust", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_LOCK_CURSOR_ADJUST); @@ -5322,6 +5350,11 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_DEPTH_CURSOR); RNA_def_property_ui_text( prop, "Cursor Surface Project", "Use the surface depth for cursor placement"); + + prop = RNA_def_property(srna, "edit_cursor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, edit_cursor_items); + RNA_def_property_enum_sdna(prop, NULL, "edit_cursor"); + RNA_def_property_ui_text(prop, "Edit Cursor", "Edit mode cursor style"); } static void rna_def_userdef_system(BlenderRNA *brna) @@ -5774,6 +5807,17 @@ static void rna_def_userdef_input(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem click_drag_direction_types[] = { + {USER_CLICK_DRAG_DIRECTION_EIGHT_WAY, + "EIGHT_WAY", + 0, + "Eight", + "Eight directions (N, NE, E, SE, S, SW, W, NW)"}, + {USER_CLICK_DRAG_DIRECTION_LEFT_RIGHT, "LEFT_RIGHT", 0, "Left Right", "Left and right"}, + {USER_CLICK_DRAG_DIRECTION_UP_DOWN, "UP_DOWN", 0, "Up Down", "Up and down"}, + {0, NULL, 0, NULL, NULL}, + }; + static const EnumPropertyItem view_zoom_styles[] = { {USER_ZOOM_CONTINUE, "CONTINUE", @@ -5880,6 +5924,11 @@ static void rna_def_userdef_input(BlenderRNA *brna) "Release Confirms", "Moving things with a mouse drag confirms when releasing the button"); + prop = RNA_def_property(srna, "click_drag_direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, click_drag_direction_types); + RNA_def_property_ui_text( + prop, "Keymap Drag Directions", "Style of click-drag direction the keymap will use"); + prop = RNA_def_property(srna, "use_numeric_input_advanced", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_FLAG_NUMINPUT_ADVANCED); RNA_def_property_ui_text(prop, @@ -5944,6 +5993,12 @@ static void rna_def_userdef_input(BlenderRNA *brna) "Number of pixels to before the cursor is considered to have moved " "(used for cycling selected items on successive clicks)"); + prop = RNA_def_property(srna, "header_highlight", PROP_INT, PROP_FACTOR); + RNA_def_property_range(prop, 0, 10); + RNA_def_property_ui_range(prop, 0, 10, 1, -1); + RNA_def_property_ui_text( + prop, "Header Highlight", "How much brighter the header of the active window is"); + /* tablet pressure curve */ prop = RNA_def_property(srna, "pressure_threshold_max", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 5d8bb533d7e..9952cfb1a57 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -389,6 +389,41 @@ static void rna_KeyMap_item_remove(wmKeyMap *km, ReportList *reports, PointerRNA RNA_POINTER_INVALIDATE(kmi_ptr); } +static void rna_keymap_set_direction_any(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = -1; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_left(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 7; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_right(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 3; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_up(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 1; + WM_keyconfig_update_tag(km, kmi); +} + +static void rna_keymap_set_direction_down(wmKeyMap *km, PointerRNA *kmi_ptr) +{ + wmKeyMapItem *kmi = kmi_ptr->data; + kmi->direction = 5; + WM_keyconfig_update_tag(km, kmi); +} + static PointerRNA rna_KeyMap_item_find_from_operator(ID *id, wmKeyMap *km, const char *idname, @@ -1197,6 +1232,31 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_any", "rna_keymap_set_direction_any"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_left", "rna_keymap_set_direction_left"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_right", "rna_keymap_set_direction_right"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_up", "rna_keymap_set_direction_up"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "set_direction_down", "rna_keymap_set_direction_down"); + parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); func = RNA_def_function(srna, "from_id", "WM_keymap_item_find_id"); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 0d970685582..6d4860623f9 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -688,6 +688,7 @@ int WM_operator_call_notest(struct bContext *C, struct wmOperator *op); * Execute this operator again, put here so it can share above code */ int WM_operator_repeat(struct bContext *C, struct wmOperator *op); +int WM_operator_invoke_last(struct bContext *C, struct wmOperator *op); int WM_operator_repeat_last(struct bContext *C, struct wmOperator *op); /** * \return true if #WM_operator_repeat can run. diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index c9624d0e14e..2baac067660 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -43,6 +43,7 @@ static BCursor *BlenderCursor[WM_CURSOR_NUM] = {0}; /* Blender cursor to GHOST standard cursor conversion. */ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) { + const int edit_style = U.edit_cursor; switch (curs) { case WM_CURSOR_DEFAULT: return GHOST_kStandardCursorDefault; @@ -50,7 +51,33 @@ static GHOST_TStandardCursor convert_to_ghost_standard_cursor(WMCursorType curs) return GHOST_kStandardCursorWait; case WM_CURSOR_EDIT: case WM_CURSOR_CROSS: - return GHOST_kStandardCursorCrosshair; + if (edit_style == 1) { + return GHOST_kStandardCursorDefault; + } + else if (edit_style == 2) { + return GHOST_kStandardCursorPointer; + } + else if (edit_style == 3) { + return GHOST_kStandardCursorCrosshairD; + } + else if (edit_style == 4) { + return GHOST_kStandardCursorCrosshairA; + } + else if (edit_style == 5) { + return GHOST_kStandardCursorCrosshairC; + } + else if (edit_style == 6) { + return GHOST_kStandardCursorBox; + } + else if (edit_style == 7) { + return GHOST_kStandardCursorBoxDot; + } + else if (edit_style == 8) { + return GHOST_kStandardCursorBoxPointer; + } + else { + return GHOST_kStandardCursorCrosshair; + } case WM_CURSOR_X_MOVE: return GHOST_kStandardCursorLeftRight; case WM_CURSOR_Y_MOVE: diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index d39a853ead1..59fa04430e2 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -272,29 +272,42 @@ int WM_event_drag_direction(const wmEvent *event) event->xy[1] - event->prev_press_xy[1], }; - int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); - int val = KM_DIRECTION_W; + const bool left_right = U.click_drag_direction & USER_CLICK_DRAG_DIRECTION_LEFT_RIGHT; + const bool up_down = U.click_drag_direction & USER_CLICK_DRAG_DIRECTION_UP_DOWN; + int theta = left_right ? + round_fl_to_int(atan2f(0.0f, (float)delta[0]) / (float)M_PI) : + up_down ? + round_fl_to_int(atan2f(0.0f, (float)delta[1]) / (float)M_PI) : + round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); + int val = up_down ? KM_DIRECTION_S : KM_DIRECTION_W; - if (theta == 0) { - val = KM_DIRECTION_E; + if (left_right || up_down) { + if (theta == 0) { + val = up_down ? KM_DIRECTION_N : KM_DIRECTION_E; + } } - else if (theta == 1) { - val = KM_DIRECTION_NE; - } - else if (theta == 2) { - val = KM_DIRECTION_N; - } - else if (theta == 3) { - val = KM_DIRECTION_NW; - } - else if (theta == -1) { - val = KM_DIRECTION_SE; - } - else if (theta == -2) { - val = KM_DIRECTION_S; - } - else if (theta == -3) { - val = KM_DIRECTION_SW; + else { + if (theta == 0) { + val = KM_DIRECTION_E; + } + else if (theta == 1) { + val = KM_DIRECTION_NE; + } + else if (theta == 2) { + val = KM_DIRECTION_N; + } + else if (theta == 3) { + val = KM_DIRECTION_NW; + } + else if (theta == -1) { + val = KM_DIRECTION_SE; + } + else if (theta == -2) { + val = KM_DIRECTION_S; + } + else if (theta == -3) { + val = KM_DIRECTION_SW; + } } #if 0 diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index cf779f1bdf9..c888f9a7692 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -1292,6 +1292,11 @@ int WM_operator_repeat(bContext *C, wmOperator *op) op->flag &= ~op_flag; return ret; } +int WM_operator_invoke_last(bContext *C, wmOperator *op) +{ + const int ret = WM_operator_name_call_ptr(C, op->type, WM_OP_INVOKE_DEFAULT, NULL, NULL); + return ret; +} int WM_operator_repeat_last(bContext *C, wmOperator *op) { const int op_flag = OP_IS_REPEAT_LAST; diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index ae8c446bd87..14c40af15f0 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -29,6 +29,7 @@ #include "ED_screen.h" #include "ED_select_utils.h" +#include "ED_view3d.h" #include "UI_interface.h" @@ -85,6 +86,18 @@ static void gesture_modal_state_to_operator(wmOperator *op, int modal_state) } } +static void gesture_toggle_xray(bContext *C) +{ + wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_toggle_xray", true); + ToolSettings *ts = CTX_data_tool_settings(C); + BLI_assert(ot); + PointerRNA ptr; + WM_operator_properties_create_ptr(&ptr, ot); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); + WM_operator_properties_free(&ptr); + ts->auto_xray_reset ^= true; +} + static int UNUSED_FUNCTION(gesture_modal_state_from_operator)(wmOperator *op) { PropertyRNA *prop; @@ -163,9 +176,26 @@ static bool gesture_box_apply(bContext *C, wmOperator *op) int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); const ARegion *region = CTX_wm_region(C); const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && RNA_boolean_get(op->ptr, "wait_for_input"); + + const bool auto_xray = ts->auto_xray && ts->auto_xray_box ? + obedit ? ts->auto_xray_edit : ts->auto_xray_object : + false; + + if (ts->auto_xray_reset) { + ts->auto_xray_reset ^= true; + } + + if (v3d && auto_xray) { + if (!XRAY_FLAG_ENABLED(v3d)) { + gesture_toggle_xray(C); + } + } if (wait_for_input) { op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT); @@ -190,8 +220,11 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); if (event->type == EVT_MODAL_MAP) { switch (event->val) { @@ -214,13 +247,22 @@ int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) gesture->modal_state = event->val; } if (gesture_box_apply(C, op)) { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_FINISHED; } + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } case GESTURE_MODAL_CANCEL: { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } @@ -286,8 +328,24 @@ static void gesture_circle_apply(bContext *C, wmOperator *op); int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && RNA_boolean_get(op->ptr, "wait_for_input"); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = win->scene->toolsettings; + const bool auto_xray = ts->auto_xray && ts->auto_xray_circle ? + obedit ? ts->auto_xray_edit : ts->auto_xray_object : + false; + + if (ts->auto_xray_reset) { + ts->auto_xray_reset ^= true; + } + + if (v3d && auto_xray) { + if (!XRAY_FLAG_ENABLED(v3d)) { + gesture_toggle_xray(C); + } + } op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE); wmGesture *gesture = op->customdata; @@ -345,11 +403,14 @@ static void gesture_circle_apply(bContext *C, wmOperator *op) int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); wmGesture *gesture = op->customdata; rcti *rect = gesture->customdata; - if (event->type == MOUSEMOVE) { + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = win->scene->toolsettings; + if (event->type == MOUSEMOVE) { rect->xmin = event->xy[0] - gesture->winrct.xmin; rect->ymin = event->xy[1] - gesture->winrct.ymin; @@ -415,6 +476,9 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (is_finished) { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_FINISHED; /* use finish or we don't get an undo */ } @@ -477,7 +541,24 @@ void WM_OT_circle_gesture(wmOperatorType *ot) int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); PropertyRNA *prop; + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = win->scene->toolsettings; + + const bool auto_xray = ts->auto_xray && ts->auto_xray_lasso ? + obedit ? ts->auto_xray_edit : ts->auto_xray_object : + false; + + if (ts->auto_xray_reset) { + ts->auto_xray_reset ^= true; + } + + if (v3d && auto_xray) { + if (!XRAY_FLAG_ENABLED(v3d)) { + gesture_toggle_xray(C); + } + } op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_LASSO); @@ -517,6 +598,10 @@ static int gesture_lasso_apply(bContext *C, wmOperator *op) int retval = OPERATOR_FINISHED; wmGesture *gesture = op->customdata; PointerRNA itemptr; + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + wmWindow *win = CTX_wm_window(C); + ToolSettings *ts = win->scene->toolsettings; float loc[2]; int i; const short *lasso = gesture->customdata; @@ -538,12 +623,20 @@ static int gesture_lasso_apply(bContext *C, wmOperator *op) OPERATOR_RETVAL_CHECK(retval); } + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } + return retval; } int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmGesture *gesture = op->customdata; + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + wmWindow *win = CTX_wm_window(C); + ToolSettings *ts = win->scene->toolsettings; if (event->type == EVT_MODAL_MAP) { switch (event->val) { @@ -597,6 +690,9 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } case EVT_ESCKEY: { + if (ts->auto_xray_reset) { + gesture_toggle_xray(C); + } gesture_modal_end(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index bd3322a8023..c2766a791aa 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -411,8 +411,60 @@ void WM_operator_properties_gesture_box_ex(wmOperatorType *ot, bool deselect, bo { PropertyRNA *prop; + static const EnumPropertyItem face_select_items[] = { + {FACE_DEFAULT, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_DEFAULT, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the selection area. If no edges are fully inside the " + "selection area, select edges that are touched by the selection area"}, + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + WM_operator_properties_border(ot); + prop = RNA_def_boolean(ot->srna, "select_origin_box", false, "Select Object Origin", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + if (deselect) { prop = RNA_def_boolean( ot->srna, "deselect", false, "Deselect", "Deselect rather than select items"); @@ -519,6 +571,57 @@ void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot) void WM_operator_properties_gesture_lasso(wmOperatorType *ot) { PropertyRNA *prop; + static const EnumPropertyItem face_select_items[] = { + {FACE_DEFAULT, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_DEFAULT, + "EDGE_DEFAULT", + 0, + "Default", + "Select edges that are fully inside the selection area. If no edges are fully inside the " + "selection area, select edges that are touched by the selection area"}, + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + WM_operator_properties_border(ot); + + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -557,6 +660,45 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot) PropertyRNA *prop; const int radius_default = 25; + static const EnumPropertyItem face_select_items[] = { + {FACE_DEFAULT, + "FACE_DEFAULT", + 0, + "Default", + "Select faces that are touched by the selection area in near select. Select faces whose " + "center is touched by the selection area in X-Ray select"}, + {FACE_TOUCH, + "FACE_TOUCH", + 0, + "Touch", + "Select faces that are touched by the selection area"}, + {FACE_ENCLOSE, + "FACE_ENCLOSE", + 0, + "Enclose", + "Select faces that are fully inside the selection area"}, + {FACE_CENTER, + "FACE_CENTER", + 0, + "Center", + "Select faces whose center is touched by the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem edge_select_items[] = { + {EDGE_TOUCH, + "EDGE_TOUCH", + 0, + "Touch", + "Select edges that are touched by the selection area"}, + {EDGE_ENCLOSE, + "EDGE_ENCLOSE", + 0, + "Enclose", + "Select edges that are fully inside the selection area"}, + {0, NULL, 0, NULL, NULL}, + }; + prop = RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); @@ -565,6 +707,13 @@ void WM_operator_properties_gesture_circle(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "select_origin_circle", true, "Select Object Origin", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "face_type", face_select_items, 0, "Face Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "edge_type", edge_select_items, 0, "Edge Select", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } void WM_operator_properties_mouse_select(wmOperatorType *ot)