3D View: x-ray support for depth picking

Selection loop would draw the selection ignoring xray.
Now draw in a separate pass after clearing the depth buffer,
as with regular drawing.

Also disable depth sorting,
caller can sort the hit-list by depth if needed.
This commit is contained in:
Campbell Barton 2017-03-10 03:15:58 +11:00
parent 9de9f25b24
commit 62cc226101
Notes: blender-bot 2023-02-14 08:49:53 +01:00
Referenced by issue #50902, `OpenGL Depth Picking` does not prioritize objects with X-Ray enabled
5 changed files with 170 additions and 85 deletions

View File

@ -8174,6 +8174,50 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
ED_view3d_clear_mats_rv3d(rv3d);
}
/**
* Drawing for selection picking,
* caller must have called 'GPU_select_load_id(base->selcode)' first.
*/
void draw_object_select(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short dflag)
{
BLI_assert(dflag & DRAW_PICKING && dflag & DRAW_CONSTCOLOR);
draw_object(scene, ar, v3d, base, dflag);
/* we draw duplicators for selection too */
if ((base->object->transflag & OB_DUPLI)) {
ListBase *lb;
DupliObject *dob;
Base tbase;
tbase.flag = OB_FROMDUPLI;
lb = object_duplilist(G.main->eval_ctx, scene, base->object);
for (dob = lb->first; dob; dob = dob->next) {
float omat[4][4];
char dt;
short dtx;
tbase.object = dob->ob;
copy_m4_m4(omat, dob->ob->obmat);
copy_m4_m4(dob->ob->obmat, dob->mat);
/* extra service: draw the duplicator in drawtype of parent */
/* MIN2 for the drawtype to allow bounding box objects in groups for lods */
dt = tbase.object->dt; tbase.object->dt = MIN2(tbase.object->dt, base->object->dt);
dtx = tbase.object->dtx; tbase.object->dtx = base->object->dtx;
draw_object(scene, ar, v3d, &tbase, dflag);
tbase.object->dt = dt;
tbase.object->dtx = dtx;
copy_m4_m4(dob->ob->obmat, omat);
}
free_object_duplilist(lb);
}
}
/* ***************** BACKBUF SEL (BBS) ********* */
static void bbs_obmode_mesh_verts__mapFunc(void *userData, int index, const float co[3],

View File

@ -100,6 +100,7 @@
#include "GPU_material.h"
#include "GPU_compositing.h"
#include "GPU_extensions.h"
#include "GPU_select.h"
#include "view3d_intern.h" /* own include */
@ -2023,6 +2024,35 @@ static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const
glDepthMask(GL_TRUE);
}
/* clears zbuffer and draws it over,
* note that in the select version we don't care about transparent flag as with regular drawing */
static void view3d_draw_xray_select(Scene *scene, ARegion *ar, View3D *v3d, bool *clear)
{
/* Not ideal, but we need to read from the previous depths before clearing
* otherwise we could have a function to load the depths after drawing.
*
* Clearing the depth buffer isn't all that common between drawing objects so accept this for now.
*/
if (U.gpu_select_pick_deph) {
GPU_select_load_id(-1);
}
View3DAfter *v3da;
if (*clear && v3d->zbuf) {
glClear(GL_DEPTH_BUFFER_BIT);
*clear = false;
}
v3d->xray = true;
while ((v3da = BLI_pophead(&v3d->afterdraw_xray))) {
if (GPU_select_load_id(v3da->base->selcol)) {
draw_object_select(scene, ar, v3d, v3da->base, v3da->dflag);
}
MEM_freeN(v3da);
}
v3d->xray = false;
}
/* *********************** */
/*
@ -2487,6 +2517,58 @@ void ED_view3d_draw_depth(Scene *scene, ARegion *ar, View3D *v3d, bool alphaover
U.obcenter_dia = obcenter_dia;
}
void ED_view3d_draw_select_loop(
ViewContext *vc, Scene *scene, View3D *v3d, ARegion *ar,
bool use_obedit_skip, bool use_nearest)
{
short code = 1;
const short dflag = DRAW_PICKING | DRAW_CONSTCOLOR;
if (vc->obedit && vc->obedit->type == OB_MBALL) {
draw_object(scene, ar, v3d, BASACT, dflag);
}
else if ((vc->obedit && vc->obedit->type == OB_ARMATURE)) {
/* if not drawing sketch, draw bones */
if (!BDR_drawSketchNames(vc)) {
draw_object(scene, ar, v3d, BASACT, dflag);
}
}
else {
Base *base;
for (base = scene->base.first; base; base = base->next) {
if (base->lay & v3d->lay) {
if ((base->object->restrictflag & OB_RESTRICT_SELECT) ||
(use_obedit_skip && (scene->obedit->data == base->object->data)))
{
base->selcol = 0;
}
else {
base->selcol = code;
if (use_nearest && (base->object->dtx & OB_DRAWXRAY)) {
ED_view3d_after_add(&v3d->afterdraw_xray, base, dflag);
}
else {
if (GPU_select_load_id(code)) {
draw_object_select(scene, ar, v3d, base, dflag);
}
}
code++;
}
}
}
if (use_nearest) {
bool xrayclear = true;
if (v3d->afterdraw_xray.first) {
view3d_draw_xray_select(scene, ar, v3d, &xrayclear);
}
}
}
}
typedef struct View3DShadow {
struct View3DShadow *next, *prev;
GPULamp *lamp;

View File

@ -143,6 +143,8 @@ void draw_motion_paths_cleanup(View3D *v3d);
/* drawobject.c */
void draw_object(Scene *scene, struct ARegion *ar, View3D *v3d, Base *base, const short dflag);
void draw_object_select(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short dflag);
bool draw_glsl_material(Scene *scene, struct Object *ob, View3D *v3d, const char dt);
void draw_object_instance(Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob, const char dt, int outline);
void draw_object_backbufsel(Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob);
@ -195,7 +197,11 @@ void draw_sim_debug_data(Scene *scene, View3D *v3d, ARegion *ar);
void view3d_main_region_draw(const struct bContext *C, struct ARegion *ar);
void ED_view3d_draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, bool alphaoverride);
void ED_view3d_draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d);
void ED_view3d_after_add(ListBase *lb, Base *base, const short dflag);
void ED_view3d_draw_select_loop(
ViewContext *vc, Scene *scene, View3D *v3d, ARegion *ar,
bool use_obedit_skip, bool use_nearest);
void ED_view3d_after_add(ListBase *lb, Base *base, const short dflag);\
void circf(float x, float y, float rad);
void circ(float x, float y, float rad);

View File

@ -1091,78 +1091,6 @@ void view3d_viewmatrix_set(Scene *scene, const View3D *v3d, RegionView3D *rv3d)
}
}
static void view3d_select_loop(ViewContext *vc, Scene *scene, View3D *v3d, ARegion *ar, bool use_obedit_skip)
{
short code = 1;
char dt;
short dtx;
if (vc->obedit && vc->obedit->type == OB_MBALL) {
draw_object(scene, ar, v3d, BASACT, DRAW_PICKING | DRAW_CONSTCOLOR);
}
else if ((vc->obedit && vc->obedit->type == OB_ARMATURE)) {
/* if not drawing sketch, draw bones */
if (!BDR_drawSketchNames(vc)) {
draw_object(scene, ar, v3d, BASACT, DRAW_PICKING | DRAW_CONSTCOLOR);
}
}
else {
Base *base;
v3d->xray = true; /* otherwise it postpones drawing */
for (base = scene->base.first; base; base = base->next) {
if (base->lay & v3d->lay) {
if ((base->object->restrictflag & OB_RESTRICT_SELECT) ||
(use_obedit_skip && (scene->obedit->data == base->object->data)))
{
base->selcol = 0;
}
else {
base->selcol = code;
if (GPU_select_load_id(code)) {
draw_object(scene, ar, v3d, base, DRAW_PICKING | DRAW_CONSTCOLOR);
/* we draw duplicators for selection too */
if ((base->object->transflag & OB_DUPLI)) {
ListBase *lb;
DupliObject *dob;
Base tbase;
tbase.flag = OB_FROMDUPLI;
lb = object_duplilist(G.main->eval_ctx, scene, base->object);
for (dob = lb->first; dob; dob = dob->next) {
float omat[4][4];
tbase.object = dob->ob;
copy_m4_m4(omat, dob->ob->obmat);
copy_m4_m4(dob->ob->obmat, dob->mat);
/* extra service: draw the duplicator in drawtype of parent */
/* MIN2 for the drawtype to allow bounding box objects in groups for lods */
dt = tbase.object->dt; tbase.object->dt = MIN2(tbase.object->dt, base->object->dt);
dtx = tbase.object->dtx; tbase.object->dtx = base->object->dtx;
draw_object(scene, ar, v3d, &tbase, DRAW_PICKING | DRAW_CONSTCOLOR);
tbase.object->dt = dt;
tbase.object->dtx = dtx;
copy_m4_m4(dob->ob->obmat, omat);
}
free_object_duplilist(lb);
}
}
code++;
}
}
}
v3d->xray = false; /* restore */
}
}
/**
* Optionally cache data for multiple calls to #view3d_opengl_select
*
@ -1200,6 +1128,7 @@ int view3d_opengl_select(
(is_pick_select == false) &&
(select_mode == VIEW3D_SELECT_PICK_NEAREST) &&
GPU_select_query_check_active());
const bool use_nearest = (is_pick_select && select_mode == VIEW3D_SELECT_PICK_NEAREST);
char gpu_select_mode;
@ -1256,7 +1185,7 @@ int view3d_opengl_select(
GPU_select_begin(buffer, bufsize, &rect, gpu_select_mode, 0);
view3d_select_loop(vc, scene, v3d, ar, use_obedit_skip);
ED_view3d_draw_select_loop(vc, scene, v3d, ar, use_obedit_skip, use_nearest);
hits = GPU_select_end();
@ -1264,7 +1193,7 @@ int view3d_opengl_select(
if (do_passes) {
GPU_select_begin(buffer, bufsize, &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
view3d_select_loop(vc, scene, v3d, ar, use_obedit_skip);
ED_view3d_draw_select_loop(vc, scene, v3d, ar, use_obedit_skip, use_nearest);
GPU_select_end();
}

View File

@ -91,6 +91,15 @@ static void rect_subregion_stride_calc(const rcti *src, const rcti *dst, SubRect
r_sub->skip = src_x - dst_x;
}
/**
* Ignore depth clearing as a change,
* only check if its been changed _and_ filled in (ignore clearing since XRAY does this).
*/
BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr)
{
return (*prev != *curr) && (*curr != DEPTH_MAX);
}
/* ----------------------------------------------------------------------------
* DepthBufCache
*
@ -142,17 +151,28 @@ static bool depth_buf_subrect_depth_any(
return false;
}
static bool depth_buf_rect_not_equal(
const DepthBufCache *rect_depth_a, const DepthBufCache *rect_depth_b,
static bool depth_buf_rect_depth_any_filled(
const DepthBufCache *rect_prev, const DepthBufCache *rect_curr,
unsigned int rect_len)
{
#if 0
return memcmp(rect_depth_a->buf, rect_depth_b->buf, rect_len * sizeof(depth_t)) != 0;
#else
const depth_t *prev = rect_prev->buf;
const depth_t *curr = rect_curr->buf;
for (unsigned int i = 0; i < rect_len; i++, curr++, prev++) {
if (depth_is_filled(prev, curr)) {
return true;
}
}
return false;
#endif
}
/**
* Both buffers are the same size, just check if the sub-rect contains any differences.
*/
static bool depth_buf_subrect_not_equal(
static bool depth_buf_subrect_depth_any_filled(
const DepthBufCache *rect_src, const DepthBufCache *rect_dst,
const SubRectStride *sub_rect)
{
@ -162,7 +182,7 @@ static bool depth_buf_subrect_not_equal(
for (unsigned int i = 0; i < sub_rect->span_len; i++) {
const depth_t *curr_end = curr + sub_rect->span;
for (; curr < curr_end; prev++, curr++) {
if (*prev != *curr) {
if (depth_is_filled(prev, curr)) {
return true;
}
}
@ -422,8 +442,11 @@ static void gpu_select_load_id_pass_nearest(const DepthBufCache *rect_prev, cons
if (id != SELECT_ID_NONE) {
unsigned int *id_ptr = ps->nearest.rect_id;
/* Check against DEPTH_MAX because XRAY will clear the buffer,
* so previously set values will become unset.
* In this case just leave those id's left as-is. */
#define EVAL_TEST() \
if (*curr != *prev) { \
if (depth_is_filled(prev, curr)) { \
*id_ptr = id; \
} ((void)0)
@ -472,7 +495,7 @@ bool gpu_select_pick_load_id(unsigned int id)
}
}
else {
if (depth_buf_rect_not_equal(ps->gl.rect_depth, ps->gl.rect_depth_test, rect_len)) {
if (depth_buf_rect_depth_any_filled(ps->gl.rect_depth, ps->gl.rect_depth_test, rect_len)) {
ps->gl.rect_depth_test->id = ps->gl.prev_id;
gpu_select_load_id_pass_nearest(ps->gl.rect_depth, ps->gl.rect_depth_test);
do_pass = true;
@ -623,16 +646,17 @@ unsigned int gpu_select_pick_end(void)
hits = -1;
}
else {
/* leave sorting up to the caller */
qsort(depth_data, depth_data_len, sizeof(DepthID), depth_cmp);
for (unsigned int i = 0; i < depth_data_len; i++) {
#ifdef DEBUG_PRINT
printf(" hit: %d: depth %u\n", depth_data[i].id, depth_data[i].depth);
printf(" hit: %u: depth %u\n", depth_data[i].id, depth_data[i].depth);
#endif
/* first 3 are dummy values */
g_pick_state.buffer[hits][0] = 1;
g_pick_state.buffer[hits][1] = 0x0;
g_pick_state.buffer[hits][2] = 0x0;
g_pick_state.buffer[hits][1] = 0x0; /* depth_data[i].depth; */ /* unused */
g_pick_state.buffer[hits][2] = 0x0; /* z-far is currently never used. */
g_pick_state.buffer[hits][3] = depth_data[i].id;
hits++;
}
@ -709,7 +733,7 @@ void gpu_select_pick_cache_load_id(void)
}
}
else {
if (depth_buf_subrect_not_equal(rect_depth, rect_depth->next, &ps->cache.sub_rect)) {
if (depth_buf_subrect_depth_any_filled(rect_depth, rect_depth->next, &ps->cache.sub_rect)) {
gpu_select_load_id_pass_nearest(rect_depth, rect_depth->next);
}
}