/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2014 Blender Foundation. * All rights reserved. * * Contributor(s): Antony Riakiotakis. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/gpu/intern/gpu_select.c * \ingroup gpu * * Interface for accessing gpu-related methods for selection. The semantics will be * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility. */ #include #include "GPU_select.h" #include "GPU_extensions.h" #include "GPU_glew.h" #include "MEM_guardedalloc.h" #include "DNA_userdef_types.h" #include "BLI_utildefines.h" #include "gpu_select_private.h" /* Internal algorithm used */ enum { /** GL_SELECT, legacy OpenGL selection */ ALGO_GL_LEGACY = 1, /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c` * Only sets 4th component (ID) correctly. */ ALGO_GL_QUERY = 2, /** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c` * Only sets 4th component (ID) correctly. */ ALGO_GL_PICK = 3, }; typedef struct GPUSelectState { /* To ignore selection id calls when not initialized */ bool select_is_active; /* flag to cache user preference for occlusion based selection */ bool use_gpu_select; /* mode of operation */ char mode; /* internal algorithm for selection */ char algorithm; /* allow GPU_select_begin/end without drawing */ bool use_cache; } GPUSelectState; static GPUSelectState g_select_state = {0}; /** * initialize and provide buffer for results */ void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, const rcti *input, char mode, int oldhits) { g_select_state.select_is_active = true; g_select_state.use_gpu_select = GPU_select_query_check_active(); g_select_state.mode = mode; if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) { g_select_state.algorithm = ALGO_GL_PICK; } else if (!g_select_state.use_gpu_select) { g_select_state.algorithm = ALGO_GL_LEGACY; } else { g_select_state.algorithm = ALGO_GL_QUERY; } switch (g_select_state.algorithm) { case ALGO_GL_LEGACY: { g_select_state.use_cache = false; glSelectBuffer(bufsize, (GLuint *)buffer); glRenderMode(GL_SELECT); glInitNames(); glPushName(-1); break; } case ALGO_GL_QUERY: { g_select_state.use_cache = false; gpu_select_query_begin((unsigned int (*)[4])buffer, bufsize / 4, input, mode, oldhits); break; } default: /* ALGO_GL_PICK */ { gpu_select_pick_begin((unsigned int (*)[4])buffer, bufsize / 4, input, mode); break; } } } /** * loads a new selection id and ends previous query, if any. In second pass of selection it also returns * if id has been hit on the first pass already. * Thus we can skip drawing un-hit objects. * * \warning We rely on the order of object rendering on passes to be the same for this to work. */ bool GPU_select_load_id(unsigned int id) { /* if no selection mode active, ignore */ if (!g_select_state.select_is_active) return true; switch (g_select_state.algorithm) { case ALGO_GL_LEGACY: { glLoadName(id); return true; } case ALGO_GL_QUERY: { return gpu_select_query_load_id(id); } default: /* ALGO_GL_PICK */ { return gpu_select_pick_load_id(id); } } } /** * Cleanup and flush selection results to buffer. * Return number of hits and hits in buffer. * if \a dopass is true, we will do a second pass with occlusion queries to get the closest hit. */ unsigned int GPU_select_end(void) { unsigned int hits = 0; switch (g_select_state.algorithm) { case ALGO_GL_LEGACY: { glPopName(); hits = glRenderMode(GL_RENDER); break; } case ALGO_GL_QUERY: { hits = gpu_select_query_end(); break; } default: /* ALGO_GL_PICK */ { hits = gpu_select_pick_end(); break; } } g_select_state.select_is_active = false; return hits; } /** * has user activated? */ bool GPU_select_query_check_active(void) { return ((U.gpu_select_method == USER_SELECT_USE_OCCLUSION_QUERY) || ((U.gpu_select_method == USER_SELECT_AUTO) && (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) || /* unsupported by nouveau, gallium 0.4, see: T47940 */ GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)))); } /* ---------------------------------------------------------------------------- * Caching * * Support multiple begin/end's as long as they are within the initial region. * Currently only used by ALGO_GL_PICK. */ void GPU_select_cache_begin(void) { /* validate on GPU_select_begin, clear if not supported */ BLI_assert(g_select_state.use_cache == false); g_select_state.use_cache = true; if (g_select_state.algorithm == ALGO_GL_PICK) { gpu_select_pick_cache_begin(); } } void GPU_select_cache_load_id(void) { BLI_assert(g_select_state.use_cache == true); if (g_select_state.algorithm == ALGO_GL_PICK) { gpu_select_pick_cache_load_id(); } } void GPU_select_cache_end(void) { if (g_select_state.algorithm == ALGO_GL_PICK) { gpu_select_pick_cache_end(); } g_select_state.use_cache = false; } bool GPU_select_is_cached(void) { return g_select_state.use_cache && gpu_select_pick_is_cached(); }