diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/draw/intern/draw_manager.c | 2 | ||||
-rw-r--r-- | source/blender/editors/include/ED_mesh.h | 6 | ||||
-rw-r--r-- | source/blender/editors/include/ED_select_buffer_utils.h | 35 | ||||
-rw-r--r-- | source/blender/editors/include/ED_view3d.h | 1 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_select.c | 45 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_draw_legacy.c | 2 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_select.c | 235 | ||||
-rw-r--r-- | source/blender/editors/util/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/editors/util/select_buffer_utils.c | 179 |
9 files changed, 461 insertions, 46 deletions
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 5c9f49e4ee5..ce37145ea85 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2779,7 +2779,7 @@ void DRW_draw_select_id_object(Scene *scene, world_clip_planes = rv3d->clip_local; } - initial_offset += 1; + BLI_assert(initial_offset > 0); switch (ob->type) { case OB_MESH: diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 4e6bf7d4df5..75e0f05d088 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -152,6 +152,12 @@ struct BMElem *EDBM_select_id_bm_elem_get(struct EDBMSelectID_Context *sel_id_ct const uint sel_id, uint *r_base_index); +uint EDBM_select_id_context_offset_for_object_elem(const struct EDBMSelectID_Context *sel_id_ctx, + int base_index, + char htype); + +uint EDBM_select_id_context_elem_len(const struct EDBMSelectID_Context *sel_id_ctx); + void EDBM_select_mirrored( struct BMEditMesh *em, const int axis, const bool extend, int *r_totmirr, int *r_totfail); void EDBM_automerge(struct Scene *scene, struct Object *ob, bool update, const char hflag); diff --git a/source/blender/editors/include/ED_select_buffer_utils.h b/source/blender/editors/include/ED_select_buffer_utils.h new file mode 100644 index 00000000000..54e4cdb6e4f --- /dev/null +++ b/source/blender/editors/include/ED_select_buffer_utils.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/** \file + * \ingroup editors + */ + +#ifndef __ED_SELECT_BUFFER_UTILS_H__ +#define __ED_SELECT_BUFFER_UTILS_H__ + +struct rcti; + +uint *ED_select_buffer_bitmap_from_rect(const uint bitmap_len, const struct rcti *rect); +uint *ED_select_buffer_bitmap_from_circle(const uint bitmap_len, + const int center[2], + const int radius); +uint *ED_select_buffer_bitmap_from_poly(const uint bitmap_len, + const int poly[][2], + const int poly_len, + const rcti *rect); + +#endif /* __ED_SELECT_BUFFER_UTILS_H__ */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 2b8f926fc2c..61d1670b88d 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -42,6 +42,7 @@ struct GPUFX; struct GPUFXSettings; struct GPUOffScreen; struct GPUViewport; +struct ID; struct ImBuf; struct MVert; struct Main; diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 372ff42f1c3..d6a32a6bd70 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -199,8 +199,19 @@ void EDBM_automerge(Scene *scene, Object *obedit, bool update, const char hflag) * \{ */ struct EDBMBaseOffset { - uint face; - uint edge; + /* For convenience only. */ + union { + uint offset; + uint face_start; + }; + union { + uint face; + uint edge_start; + }; + union { + uint edge; + uint vert_start; + }; uint vert; }; @@ -209,6 +220,8 @@ struct EDBMSelectID_Context { /** Borrow from caller (not freed). */ struct Base **bases; uint bases_len; + /** Total number of items `base_array_index_offsets[bases_len - 1].vert`. */ + uint base_array_index_len; }; static bool check_ob_drawface_dot(short select_mode, const View3D *v3d, char dt) @@ -234,7 +247,7 @@ static void edbm_select_pick_draw_bases(struct EDBMSelectID_Context *sel_id_ctx, Scene *scene_eval = (Scene *)DEG_get_evaluated_id(vc->depsgraph, &vc->scene->id); DRW_framebuffer_select_id_setup(vc->ar, true); - uint offset = 0; + uint offset = 1; for (uint base_index = 0; base_index < sel_id_ctx->bases_len; base_index++) { Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, sel_id_ctx->bases[base_index]->object); @@ -252,9 +265,12 @@ static void edbm_select_pick_draw_bases(struct EDBMSelectID_Context *sel_id_ctx, &base_ofs->edge, &base_ofs->face); + base_ofs->offset = offset; offset = base_ofs->vert; } + sel_id_ctx->base_array_index_len = offset; + DRW_framebuffer_select_id_release(vc->ar); } @@ -306,6 +322,29 @@ BMElem *EDBM_select_id_bm_elem_get(struct EDBMSelectID_Context *sel_id_ctx, } } +uint EDBM_select_id_context_offset_for_object_elem(const struct EDBMSelectID_Context *sel_id_ctx, + int base_index, + char htype) +{ + struct EDBMBaseOffset *base_ofs = &sel_id_ctx->base_array_index_offsets[base_index]; + if (htype == BM_VERT) { + return base_ofs->vert_start - 1; + } + if (htype == BM_EDGE) { + return base_ofs->edge_start - 1; + } + if (htype == BM_FACE) { + return base_ofs->face_start - 1; + } + BLI_assert(0); + return 0; +} + +uint EDBM_select_id_context_elem_len(const struct EDBMSelectID_Context *sel_id_ctx) +{ + return sel_id_ctx->base_array_index_len; +} + struct EDBMSelectID_Context *EDBM_select_id_context_create(ViewContext *vc, Base **bases, uint bases_len, diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index 3443f333234..c17eb5cef81 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -214,7 +214,7 @@ static void validate_object_select_id(struct Depsgraph *depsgraph, obact_eval, select_mode, false, - 0, + 1, &bm_vertoffs, &bm_wireoffs, &bm_solidoffs); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index a93c14fa1ef..4ea175985b9 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -41,6 +41,7 @@ #include "MEM_guardedalloc.h" #include "BLI_array.h" +#include "BLI_bitmap.h" #include "BLI_math.h" #include "BLI_lasso_2d.h" #include "BLI_rect.h" @@ -88,6 +89,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_select_buffer_utils.h" #include "ED_select_utils.h" #include "ED_sculpt.h" #include "ED_mball.h" @@ -179,20 +181,66 @@ static bool object_deselect_all_except(ViewLayer *view_layer, Base *b) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Internal Edit-Mesh Select Buffer Wrapper + * + * Avoid duplicate code when using edit-mode selection, + * actual logic is handled outside of this function. + * + * \note Currently this #EDBMSelectID_Context which is mesh specific + * however the logic could also be used for non-meshes too. + * + * \{ */ + +struct EditSelectBuf_Cache { + Base **bases; + uint bases_len; + struct EDBMSelectID_Context *sel_id_ctx; + BLI_bitmap *select_bitmap; +}; + +static void editselect_buf_cache_init(struct EditSelectBuf_Cache *esel, ViewContext *vc) +{ + esel->bases = BKE_view_layer_array_from_bases_in_edit_mode( + vc->view_layer, vc->v3d, &esel->bases_len); + esel->sel_id_ctx = EDBM_select_id_context_create( + vc, esel->bases, esel->bases_len, vc->scene->toolsettings->selectmode); + for (int i = 0; i < esel->bases_len; i++) { + esel->bases[i]->object->runtime.select_id = i; + } +} + +static void editselect_buf_cache_free(struct EditSelectBuf_Cache *esel) +{ + if (esel->sel_id_ctx) { + EDBM_select_id_context_destroy(esel->sel_id_ctx); + } + MEM_SAFE_FREE(esel->select_bitmap); + MEM_SAFE_FREE(esel->bases); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Internal Edit-Mesh Utilities * \{ */ -static bool edbm_backbuf_check_and_select_verts(BMEditMesh *em, const eSelectOp sel_op) +static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel, + Object *ob, + BMEditMesh *em, + const eSelectOp sel_op) { BMVert *eve; BMIter iter; - uint index = bm_wireoffs; bool changed = false; + const BLI_bitmap *select_bitmap = esel->select_bitmap; + uint index = EDBM_select_id_context_offset_for_object_elem( + esel->sel_id_ctx, ob->runtime.select_id, BM_VERT); + BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { 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 = EDBM_backbuf_check(index); + 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); if (sel_op_result != -1) { BM_vert_select_set(em->bm, eve, sel_op_result); @@ -204,17 +252,23 @@ static bool edbm_backbuf_check_and_select_verts(BMEditMesh *em, const eSelectOp return changed; } -static bool edbm_backbuf_check_and_select_edges(BMEditMesh *em, const eSelectOp sel_op) +static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel, + Object *ob, + BMEditMesh *em, + const eSelectOp sel_op) { BMEdge *eed; BMIter iter; - uint index = bm_solidoffs; bool changed = false; + const BLI_bitmap *select_bitmap = esel->select_bitmap; + uint index = EDBM_select_id_context_offset_for_object_elem( + esel->sel_id_ctx, ob->runtime.select_id, BM_EDGE); + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { 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 = EDBM_backbuf_check(index); + 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); if (sel_op_result != -1) { BM_edge_select_set(em->bm, eed, sel_op_result); @@ -226,17 +280,23 @@ static bool edbm_backbuf_check_and_select_edges(BMEditMesh *em, const eSelectOp return changed; } -static bool edbm_backbuf_check_and_select_faces(BMEditMesh *em, const eSelectOp sel_op) +static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel, + Object *ob, + BMEditMesh *em, + const eSelectOp sel_op) { BMFace *efa; BMIter iter; - uint index = 1; bool changed = false; + const BLI_bitmap *select_bitmap = esel->select_bitmap; + uint index = EDBM_select_id_context_offset_for_object_elem( + esel->sel_id_ctx, ob->runtime.select_id, BM_FACE); + 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 = EDBM_backbuf_check(index); + 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); if (sel_op_result != -1) { BM_face_select_set(em->bm, efa, sel_op_result); @@ -621,14 +681,26 @@ static void do_lasso_select_mesh__doSelectVert(void *userData, data->is_changed = true; } } -static void do_lasso_select_mesh__doSelectEdge_pass0( - void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) -{ - LassoSelectUserData *data = userData; +struct LassoSelectUserData_ForMeshEdge { + LassoSelectUserData *data; + struct EditSelectBuf_Cache *esel; + uint backbuf_offset; +}; +static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, + BMEdge *eed, + const float screen_co_a[2], + const float screen_co_b[2], + int index) +{ + struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data; + LassoSelectUserData *data = data_for_edge->data; + const bool is_visible = (data_for_edge->esel ? + BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, + data_for_edge->backbuf_offset + index) : + true); const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = - (EDBM_backbuf_check(bm_solidoffs + index) && - edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && + (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), IS_CLIPPED) && BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); @@ -674,6 +746,7 @@ static void do_lasso_select_mesh__doSelectFace(void *userData, static bool do_lasso_select_mesh(ViewContext *vc, const int mcords[][2], short moves, + struct EditSelectBuf_Cache *esel, const eSelectOp sel_op) { LassoSelectUserData data; @@ -699,12 +772,21 @@ static bool do_lasso_select_mesh(ViewContext *vc, ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); GPU_matrix_set(vc->rv3d->viewmat); - bbsel = EDBM_backbuf_border_mask_init( - vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + + bbsel = !XRAY_FLAG_ENABLED(vc->v3d); + + if (bbsel) { + /* Lazy initialize. */ + if (esel->sel_id_ctx == NULL) { + editselect_buf_cache_init(esel, vc); + const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx); + esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect); + } + } if (ts->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { - data.is_changed |= edbm_backbuf_check_and_select_verts(vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenVert( @@ -713,17 +795,24 @@ static bool do_lasso_select_mesh(ViewContext *vc, } if (ts->selectmode & SCE_SELECT_EDGE) { /* Does both bbsel and non-bbsel versions (need screen cos for both) */ + struct LassoSelectUserData_ForMeshEdge data_for_edge = { + .data = &data, + .esel = bbsel ? esel : NULL, + .backbuf_offset = bbsel ? EDBM_select_id_context_offset_for_object_elem( + esel->sel_id_ctx, vc->obedit->runtime.select_id, BM_EDGE) : + 0, + }; mesh_foreachScreenEdge( - vc, do_lasso_select_mesh__doSelectEdge_pass0, &data, V3D_PROJ_TEST_CLIP_NEAR); + vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, V3D_PROJ_TEST_CLIP_NEAR); if (data.is_done == false) { mesh_foreachScreenEdge( - vc, do_lasso_select_mesh__doSelectEdge_pass1, &data, V3D_PROJ_TEST_CLIP_NEAR); + vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, V3D_PROJ_TEST_CLIP_NEAR); } } if (ts->selectmode & SCE_SELECT_FACE) { if (bbsel) { - data.is_changed |= edbm_backbuf_check_and_select_faces(vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenFace( @@ -1121,13 +1210,15 @@ static bool view3d_lasso_select( } else { /* Edit Mode */ + struct EditSelectBuf_Cache esel = {NULL}; + FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter) { ED_view3d_viewcontext_init_object(vc, ob_iter); bool changed = false; switch (vc->obedit->type) { case OB_MESH: - changed = do_lasso_select_mesh(vc, mcords, moves, sel_op); + changed = do_lasso_select_mesh(vc, mcords, moves, &esel, sel_op); break; case OB_CURVE: case OB_SURF: @@ -1154,6 +1245,8 @@ static bool view3d_lasso_select( } } FOREACH_OBJECT_IN_MODE_END; + + editselect_buf_cache_free(&esel); } return changed_multi; } @@ -2520,12 +2613,22 @@ static void do_mesh_box_select__doSelectVert(void *userData, data->is_changed = true; } } +struct BoxSelectUserData_ForMeshEdge { + BoxSelectUserData *data; + struct EditSelectBuf_Cache *esel; + uint backbuf_offset; +}; static void do_mesh_box_select__doSelectEdge_pass0( void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) { - BoxSelectUserData *data = userData; + struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData; + BoxSelectUserData *data = data_for_edge->data; + const bool is_visible = (data_for_edge->esel ? + BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, + data_for_edge->backbuf_offset + index) : + true); const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = (EDBM_backbuf_check(bm_solidoffs + index) && + 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 (sel_op_result != -1) { @@ -2537,10 +2640,14 @@ static void do_mesh_box_select__doSelectEdge_pass0( static void do_mesh_box_select__doSelectEdge_pass1( void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) { - BoxSelectUserData *data = userData; + struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData; + BoxSelectUserData *data = data_for_edge->data; + const bool is_visible = (data_for_edge->esel ? + BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, + data_for_edge->backbuf_offset + index) : + true); const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = (EDBM_backbuf_check(bm_solidoffs + index) && - edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b)); + 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 (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); @@ -2561,7 +2668,10 @@ static void do_mesh_box_select__doSelectFace(void *userData, data->is_changed = true; } } -static bool do_mesh_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op) +static bool do_mesh_box_select(ViewContext *vc, + const rcti *rect, + struct EditSelectBuf_Cache *esel, + const eSelectOp sel_op) { BoxSelectUserData data; ToolSettings *ts = vc->scene->toolsettings; @@ -2580,11 +2690,20 @@ static bool do_mesh_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_ ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); GPU_matrix_set(vc->rv3d->viewmat); - bbsel = EDBM_backbuf_border_init(vc, rect->xmin, rect->ymin, rect->xmax, rect->ymax); + bbsel = !XRAY_FLAG_ENABLED(vc->v3d); + + if (bbsel) { + /* Lazy initialize. */ + if (esel->sel_id_ctx == NULL) { + editselect_buf_cache_init(esel, vc); + const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx); + esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect); + } + } if (ts->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { - data.is_changed |= edbm_backbuf_check_and_select_verts(vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenVert( @@ -2593,17 +2712,24 @@ static bool do_mesh_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_ } if (ts->selectmode & SCE_SELECT_EDGE) { /* Does both bbsel and non-bbsel versions (need screen cos for both) */ + struct BoxSelectUserData_ForMeshEdge cb_data = { + .data = &data, + .esel = bbsel ? esel : NULL, + .backbuf_offset = bbsel ? EDBM_select_id_context_offset_for_object_elem( + esel->sel_id_ctx, vc->obedit->runtime.select_id, BM_EDGE) : + 0, + }; mesh_foreachScreenEdge( - vc, do_mesh_box_select__doSelectEdge_pass0, &data, V3D_PROJ_TEST_CLIP_NEAR); + vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, V3D_PROJ_TEST_CLIP_NEAR); if (data.is_done == false) { mesh_foreachScreenEdge( - vc, do_mesh_box_select__doSelectEdge_pass1, &data, V3D_PROJ_TEST_CLIP_NEAR); + vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, V3D_PROJ_TEST_CLIP_NEAR); } } if (ts->selectmode & SCE_SELECT_FACE) { if (bbsel) { - data.is_changed |= edbm_backbuf_check_and_select_faces(vc->em, sel_op); + data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op); } else { mesh_foreachScreenFace( @@ -2941,6 +3067,8 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) if (vc.obedit) { + struct EditSelectBuf_Cache esel = {NULL}; + FOREACH_OBJECT_IN_MODE_BEGIN ( vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter) { ED_view3d_viewcontext_init_object(&vc, ob_iter); @@ -2949,7 +3077,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, &rect, sel_op); + changed = do_mesh_box_select(&vc, &rect, &esel, sel_op); if (changed) { DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); @@ -2991,6 +3119,8 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) changed_multi |= changed; } FOREACH_OBJECT_IN_MODE_END; + + editselect_buf_cache_free(&esel); } else { /* no editmode, unified for bones and objects */ if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) { @@ -3120,7 +3250,11 @@ static void mesh_circle_doSelectFace(void *userData, } } -static bool mesh_circle_select(ViewContext *vc, eSelectOp sel_op, const int mval[2], float rad) +static bool mesh_circle_select(ViewContext *vc, + struct EditSelectBuf_Cache *esel, + eSelectOp sel_op, + const int mval[2], + float rad) { ToolSettings *ts = vc->scene->toolsettings; int bbsel; @@ -3136,14 +3270,26 @@ static bool mesh_circle_select(ViewContext *vc, eSelectOp sel_op, const int mval } const bool select = (sel_op != SEL_OP_SUB); - bbsel = EDBM_backbuf_circle_init(vc, mval[0], mval[1], (short)(rad + 1.0f)); ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */ view3d_userdata_circleselect_init(&data, vc, select, mval, rad); + bbsel = !XRAY_FLAG_ENABLED(vc->v3d); + + if (bbsel) { + /* Lazy initialize. */ + if (esel->sel_id_ctx == NULL) { + editselect_buf_cache_init(esel, vc); + const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx); + esel->select_bitmap = ED_select_buffer_bitmap_from_circle( + buffer_len, mval, (int)(rad + 1.0f)); + } + } + if (ts->selectmode & SCE_SELECT_VERTEX) { if (bbsel) { - changed |= edbm_backbuf_check_and_select_verts(vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + changed |= edbm_backbuf_check_and_select_verts( + esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } else { mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -3152,7 +3298,8 @@ static bool mesh_circle_select(ViewContext *vc, eSelectOp sel_op, const int mval if (ts->selectmode & SCE_SELECT_EDGE) { if (bbsel) { - changed |= edbm_backbuf_check_and_select_edges(vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + changed |= edbm_backbuf_check_and_select_edges( + esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } else { mesh_foreachScreenEdge(vc, mesh_circle_doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR); @@ -3161,7 +3308,8 @@ static bool mesh_circle_select(ViewContext *vc, eSelectOp sel_op, const int mval if (ts->selectmode & SCE_SELECT_FACE) { if (bbsel) { - changed |= edbm_backbuf_check_and_select_faces(vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); + changed |= edbm_backbuf_check_and_select_faces( + esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB); } else { mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT); @@ -3593,6 +3741,7 @@ static bool mball_circle_select(ViewContext *vc, /** Callbacks for circle selection in Editmode */ static bool obedit_circle_select(ViewContext *vc, + struct EditSelectBuf_Cache *esel, const eSelectOp sel_op, const int mval[2], float rad) @@ -3600,7 +3749,7 @@ static bool obedit_circle_select(ViewContext *vc, BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB)); switch (vc->obedit->type) { case OB_MESH: - return mesh_circle_select(vc, sel_op, mval, rad); + return mesh_circle_select(vc, esel, sel_op, mval, rad); case OB_CURVE: case OB_SURF: return nurbscurve_circle_select(vc, sel_op, mval, rad); @@ -3674,6 +3823,8 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) view3d_operator_needs_opengl(C); BKE_object_update_select_id(CTX_data_main(C)); + struct EditSelectBuf_Cache esel = {NULL}; + FOREACH_OBJECT_IN_MODE_BEGIN (vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter) { ED_view3d_viewcontext_init_object(&vc, ob_iter); @@ -3681,7 +3832,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) obedit = vc.obedit; if (obedit) { - if (obedit_circle_select(&vc, sel_op, mval, (float)radius)) { + if (obedit_circle_select(&vc, &esel, sel_op, mval, (float)radius)) { DEG_id_tag_update(obact->data, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); } @@ -3700,6 +3851,8 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) } } FOREACH_OBJECT_IN_MODE_END; + + editselect_buf_cache_free(&esel); } else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) { if (PE_circle_select(C, sel_op, mval, (float)radius)) { diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 0564cb07897..c09237d825d 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC ed_util.c gizmo_utils.c numinput.c + select_buffer_utils.c select_utils.c # general includes @@ -79,6 +80,7 @@ set(SRC ../include/ED_screen.h ../include/ED_screen_types.h ../include/ED_sculpt.h + ../include/ED_select_buffer_utils.h ../include/ED_select_utils.h ../include/ED_sequencer.h ../include/ED_sound.h diff --git a/source/blender/editors/util/select_buffer_utils.c b/source/blender/editors/util/select_buffer_utils.c new file mode 100644 index 00000000000..8818757653e --- /dev/null +++ b/source/blender/editors/util/select_buffer_utils.c @@ -0,0 +1,179 @@ +/* + * 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) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edutil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_bitmap_draw_2d.h" +#include "BLI_rect.h" +#include "BLI_utildefines.h" + +#include "ED_select_buffer_utils.h" + +/* Only for #ED_view3d_select_id_read, + * note that this file shouldn't have 3D view specific logic in it, we could have a more general + * way to read from selection buffers that doesn't depend on the view3d API. */ +#include "ED_view3d.h" + +/** + * \param bitmap_len: Number of indices in the selection id buffer. + * \param rect: The rectangle to sample indices from (min/max inclusive). + * \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure. + */ +uint *ED_select_buffer_bitmap_from_rect(const uint bitmap_len, const rcti *rect) +{ + uint buf_len; + const uint *buf = ED_view3d_select_id_read( + rect->xmin, rect->ymin, rect->xmax, rect->ymax, &buf_len); + if (buf == NULL) { + return NULL; + } + + const uint *buf_iter = buf; + + BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__); + + while (buf_len--) { + const uint index = *buf_iter - 1; + if (index < bitmap_len) { + BLI_BITMAP_ENABLE(bitmap_buf, index); + } + buf_iter++; + } + MEM_freeN((void *)buf); + return bitmap_buf; +} + +/** + * \param bitmap_len: Number of indices in the selection id buffer. + * \param center: Circle center. + * \param radius: Circle radius. + * \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure. + */ +uint *ED_select_buffer_bitmap_from_circle(const uint bitmap_len, + const int center[2], + const int radius) +{ + if (bitmap_len == 0) { + return NULL; + } + + const int xmin = center[0] - radius; + const int xmax = center[0] + radius; + const int ymin = center[1] - radius; + const int ymax = center[1] + radius; + + const uint *buf = ED_view3d_select_id_read(xmin, ymin, xmax, ymax, NULL); + if (buf == NULL) { + return NULL; + } + + const uint *buf_iter = buf; + + BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__); + const int radius_sq = radius * radius; + for (int yc = -radius; yc <= radius; yc++) { + for (int xc = -radius; xc <= radius; xc++, buf_iter++) { + if (xc * xc + yc * yc < radius_sq) { + /* Intentionally wrap to max value if this is zero. */ + const uint index = *buf_iter - 1; + if (index < bitmap_len) { + BLI_BITMAP_ENABLE(bitmap_buf, index); + } + } + } + } + MEM_freeN((void *)buf); + return bitmap_buf; +} + +struct PolyMaskData { + BLI_bitmap *px; + int width; +}; + +static void ed_select_buffer_mask_px_cb(int x, int x_end, int y, void *user_data) +{ + struct PolyMaskData *data = user_data; + BLI_bitmap *px = data->px; + int i = (y * data->width) + x; + do { + BLI_BITMAP_ENABLE(px, i); + i++; + } while (++x != x_end); +} + +/** + * \param bitmap_len: Number of indices in the selection id buffer. + * \param center: Circle center. + * \param radius: Circle radius. + * \returns a #BLI_bitmap the length of \a bitmap_len or NULL on failure. + */ +uint *ED_select_buffer_bitmap_from_poly(const uint bitmap_len, + const int poly[][2], + const int poly_len, + const rcti *rect) + +{ + if (bitmap_len == 0) { + return NULL; + } + + struct PolyMaskData poly_mask_data; + uint buf_len; + const uint *buf = ED_view3d_select_id_read( + rect->xmin, rect->ymin, rect->xmax, rect->ymax, &buf_len); + if (buf == NULL) { + return NULL; + } + + BLI_bitmap *buf_mask = BLI_BITMAP_NEW(buf_len, __func__); + poly_mask_data.px = buf_mask; + poly_mask_data.width = (rect->xmax - rect->xmin) + 1; + + BLI_bitmap_draw_2d_poly_v2i_n(rect->xmin, + rect->ymin, + rect->xmax + 1, + rect->ymax + 1, + poly, + poly_len, + ed_select_buffer_mask_px_cb, + &poly_mask_data); + + /* Build selection lookup. */ + const uint *buf_iter = buf; + BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__); + int i = 0; + while (buf_len--) { + const uint index = *buf_iter - 1; + if (index < bitmap_len && BLI_BITMAP_TEST(buf_mask, i)) { + BLI_BITMAP_ENABLE(bitmap_buf, index); + } + buf_iter++; + i++; + } + MEM_freeN((void *)buf); + MEM_freeN(buf_mask); + + return bitmap_buf; +} |