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