UI: Select Through Option #109319

Open
Lukas Sneyd wants to merge 11 commits from lcas/blender:select-through into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 349 additions and 83 deletions

View File

@ -920,7 +920,7 @@ class VIEW3D_HT_header(Header):
sub.active = overlay.show_overlays
sub.popover(panel="VIEW3D_PT_overlay", text="")
row = layout.row()
row = layout.row(align=True)
row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'})
# While exposing `shading.show_xray(_wireframe)` is correct.
@ -937,6 +937,7 @@ class VIEW3D_HT_header(Header):
icon='XRAY',
depress=draw_depressed,
)
row.popover(panel="VIEW3D_PT_xray", text="")
row = layout.row(align=True)
row.prop(shading, "type", text="", expand=True)
@ -6361,17 +6362,7 @@ class VIEW3D_PT_shading_options(Panel):
row = col.row(align=True)
if shading.type == 'WIREFRAME':
row.prop(shading, "show_xray_wireframe", text="")
sub = row.row()
sub.active = shading.show_xray_wireframe
sub.prop(shading, "xray_alpha_wireframe", text="X-Ray")
elif shading.type == 'SOLID':
row.prop(shading, "show_xray", text="")
sub = row.row()
sub.active = shading.show_xray
sub.prop(shading, "xray_alpha", text="X-Ray")
# X-ray mode is off when alpha is 1.0
if shading.type == 'SOLID':
xray_active = shading.show_xray and shading.xray_alpha != 1
row = col.row(align=True)
@ -6556,6 +6547,56 @@ class VIEW3D_PT_gizmo_display(Panel):
col.prop(view, "show_gizmo_camera_dof_distance", text="Focus Distance")
class VIEW3D_PT_xray(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "X-Ray"
bl_ui_units_x = 13
def draw(self, context):
layout = self.layout
layout.label(text="X-Ray Settings")
shading = VIEW3D_PT_shading.get_shading(context)
col = layout.column()
row = col.row(align=True)
if shading.type == 'WIREFRAME':
row.prop(shading, "show_xray_wireframe", text="")
sub = row.row()
sub.active = shading.show_xray_wireframe
sub.prop(shading, "xray_alpha_wireframe", text="X-Ray Wireframe")
elif shading.type == 'SOLID':
row.prop(shading, "show_xray", text="")
sub = row.row()
sub.active = shading.show_xray
sub.prop(shading, "xray_alpha", text="X-Ray Solid")
class VIEW3D_PT_select_through(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_parent_id = 'VIEW3D_PT_xray'
bl_label = "Select Through"
bl_ui_units_x = 10
def draw(self, context):
layout = self.layout
tool_settings = context.tool_settings
row = layout.row()
row.prop(tool_settings, "select_through", text="Enable")
sub = row.row()
sub.active = tool_settings.select_through
sub.prop(tool_settings, "select_through_object", text="Object")
sub.prop(tool_settings, "select_through_edit", text="Edit")
row = layout.row()
sub = row.row(align=True)
sub.active = tool_settings.select_through
sub.prop(tool_settings, "select_through_box", text="Box", toggle=True)
sub.prop(tool_settings, "select_through_lasso", text="Lasso", toggle=True)
sub.prop(tool_settings, "select_through_circle", text="Circle", toggle=True)
class VIEW3D_PT_overlay(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
@ -8541,6 +8582,8 @@ classes = (
VIEW3D_PT_shading_render_pass,
VIEW3D_PT_shading_compositor,
VIEW3D_PT_gizmo_display,
VIEW3D_PT_xray,
VIEW3D_PT_select_through,
VIEW3D_PT_overlay,
VIEW3D_PT_overlay_guides,
VIEW3D_PT_overlay_object,

View File

@ -377,6 +377,13 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
if (idprop) {
IDP_ClearProperty(idprop);
}
/* Select through */
ts->select_through = true;
ts->select_through_object = true;
ts->select_through_box = true;
ts->select_through_lasso = true;
ts->select_through_circle = true;
}
void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)

View File

@ -569,42 +569,219 @@ 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,
int circle_data[3])
{
View3D *v3d = vc->v3d;
bool changed = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
int totobj = MAXPICKELEMS; /* XXX solve later */
/* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */
GPUSelectResult *buffer = static_cast<GPUSelectResult *>(
MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__));
ToolSettings *ts = vc->scene->toolsettings;
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<Base *> 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<Base *>(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 ?
(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;
}
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;
}
@ -834,7 +1011,7 @@ 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 use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !(ts->select_through && ts->select_through_edit && ts->select_through_lasso);
EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (use_zbuf) {
@ -1379,7 +1556,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, NULL);
if (changed_multi) {
ED_outliner_select_sync_from_object_tag(C);
}
@ -2195,40 +2372,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,
@ -3819,7 +3962,8 @@ 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 use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) &&
!(ts->select_through && ts->select_through_edit && ts->select_through_box);
EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (use_zbuf) {
@ -4040,14 +4184,24 @@ static bool do_object_box_select(bContext *C,
{
View3D *v3d = vc->v3d;
int totobj = MAXPICKELEMS; /* XXX solve later */
ToolSettings *ts = vc->scene->toolsettings;
/* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */
GPUSelectResult *buffer = static_cast<GPUSelectResult *>(
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);
int hits = 0;
if (XRAY_FLAG_ENABLED(vc->v3d) ||
ts->select_through && ts->select_through_object && ts->select_through_box) {
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;
@ -4479,7 +4633,7 @@ static bool mesh_circle_select(ViewContext *vc,
view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d) && !(ts->select_through && ts->select_through_edit && ts->select_through_circle);
if (use_zbuf) {
if (wm_userdata->data == nullptr) {
@ -5243,9 +5397,14 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
const eSelectOp sel_op = ED_select_op_modal(
static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture));
ED_view3d_viewcontext_init(C, &vc, depsgraph);
ToolSettings *ts = vc.scene->toolsettings;
bool default_select = ts->select_through && ts->select_through_object && ts->select_through_circle;
if (!default_select) {
BKE_object_update_select_id(CTX_data_main(C));
}
Object *obact = vc.obact;
Object *obedit = vc.obedit;
@ -5291,11 +5450,17 @@ 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);
if (default_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 {
int circle_data[3] = {mval[0], mval[1], radius};
do_lasso_select_objects(&vc, NULL, NULL, sel_op, circle_data);
}
}

View File

@ -398,6 +398,13 @@
/* Placement */ \
.snap_mode_tools = SCE_SNAP_TO_GEOM,\
.plane_axis = 2,\
\
/* Select through */ \
.select_through = true, \
.select_through_object = true, \
.select_through_box = true, \
.select_through_lasso = true, \
.select_through_circle = true, \
}
#define _DNA_DEFAULT_Sculpt \

View File

@ -1585,7 +1585,15 @@ typedef struct ToolSettings {
char gpencil_v3d_align;
/** General 2D Editor. */
char gpencil_v2d_align;
char _pad0[2];
/* Select Through */
char select_through;
char select_through_object;
char select_through_edit;
char select_through_box;
char select_through_lasso;
char select_through_circle;
char _pad0[4];
/* Annotations. */
/** Stroke placement settings - 3D View. */

View File

@ -3829,6 +3829,42 @@ 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);
/* 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, "Select Through", "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_object", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "select_through_object", 0);
RNA_def_property_ui_text(prop, "Select Through 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, "Select Through 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, "Select Through 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, "Select Through 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,
"Select Through 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);