414ae392399406d48ec40745093658d08da64604
[blender.git] / source / blender / editors / space_view3d / view3d_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spview3d
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <float.h>
28 #include <assert.h>
29
30 #include "DNA_action_types.h"
31 #include "DNA_armature_types.h"
32 #include "DNA_curve_types.h"
33 #include "DNA_meta_types.h"
34 #include "DNA_mesh_types.h"
35 #include "DNA_meshdata_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_scene_types.h"
38 #include "DNA_tracking_types.h"
39 #include "DNA_gpencil_types.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_array.h"
44 #include "BLI_bitmap.h"
45 #include "BLI_math.h"
46 #include "BLI_lasso_2d.h"
47 #include "BLI_rect.h"
48 #include "BLI_linklist.h"
49 #include "BLI_listbase.h"
50 #include "BLI_string.h"
51 #include "BLI_utildefines.h"
52
53 #ifdef __BIG_ENDIAN__
54 #  include "BLI_endian_switch.h"
55 #endif
56
57 /* vertex box select */
58 #include "IMB_imbuf_types.h"
59 #include "IMB_imbuf.h"
60 #include "BKE_global.h"
61 #include "BKE_main.h"
62
63 #include "BKE_armature.h"
64 #include "BKE_context.h"
65 #include "BKE_curve.h"
66 #include "BKE_layer.h"
67 #include "BKE_mball.h"
68 #include "BKE_mesh.h"
69 #include "BKE_object.h"
70 #include "BKE_paint.h"
71 #include "BKE_editmesh.h"
72 #include "BKE_scene.h"
73 #include "BKE_tracking.h"
74 #include "BKE_workspace.h"
75
76 #include "DEG_depsgraph.h"
77
78 #include "WM_api.h"
79 #include "WM_types.h"
80
81 #include "RNA_access.h"
82 #include "RNA_define.h"
83 #include "RNA_enum_types.h"
84
85 #include "ED_armature.h"
86 #include "ED_curve.h"
87 #include "ED_lattice.h"
88 #include "ED_particle.h"
89 #include "ED_mesh.h"
90 #include "ED_object.h"
91 #include "ED_screen.h"
92 #include "ED_select_buffer_utils.h"
93 #include "ED_select_utils.h"
94 #include "ED_sculpt.h"
95 #include "ED_mball.h"
96 #include "ED_gpencil.h"
97
98 #include "UI_interface.h"
99
100 #include "GPU_draw.h"
101 #include "GPU_glew.h"
102 #include "GPU_matrix.h"
103
104 #include "view3d_intern.h" /* own include */
105
106 // #include "PIL_time_utildefines.h"
107
108 /* -------------------------------------------------------------------- */
109 /** \name Public Utilities
110  * \{ */
111
112 float ED_view3d_select_dist_px(void)
113 {
114   return 75.0f * U.pixelsize;
115 }
116
117 /* TODO: should return whether there is valid context to continue */
118 void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc)
119 {
120   memset(vc, 0, sizeof(ViewContext));
121   vc->C = C;
122   vc->ar = CTX_wm_region(C);
123   vc->bmain = CTX_data_main(C);
124   vc->depsgraph = CTX_data_depsgraph(C);
125   vc->scene = CTX_data_scene(C);
126   vc->view_layer = CTX_data_view_layer(C);
127   vc->v3d = CTX_wm_view3d(C);
128   vc->win = CTX_wm_window(C);
129   vc->rv3d = CTX_wm_region_view3d(C);
130   vc->obact = CTX_data_active_object(C);
131   vc->obedit = CTX_data_edit_object(C);
132 }
133
134 void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
135 {
136   vc->obact = obact;
137   if (vc->obedit) {
138     BLI_assert(BKE_object_is_in_editmode(obact));
139     vc->obedit = obact;
140     if (vc->em) {
141       vc->em = BKE_editmesh_from_object(vc->obedit);
142     }
143   }
144 }
145
146 /** \} */
147
148 /* -------------------------------------------------------------------- */
149 /** \name Internal Object Utilities
150  * \{ */
151
152 static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d)
153 {
154   bool changed = false;
155   for (Base *base = view_layer->object_bases.first; base; base = base->next) {
156     if (base->flag & BASE_SELECTED) {
157       if (BASE_SELECTABLE(v3d, base)) {
158         ED_object_base_select(base, BA_DESELECT);
159         changed = true;
160       }
161     }
162   }
163   return changed;
164 }
165
166 /* deselect all except b */
167 static bool object_deselect_all_except(ViewLayer *view_layer, Base *b)
168 {
169   bool changed = false;
170   for (Base *base = view_layer->object_bases.first; base; base = base->next) {
171     if (base->flag & BASE_SELECTED) {
172       if (b != base) {
173         ED_object_base_select(base, BA_DESELECT);
174         changed = true;
175       }
176     }
177   }
178   return changed;
179 }
180
181 /** \} */
182
183 /* -------------------------------------------------------------------- */
184 /** \name Internal Edit-Mesh Select Buffer Wrapper
185  *
186  * Avoid duplicate code when using edit-mode selection,
187  * actual logic is handled outside of this function.
188  *
189  * \note Currently this #EDBMSelectID_Context which is mesh specific
190  * however the logic could also be used for non-meshes too.
191  *
192  * \{ */
193
194 struct EditSelectBuf_Cache {
195   Base **bases;
196   uint bases_len;
197   struct EDBMSelectID_Context *sel_id_ctx;
198   BLI_bitmap *select_bitmap;
199 };
200
201 static void editselect_buf_cache_init(struct EditSelectBuf_Cache *esel, ViewContext *vc)
202 {
203   if (vc->obedit) {
204     esel->bases = BKE_view_layer_array_from_bases_in_edit_mode(
205         vc->view_layer, vc->v3d, &esel->bases_len);
206   }
207   else {
208     /* Use for paint modes, currently only a single object at a time. */
209     if (vc->obact) {
210       esel->bases = MEM_mallocN(sizeof(esel->bases), __func__);
211       esel->bases[0] = BKE_view_layer_base_find(vc->view_layer, vc->obact);
212       esel->bases_len = 1;
213     }
214     else {
215       esel->bases = NULL;
216       esel->bases_len = 0;
217     }
218   }
219   esel->sel_id_ctx = EDBM_select_id_context_create(
220       vc, esel->bases, esel->bases_len, vc->scene->toolsettings->selectmode);
221   for (int i = 0; i < esel->bases_len; i++) {
222     esel->bases[i]->object->runtime.select_id = i;
223   }
224 }
225
226 static void editselect_buf_cache_free(struct EditSelectBuf_Cache *esel)
227 {
228   if (esel->sel_id_ctx) {
229     EDBM_select_id_context_destroy(esel->sel_id_ctx);
230   }
231   MEM_SAFE_FREE(esel->select_bitmap);
232   MEM_SAFE_FREE(esel->bases);
233 }
234
235 /** \} */
236
237 /* -------------------------------------------------------------------- */
238 /** \name Internal Edit-Mesh Utilities
239  * \{ */
240
241 static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel,
242                                                 Object *ob,
243                                                 BMEditMesh *em,
244                                                 const eSelectOp sel_op)
245 {
246   BMVert *eve;
247   BMIter iter;
248   bool changed = false;
249
250   const BLI_bitmap *select_bitmap = esel->select_bitmap;
251   uint index = EDBM_select_id_context_offset_for_object_elem(
252       esel->sel_id_ctx, ob->runtime.select_id, BM_VERT);
253
254   BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
255     if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
256       const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
257       const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
258       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
259       if (sel_op_result != -1) {
260         BM_vert_select_set(em->bm, eve, sel_op_result);
261         changed = true;
262       }
263     }
264     index++;
265   }
266   return changed;
267 }
268
269 static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel,
270                                                 Object *ob,
271                                                 BMEditMesh *em,
272                                                 const eSelectOp sel_op)
273 {
274   BMEdge *eed;
275   BMIter iter;
276   bool changed = false;
277
278   const BLI_bitmap *select_bitmap = esel->select_bitmap;
279   uint index = EDBM_select_id_context_offset_for_object_elem(
280       esel->sel_id_ctx, ob->runtime.select_id, BM_EDGE);
281
282   BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
283     if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
284       const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
285       const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
286       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
287       if (sel_op_result != -1) {
288         BM_edge_select_set(em->bm, eed, sel_op_result);
289         changed = true;
290       }
291     }
292     index++;
293   }
294   return changed;
295 }
296
297 static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel,
298                                                 Object *ob,
299                                                 BMEditMesh *em,
300                                                 const eSelectOp sel_op)
301 {
302   BMFace *efa;
303   BMIter iter;
304   bool changed = false;
305
306   const BLI_bitmap *select_bitmap = esel->select_bitmap;
307   uint index = EDBM_select_id_context_offset_for_object_elem(
308       esel->sel_id_ctx, ob->runtime.select_id, BM_FACE);
309
310   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
311     if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
312       const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
313       const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
314       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
315       if (sel_op_result != -1) {
316         BM_face_select_set(em->bm, efa, sel_op_result);
317         changed = true;
318       }
319     }
320     index++;
321   }
322   return changed;
323 }
324
325 /* object mode, edbm_ prefix is confusing here, rename? */
326 static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me,
327                                                        struct EditSelectBuf_Cache *esel,
328                                                        const eSelectOp sel_op)
329 {
330   MVert *mv = me->mvert;
331   uint index;
332   bool changed = false;
333
334   const BLI_bitmap *select_bitmap = esel->select_bitmap;
335
336   if (mv) {
337     for (index = 0; index < me->totvert; index++, mv++) {
338       if (!(mv->flag & ME_HIDE)) {
339         const bool is_select = mv->flag & SELECT;
340         const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
341         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
342         if (sel_op_result != -1) {
343           SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT);
344           changed = true;
345         }
346       }
347     }
348   }
349   return changed;
350 }
351
352 /* object mode, edbm_ prefix is confusing here, rename? */
353 static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me,
354                                                        struct EditSelectBuf_Cache *esel,
355                                                        const eSelectOp sel_op)
356 {
357   MPoly *mpoly = me->mpoly;
358   uint index;
359   bool changed = false;
360
361   const BLI_bitmap *select_bitmap = esel->select_bitmap;
362
363   if (mpoly) {
364     for (index = 0; index < me->totpoly; index++, mpoly++) {
365       if (!(mpoly->flag & ME_HIDE)) {
366         const bool is_select = mpoly->flag & ME_FACE_SEL;
367         const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
368         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
369         if (sel_op_result != -1) {
370           SET_FLAG_FROM_TEST(mpoly->flag, sel_op_result, ME_FACE_SEL);
371           changed = true;
372         }
373       }
374     }
375   }
376   return changed;
377 }
378
379 /** \} */
380
381 /* -------------------------------------------------------------------- */
382 /** \name Lasso Select
383  * \{ */
384
385 typedef struct LassoSelectUserData {
386   ViewContext *vc;
387   const rcti *rect;
388   const rctf *rect_fl;
389   rctf _rect_fl;
390   const int (*mcords)[2];
391   int moves;
392   eSelectOp sel_op;
393
394   /* runtime */
395   int pass;
396   bool is_done;
397   bool is_changed;
398 } LassoSelectUserData;
399
400 static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data,
401                                              ViewContext *vc,
402                                              const rcti *rect,
403                                              const int (*mcords)[2],
404                                              const int moves,
405                                              const eSelectOp sel_op)
406 {
407   r_data->vc = vc;
408
409   r_data->rect = rect;
410   r_data->rect_fl = &r_data->_rect_fl;
411   BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
412
413   r_data->mcords = mcords;
414   r_data->moves = moves;
415   r_data->sel_op = sel_op;
416
417   /* runtime */
418   r_data->pass = 0;
419   r_data->is_done = false;
420   r_data->is_changed = false;
421 }
422
423 static bool view3d_selectable_data(bContext *C)
424 {
425   Object *ob = CTX_data_active_object(C);
426
427   if (!ED_operator_region_view3d_active(C)) {
428     return 0;
429   }
430
431   if (ob) {
432     if (ob->mode & OB_MODE_EDIT) {
433       if (ob->type == OB_FONT) {
434         return 0;
435       }
436     }
437     else {
438       if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
439           !BKE_paint_select_elem_test(ob)) {
440         return 0;
441       }
442     }
443   }
444
445   return 1;
446 }
447
448 /* helper also for box_select */
449 static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
450 {
451   return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2);
452 }
453
454 static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
455 {
456   int d1, d2, d3, d4;
457
458   /* check points in rect */
459   if (edge_fully_inside_rect(rect, v1, v2)) {
460     return 1;
461   }
462
463   /* check points completely out rect */
464   if (v1[0] < rect->xmin && v2[0] < rect->xmin) {
465     return 0;
466   }
467   if (v1[0] > rect->xmax && v2[0] > rect->xmax) {
468     return 0;
469   }
470   if (v1[1] < rect->ymin && v2[1] < rect->ymin) {
471     return 0;
472   }
473   if (v1[1] > rect->ymax && v2[1] > rect->ymax) {
474     return 0;
475   }
476
477   /* simple check lines intersecting. */
478   d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
479   d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
480   d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
481   d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
482
483   if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) {
484     return 0;
485   }
486   if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) {
487     return 0;
488   }
489
490   return 1;
491 }
492
493 static void do_lasso_select_pose__do_tag(void *userData,
494                                          struct bPoseChannel *pchan,
495                                          const float screen_co_a[2],
496                                          const float screen_co_b[2])
497 {
498   LassoSelectUserData *data = userData;
499   bArmature *arm = data->vc->obact->data;
500
501   if (PBONE_SELECTABLE(arm, pchan->bone)) {
502     bool is_point_done = false;
503     int points_proj_tot = 0;
504
505     /* project head location to screenspace */
506     if (screen_co_a[0] != IS_CLIPPED) {
507       points_proj_tot++;
508       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
509           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) {
510         is_point_done = true;
511       }
512     }
513
514     /* project tail location to screenspace */
515     if (screen_co_b[0] != IS_CLIPPED) {
516       points_proj_tot++;
517       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
518           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) {
519         is_point_done = true;
520       }
521     }
522
523     /* if one of points selected, we skip the bone itself */
524     if ((is_point_done == true) ||
525         ((is_point_done == false) && (points_proj_tot == 2) &&
526          BLI_lasso_is_edge_inside(
527              data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))) {
528       pchan->bone->flag |= BONE_DONE;
529     }
530     data->is_changed |= is_point_done;
531   }
532 }
533 static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2], short moves)
534 {
535   ViewContext vc_tmp;
536   LassoSelectUserData data;
537   rcti rect;
538
539   if ((ob->type != OB_ARMATURE) || (ob->pose == NULL)) {
540     return;
541   }
542
543   vc_tmp = *vc;
544   vc_tmp.obact = ob;
545
546   BLI_lasso_boundbox(&rect, mcords, moves);
547
548   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, 0);
549
550   ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d);
551
552   pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__do_tag, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
553 }
554
555 static bool do_lasso_select_objects(ViewContext *vc,
556                                     const int mcords[][2],
557                                     const short moves,
558                                     const eSelectOp sel_op)
559 {
560   View3D *v3d = vc->v3d;
561   Base *base;
562
563   bool changed = false;
564   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
565     changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
566   }
567
568   for (base = vc->view_layer->object_bases.first; base; base = base->next) {
569     if (BASE_SELECTABLE(v3d, base)) { /* use this to avoid un-needed lasso lookups */
570       const bool is_select = base->flag & BASE_SELECTED;
571       const bool is_inside = ((ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) &&
572                               BLI_lasso_is_point_inside(
573                                   mcords, moves, base->sx, base->sy, IS_CLIPPED));
574       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
575       if (sel_op_result != -1) {
576         ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT);
577         changed = true;
578       }
579     }
580   }
581
582   if (changed) {
583     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
584     WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene);
585   }
586   return changed;
587 }
588
589 /**
590  * Use for lasso & box select.
591  */
592 static Base **do_pose_tag_select_op_prepare(ViewContext *vc, uint *r_bases_len)
593 {
594   Base **bases = NULL;
595   BLI_array_declare(bases);
596   FOREACH_BASE_IN_MODE_BEGIN (vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter) {
597     Object *ob_iter = base_iter->object;
598     bArmature *arm = ob_iter->data;
599     for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
600       Bone *bone = pchan->bone;
601       bone->flag &= ~BONE_DONE;
602     }
603     arm->id.tag |= LIB_TAG_DOIT;
604     ob_iter->id.tag &= ~LIB_TAG_DOIT;
605     BLI_array_append(bases, base_iter);
606   }
607   FOREACH_BASE_IN_MODE_END;
608   *r_bases_len = BLI_array_len(bases);
609   return bases;
610 }
611
612 static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const eSelectOp sel_op)
613 {
614   bool changed_multi = false;
615
616   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
617     for (int i = 0; i < bases_len; i++) {
618       Base *base_iter = bases[i];
619       Object *ob_iter = base_iter->object;
620       if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) {
621         ED_pose_bone_select_tag_update(ob_iter);
622         changed_multi = true;
623       }
624     }
625   }
626
627   for (int i = 0; i < bases_len; i++) {
628     Base *base_iter = bases[i];
629     Object *ob_iter = base_iter->object;
630     bArmature *arm = ob_iter->data;
631
632     /* Don't handle twice. */
633     if (arm->id.tag & LIB_TAG_DOIT) {
634       arm->id.tag &= ~LIB_TAG_DOIT;
635     }
636     else {
637       continue;
638     }
639
640     bool changed = true;
641     for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
642       Bone *bone = pchan->bone;
643       if ((bone->flag & BONE_UNSELECTABLE) == 0) {
644         const bool is_select = bone->flag & BONE_SELECTED;
645         const bool is_inside = bone->flag & BONE_DONE;
646         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
647         if (sel_op_result != -1) {
648           SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED);
649           if (sel_op_result == 0) {
650             if (arm->act_bone == bone) {
651               arm->act_bone = NULL;
652             }
653           }
654           changed = true;
655         }
656       }
657     }
658     if (changed) {
659       ED_pose_bone_select_tag_update(ob_iter);
660       changed_multi = true;
661     }
662   }
663   return changed_multi;
664 }
665
666 static bool do_lasso_select_pose(ViewContext *vc,
667                                  const int mcords[][2],
668                                  const short moves,
669                                  const eSelectOp sel_op)
670 {
671   uint bases_len;
672   Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
673
674   for (int i = 0; i < bases_len; i++) {
675     Base *base_iter = bases[i];
676     Object *ob_iter = base_iter->object;
677     do_lasso_tag_pose(vc, ob_iter, mcords, moves);
678   }
679
680   const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op);
681   if (changed_multi) {
682     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
683     WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene);
684   }
685
686   MEM_freeN(bases);
687   return changed_multi;
688 }
689
690 static void do_lasso_select_mesh__doSelectVert(void *userData,
691                                                BMVert *eve,
692                                                const float screen_co[2],
693                                                int UNUSED(index))
694 {
695   LassoSelectUserData *data = userData;
696   const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
697   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
698                           BLI_lasso_is_point_inside(
699                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
700   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
701   if (sel_op_result != -1) {
702     BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
703     data->is_changed = true;
704   }
705 }
706 struct LassoSelectUserData_ForMeshEdge {
707   LassoSelectUserData *data;
708   struct EditSelectBuf_Cache *esel;
709   uint backbuf_offset;
710 };
711 static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data,
712                                                      BMEdge *eed,
713                                                      const float screen_co_a[2],
714                                                      const float screen_co_b[2],
715                                                      int index)
716 {
717   struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data;
718   LassoSelectUserData *data = data_for_edge->data;
719   const bool is_visible = (data_for_edge->esel ?
720                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
721                                                     data_for_edge->backbuf_offset + index) :
722                                true);
723   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
724   const bool is_inside =
725       (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) &&
726        BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), IS_CLIPPED) &&
727        BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), IS_CLIPPED));
728   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
729   if (sel_op_result != -1) {
730     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
731     data->is_done = true;
732     data->is_changed = true;
733   }
734 }
735 static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data,
736                                                      BMEdge *eed,
737                                                      const float screen_co_a[2],
738                                                      const float screen_co_b[2],
739                                                      int index)
740 {
741   struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data;
742   LassoSelectUserData *data = data_for_edge->data;
743   const bool is_visible = (data_for_edge->esel ?
744                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
745                                                     data_for_edge->backbuf_offset + index) :
746                                true);
747   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
748   const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcords,
749                                                                  data->moves,
750                                                                  UNPACK2(screen_co_a),
751                                                                  UNPACK2(screen_co_b),
752                                                                  IS_CLIPPED));
753   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
754   if (sel_op_result != -1) {
755     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
756     data->is_changed = true;
757   }
758 }
759
760 static void do_lasso_select_mesh__doSelectFace(void *userData,
761                                                BMFace *efa,
762                                                const float screen_co[2],
763                                                int UNUSED(index))
764 {
765   LassoSelectUserData *data = userData;
766   const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
767   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
768                           BLI_lasso_is_point_inside(
769                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
770   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
771   if (sel_op_result != -1) {
772     BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
773     data->is_changed = true;
774   }
775 }
776
777 static bool do_lasso_select_mesh(ViewContext *vc,
778                                  const int mcords[][2],
779                                  short moves,
780                                  struct EditSelectBuf_Cache *esel,
781                                  const eSelectOp sel_op)
782 {
783   LassoSelectUserData data;
784   ToolSettings *ts = vc->scene->toolsettings;
785   rcti rect;
786
787   /* set editmesh */
788   vc->em = BKE_editmesh_from_object(vc->obedit);
789
790   BLI_lasso_boundbox(&rect, mcords, moves);
791
792   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
793
794   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
795     if (vc->em->bm->totvertsel) {
796       EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
797       data.is_changed = true;
798     }
799   }
800
801   /* for non zbuf projections, don't change the GL state */
802   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
803
804   GPU_matrix_set(vc->rv3d->viewmat);
805
806   const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
807
808   if (use_zbuf) {
809     /* Lazy initialize. */
810     if (esel->sel_id_ctx == NULL) {
811       editselect_buf_cache_init(esel, vc);
812       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
813       esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
814     }
815   }
816
817   if (ts->selectmode & SCE_SELECT_VERTEX) {
818     if (use_zbuf) {
819       data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op);
820     }
821     else {
822       mesh_foreachScreenVert(
823           vc, do_lasso_select_mesh__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
824     }
825   }
826   if (ts->selectmode & SCE_SELECT_EDGE) {
827     /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
828     struct LassoSelectUserData_ForMeshEdge data_for_edge = {
829         .data = &data,
830         .esel = use_zbuf ? esel : NULL,
831         .backbuf_offset = use_zbuf ?
832                               EDBM_select_id_context_offset_for_object_elem(
833                                   esel->sel_id_ctx, vc->obedit->runtime.select_id, BM_EDGE) :
834                               0,
835     };
836     mesh_foreachScreenEdge(
837         vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, V3D_PROJ_TEST_CLIP_NEAR);
838     if (data.is_done == false) {
839       mesh_foreachScreenEdge(
840           vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, V3D_PROJ_TEST_CLIP_NEAR);
841     }
842   }
843
844   if (ts->selectmode & SCE_SELECT_FACE) {
845     if (use_zbuf) {
846       data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op);
847     }
848     else {
849       mesh_foreachScreenFace(
850           vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
851     }
852   }
853
854   if (data.is_changed) {
855     EDBM_selectmode_flush(vc->em);
856   }
857   return data.is_changed;
858 }
859
860 static void do_lasso_select_curve__doSelect(void *userData,
861                                             Nurb *UNUSED(nu),
862                                             BPoint *bp,
863                                             BezTriple *bezt,
864                                             int beztindex,
865                                             const float screen_co[2])
866 {
867   LassoSelectUserData *data = userData;
868
869   const bool is_inside = BLI_lasso_is_point_inside(
870       data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED);
871   if (bp) {
872     const bool is_select = bp->f1 & SELECT;
873     const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
874     if (sel_op_result != -1) {
875       SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
876       data->is_changed = true;
877     }
878   }
879   else {
880     if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {
881       /* can only be (beztindex == 0) here since handles are hidden */
882       const bool is_select = bezt->f2 & SELECT;
883       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
884       if (sel_op_result != -1) {
885         SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, SELECT);
886       }
887       bezt->f1 = bezt->f3 = bezt->f2;
888       data->is_changed = true;
889     }
890     else {
891       char *flag_p = (&bezt->f1) + beztindex;
892       const bool is_select = *flag_p & SELECT;
893       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
894       if (sel_op_result != -1) {
895         SET_FLAG_FROM_TEST(*flag_p, sel_op_result, SELECT);
896         data->is_changed = true;
897       }
898     }
899   }
900 }
901
902 static bool do_lasso_select_curve(ViewContext *vc,
903                                   const int mcords[][2],
904                                   short moves,
905                                   const eSelectOp sel_op)
906 {
907   LassoSelectUserData data;
908   rcti rect;
909
910   BLI_lasso_boundbox(&rect, mcords, moves);
911
912   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
913
914   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
915     Curve *curve = (Curve *)vc->obedit->data;
916     data.is_changed |= ED_curve_deselect_all(curve->editnurb);
917   }
918
919   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
920   nurbs_foreachScreenVert(vc, do_lasso_select_curve__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
921   if (data.is_changed) {
922     BKE_curve_nurb_vert_active_validate(vc->obedit->data);
923   }
924   return data.is_changed;
925 }
926
927 static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const float screen_co[2])
928 {
929   LassoSelectUserData *data = userData;
930   const bool is_select = bp->f1 & SELECT;
931   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
932                           BLI_lasso_is_point_inside(
933                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
934   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
935   if (sel_op_result != -1) {
936     SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
937     data->is_changed = true;
938   }
939 }
940 static bool do_lasso_select_lattice(ViewContext *vc,
941                                     const int mcords[][2],
942                                     short moves,
943                                     const eSelectOp sel_op)
944 {
945   LassoSelectUserData data;
946   rcti rect;
947
948   BLI_lasso_boundbox(&rect, mcords, moves);
949
950   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
951
952   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
953     data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
954   }
955
956   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
957   lattice_foreachScreenVert(
958       vc, do_lasso_select_lattice__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
959   return data.is_changed;
960 }
961
962 static void do_lasso_select_armature__doSelectBone(void *userData,
963                                                    EditBone *ebone,
964                                                    const float screen_co_a[2],
965                                                    const float screen_co_b[2])
966 {
967   LassoSelectUserData *data = userData;
968   bArmature *arm = data->vc->obedit->data;
969   if (EBONE_VISIBLE(arm, ebone)) {
970     int is_ignore_flag = 0;
971     int is_inside_flag = 0;
972
973     if (screen_co_a[0] != IS_CLIPPED) {
974       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
975           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) {
976         is_inside_flag |= BONESEL_ROOT;
977       }
978     }
979     else {
980       is_ignore_flag |= BONESEL_ROOT;
981     }
982
983     if (screen_co_b[0] != IS_CLIPPED) {
984       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
985           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) {
986         is_inside_flag |= BONESEL_TIP;
987       }
988     }
989     else {
990       is_ignore_flag |= BONESEL_TIP;
991     }
992
993     if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) ||
994         BLI_lasso_is_edge_inside(
995             data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) {
996       is_inside_flag |= BONESEL_BONE;
997     }
998
999     ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1000   }
1001 }
1002
1003 static bool do_lasso_select_armature(ViewContext *vc,
1004                                      const int mcords[][2],
1005                                      short moves,
1006                                      const eSelectOp sel_op)
1007 {
1008   LassoSelectUserData data;
1009   rcti rect;
1010
1011   BLI_lasso_boundbox(&rect, mcords, moves);
1012
1013   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
1014
1015   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1016     data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit);
1017   }
1018
1019   bArmature *arm = vc->obedit->data;
1020
1021   ED_armature_ebone_listbase_temp_clear(arm->edbo);
1022
1023   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
1024
1025   armature_foreachScreenBone(
1026       vc, do_lasso_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
1027
1028   data.is_changed |= ED_armature_edit_select_op_from_tagged(vc->obedit->data, sel_op);
1029
1030   if (data.is_changed) {
1031     WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obedit);
1032   }
1033   return data.is_changed;
1034 }
1035
1036 static void do_lasso_select_mball__doSelectElem(void *userData,
1037                                                 struct MetaElem *ml,
1038                                                 const float screen_co[2])
1039 {
1040   LassoSelectUserData *data = userData;
1041   const bool is_select = ml->flag & SELECT;
1042   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1043                           BLI_lasso_is_point_inside(
1044                               data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX));
1045   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1046   if (sel_op_result != -1) {
1047     SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
1048     data->is_changed = true;
1049   }
1050 }
1051 static bool do_lasso_select_meta(ViewContext *vc,
1052                                  const int mcords[][2],
1053                                  short moves,
1054                                  const eSelectOp sel_op)
1055 {
1056   LassoSelectUserData data;
1057   rcti rect;
1058
1059   MetaBall *mb = (MetaBall *)vc->obedit->data;
1060
1061   BLI_lasso_boundbox(&rect, mcords, moves);
1062
1063   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
1064
1065   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1066     data.is_changed |= BKE_mball_deselect_all(mb);
1067   }
1068
1069   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
1070
1071   mball_foreachScreenElem(
1072       vc, do_lasso_select_mball__doSelectElem, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
1073
1074   return data.is_changed;
1075 }
1076
1077 static void do_lasso_select_meshobject__doSelectVert(void *userData,
1078                                                      MVert *mv,
1079                                                      const float screen_co[2],
1080                                                      int UNUSED(index))
1081 {
1082   LassoSelectUserData *data = userData;
1083   const bool is_select = mv->flag & SELECT;
1084   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1085                           BLI_lasso_is_point_inside(
1086                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
1087   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1088   if (sel_op_result != -1) {
1089     SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT);
1090     data->is_changed = true;
1091   }
1092 }
1093 static bool do_lasso_select_paintvert(ViewContext *vc,
1094                                       const int mcords[][2],
1095                                       short moves,
1096                                       const eSelectOp sel_op)
1097 {
1098   struct EditSelectBuf_Cache esel = {NULL};
1099   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
1100   Object *ob = vc->obact;
1101   Mesh *me = ob->data;
1102   rcti rect;
1103
1104   if (me == NULL || me->totvert == 0) {
1105     return false;
1106   }
1107
1108   bool changed = false;
1109   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1110     /* flush selection at the end */
1111     changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
1112   }
1113
1114   BLI_lasso_boundbox(&rect, mcords, moves);
1115
1116   if (use_zbuf) {
1117     editselect_buf_cache_init(&esel, vc);
1118
1119     const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
1120     esel.select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
1121   }
1122
1123   if (use_zbuf) {
1124     changed |= edbm_backbuf_check_and_select_verts_obmode(me, &esel, sel_op);
1125   }
1126   else {
1127     LassoSelectUserData data;
1128
1129     view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
1130
1131     ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d);
1132
1133     meshobject_foreachScreenVert(
1134         vc, do_lasso_select_meshobject__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
1135
1136     changed |= data.is_changed;
1137   }
1138
1139   editselect_buf_cache_free(&esel);
1140
1141   if (changed) {
1142     if (SEL_OP_CAN_DESELECT(sel_op)) {
1143       BKE_mesh_mselect_validate(me);
1144     }
1145     paintvert_flush_flags(ob);
1146     paintvert_tag_select_update(vc->C, ob);
1147   }
1148
1149   return changed;
1150 }
1151 static bool do_lasso_select_paintface(ViewContext *vc,
1152                                       const int mcords[][2],
1153                                       short moves,
1154                                       const eSelectOp sel_op)
1155 {
1156   Object *ob = vc->obact;
1157   Mesh *me = ob->data;
1158   rcti rect;
1159
1160   if (me == NULL || me->totpoly == 0) {
1161     return false;
1162   }
1163
1164   bool changed = false;
1165   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1166     /* flush selection at the end */
1167     changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
1168   }
1169
1170   BLI_lasso_boundbox(&rect, mcords, moves);
1171
1172   {
1173     struct EditSelectBuf_Cache esel = {NULL};
1174     editselect_buf_cache_init(&esel, vc);
1175     const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
1176     esel.select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
1177     changed |= edbm_backbuf_check_and_select_faces_obmode(me, &esel, sel_op);
1178     editselect_buf_cache_free(&esel);
1179   }
1180
1181   if (changed) {
1182     paintface_flush_flags(vc->C, ob, SELECT);
1183   }
1184   return changed;
1185 }
1186
1187 #if 0
1188 static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp sel_op)
1189 {
1190   SpaceNode *snode = sa->spacedata.first;
1191
1192   bNode *node;
1193   rcti rect;
1194   int node_cent[2];
1195   float node_centf[2];
1196   bool changed = false;
1197
1198   BLI_lasso_boundbox(&rect, mcords, moves);
1199
1200   /* store selection in temp test flag */
1201   for (node = snode->edittree->nodes.first; node; node = node->next) {
1202     node_centf[0] = BLI_RCT_CENTER_X(&node->totr);
1203     node_centf[1] = BLI_RCT_CENTER_Y(&node->totr);
1204
1205     ipoco_to_areaco_noclip(G.v2d, node_centf, node_cent);
1206     const bool is_select = node->flag & SELECT;
1207     const bool is_inside = (BLI_rcti_isect_pt_v(&rect, node_cent) &&
1208                             BLI_lasso_is_point_inside(mcords, moves, node_cent[0], node_cent[1]));
1209     const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1210     if (sel_op_result != -1) {
1211       SET_FLAG_FROM_TEST(node->flag, sel_op_result, SELECT);
1212       changed = true;
1213     }
1214   }
1215   if (changed) {
1216     BIF_undo_push("Lasso select nodes");
1217   }
1218 }
1219 #endif
1220
1221 static bool view3d_lasso_select(
1222     bContext *C, ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op)
1223 {
1224   Object *ob = CTX_data_active_object(C);
1225   bool changed_multi = false;
1226
1227   if (vc->obedit == NULL) { /* Object Mode */
1228     if (BKE_paint_select_face_test(ob)) {
1229       changed_multi |= do_lasso_select_paintface(vc, mcords, moves, sel_op);
1230     }
1231     else if (BKE_paint_select_vert_test(ob)) {
1232       changed_multi |= do_lasso_select_paintvert(vc, mcords, moves, sel_op);
1233     }
1234     else if (ob &&
1235              (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
1236       /* pass */
1237     }
1238     else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1239       changed_multi |= PE_lasso_select(C, mcords, moves, sel_op);
1240     }
1241     else if (ob && (ob->mode & OB_MODE_POSE)) {
1242       changed_multi |= do_lasso_select_pose(vc, mcords, moves, sel_op);
1243     }
1244     else {
1245       changed_multi |= do_lasso_select_objects(vc, mcords, moves, sel_op);
1246     }
1247   }
1248   else { /* Edit Mode */
1249
1250     /* TODO(campbell): cache selection buffer between executions. */
1251     struct EditSelectBuf_Cache esel = {NULL};
1252
1253     FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter) {
1254       ED_view3d_viewcontext_init_object(vc, ob_iter);
1255       bool changed = false;
1256
1257       switch (vc->obedit->type) {
1258         case OB_MESH:
1259           changed = do_lasso_select_mesh(vc, mcords, moves, &esel, sel_op);
1260           break;
1261         case OB_CURVE:
1262         case OB_SURF:
1263           changed = do_lasso_select_curve(vc, mcords, moves, sel_op);
1264           break;
1265         case OB_LATTICE:
1266           changed = do_lasso_select_lattice(vc, mcords, moves, sel_op);
1267           break;
1268         case OB_ARMATURE:
1269           changed = do_lasso_select_armature(vc, mcords, moves, sel_op);
1270           break;
1271         case OB_MBALL:
1272           changed = do_lasso_select_meta(vc, mcords, moves, sel_op);
1273           break;
1274         default:
1275           assert(!"lasso select on incorrect object type");
1276           break;
1277       }
1278
1279       if (changed) {
1280         DEG_id_tag_update(vc->obedit->data, ID_RECALC_SELECT);
1281         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data);
1282         changed_multi = true;
1283       }
1284     }
1285     FOREACH_OBJECT_IN_MODE_END;
1286
1287     editselect_buf_cache_free(&esel);
1288   }
1289   return changed_multi;
1290 }
1291
1292 /* lasso operator gives properties, but since old code works
1293  * with short array we convert */
1294 static int view3d_lasso_select_exec(bContext *C, wmOperator *op)
1295 {
1296   ViewContext vc;
1297   int mcords_tot;
1298   const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
1299
1300   if (mcords) {
1301     view3d_operator_needs_opengl(C);
1302     BKE_object_update_select_id(CTX_data_main(C));
1303
1304     /* setup view context for argument to callbacks */
1305     ED_view3d_viewcontext_init(C, &vc);
1306
1307     eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1308     bool changed_multi = view3d_lasso_select(C, &vc, mcords, mcords_tot, sel_op);
1309
1310     MEM_freeN((void *)mcords);
1311
1312     if (changed_multi) {
1313       return OPERATOR_FINISHED;
1314     }
1315     else {
1316       return OPERATOR_CANCELLED;
1317     }
1318   }
1319   return OPERATOR_PASS_THROUGH;
1320 }
1321
1322 void VIEW3D_OT_select_lasso(wmOperatorType *ot)
1323 {
1324   ot->name = "Lasso Select";
1325   ot->description = "Select items using lasso selection";
1326   ot->idname = "VIEW3D_OT_select_lasso";
1327
1328   ot->invoke = WM_gesture_lasso_invoke;
1329   ot->modal = WM_gesture_lasso_modal;
1330   ot->exec = view3d_lasso_select_exec;
1331   ot->poll = view3d_selectable_data;
1332   ot->cancel = WM_gesture_lasso_cancel;
1333
1334   /* flags */
1335   ot->flag = OPTYPE_UNDO;
1336
1337   /* properties */
1338   WM_operator_properties_gesture_lasso(ot);
1339   WM_operator_properties_select_operation(ot);
1340 }
1341
1342 /** \} */
1343
1344 /* -------------------------------------------------------------------- */
1345 /** \name Cursor Picking
1346  * \{ */
1347
1348 /* The max number of menu items in an object select menu */
1349 typedef struct SelMenuItemF {
1350   char idname[MAX_ID_NAME - 2];
1351   int icon;
1352 } SelMenuItemF;
1353
1354 #define SEL_MENU_SIZE 22
1355 static SelMenuItemF object_mouse_select_menu_data[SEL_MENU_SIZE];
1356
1357 /* special (crappy) operator only for menu select */
1358 static const EnumPropertyItem *object_select_menu_enum_itemf(bContext *C,
1359                                                              PointerRNA *UNUSED(ptr),
1360                                                              PropertyRNA *UNUSED(prop),
1361                                                              bool *r_free)
1362 {
1363   EnumPropertyItem *item = NULL, item_tmp = {0};
1364   int totitem = 0;
1365   int i = 0;
1366
1367   /* don't need context but avoid docgen using this */
1368   if (C == NULL || object_mouse_select_menu_data[i].idname[0] == '\0') {
1369     return DummyRNA_NULL_items;
1370   }
1371
1372   for (; i < SEL_MENU_SIZE && object_mouse_select_menu_data[i].idname[0] != '\0'; i++) {
1373     item_tmp.name = object_mouse_select_menu_data[i].idname;
1374     item_tmp.identifier = object_mouse_select_menu_data[i].idname;
1375     item_tmp.value = i;
1376     item_tmp.icon = object_mouse_select_menu_data[i].icon;
1377     RNA_enum_item_add(&item, &totitem, &item_tmp);
1378   }
1379
1380   RNA_enum_item_end(&item, &totitem);
1381   *r_free = true;
1382
1383   return item;
1384 }
1385
1386 static int object_select_menu_exec(bContext *C, wmOperator *op)
1387 {
1388   const int name_index = RNA_enum_get(op->ptr, "name");
1389   const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1390   bool changed = false;
1391   const char *name = object_mouse_select_menu_data[name_index].idname;
1392
1393   if (!toggle) {
1394     CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1395       if ((base->flag & BASE_SELECTED) != 0) {
1396         ED_object_base_select(base, BA_DESELECT);
1397         changed = true;
1398       }
1399     }
1400     CTX_DATA_END;
1401   }
1402
1403   CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1404     /* This is a bit dodgy, there should only be ONE object with this name,
1405      * but library objects can mess this up. */
1406     if (STREQ(name, base->object->id.name + 2)) {
1407       ED_object_base_activate(C, base);
1408       ED_object_base_select(base, BA_SELECT);
1409       changed = true;
1410     }
1411   }
1412   CTX_DATA_END;
1413
1414   /* weak but ensures we activate menu again before using the enum */
1415   memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
1416
1417   /* undo? */
1418   if (changed) {
1419     Scene *scene = CTX_data_scene(C);
1420     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1421     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1422     return OPERATOR_FINISHED;
1423   }
1424   else {
1425     return OPERATOR_CANCELLED;
1426   }
1427 }
1428
1429 void VIEW3D_OT_select_menu(wmOperatorType *ot)
1430 {
1431   PropertyRNA *prop;
1432
1433   /* identifiers */
1434   ot->name = "Select Menu";
1435   ot->description = "Menu object selection";
1436   ot->idname = "VIEW3D_OT_select_menu";
1437
1438   /* api callbacks */
1439   ot->invoke = WM_menu_invoke;
1440   ot->exec = object_select_menu_exec;
1441
1442   /* flags */
1443   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1444
1445   /* keyingset to use (dynamic enum) */
1446   prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Object Name", "");
1447   RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
1448   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
1449   ot->prop = prop;
1450
1451   RNA_def_boolean(
1452       ot->srna, "toggle", 0, "Toggle", "Toggle selection instead of deselecting everything first");
1453 }
1454
1455 static Base *object_mouse_select_menu(
1456     bContext *C, ViewContext *vc, uint *buffer, int hits, const int mval[2], bool toggle)
1457 {
1458   short baseCount = 0;
1459   bool ok;
1460   LinkNode *linklist = NULL;
1461
1462   /* handle base->object->select_id */
1463   CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1464     ok = false;
1465
1466     /* two selection methods, the CTRL select uses max dist of 15 */
1467     if (buffer) {
1468       for (int a = 0; a < hits; a++) {
1469         /* index was converted */
1470         if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & ~0xFFFF0000)) {
1471           ok = true;
1472           break;
1473         }
1474       }
1475     }
1476     else {
1477       const int dist = 15 * U.pixelsize;
1478       if (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) {
1479         const int delta_px[2] = {base->sx - mval[0], base->sy - mval[1]};
1480         if (len_manhattan_v2_int(delta_px) < dist) {
1481           ok = true;
1482         }
1483       }
1484     }
1485
1486     if (ok) {
1487       baseCount++;
1488       BLI_linklist_prepend(&linklist, base);
1489
1490       if (baseCount == SEL_MENU_SIZE) {
1491         break;
1492       }
1493     }
1494   }
1495   CTX_DATA_END;
1496
1497   if (baseCount == 0) {
1498     return NULL;
1499   }
1500   if (baseCount == 1) {
1501     Base *base = (Base *)linklist->link;
1502     BLI_linklist_free(linklist, NULL);
1503     return base;
1504   }
1505   else {
1506     /* UI, full in static array values that we later use in an enum function */
1507     LinkNode *node;
1508     int i;
1509
1510     memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
1511
1512     for (node = linklist, i = 0; node; node = node->next, i++) {
1513       Base *base = node->link;
1514       Object *ob = base->object;
1515       const char *name = ob->id.name + 2;
1516
1517       BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
1518       object_mouse_select_menu_data[i].icon = UI_icon_from_id(&ob->id);
1519     }
1520
1521     {
1522       wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
1523       PointerRNA ptr;
1524
1525       WM_operator_properties_create_ptr(&ptr, ot);
1526       RNA_boolean_set(&ptr, "toggle", toggle);
1527       WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
1528       WM_operator_properties_free(&ptr);
1529     }
1530
1531     BLI_linklist_free(linklist, NULL);
1532     return NULL;
1533   }
1534 }
1535
1536 static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
1537 {
1538   uint i;
1539   for (i = 0; i < hits; i++) {
1540     if (buffer[(4 * i) + 3] & 0xFFFF0000) {
1541       return true;
1542     }
1543   }
1544   return false;
1545 }
1546
1547 /* utility function for mixed_bones_object_selectbuffer */
1548 static int selectbuffer_ret_hits_15(uint *UNUSED(buffer), const int hits15)
1549 {
1550   return hits15;
1551 }
1552
1553 static int selectbuffer_ret_hits_9(uint *buffer, const int hits15, const int hits9)
1554 {
1555   const int offs = 4 * hits15;
1556   memcpy(buffer, buffer + offs, 4 * hits9 * sizeof(uint));
1557   return hits9;
1558 }
1559
1560 static int selectbuffer_ret_hits_5(uint *buffer,
1561                                    const int hits15,
1562                                    const int hits9,
1563                                    const int hits5)
1564 {
1565   const int offs = 4 * hits15 + 4 * hits9;
1566   memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(uint));
1567   return hits5;
1568 }
1569
1570 /**
1571  * Populate a select buffer with objects and bones, if there are any.
1572  * Checks three selection levels and compare.
1573  */
1574 static int mixed_bones_object_selectbuffer(ViewContext *vc,
1575                                            uint *buffer,
1576                                            const int mval[2],
1577                                            eV3DSelectObjectFilter select_filter,
1578                                            bool do_nearest)
1579 {
1580   rcti rect;
1581   int hits15, hits9 = 0, hits5 = 0;
1582   bool has_bones15 = false, has_bones9 = false, has_bones5 = false;
1583
1584   const int select_mode = (do_nearest ? VIEW3D_SELECT_PICK_NEAREST : VIEW3D_SELECT_PICK_ALL);
1585   int hits = 0;
1586
1587   /* we _must_ end cache before return, use 'goto finally' */
1588   view3d_opengl_select_cache_begin();
1589
1590   BLI_rcti_init_pt_radius(&rect, mval, 14);
1591   hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter);
1592   if (hits15 == 1) {
1593     hits = selectbuffer_ret_hits_15(buffer, hits15);
1594     goto finally;
1595   }
1596   else if (hits15 > 0) {
1597     int offs;
1598     has_bones15 = selectbuffer_has_bones(buffer, hits15);
1599
1600     offs = 4 * hits15;
1601     BLI_rcti_init_pt_radius(&rect, mval, 9);
1602     hits9 = view3d_opengl_select(
1603         vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter);
1604     if (hits9 == 1) {
1605       hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
1606       goto finally;
1607     }
1608     else if (hits9 > 0) {
1609       has_bones9 = selectbuffer_has_bones(buffer + offs, hits9);
1610
1611       offs += 4 * hits9;
1612       BLI_rcti_init_pt_radius(&rect, mval, 5);
1613       hits5 = view3d_opengl_select(
1614           vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter);
1615       if (hits5 == 1) {
1616         hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
1617         goto finally;
1618       }
1619       else if (hits5 > 0) {
1620         has_bones5 = selectbuffer_has_bones(buffer + offs, hits5);
1621       }
1622     }
1623
1624     if (has_bones5) {
1625       hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
1626       goto finally;
1627     }
1628     else if (has_bones9) {
1629       hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
1630       goto finally;
1631     }
1632     else if (has_bones15) {
1633       hits = selectbuffer_ret_hits_15(buffer, hits15);
1634       goto finally;
1635     }
1636
1637     if (hits5 > 0) {
1638       hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
1639       goto finally;
1640     }
1641     else if (hits9 > 0) {
1642       hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
1643       goto finally;
1644     }
1645     else {
1646       hits = selectbuffer_ret_hits_15(buffer, hits15);
1647       goto finally;
1648     }
1649   }
1650
1651 finally:
1652   view3d_opengl_select_cache_end();
1653   return hits;
1654 }
1655
1656 static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
1657                                                     uint *buffer,
1658                                                     const int mval[2],
1659                                                     eV3DSelectObjectFilter select_filter,
1660                                                     bool use_cycle,
1661                                                     bool enumerate,
1662                                                     bool *r_do_nearest)
1663 {
1664   static int last_mval[2] = {-100, -100};
1665   bool do_nearest = false;
1666   View3D *v3d = vc->v3d;
1667
1668   /* define if we use solid nearest select or not */
1669   if (use_cycle) {
1670     if (!XRAY_ACTIVE(v3d)) {
1671       do_nearest = true;
1672       if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
1673         do_nearest = false;
1674       }
1675     }
1676     copy_v2_v2_int(last_mval, mval);
1677   }
1678   else {
1679     if (!XRAY_ACTIVE(v3d)) {
1680       do_nearest = true;
1681     }
1682   }
1683
1684   if (r_do_nearest) {
1685     *r_do_nearest = do_nearest;
1686   }
1687
1688   do_nearest = do_nearest && !enumerate;
1689
1690   int hits = mixed_bones_object_selectbuffer(vc, buffer, mval, select_filter, do_nearest);
1691
1692   if (vc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
1693     const bool is_pose_mode = ((vc->obact && vc->obact->mode & OB_MODE_POSE) ||
1694                                (select_filter == VIEW3D_SELECT_FILTER_WPAINT_POSE_MODE_LOCK));
1695     struct {
1696       uint data[4];
1697     } *buffer4 = (void *)buffer;
1698     uint j = 0;
1699     for (uint i = 0; i < hits; i++) {
1700       if (((buffer4[i].data[3] & 0xFFFF0000) != 0) == is_pose_mode) {
1701         if (i != j) {
1702           buffer4[j] = buffer4[i];
1703         }
1704         j++;
1705       }
1706     }
1707     hits = j;
1708   }
1709
1710   return hits;
1711 }
1712
1713 /* returns basact */
1714 static Base *mouse_select_eval_buffer(ViewContext *vc,
1715                                       const uint *buffer,
1716                                       int hits,
1717                                       Base *startbase,
1718                                       bool has_bones,
1719                                       bool do_nearest)
1720 {
1721   ViewLayer *view_layer = vc->view_layer;
1722   View3D *v3d = vc->v3d;
1723   Base *base, *basact = NULL;
1724   int a;
1725
1726   if (do_nearest) {
1727     uint min = 0xFFFFFFFF;
1728     int selcol = 0, notcol = 0;
1729
1730     if (has_bones) {
1731       /* we skip non-bone hits */
1732       for (a = 0; a < hits; a++) {
1733         if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) {
1734           min = buffer[4 * a + 1];
1735           selcol = buffer[4 * a + 3] & 0xFFFF;
1736         }
1737       }
1738     }
1739     else {
1740       /* only exclude active object when it is selected... */
1741       if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED) && hits > 1) {
1742         notcol = BASACT(view_layer)->object->runtime.select_id;
1743       }
1744
1745       for (a = 0; a < hits; a++) {
1746         if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) {
1747           min = buffer[4 * a + 1];
1748           selcol = buffer[4 * a + 3] & 0xFFFF;
1749         }
1750       }
1751     }
1752
1753     base = FIRSTBASE(view_layer);
1754     while (base) {
1755       if (BASE_SELECTABLE(v3d, base)) {
1756         if (base->object->runtime.select_id == selcol) {
1757           break;
1758         }
1759       }
1760       base = base->next;
1761     }
1762     if (base) {
1763       basact = base;
1764     }
1765   }
1766   else {
1767
1768     base = startbase;
1769     while (base) {
1770       /* skip objects with select restriction, to prevent prematurely ending this loop
1771        * with an un-selectable choice */
1772       if ((base->flag & BASE_SELECTABLE) == 0) {
1773         base = base->next;
1774         if (base == NULL) {
1775           base = FIRSTBASE(view_layer);
1776         }
1777         if (base == startbase) {
1778           break;
1779         }
1780       }
1781
1782       if (BASE_SELECTABLE(v3d, base)) {
1783         for (a = 0; a < hits; a++) {
1784           if (has_bones) {
1785             /* skip non-bone objects */
1786             if ((buffer[4 * a + 3] & 0xFFFF0000)) {
1787               if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
1788                 basact = base;
1789               }
1790             }
1791           }
1792           else {
1793             if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
1794               basact = base;
1795             }
1796           }
1797         }
1798       }
1799
1800       if (basact) {
1801         break;
1802       }
1803
1804       base = base->next;
1805       if (base == NULL) {
1806         base = FIRSTBASE(view_layer);
1807       }
1808       if (base == startbase) {
1809         break;
1810       }
1811     }
1812   }
1813
1814   return basact;
1815 }
1816
1817 /* mval comes from event->mval, only use within region handlers */
1818 Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
1819 {
1820   ViewContext vc;
1821   Base *basact = NULL;
1822   uint buffer[MAXPICKBUF];
1823
1824   /* setup view context for argument to callbacks */
1825   view3d_operator_needs_opengl(C);
1826   BKE_object_update_select_id(CTX_data_main(C));
1827
1828   ED_view3d_viewcontext_init(C, &vc);
1829
1830   const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
1831   const int hits = mixed_bones_object_selectbuffer(
1832       &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest);
1833
1834   if (hits > 0) {
1835     const bool has_bones = selectbuffer_has_bones(buffer, hits);
1836     basact = mouse_select_eval_buffer(
1837         &vc, buffer, hits, vc.view_layer->object_bases.first, has_bones, do_nearest);
1838   }
1839
1840   return basact;
1841 }
1842
1843 Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
1844 {
1845   Base *base = ED_view3d_give_base_under_cursor(C, mval);
1846   if (base) {
1847     return base->object;
1848   }
1849   return NULL;
1850 }
1851
1852 bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2])
1853 {
1854   return ED_view3d_give_object_under_cursor(C, mval) != NULL;
1855 }
1856
1857 static void deselect_all_tracks(MovieTracking *tracking)
1858 {
1859   MovieTrackingObject *object;
1860
1861   object = tracking->objects.first;
1862   while (object) {
1863     ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object);
1864     MovieTrackingTrack *track = tracksbase->first;
1865
1866     while (track) {
1867       BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
1868
1869       track = track->next;
1870     }
1871
1872     object = object->next;
1873   }
1874 }
1875
1876 /* mval is region coords */
1877 static bool ed_object_select_pick(bContext *C,
1878                                   const int mval[2],
1879                                   bool extend,
1880                                   bool deselect,
1881                                   bool toggle,
1882                                   bool obcenter,
1883                                   bool enumerate,
1884                                   bool object)
1885 {
1886   ViewContext vc;
1887   /* setup view context for argument to callbacks */
1888   ED_view3d_viewcontext_init(C, &vc);
1889
1890   ARegion *ar = CTX_wm_region(C);
1891   Scene *scene = CTX_data_scene(C);
1892   ViewLayer *view_layer = CTX_data_view_layer(C);
1893   View3D *v3d = CTX_wm_view3d(C);
1894   /* Don't set when the context has no active object (hidden), see: T60807. */
1895   const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL;
1896   Base *base, *startbase = NULL, *basact = NULL;
1897   const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT;
1898   bool is_obedit;
1899   float dist = ED_view3d_select_dist_px() * 1.3333f;
1900   bool retval = false;
1901   int hits;
1902   const float mval_fl[2] = {(float)mval[0], (float)mval[1]};
1903
1904   is_obedit = (vc.obedit != NULL);
1905   if (object) {
1906     /* signal for view3d_opengl_select to skip editmode objects */
1907     vc.obedit = NULL;
1908   }
1909
1910   /* In pose mode we don't want to mess with object selection. */
1911   const bool is_pose_mode = (vc.obact && vc.obact->mode & OB_MODE_POSE);
1912
1913   /* always start list from basact in wire mode */
1914   startbase = FIRSTBASE(view_layer);
1915   if (oldbasact && oldbasact->next) {
1916     startbase = oldbasact->next;
1917   }
1918
1919   /* This block uses the control key to make the object selected
1920    * by its center point rather than its contents */
1921
1922   /* in editmode do not activate */
1923   if (obcenter) {
1924
1925     /* note; shift+alt goes to group-flush-selecting */
1926     if (enumerate) {
1927       basact = object_mouse_select_menu(C, &vc, NULL, 0, mval, toggle);
1928     }
1929     else {
1930       base = startbase;
1931       while (base) {
1932         if (BASE_SELECTABLE(v3d, base)) {
1933           float screen_co[2];
1934           if (ED_view3d_project_float_global(ar,
1935                                              base->object->obmat[3],
1936                                              screen_co,
1937                                              V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN |
1938                                                  V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1939             float dist_temp = len_manhattan_v2v2(mval_fl, screen_co);
1940             if (base == oldbasact) {
1941               dist_temp += 10.0f;
1942             }
1943             if (dist_temp < dist) {
1944               dist = dist_temp;
1945               basact = base;
1946             }
1947           }
1948         }
1949         base = base->next;
1950
1951         if (base == NULL) {
1952           base = FIRSTBASE(view_layer);
1953         }
1954         if (base == startbase) {
1955           break;
1956         }
1957       }
1958     }
1959     if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
1960       if (is_obedit == false) {
1961         if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
1962           if (object_mode == OB_MODE_OBJECT) {
1963             struct Main *bmain = CTX_data_main(C);
1964             ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
1965           }
1966           if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
1967             basact = NULL;
1968           }
1969         }
1970       }
1971     }
1972   }
1973   else {
1974     uint buffer[MAXPICKBUF];
1975     bool do_nearest;
1976
1977     // TIMEIT_START(select_time);
1978
1979     /* if objects have posemode set, the bones are in the same selection buffer */
1980     const eV3DSelectObjectFilter select_filter = ((object == false) ?
1981                                                       ED_view3d_select_filter_from_mode(scene,
1982                                                                                         vc.obact) :
1983                                                       VIEW3D_SELECT_FILTER_NOP);
1984     hits = mixed_bones_object_selectbuffer_extended(
1985         &vc, buffer, mval, select_filter, true, enumerate, &do_nearest);
1986
1987     // TIMEIT_END(select_time);
1988
1989     if (hits > 0) {
1990       /* note: bundles are handling in the same way as bones */
1991       const bool has_bones = selectbuffer_has_bones(buffer, hits);
1992
1993       /* note; shift+alt goes to group-flush-selecting */
1994       if (enumerate) {
1995         basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, toggle);
1996       }
1997       else {
1998         basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest);
1999       }
2000
2001       if (has_bones && basact) {
2002         if (basact->object->type == OB_CAMERA) {
2003           if (oldbasact == basact) {
2004             int i, hitresult;
2005             bool changed = false;
2006
2007             for (i = 0; i < hits; i++) {
2008               hitresult = buffer[3 + (i * 4)];
2009
2010               /* if there's bundles in buffer select bundles first,
2011                * so non-camera elements should be ignored in buffer */
2012               if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) {
2013                 continue;
2014               }
2015
2016               /* index of bundle is 1<<16-based. if there's no "bone" index
2017                * in height word, this buffer value belongs to camera. not to bundle
2018                */
2019               if (buffer[4 * i + 3] & 0xFFFF0000) {
2020                 MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
2021                 MovieTracking *tracking = &clip->tracking;
2022                 ListBase *tracksbase;
2023                 MovieTrackingTrack *track;
2024
2025                 track = BKE_tracking_track_get_indexed(
2026                     &clip->tracking, hitresult >> 16, &tracksbase);
2027
2028                 if (TRACK_SELECTED(track) && extend) {
2029                   changed = false;
2030                   BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
2031                 }
2032                 else {
2033                   int oldsel = TRACK_SELECTED(track) ? 1 : 0;
2034                   if (!extend) {
2035                     deselect_all_tracks(tracking);
2036                   }
2037
2038                   BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, extend);
2039
2040                   if (oldsel != (TRACK_SELECTED(track) ? 1 : 0)) {
2041                     changed = true;
2042                   }
2043                 }
2044
2045                 ED_object_base_select(basact, BA_SELECT);
2046
2047                 retval = true;
2048
2049                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
2050                 DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
2051                 WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
2052                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
2053
2054                 break;
2055               }
2056             }
2057
2058             if (!changed) {
2059               /* fallback to regular object selection if no new bundles were selected,
2060                * allows to select object parented to reconstruction object */
2061               basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest);
2062             }
2063           }
2064         }
2065         else if (ED_armature_pose_select_pick_with_buffer(view_layer,
2066                                                           v3d,
2067                                                           basact,
2068                                                           buffer,
2069                                                           hits,
2070                                                           extend,
2071                                                           deselect,
2072                                                           toggle,
2073                                                           do_nearest)) {
2074           /* then bone is found */
2075
2076           /* we make the armature selected:
2077            * not-selected active object in posemode won't work well for tools */
2078           ED_object_base_select(basact, BA_SELECT);
2079
2080           retval = true;
2081           WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
2082           WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
2083
2084           /* in weightpaint, we use selected bone to select vertexgroup,
2085            * so no switch to new active object */
2086           if (oldbasact && (oldbasact->object->mode & OB_MODE_WEIGHT_PAINT)) {
2087             /* prevent activating */
2088             basact = NULL;
2089           }
2090         }
2091         /* prevent bone selecting to pass on to object selecting */
2092         if (basact == oldbasact) {
2093           basact = NULL;
2094         }
2095       }
2096
2097       if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
2098         if (is_obedit == false) {
2099           if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
2100             if (object_mode == OB_MODE_OBJECT) {
2101               struct Main *bmain = CTX_data_main(C);
2102               ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
2103             }
2104             if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2105               basact = NULL;
2106             }
2107           }
2108         }
2109       }
2110     }
2111   }
2112
2113   if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
2114     /* Disallow switching modes,
2115      * special exception for edit-mode - vertex-parent operator. */
2116     if (is_obedit == false) {
2117       if (oldbasact && basact) {
2118         if ((oldbasact->object->mode != basact->object->mode) &&
2119             (oldbasact->object->mode & basact->object->mode) == 0) {
2120           basact = NULL;
2121         }
2122       }
2123     }
2124   }
2125
2126   /* Ensure code above doesn't change the active base. */
2127   BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL));
2128
2129   /* so, do we have something selected? */
2130   if (basact) {
2131     retval = true;
2132
2133     if (vc.obedit) {
2134       /* only do select */
2135       object_deselect_all_except(view_layer, basact);
2136       ED_object_base_select(basact, BA_SELECT);
2137     }
2138     /* also prevent making it active on mouse selection */
2139     else if (BASE_SELECTABLE(v3d, basact)) {
2140       if (extend) {
2141         ED_object_base_select(basact, BA_SELECT);
2142       }
2143       else if (deselect) {
2144         ED_object_base_select(basact, BA_DESELECT);
2145       }
2146       else if (toggle) {
2147         if (basact->flag & BASE_SELECTED) {
2148           if (basact == oldbasact) {
2149             ED_object_base_select(basact, BA_DESELECT);
2150           }
2151         }
2152         else {
2153           ED_object_base_select(basact, BA_SELECT);
2154         }
2155       }
2156       else {
2157         /* When enabled, this puts other objects out of multi pose-mode. */
2158         if (is_pose_mode == false) {
2159           object_deselect_all_except(view_layer, basact);
2160           ED_object_base_select(basact, BA_SELECT);
2161         }
2162       }
2163
2164       if ((oldbasact != basact) && (is_obedit == false)) {
2165         ED_object_base_activate(C, basact); /* adds notifier */
2166       }
2167
2168       /* Set special modes for grease pencil
2169        * The grease pencil modes are not real modes, but a hack to make the interface
2170        * consistent, so need some tricks to keep UI synchronized */
2171       // XXX: This stuff needs reviewing (Aligorith)
2172       if (false && (((oldbasact) && oldbasact->object->type == OB_GPENCIL) ||
2173                     (basact->object->type == OB_GPENCIL))) {
2174         /* set cursor */
2175         if (ELEM(basact->object->mode,
2176                  OB_MODE_PAINT_GPENCIL,
2177                  OB_MODE_SCULPT_GPENCIL,
2178                  OB_MODE_WEIGHT_GPENCIL)) {
2179           ED_gpencil_toggle_brush_cursor(C, true, NULL);
2180         }
2181         else {
2182           /* TODO: maybe is better use restore */
2183           ED_gpencil_toggle_brush_cursor(C, false, NULL);
2184         }
2185       }
2186     }
2187
2188     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
2189     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
2190   }
2191
2192   return retval;
2193 }
2194
2195 /* mouse selection in weight paint */
2196 /* gets called via generic mouse select operator */
2197 static bool ed_wpaint_vertex_select_pick(
2198     bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, Object *obact)
2199 {
2200   View3D *v3d = CTX_wm_view3d(C);
2201   const bool use_zbuf = !XRAY_ENABLED(v3d);
2202
2203   Mesh *me = obact->data; /* already checked for NULL */
2204   uint index = 0;
2205   MVert *mv;
2206
2207   if (ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index)) {
2208     mv = &me->mvert[index];
2209     if (extend) {
2210       mv->flag |= SELECT;
2211     }
2212     else if (deselect) {
2213       mv->flag &= ~SELECT;
2214     }
2215     else if (toggle) {
2216       mv->flag ^= SELECT;
2217     }
2218     else {
2219       paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
2220       mv->flag |= SELECT;
2221     }
2222
2223     /* update mselect */
2224     if (mv->flag & SELECT) {
2225       BKE_mesh_mselect_active_set(me, index, ME_VSEL);
2226     }
2227     else {
2228       BKE_mesh_mselect_validate(me);
2229     }
2230
2231     paintvert_flush_flags(obact);
2232     paintvert_tag_select_update(C, obact);
2233     return true;
2234   }
2235   return false;
2236 }
2237
2238 static int view3d_select_exec(bContext *C, wmOperator *op)
2239 {
2240   Scene *scene = CTX_data_scene(C);
2241   Object *obedit = CTX_data_edit_object(C);
2242   Object *obact = CTX_data_active_object(C);
2243   bool extend = RNA_boolean_get(op->ptr, "extend");
2244   bool deselect = RNA_boolean_get(op->ptr, "deselect");
2245   bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
2246   bool toggle = RNA_boolean_get(op->ptr, "toggle");
2247   bool center = RNA_boolean_get(op->ptr, "center");
2248   bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
2249   /* only force object select for editmode to support vertex parenting,
2250    * or paint-select to allow pose bone select with vert/face select */
2251   bool object = (RNA_boolean_get(op->ptr, "object") &&
2252                  (obedit || BKE_paint_select_elem_test(obact) ||
2253                   /* so its possible to select bones in weightpaint mode (LMB select) */
2254                   (obact && (obact->mode & OB_MODE_WEIGHT_PAINT) &&
2255                    BKE_object_pose_armature_get(obact))));
2256
2257   bool retval = false;
2258   int location[2];
2259
2260   RNA_int_get_array(op->ptr, "location", location);
2261
2262   view3d_operator_needs_opengl(C);
2263   BKE_object_update_select_id(CTX_data_main(C));
2264
2265   if (object) {
2266     obedit = NULL;
2267     obact = NULL;
2268
2269     /* ack, this is incorrect but to do this correctly we would need an
2270      * alternative editmode/objectmode keymap, this copies the functionality
2271      * from 2.4x where Ctrl+Select in editmode does object select only */
2272     center = false;
2273   }
2274
2275   if (obedit && object == false) {
2276     if (obedit->type == OB_MESH) {
2277       retval = EDBM_select_pick(C, location, extend, deselect, toggle);
2278       if (!retval && deselect_all) {
2279         retval = EDBM_mesh_deselect_all_multi(C);
2280       }
2281     }
2282     else if (obedit->type == OB_ARMATURE) {
2283       retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
2284       if (!retval && deselect_all) {
2285         retval = ED_armature_edit_deselect_all_visible_multi(C);
2286       }
2287     }
2288     else if (obedit->type == OB_LATTICE) {
2289       retval = ED_lattice_select_pick(C, location, extend, deselect, toggle);
2290       if (!retval && deselect_all) {
2291         retval = ED_lattice_deselect_all_multi(C);
2292       }
2293     }
2294     else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
2295       retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle);
2296       if (!retval && deselect_all) {
2297         retval = ED_curve_deselect_all_multi(C);
2298       }
2299     }
2300     else if (obedit->type == OB_MBALL) {
2301       retval = ED_mball_select_pick(C, location, extend, deselect, toggle);
2302       if (!retval && deselect_all) {
2303         retval = ED_mball_deselect_all_multi(C);
2304       }
2305     }
2306     else if (obedit->type == OB_FONT) {
2307       retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle);
2308       if (!retval && deselect_all) {
2309         /* pass */
2310       }
2311     }
2312   }
2313   else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
2314     retval = PE_mouse_particles(C, location, extend, deselect, toggle);
2315     if (!retval && deselect_all) {
2316       retval = PE_deselect_all_visible(C);
2317     }
2318   }
2319   else if (obact && BKE_paint_select_face_test(obact)) {
2320     retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
2321     if (!retval && deselect_all) {
2322       retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false);
2323     }
2324   }
2325   else if (BKE_paint_select_vert_test(obact)) {
2326     retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact);
2327     if (!retval && deselect_all) {
2328       retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
2329     }
2330   }
2331   else {
2332     retval = ed_object_select_pick(
2333         C, location, extend, deselect, toggle, center, enumerate, object);
2334     if (!retval && deselect_all) {
2335       if (ED_pose_object_from_context(C)) {
2336         retval = ED_pose_deselect_all_multi(C, SEL_DESELECT, false);
2337       }
2338       else {
2339         retval = ED_object_base_deselect_all(
2340             CTX_data_view_layer(C), CTX_wm_view3d(C), SEL_DESELECT);
2341         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
2342       }
2343     }
2344   }
2345
2346   /* Pass-through allows tweaks
2347    * FINISHED to signal one operator worked
2348    * */
2349   if (retval) {
2350     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
2351     return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2352   }
2353   else {
2354     return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */
2355   }
2356 }
2357
2358 static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2359 {
2360   RNA_int_set_array(op->ptr, "location", event->mval);
2361
2362   return view3d_select_exec(C, op);
2363 }
2364
2365 void VIEW3D_OT_select(wmOperatorType *ot)
2366 {
2367   PropertyRNA *prop;
2368
2369   /* identifiers */
2370   ot->name = "Select";
2371   ot->description = "Select and activate item(s)";
2372   ot->idname = "VIEW3D_OT_select";
2373
2374   /* api callbacks */
2375   ot->invoke = view3d_select_invoke;
2376   ot->exec = view3d_select_exec;
2377   ot->poll = ED_operator_view3d_active;
2378
2379   /* flags */
2380   ot->flag = OPTYPE_UNDO;
2381
2382   /* properties */
2383   WM_operator_properties_mouse_select(ot);
2384
2385   prop = RNA_def_boolean(
2386       ot->srna,
2387       "center",
2388       0,
2389       "Center",
2390       "Use the object center when selecting, in editmode used to extend object selection");
2391   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2392   prop = RNA_def_boolean(
2393       ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)");
2394   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2395   prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (editmode only)");
2396   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2397
2398   prop = RNA_def_int_vector(ot->srna,
2399                             "location",
2400                             2,
2401                             NULL,
2402                             INT_MIN,
2403                             INT_MAX,
2404                             "Location",
2405                             "Mouse location",
2406                             INT_MIN,
2407                             INT_MAX);
2408   RNA_def_property_flag(prop, PROP_HIDDEN);
2409 }
2410
2411 /** \} */
2412
2413 /* -------------------------------------------------------------------- */
2414 /** \name Box Select
2415  * \{ */
2416
2417 typedef struct BoxSelectUserData {
2418   ViewContext *vc;
2419   const rcti *rect;
2420   const rctf *rect_fl;
2421   rctf _rect_fl;
2422   eSelectOp sel_op;
2423
2424   /* runtime */
2425   bool is_done;
2426   bool is_changed;
2427 } BoxSelectUserData;
2428
2429 static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data,
2430                                            ViewContext *vc,
2431                                            const rcti *rect,
2432                                            const eSelectOp sel_op)
2433 {
2434   r_data->vc = vc;
2435
2436   r_data->rect = rect;
2437   r_data->rect_fl = &r_data->_rect_fl;
2438   BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
2439
2440   r_data->sel_op = sel_op;
2441
2442   /* runtime */
2443   r_data->is_done = false;
2444   r_data->is_changed = false;
2445 }
2446
2447 bool edge_inside_circle(const float cent[2],
2448                         float radius,
2449                         const float screen_co_a[2],
2450                         const float screen_co_b[2])
2451 {
2452   const float radius_squared = radius * radius;
2453   return (dist_squared_to_line_segment_v2(cent, screen_co_a, screen_co_b) < radius_squared);
2454 }
2455
2456 static void do_paintvert_box_select__doSelectVert(void *userData,
2457                                                   MVert *mv,
2458                                                   const float screen_co[2],
2459                                                   int UNUSED(index))
2460 {
2461   BoxSelectUserData *data = userData;
2462   const bool is_select = mv->flag & SELECT;
2463   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2464   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2465   if (sel_op_result != -1) {
2466     SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT);
2467     data->is_changed = true;
2468   }
2469 }
2470 static bool do_paintvert_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
2471 {
2472   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
2473
2474   Mesh *me;
2475
2476   me = vc->obact->data;
2477   if ((me == NULL) || (me->totvert == 0)) {
2478     return OPERATOR_CANCELLED;
2479   }
2480
2481   bool changed = false;
2482   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2483     changed |= paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, false);
2484   }
2485
2486   if (BLI_rcti_is_empty(rect)) {
2487     /* pass */
2488   }
2489   else if (use_zbuf) {
2490     struct EditSelectBuf_Cache esel = {NULL};
2491     editselect_buf_cache_init(&esel, vc);
2492     const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
2493     esel.select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
2494     changed |= edbm_backbuf_check_and_select_verts_obmode(me, &esel, sel_op);
2495     editselect_buf_cache_free(&esel);
2496   }
2497   else {
2498     BoxSelectUserData data;
2499
2500     view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2501
2502     ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d);
2503
2504     meshobject_foreachScreenVert(
2505         vc, do_paintvert_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2506     changed |= data.is_changed;
2507   }
2508
2509   if (changed) {
2510     if (SEL_OP_CAN_DESELECT(sel_op)) {
2511       BKE_mesh_mselect_validate(me);
2512     }
2513     paintvert_flush_flags(vc->obact);
2514     paintvert_tag_select_update(vc->C, vc->obact);
2515   }
2516   return changed;
2517 }
2518
2519 static bool do_paintface_box_select(ViewContext *vc, const rcti *rect, int sel_op)
2520 {
2521   Object *ob = vc->obact;
2522   Mesh *me;
2523
2524   me = BKE_mesh_from_object(ob);
2525   if ((me == NULL) || (me->totpoly == 0)) {
2526     return false;
2527   }
2528
2529   bool changed = false;
2530   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2531     changed |= paintface_deselect_all_visible(vc->C, vc->obact, SEL_DESELECT, false);
2532   }
2533
2534   if (BLI_rcti_is_empty(rect)) {
2535     /* pass */
2536   }
2537   else {
2538     struct EditSelectBuf_Cache esel = {NULL};
2539     editselect_buf_cache_init(&esel, vc);
2540     const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
2541     esel.select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
2542     changed |= edbm_backbuf_check_and_select_faces_obmode(me, &esel, sel_op);
2543     editselect_buf_cache_free(&esel);
2544   }
2545
2546   if (changed) {
2547     paintface_flush_flags(vc->C, vc->obact, SELECT);
2548   }
2549   return changed;
2550 }
2551
2552 static void do_nurbs_box_select__doSelect(void *userData,
2553                                           Nurb *UNUSED(nu),
2554                                           BPoint *bp,
2555                                           BezTriple *bezt,
2556                                           int beztindex,
2557                                           const float screen_co[2])
2558 {
2559   BoxSelectUserData *data = userData;
2560
2561   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2562   if (bp) {
2563     const bool is_select = bp->f1 & SELECT;
2564     const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2565     if (sel_op_result != -1) {
2566       SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
2567       data->is_changed = true;
2568     }
2569   }
2570   else {
2571     if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {
2572       /* can only be (beztindex == 0) here since handles are hidden */
2573       const bool is_select = bezt->f2 & SELECT;
2574       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2575       if (sel_op_result != -1) {
2576         SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, SELECT);
2577         data->is_changed = true;
2578       }
2579       bezt->f1 = bezt->f3 = bezt->f2;
2580     }
2581     else {
2582       char *flag_p = (&bezt->f1) + beztindex;
2583       const bool is_select = *flag_p & SELECT;
2584       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2585       if (sel_op_result != -1) {
2586         SET_FLAG_FROM_TEST(*flag_p, sel_op_result, SELECT);
2587         data->is_changed = true;
2588       }
2589     }
2590   }
2591 }
2592 static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op)
2593 {
2594   BoxSelectUserData data;
2595
2596   view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2597
2598   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2599     Curve *curve = (Curve *)vc->obedit->data;
2600     data.is_changed |= ED_curve_deselect_all(curve->editnurb);
2601   }
2602
2603   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2604   nurbs_foreachScreenVert(vc, do_nurbs_box_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2605   BKE_curve_nurb_vert_active_validate(vc->obedit->data);
2606
2607   return data.is_changed;
2608 }
2609
2610 static void do_lattice_box_select__doSelect(void *userData, BPoint *bp, const float screen_co[2])
2611 {
2612   BoxSelectUserData *data = userData;
2613   const bool is_select = bp->f1 & SELECT;
2614   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2615   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2616   if (sel_op_result != -1) {
2617     SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
2618     data->is_changed = true;
2619   }
2620 }
2621 static bool do_lattice_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op)
2622 {
2623   BoxSelectUserData data;
2624
2625   view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2626
2627   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2628     data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
2629   }
2630
2631   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2632   lattice_foreachScreenVert(
2633       vc, do_lattice_box_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2634
2635   return data.is_changed;
2636 }
2637
2638 static void do_mesh_box_select__doSelectVert(void *userData,
2639                                              BMVert *eve,
2640                                              const float screen_co[2],
2641                                              int UNUSED(index))
2642 {
2643   BoxSelectUserData *data = userData;
2644   const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
2645   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2646   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2647   if (sel_op_result != -1) {
2648     BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
2649     data->is_changed = true;
2650   }
2651 }
2652 struct BoxSelectUserData_ForMeshEdge {
2653   BoxSelectUserData *data;
2654   struct EditSelectBuf_Cache *esel;
2655   uint backbuf_offset;
2656 };
2657 static void do_mesh_box_select__doSelectEdge_pass0(
2658     void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
2659 {
2660   struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData;
2661   BoxSelectUserData *data = data_for_edge->data;
2662   const bool is_visible = (data_for_edge->esel ?
2663                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
2664                                                     data_for_edge->backbuf_offset + index) :
2665                                true);
2666   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
2667   const bool is_inside = (is_visible &&
2668                           edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
2669   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2670   if (sel_op_result != -1) {
2671     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
2672     data->is_done = true;
2673     data->is_changed = true;
2674   }
2675 }
2676 static void do_mesh_box_select__doSelectEdge_pass1(
2677     void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
2678 {
2679   struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData;
2680   BoxSelectUserData *data = data_for_edge->data;
2681   const bool is_visible = (data_for_edge->esel ?
2682                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
2683                                                     data_for_edge->backbuf_offset + index) :
2684                                true);
2685   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
2686   const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
2687   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2688   if (sel_op_result != -1) {
2689     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
2690     data->is_changed = true;
2691   }
2692 }
2693 static void do_mesh_box_select__doSelectFace(void *userData,
2694                                              BMFace *efa,
2695                                              const float screen_co[2],
2696                                              int UNUSED(index))
2697 {
2698   BoxSelectUserData *data = userData;
2699   const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
2700   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2701   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2702   if (sel_op_result != -1) {
2703     BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
2704     data->is_changed = true;
2705   }
2706 }
2707 static bool do_mesh_box_select(ViewContext *vc,
2708                                const rcti *rect,
2709                                struct EditSelectBuf_Cache *esel,
2710                                const eSelectOp sel_op)
2711 {
2712   BoxSelectUserData data;
2713   ToolSettings *ts = vc->scene->toolsettings;
2714
2715   view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2716
2717   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2718     if (vc->em->bm->totvertsel) {
2719       EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
2720       data.is_changed = true;
2721     }
2722   }
2723
2724   /* for non zbuf projections, don't change the GL state */
2725   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
2726
2727   GPU_matrix_set(vc->rv3d->viewmat);
2728
2729   const int use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
2730
2731   if (use_zbuf) {
2732     /* Lazy initialize. */
2733     if (esel->sel_id_ctx == NULL) {
2734       editselect_buf_cache_init(esel, vc);
2735       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
2736       esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
2737     }
2738   }
2739
2740   if (ts->selectmode & SCE_SELECT_VERTEX) {
2741     if (use_zbuf) {
2742       data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op);
2743     }
2744     else {
2745       mesh_foreachScreenVert(
2746           vc, do_mesh_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2747     }
2748   }
2749   if (ts->selectmode & SCE_SELECT_EDGE) {
2750     /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
2751     struct BoxSelectUserData_ForMeshEdge cb_data = {
2752         .data = &data,
2753         .esel = use_zbuf ? esel : NULL,
2754         .backbuf_offset = use_zbuf ?
2755                               EDBM_select_id_context_offset_for_object_elem(
2756                                   esel->sel_id_ctx, vc->obedit->runtime.select_id, BM_EDGE) :
2757                               0,
2758     };
2759     mesh_foreachScreenEdge(
2760         vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, V3D_PROJ_TEST_CLIP_NEAR);
2761     if (data.is_done == false) {
2762       mesh_foreachScreenEdge(
2763           vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, V3D_PROJ_TEST_CLIP_NEAR);
2764     }
2765   }
2766
2767   if (ts->selectmode & SCE_SELECT_FACE) {
2768     if (use_zbuf) {
2769       data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op);
2770     }
2771     else {
2772       mesh_foreachScreenFace(
2773           vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2774     }
2775   }
2776
2777   if (data.is_changed) {
2778     EDBM_selectmode_flush(vc->em);
2779   }
2780   return data.is_changed;
2781 }
2782
2783 static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
2784 {
2785   Object *ob = vc->obedit;
2786   MetaBall *mb = (MetaBall *)ob->data;
2787   MetaElem *ml;
2788   int a;
2789   bool changed = false;
2790
2791   uint buffer[MAXPICKBUF];
2792   int hits;
2793
2794   hits = view3d_opengl_select(
2795       vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
2796
2797   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2798     changed |= BKE_mball_deselect_all(mb);
2799   }
2800
2801   int metaelem_id = 0;
2802   for (ml = mb->editelems->first; ml; ml = ml->next, metaelem_id += 0x10000) {
2803     bool is_inside_radius = false;
2804     bool is_inside_stiff = false;
2805
2806     for (a = 0; a < hits; a++) {
2807       int hitresult = buffer[(4 * a) + 3];
2808
2809       if (hitresult == -1) {
2810         continue;
2811       }
2812       else if (hitresult & MBALL_NOSEL) {
2813         continue;
2814       }
2815
2816       const uint hit_object = hitresult & 0xFFFF;
2817       if (vc->obedit->runtime.select_id != hit_object) {
2818         continue;
2819       }
2820
2821       if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) {
2822         continue;
2823       }
2824
2825       if (hitresult & MBALLSEL_RADIUS) {
2826         is_inside_radius = true;
2827         break;
2828       }
2829
2830       if (hitresult & MBALLSEL_STIFF) {
2831         is_inside_stiff = true;
2832         break;
2833       }
2834     }
2835     const int flag_prev = ml->flag;
2836     if (is_inside_radius) {
2837       ml->flag |= MB_SCALE_RAD;
2838     }
2839     if (is_inside_stiff) {
2840       ml->flag &= ~MB_SCALE_RAD;
2841     }
2842
2843     const bool is_select = (ml->flag & SELECT);
2844     const bool is_inside = is_inside_radius || is_inside_stiff;
2845
2846     const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2847     if (sel_op_result != -1) {
2848       SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
2849     }
2850     changed |= (flag_prev != ml->flag);
2851   }
2852
2853   return changed;
2854 }
2855
2856 static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
2857 {
2858   bool changed = false;
2859   int a;
2860
2861   uint buffer[MAXPICKBUF];
2862   int hits;
2863
2864   hits = view3d_opengl_select(
2865       vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
2866
2867   uint bases_len = 0;
2868   Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
2869       vc->view_layer, vc->v3d, &bases_len);
2870
2871   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2872     changed |= ED_armature_edit_deselect_all_visible_multi_ex(bases, bases_len);
2873   }
2874
2875   for (uint base_index = 0; base_index < bases_len; base_index++) {
2876     Object *obedit = bases[base_index]->object;
2877     obedit->id.tag &= ~LIB_TAG_DOIT;
2878
2879     bArmature *arm = obedit->data;
2880     ED_armature_ebone_listbase_temp_clear(arm->edbo);
2881   }
2882
2883   /* first we only check points inside the border */
2884   for (a = 0; a < hits; a++) {
2885     int select_id = buffer[(4 * a) + 3];
2886     if (select_id != -1) {
2887       if ((select_id & 0xFFFF0000) == 0) {
2888         continue;
2889       }
2890
2891       EditBone *ebone;
2892       Base *base_edit = ED_armature_base_and_ebone_from_select_buffer(
2893           bases, bases_len, select_id, &ebone);
2894       ebone->temp.i |= select_id & BONESEL_ANY;
2895       base_edit->object->id.tag |= LIB_TAG_DOIT;
2896     }
2897   }
2898
2899   for (uint base_index = 0; base_index < bases_len; base_index++) {
2900     Object *obedit = bases[base_index]->object;
2901     if (obedit->id.tag & LIB_TAG_DOIT) {
2902       obedit->id.tag &= ~LIB_TAG_DOIT;
2903       changed |= ED_armature_edit_select_op_from_tagged(obedit->data, sel_op);
2904     }
2905   }
2906
2907   MEM_freeN(bases);
2908
2909   return changed;
2910 }
2911
2912 /**
2913  * Compare result of 'GPU_select': 'uint[4]',
2914  * needed for when we need to align with object draw-order.
2915  */
2916 static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
2917 {
2918   /* 4th element is select id */
2919   uint sel_a = ((uint *)sel_a_p)[3];
2920   uint sel_b = ((uint *)sel_b_p)[3];
2921
2922 #ifdef __BIG_ENDIAN__
2923   BLI_endian_switch_uint32(&sel_a);
2924   BLI_endian_switch_uint32(&sel_b);
2925 #endif
2926
2927   if (sel_a < sel_b) {
2928     return -1;
2929   }
2930   else if (sel_a > sel_b) {
2931     return 1;
2932   }
2933   else {
2934     return 0;
2935   }
2936 }
2937
2938 static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
2939 {
2940   View3D *v3d = vc->v3d;
2941   int totobj = MAXPICKBUF; /* XXX solve later */
2942
2943   /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
2944   uint *vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer");
2945   const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
2946                                                                                  vc->obact);
2947   const int hits = view3d_opengl_select(
2948       vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
2949
2950   for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) {
2951     base->object->id.tag &= ~LIB_TAG_DOIT;
2952   }
2953
2954   Base **bases = NULL;
2955   BLI_array_declare(bases);
2956
2957   bool changed = false;
2958   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2959     changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
2960   }
2961
2962   if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) {
2963     goto finally;
2964   }
2965
2966   for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) {
2967     if (BASE_SELECTABLE(v3d, base)) {
2968       if ((base->object->runtime.select_id & 0x0000FFFF) != 0) {
2969         BLI_array_append(bases, base);
2970       }
2971     }
2972   }
2973
2974   /* The draw order doesn't always match the order we populate the engine, see: T51695. */
2975   qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp);
2976
2977   for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) {
2978     Bone *bone;
2979     Base *base = ED_armature_base_and_bone_from_select_buffer(
2980         bases, BLI_array_len(bases), *col, &bone);
2981     if (base != NULL) {
2982       base->object->id.tag |= LIB_TAG_DOIT;
2983     }
2984   }
2985
2986   for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) {
2987     if (BASE_SELECTABLE(v3d, base)) {
2988       const bool is_select = base->flag & BASE_SELECTED;
2989       const bool is_inside = base->object->id.tag & LIB_TAG_DOIT;
2990       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2991       if (sel_op_result != -1) {
2992         ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT);
2993         changed = true;
2994       }
2995     }
2996   }
2997
2998 finally:
2999   if (bases != NULL) {
3000     MEM_freeN(bases);
3001   }
3002
3003   MEM_freeN(vbuffer);
3004
3005   if (changed) {
3006     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
3007     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
3008   }
3009   return changed;
3010 }
3011
3012 static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
3013 {
3014   uint bases_len;
3015   Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
3016
3017   int totobj = MAXPICKBUF; /* XXX solve later */
3018
3019   /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
3020   uint *vbuffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer");
3021   const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
3022                                                                                  vc->obact);
3023   const int hits = view3d_opengl_select(
3024       vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
3025   /*
3026    * LOGIC NOTES (theeth):
3027    * The buffer and ListBase have the same relative order, which makes the selection
3028    * very simple. Loop through both data sets at the same time, if the color
3029    * is the same as the object, we have a hit and can move to the next color
3030    * and object pair, if not, just move to the next object,
3031    * keeping the same color until we have a hit.
3032    */
3033
3034   if (hits > 0) {
3035     /* no need to loop if there's no hit */
3036
3037     /* The draw order doesn't always match the order we populate the engine, see: T51695. */
3038     qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp);
3039
3040     for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) {
3041       Bone *bone;
3042       Base *base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, *col, &bone);
3043
3044       if (base == NULL) {
3045         continue;
3046       }
3047
3048       /* Loop over contiguous bone hits for 'base'. */
3049       for (; col != col_end; col += 4) {
3050         /* should never fail */
3051         if (bone != NULL) {
3052           base->object->id.tag |= LIB_TAG_DOIT;
3053           bone->flag |= BONE_DONE;
3054         }
3055
3056         /* Select the next bone if we're not switching bases. */
3057         if (col + 4 != col_end) {
3058           if ((base->object->runtime.select_id & 0x0000FFFF) != (col[4] & 0x0000FFFF)) {
3059             break;
3060           }
3061           if (base->object->pose != NULL) {
3062             const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16;
3063             bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);
3064             bone = pchan ? pchan->bone : NULL;
3065           }
3066           else {
3067             bone = NULL;
3068           }
3069         }
3070       }
3071     }
3072   }
3073
3074   const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op);
3075   if (changed_multi) {
3076     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
3077     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
3078   }
3079
3080   if (bases != NULL) {
3081     MEM_freeN(bases);
3082   }
3083   MEM_freeN(vbuffer);
3084
3085   return changed_multi;
3086 }
3087
3088 static int view3d_box_select_exec(bContext *C, wmOperator *op)
3089 {
3090   ViewContext vc;
3091   rcti rect;
3092   bool changed_multi = false;
3093
3094   view3d_operator_needs_opengl(C);
3095   BKE_object_update_select_id(CTX_data_main(C));
3096
3097   /* setup view context for argument to callbacks */
3098   ED_view3d_viewcontext_init(C, &vc);
3099
3100   eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
3101   WM_operator_properties_border_to_rcti(op, &rect);
3102
3103   if (vc.obedit) {
3104
3105     struct EditSelectBuf_Cache esel = {NULL};
3106
3107     FOREACH_OBJECT_IN_MODE_BEGIN (
3108         vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter) {
3109       ED_view3d_viewcontext_init_object(&vc, ob_iter);
3110       bool changed = false;
3111
3112       switch (vc.obedit->type) {
3113         case OB_MESH:
3114           vc.em = BKE_editmesh_from_object(vc.obedit);
3115           changed = do_mesh_box_select(&vc, &rect, &esel, sel_op);
3116           if (changed) {
3117             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3118             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3119           }
3120           break;
3121         case OB_CURVE:
3122         case OB_SURF:
3123           changed = do_nurbs_box_select(&vc, &rect, sel_op);
3124           if (changed) {
3125             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3126             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3127           }
3128           break;
3129         case OB_MBALL:
3130           changed = do_meta_box_select(&vc, &rect, sel_op);
3131           if (changed) {
3132             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3133             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3134           }
3135           break;
3136         case OB_ARMATURE:
3137           changed = do_armature_box_select(&vc, &rect, sel_op);
3138           if (changed) {
3139             DEG_id_tag_update(&vc.obedit->id, ID_RECALC_SELECT);
3140             WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
3141           }
3142           break;
3143         case OB_LATTICE:
3144           changed = do_lattice_box_select(&vc, &rect, sel_op);
3145           if (changed) {
3146             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3147             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3148           }
3149           break;
3150         default:
3151           assert(!"box select on incorrect object type");
3152           break;
3153       }
3154       changed_multi |= changed;
3155     }
3156     FOREACH_OBJECT_IN_MODE_END;
3157
3158     editselect_buf_cache_free(&esel);
3159   }
3160   else { /* no editmode, unified for bones and objects */
3161     if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) {
3162       /* XXX, this is not selection, could be it's own operator. */
3163       changed_multi = ED_sculpt_mask_box_select(
3164           C, &vc, &rect, sel_op == SEL_OP_ADD ? true : false);
3165     }
3166     else if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
3167       changed_multi = do_paintface_box_select(&vc, &rect, sel_op);
3168     }
3169     else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
3170       changed_multi = do_paintvert_box_select(&vc, &rect, sel_op);
3171     }
3172     else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
3173       changed_multi = PE_box_select(C, &rect, sel_op);
3174     }
3175     else if (vc.obact && vc.obact->mode & OB_MODE_POSE) {
3176       changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
3177     }
3178     else { /* object mode with none active */
3179       changed_multi = do_object_box_select(C, &vc, &rect, sel_op);
3180     }
3181   }
3182
3183   if (changed_multi) {
3184     return OPERATOR_FINISHED;
3185   }
3186   else {
3187     return OPERATOR_CANCELLED;
3188   }
3189 }
3190
3191 void VIEW3D_OT_select_box(wmOperatorType *ot)
3192 {
3193   /* identifiers */
3194   ot->name = "Box Select";
3195   ot->description = "Select items using box selection";
3196   ot->idname = "VIEW3D_OT_select_box";
3197
3198   /* api callbacks */
3199   ot->invoke = WM_gesture_box_invoke;
3200   ot->exec = view3d_box_select_exec;
3201   ot->modal = WM_gesture_box_modal;
3202   ot->poll = view3d_selectable_data;
3203   ot->cancel = WM_gesture_box_cancel;
3204
3205   /* flags */
3206   ot->flag = OPTYPE_UNDO;
3207
3208   /* rna */
3209   WM_operator_properties_gesture_box(ot);
3210   WM_operator_properties_select_operation(ot);
3211 }
3212
3213 /** \} */
3214
3215 /* -------------------------------------------------------------------- */
3216 /** \name Circle Select
3217  * \{ */
3218
3219 typedef struct CircleSelectUserData {
3220   ViewContext *vc;
3221   bool select;
3222   int mval[2];
3223   float mval_fl[2];
3224   float radius;
3225   float radius_squared;
3226
3227   /* runtime */
3228   bool is_changed;
3229 } CircleSelectUserData;
3230
3231 static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data,
3232                                               ViewContext *vc,
3233                                               const bool select,
3234                                               const int mval[2],
3235                                               const float rad)
3236 {
3237   r_data->vc = vc;
3238   r_data->select = select;
3239   copy_v2_v2_int(r_data->mval, mval);
3240   r_data->mval_fl[0] = mval[0];
3241   r_data->mval_fl[1] = mval[1];
3242
3243   r_data->radius = rad;
3244   r_data->radius_squared = rad * rad;
3245
3246   /* runtime */
3247   r_data->is_changed = false;
3248 }
3249
3250 static void mesh_circle_doSelectVert(void *userData,
3251                                      BMVert *eve,
3252                                      const float screen_co[2],
3253                                      int UNUSED(index))
3254 {
3255   CircleSelectUserData *data = userData;
3256
3257   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3258     BM_vert_select_set(data->vc->em->bm, eve, data->select);
3259     data->is_changed = true;
3260   }
3261 }
3262 static void mesh_circle_doSelectEdge(void *userData,
3263                                      BMEdge *eed,
3264                                      const float screen_co_a[2],
3265                                      const float screen_co_b[2],
3266                                      int UNUSED(index))
3267 {
3268   CircleSelectUserData *data = userData;
3269
3270   if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
3271     BM_edge_select_set(data->vc->em->bm, eed, data->select);
3272     data->is_changed = true;
3273   }
3274 }
3275 static void mesh_circle_doSelectFace(void *userData,
3276                                      BMFace *efa,
3277                                      const float screen_co[2],
3278                                      int UNUSED(index))
3279 {
3280   CircleSelectUserData *data = userData;
3281
3282   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3283     BM_face_select_set(data->vc->em->bm, efa, data->select);
3284     data->is_changed = true;
3285   }
3286 }
3287
3288 static bool mesh_circle_select(ViewContext *vc,
3289                                struct EditSelectBuf_Cache *esel,
3290                                eSelectOp sel_op,
3291                                const int mval[2],
3292                                float rad)
3293 {
3294   ToolSettings *ts = vc->scene->toolsettings;
3295   CircleSelectUserData data;
3296   vc->em = BKE_editmesh_from_object(vc->obedit);
3297
3298   bool changed = false;
3299   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3300     if (vc->em->bm->totvertsel) {
3301       EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
3302       changed = true;
3303     }
3304   }
3305   const bool select = (sel_op != SEL_OP_SUB);
3306
3307   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3308
3309   view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
3310
3311   const int use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
3312
3313   if (use_zbuf) {
3314     /* Lazy initialize. */
3315     if (esel->sel_id_ctx == NULL) {
3316       editselect_buf_cache_init(esel, vc);
3317       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
3318       esel->select_bitmap = ED_select_buffer_bitmap_from_circle(
3319           buffer_len, mval, (int)(rad + 1.0f));
3320     }
3321   }
3322
3323   if (ts->selectmode & SCE_SELECT_VERTEX) {
3324     if (use_zbuf) {
3325       changed |= edbm_backbuf_check_and_select_verts(
3326           esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
3327     }
3328     else {
3329       mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
3330     }
3331   }
3332
3333   if (ts->selectmode & SCE_SELECT_EDGE) {
3334     if (use_zbuf) {
3335       changed |= edbm_backbuf_check_and_select_edges(
3336           esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
3337     }
3338     else {
3339       mesh_foreachScreenEdge(vc, mesh_circle_doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR);
3340     }
3341   }
3342
3343   if (ts->selectmode & SCE_SELECT_FACE) {
3344     if (use_zbuf) {
3345       changed |= edbm_backbuf_check_and_select_faces(
3346           esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
3347     }
3348     else {
3349       mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
3350     }
3351   }
3352
3353   changed |= data.is_changed;
3354
3355   if (changed) {
3356     EDBM_selectmode_flush(vc->em);
3357   }
3358   return changed;
3359 }
3360
3361 static bool paint_facesel_circle_select(ViewContext *vc,
3362                                         const eSelectOp sel_op,
3363                                         const int mval[2],
3364                                         float rad)
3365 {
3366   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
3367   Object *ob = vc->obact;
3368   Mesh *me = ob->data;
3369
3370   bool changed = false;
3371   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3372     /* flush selection at the end */
3373     changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
3374   }
3375
3376   {
3377     /* TODO(campbell): cache selection buffer between executions. */
3378     struct EditSelectBuf_Cache esel = {NULL};
3379     editselect_buf_cache_init(&esel, vc);
3380     const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
3381     esel.select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
3382     changed |= edbm_backbuf_check_and_select_faces_obmode(me, &esel, sel_op);
3383     editselect_buf_cache_free(&esel);
3384   }
3385
3386   if (changed) {
3387     paintface_flush_flags(vc->C, ob, SELECT);
3388   }
3389   return changed;
3390 }
3391
3392 static void paint_vertsel_circle_select_doSelectVert(void *userData,
3393                                                      MVert *mv,
3394                                                      const float screen_co[2],
3395                                                      int UNUSED(index))
3396 {
3397   CircleSelectUserData *data = userData;
3398
3399   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3400     SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT);
3401     data->is_changed = true;
3402   }
3403 }
3404 static bool paint_vertsel_circle_select(ViewContext *vc,
3405                                         const eSelectOp sel_op,
3406                                         const int mval[2],
3407                                         float rad)
3408 {
3409   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
3410   struct EditSelectBuf_Cache esel = {NULL};
3411   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
3412   Object *ob = vc->obact;
3413   Mesh *me = ob->data;
3414   /* CircleSelectUserData data = {NULL}; */ /* UNUSED */
3415
3416   bool changed = false;
3417   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3418     changed |= paintvert_deselect_all_visible(
3419         ob, SEL_DESELECT, false); /* flush selection at the end */
3420   }
3421
3422   const bool select = (sel_op != SEL_OP_SUB);
3423
3424   if (use_zbuf) {
3425     /* TODO(campbell): cache selection buffer between executions. */
3426     editselect_buf_cache_init(&esel, vc);
3427
3428     const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
3429     esel.select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
3430   }
3431
3432   if (use_zbuf) {
3433     changed |= edbm_backbuf_check_and_select_verts_obmode(me, &esel, sel_op);
3434   }
3435   else {
3436     CircleSelectUserData data;
3437
3438     ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
3439
3440     view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
3441     meshobject_foreachScreenVert(
3442         vc, paint_vertsel_circle_select_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
3443     changed |= data.is_changed;
3444   }
3445
3446   editselect_buf_cache_free(&esel);
3447
3448   if (changed) {
3449     if (sel_op == SEL_OP_SUB) {
3450       BKE_mesh_mselect_validate(me);
3451     }
3452     paintvert_flush_flags(ob);
3453     paintvert_tag_select_update(vc->C, ob);
3454   }
3455   return changed;
3456 }
3457
3458 static void nurbscurve_circle_doSelect(void *userData,
3459                                        Nurb *UNUSED(nu),
3460                                        BPoint *bp,
3461                                        BezTriple *bezt,
3462                                        int beztindex,
3463                                        const float screen_co[2])
3464 {
3465   CircleSelectUserData *data = userData;
3466
3467   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3468     if (bp) {
3469       bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
3470     }
3471     else {
3472       if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {