Blender V4.3
view3d_gizmo_preselect_type.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
15#include "MEM_guardedalloc.h"
16
17#include "DNA_mesh_types.h"
18#include "DNA_view3d_types.h"
19
20#include "BKE_context.hh"
21#include "BKE_editmesh.hh"
22#include "BKE_global.hh"
23#include "BKE_layer.hh"
24#include "BKE_mesh.hh"
25#include "BKE_mesh_wrapper.hh"
26#include "BKE_object.hh"
27
28#include "BLI_math_matrix.h"
29#include "BLI_math_vector.h"
30
31#include "DEG_depsgraph.hh"
33
34#include "RNA_access.hh"
35#include "RNA_define.hh"
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40#include "bmesh.hh"
41
42#include "ED_gizmo_library.hh"
43#include "ED_mesh.hh"
44#include "ED_screen.hh"
45#include "ED_view3d.hh"
46
47using blender::Array;
48using blender::float3;
49using blender::Span;
50using blender::Vector;
51
52/* -------------------------------------------------------------------- */
72{
73 if (G.moving == false) {
75 if (!(rv3d && (rv3d->rflag & RV3D_NAVIGATING))) {
76 return true;
77 }
78 }
80 return false;
81}
82
85/* -------------------------------------------------------------------- */
98
99static void gizmo_preselect_elem_draw(const bContext *C, wmGizmo *gz)
100{
101 if (!gizmo_preselect_poll_for_draw(C, gz)) {
102 return;
103 }
104
105 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
106 if (gz_ele->base_index != -1) {
107 Object *ob = gz_ele->bases[gz_ele->base_index]->object;
108 EDBM_preselect_elem_draw(gz_ele->psel, ob->object_to_world().ptr());
109 }
110}
111
112static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
113{
114 wmEvent *event = CTX_wm_window(C)->eventstate;
115 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
116
117 /* Hack: Switch action mode based on key input */
118 const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0;
119 const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0;
121 if (is_ctrl_pressed && !is_shift_pressed) {
123 }
124 if (!is_ctrl_pressed && is_shift_pressed) {
126 }
127
128 struct Best {
129 Object *ob;
130 BMElem *ele;
131 float dist;
132 int base_index;
133 } best{};
134 best.dist = ED_view3d_select_dist_px();
135
136 {
137 const Scene *scene = CTX_data_scene(C);
138 ViewLayer *view_layer = CTX_data_view_layer(C);
139 View3D *v3d = CTX_wm_view3d(C);
140 BKE_view_layer_synced_ensure(scene, view_layer);
141 if (gz_ele->bases.is_empty() ||
142 (gz_ele->bases[0] != BKE_view_layer_active_base_get(view_layer)))
143 {
144 gz_ele->bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, v3d);
145 }
146 }
147
149 copy_v2_v2_int(vc.mval, mval);
150
151 {
152 /* TODO: support faces. */
153 int base_index_vert = -1;
154 int base_index_edge = -1;
155 int base_index_face = -1;
156 BMVert *eve_test;
157 BMEdge *eed_test;
158 BMFace *efa_test;
159
161 gz_ele->bases,
162 false,
163 true,
164 &base_index_vert,
165 &base_index_edge,
166 &base_index_face,
167 &eve_test,
168 &eed_test,
169 &efa_test))
170 {
172 /* Delete action */
173 if (efa_test) {
174 best.ele = (BMElem *)efa_test;
175 best.base_index = base_index_face;
176 }
177 }
178
179 else {
180 /* Transform and create action */
181 if (eed_test) {
182 best.ele = (BMElem *)eed_test;
183 best.base_index = base_index_edge;
184 }
185 }
186
187 /* All actions use same vertex pre-selection. */
188 /* Re-topology should always prioritize edge pre-selection.
189 * Only pre-select a vertex when the cursor is really close to it. */
190 if (eve_test) {
191 BMVert *vert = (BMVert *)eve_test;
192 float vert_p_co[2], vert_co[3];
193 const float mval_f[2] = {float(vc.mval[0]), float(vc.mval[1])};
195 vert_co, gz_ele->bases[base_index_vert]->object->object_to_world().ptr(), vert->co);
196 ED_view3d_project_v2(vc.region, vert_co, vert_p_co);
197 float len = len_v2v2(vert_p_co, mval_f);
198 if (len < 35) {
199 best.ele = (BMElem *)eve_test;
200 best.base_index = base_index_vert;
201 }
202 if (!BM_vert_is_boundary(vert) &&
204 {
205 best.ele = (BMElem *)eve_test;
206 best.base_index = base_index_vert;
207 }
208 }
209
210 /* Check above should never fail, if it does it's an internal error. */
211 BLI_assert(best.base_index != -1);
212
213 Base *base = gz_ele->bases[best.base_index];
214 best.ob = base->object;
215 }
216 }
217
218 BMesh *bm = nullptr;
219
220 gz_ele->base_index = -1;
221 gz_ele->vert_index = -1;
222 gz_ele->edge_index = -1;
223 gz_ele->face_index = -1;
224
225 if (best.ele) {
226 gz_ele->base_index = best.base_index;
227 bm = BKE_editmesh_from_object(gz_ele->bases[gz_ele->base_index]->object)->bm;
228 BM_mesh_elem_index_ensure(bm, best.ele->head.htype);
229
230 if (best.ele->head.htype == BM_VERT) {
231 gz_ele->vert_index = BM_elem_index_get(best.ele);
232 }
233 else if (best.ele->head.htype == BM_EDGE) {
234 gz_ele->edge_index = BM_elem_index_get(best.ele);
235 }
236 else if (best.ele->head.htype == BM_FACE) {
237 gz_ele->face_index = BM_elem_index_get(best.ele);
238 }
239 }
240
241 if (best.ele) {
242 Span<float3> vert_positions;
243 {
244 Object *ob = gz_ele->bases[gz_ele->base_index]->object;
246 const Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
247 const Mesh *mesh_eval = BKE_object_get_editmesh_eval_cage(ob_eval);
248 if (BKE_mesh_wrapper_vert_len(mesh_eval) == bm->totvert) {
249 vert_positions = BKE_mesh_wrapper_vert_coords(mesh_eval);
250 }
251 }
252 EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, vert_positions);
253 EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval);
254 }
255 else {
258 }
259
260 RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
261 RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
262 RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
263 RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
264
265 if (best.ele) {
266 ARegion *region = CTX_wm_region(C);
268 }
269
270 return best.ele ? 0 : -1;
271}
272
274{
275 /* Needed so it's possible to "highlight" the gizmo without having the
276 * tweak operator attempting to handle it's input. */
278
279 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
280 if (gz_ele->psel == nullptr) {
282 }
283 gz_ele->base_index = -1;
284 new (&gz_ele->bases) Vector<Base *>();
285}
286
288{
289 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
291 gz_ele->psel = nullptr;
292 gz_ele->bases.~Vector();
293}
294
296 wmGizmo * /*gz*/,
297 const wmEvent * /*event*/)
298{
300}
301
303{
304 /* identifiers */
305 gzt->idname = "GIZMO_GT_mesh_preselect_elem_3d";
306
307 /* api callbacks */
313
314 gzt->struct_size = sizeof(MeshElemGizmo3D);
315
316 RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
317 RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX);
318 RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
319 RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX);
320}
321
324/* -------------------------------------------------------------------- */
335
337{
338 if (!gizmo_preselect_poll_for_draw(C, gz)) {
339 return;
340 }
341
343 if (gz_ring->base_index != -1) {
344 Object *ob = gz_ring->bases[gz_ring->base_index]->object;
345 EDBM_preselect_edgering_draw(gz_ring->psel, ob->object_to_world().ptr());
346 }
347}
348
349static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
350{
352 struct Best {
353 Object *ob;
354 BMEdge *eed;
355 float dist;
356 int base_index;
357 } best{};
358 best.dist = ED_view3d_select_dist_px();
359
360 struct Prev {
361 int base_index;
362 int edge_index;
363 } prev{};
364 prev.base_index = gz_ring->base_index;
365 prev.edge_index = gz_ring->edge_index;
366
367 {
368 const Scene *scene = CTX_data_scene(C);
369 ViewLayer *view_layer = CTX_data_view_layer(C);
370 View3D *v3d = CTX_wm_view3d(C);
371 BKE_view_layer_synced_ensure(scene, view_layer);
372 if (gz_ring->bases.is_empty() ||
373 (gz_ring->bases[0] != BKE_view_layer_active_base_get(view_layer)))
374 {
375 gz_ring->bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, v3d);
376 }
377 }
378
380 copy_v2_v2_int(vc.mval, mval);
381
382 uint base_index;
384 &vc, &best.dist, nullptr, false, false, nullptr, gz_ring->bases, &base_index);
385
386 if (eed_test) {
387 best.ob = gz_ring->bases[base_index]->object;
388 best.eed = eed_test;
389 best.base_index = base_index;
390 }
391
392 BMesh *bm = nullptr;
393 if (best.eed) {
394 gz_ring->base_index = best.base_index;
395 bm = BKE_editmesh_from_object(gz_ring->bases[gz_ring->base_index]->object)->bm;
397 gz_ring->edge_index = BM_elem_index_get(best.eed);
398 }
399 else {
400 gz_ring->base_index = -1;
401 gz_ring->edge_index = -1;
402 }
403
404 if ((prev.base_index == gz_ring->base_index) && (prev.edge_index == gz_ring->edge_index)) {
405 /* pass (only recalculate on change) */
406 }
407 else {
408 if (best.eed) {
409 Object *ob = gz_ring->bases[gz_ring->base_index]->object;
410 Scene *scene_eval = (Scene *)DEG_get_evaluated_id(vc.depsgraph, &vc.scene->id);
411 Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, ob);
412 BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
413 /* Re-allocate coords each update isn't ideal, however we can't be sure
414 * the mesh hasn't been edited since last update. */
415 Array<float3> storage;
417 vc.depsgraph, em_eval, scene_eval, ob_eval, storage);
418 EDBM_preselect_edgering_update_from_edge(gz_ring->psel, bm, best.eed, 1, vert_positions);
419 }
420 else {
422 }
423
424 RNA_int_set(gz->ptr, "object_index", gz_ring->base_index);
425 RNA_int_set(gz->ptr, "edge_index", gz_ring->edge_index);
426
427 ARegion *region = CTX_wm_region(C);
429 }
430
431 return best.eed ? 0 : -1;
432}
433
435{
436 /* Needed so it's possible to "highlight" the gizmo without having the
437 * tweak operator attempting to handle it's input. */
439
441 if (gz_ring->psel == nullptr) {
443 }
444 gz_ring->base_index = -1;
445 new (&gz_ring->bases) Vector<Base *>();
446}
447
449{
452 gz_ring->psel = nullptr;
453 gz_ring->bases.~Vector();
454}
455
457 wmGizmo * /*gz*/,
458 const wmEvent * /*event*/)
459{
461}
462
464{
465 /* identifiers */
466 gzt->idname = "GIZMO_GT_mesh_preselect_edgering_3d";
467
468 /* api callbacks */
474
475 gzt->struct_size = sizeof(MeshEdgeRingGizmo3D);
476
477 RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
478 RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
479}
480
483/* -------------------------------------------------------------------- */
492
495/* -------------------------------------------------------------------- */
503 const wmGizmo *gz,
504 Base **r_base,
505 BMElem **r_ele)
506{
507 const Scene *scene = CTX_data_scene(C);
508 ViewLayer *view_layer = CTX_data_view_layer(C);
509
510 const int object_index = RNA_int_get(gz->ptr, "object_index");
511
512 /* weak, allocate an array just to access the index. */
513 Base *base = nullptr;
514 Object *obedit = nullptr;
515 if (object_index != -1) {
517 scene, view_layer, CTX_wm_view3d(C));
518 if (object_index < bases.size()) {
519 base = bases[object_index];
520 obedit = base->object;
521 }
522 }
523
524 *r_base = base;
525 *r_ele = nullptr;
526
527 if (obedit) {
529 BMesh *bm = em->bm;
530 PropertyRNA *prop;
531
532 /* Ring select only defines edge, check properties exist first. */
533 prop = RNA_struct_find_property(gz->ptr, "vert_index");
534 const int vert_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
535 prop = RNA_struct_find_property(gz->ptr, "edge_index");
536 const int edge_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
537 prop = RNA_struct_find_property(gz->ptr, "face_index");
538 const int face_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
539
540 if (vert_index != -1) {
541 *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index);
542 }
543 else if (edge_index != -1) {
544 *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index);
545 }
546 else if (face_index != -1) {
547 *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index);
548 }
549 }
550}
551
553{
554 if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_elem_3d")) {
555 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
556 gz_ele->base_index = -1;
557 gz_ele->vert_index = -1;
558 gz_ele->edge_index = -1;
559 gz_ele->face_index = -1;
560 }
561 else if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_edgering_3d")) {
563 gz_ele->base_index = -1;
564 gz_ele->edge_index = -1;
565 }
566 else {
568 }
569
570 const char *prop_ids[] = {"object_index", "vert_index", "edge_index", "face_index"};
571 for (int i = 0; i < ARRAY_SIZE(prop_ids); i++) {
572 PropertyRNA *prop = RNA_struct_find_property(gz->ptr, prop_ids[i]);
573 if (prop == nullptr) {
574 continue;
575 }
576 RNA_property_int_set(gz->ptr, prop, -1);
577 }
578}
579
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
blender::Span< blender::float3 > BKE_editmesh_vert_coords_when_deformed(Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, Object *obedit, blender::Array< blender::float3 > &r_alloc)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Span< blender::float3 > BKE_mesh_wrapper_vert_coords(const Mesh *mesh)
int BKE_mesh_wrapper_vert_len(const Mesh *mesh)
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_editmesh_eval_cage(const Object *object)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
unsigned int uint
#define ARRAY_SIZE(arr)
#define STREQ(a, b)
ID * DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ RV3D_NAVIGATING
@ OPERATOR_PASS_THROUGH
bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, blender::Span< Base * > bases, bool use_boundary_vertices, bool use_boundary_edges, int *r_base_index_vert, int *r_base_index_edge, int *r_base_index_face, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
void EDBM_preselect_edgering_destroy(EditMesh_PreSelEdgeRing *psel)
void EDBM_preselect_elem_clear(EditMesh_PreSelElem *psel)
eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(EditMesh_PreSelElem *psel)
@ PRESELECT_ACTION_CREATE
Definition ED_mesh.hh:347
@ PRESELECT_ACTION_DELETE
Definition ED_mesh.hh:348
@ PRESELECT_ACTION_TRANSFORM
Definition ED_mesh.hh:346
void EDBM_preselect_edgering_clear(EditMesh_PreSelEdgeRing *psel)
void EDBM_preselect_elem_draw(EditMesh_PreSelElem *psel, const float matrix[4][4])
BMEdge * EDBM_edge_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan, float *r_dist_center, bool use_select_bias, bool use_cycle, BMEdge **r_eed_zbuf, blender::Span< Base * > bases, uint *r_base_index)
void EDBM_preselect_elem_destroy(EditMesh_PreSelElem *psel)
ViewContext em_setup_viewcontext(bContext *C)
EditMesh_PreSelElem * EDBM_preselect_elem_create()
void EDBM_preselect_edgering_draw(EditMesh_PreSelEdgeRing *psel, const float matrix[4][4])
void EDBM_preselect_elem_update_preview(EditMesh_PreSelElem *psel, ViewContext *vc, BMesh *bm, BMElem *ele, const int mval[2])
EditMesh_PreSelEdgeRing * EDBM_preselect_edgering_create()
void EDBM_preselect_edgering_update_from_edge(EditMesh_PreSelEdgeRing *psel, BMesh *bm, BMEdge *eed_start, int previewlines, blender::Span< blender::float3 > vert_positions)
void EDBM_preselect_action_set(EditMesh_PreSelElem *psel, eEditMesh_PreSelPreviewAction action)
void EDBM_preselect_elem_update_from_single(EditMesh_PreSelElem *psel, BMesh *bm, BMElem *ele, blender::Span< blender::float3 > vert_positions)
void EDBM_preselect_preview_clear(EditMesh_PreSelElem *psel)
void ED_region_tag_redraw_editor_overlays(ARegion *region)
Definition area.cc:669
float ED_view3d_select_dist_px()
void ED_view3d_project_v2(const ARegion *region, const float world[3], float r_region_co[2])
Read Guarded memory(de)allocation.
@ WM_GIZMO_HIDDEN_KEYMAP
@ KM_CTRL
Definition WM_types.hh:256
@ KM_SHIFT
Definition WM_types.hh:255
#define BM_elem_index_get(ele)
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMEdge * BM_edge_at_index_find(BMesh *bm, const int index)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMVert * BM_vert_at_index_find(BMesh *bm, const int index)
BMFace * BM_face_at_index_find(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
bool BM_vert_is_boundary(const BMVert *v)
int64_t size() const
const Depsgraph * depsgraph
int len
draw_view in_light_buf[] float
#define G(x, y, z)
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_int_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
float co[3]
int totvert
struct Object * object
EditMesh_PreSelEdgeRing * psel
EditMesh_PreSelElem * psel
ARegion * region
Definition ED_view3d.hh:73
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
Depsgraph * depsgraph
Definition ED_view3d.hh:68
StructRNA * srna
wmGizmoFnDraw draw
wmGizmoFnSetup setup
const char * idname
wmGizmoFnTestSelect test_select
wmGizmoFnInvoke invoke
wmGizmoFnFree free
const wmGizmoType * type
PointerRNA * ptr
eWM_GizmoFlag flag
struct wmEvent * eventstate
static void gizmo_preselect_elem_draw(const bContext *C, wmGizmo *gz)
void ED_view3d_gizmo_mesh_preselect_get_active(const bContext *C, const wmGizmo *gz, Base **r_base, BMElem **r_ele)
void ED_gizmotypes_preselect_3d()
static int gizmo_preselect_edgering_invoke(bContext *, wmGizmo *, const wmEvent *)
void ED_view3d_gizmo_mesh_preselect_clear(wmGizmo *gz)
static void GIZMO_GT_mesh_preselect_elem_3d(wmGizmoType *gzt)
static bool gizmo_preselect_poll_for_draw(const bContext *C, wmGizmo *gz)
static void gizmo_preselect_elem_setup(wmGizmo *gz)
static void gizmo_preselect_edgering_draw(const bContext *C, wmGizmo *gz)
static void gizmo_preselect_edgering_setup(wmGizmo *gz)
static int gizmo_preselect_elem_invoke(bContext *, wmGizmo *, const wmEvent *)
static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
static void gizmo_preselect_elem_free(wmGizmo *gz)
static void GIZMO_GT_mesh_preselect_edgering_3d(wmGizmoType *gzt)
static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
static void gizmo_preselect_edgering_free(wmGizmo *gz)
void WM_gizmotype_append(void(*gtfunc)(wmGizmoType *))