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