Fix T54686: objects don't occlude each other for edit-mesh select (part 2)
authorCampbell Barton <ideasman42@gmail.com>
Sat, 18 May 2019 13:12:47 +0000 (23:12 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 18 May 2019 13:58:46 +0000 (23:58 +1000)
The previous fix 8a6414ed46f66, resolved selection picking but didn't
work for box/circle/lasso select.

- Add ED_select_buffer_utils.h for general select-buffer operations
  unrelated to edit-mesh.

- Circle select still needs to cache select-id's for each update.

source/blender/draw/intern/draw_manager.c
source/blender/editors/include/ED_mesh.h
source/blender/editors/include/ED_select_buffer_utils.h [new file with mode: 0644]
source/blender/editors/include/ED_view3d.h
source/blender/editors/mesh/editmesh_select.c
source/blender/editors/space_view3d/view3d_draw_legacy.c
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/util/CMakeLists.txt
source/blender/editors/util/select_buffer_utils.c [new file with mode: 0644]

index 5c9f49e4ee5fb0e8bad4c292f7f72e1e2dbf99b1..ce37145ea857224f37048f23e346b228a562c15f 100644 (file)
@@ -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:
index 4e6bf7d4df5e632462377034861ea2057f38e0f6..75e0f05d088ec08783007b829d4756d57b66e814 100644 (file)
@@ -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 (file)
index 0000000..54e4cdb
--- /dev/null
@@ -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__ */
index 2b8f926fc2c67aac9abf0b7d7086d8e82b23eca9..61d1670b88d80f9d08baf205bb074964c7cdbd0a 100644 (file)
@@ -42,6 +42,7 @@ struct GPUFX;
 struct GPUFXSettings;
 struct GPUOffScreen;
 struct GPUViewport;
+struct ID;
 struct ImBuf;
 struct MVert;
 struct Main;
index 372ff42f1c3168cfb2f2de7903ac6a2a23b1f653..d6a32a6bd70609d158d256881e57ccda7f50c9f6 100644 (file)
@@ -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,
index 3443f3332345b8673f3fe7872de99f9e97667bfd..c17eb5cef812077f293aa555b6ff148002768c6c 100644 (file)
@@ -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);
index a93c14fa1ef0c4e984660ba2172942c78c94b2c4..4ea175985b9ef830f96457071d915cb78ac72586 100644 (file)
@@ -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"
@@ -178,21 +180,67 @@ 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)) {
index 0564cb078975f4d61cc4a9f39174c74d269db0e6..c09237d825d8d3dd3a565da80d2b012a2671917d 100644 (file)
@@ -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 (file)
index 0000000..8818757
--- /dev/null
@@ -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;
+}