Blender V5.0
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
14
15#include "DNA_mesh_types.h"
16#include "DNA_view3d_types.h"
17
18#include "BKE_context.hh"
19#include "BKE_editmesh.hh"
20#include "BKE_global.hh"
21#include "BKE_layer.hh"
22#include "BKE_mesh.hh"
23#include "BKE_mesh_wrapper.hh"
24#include "BKE_object.hh"
25
26#include "BLI_math_matrix.h"
27#include "BLI_math_vector.h"
28
29#include "DEG_depsgraph.hh"
31
32#include "RNA_access.hh"
33#include "RNA_define.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "bmesh.hh"
39
40#include "ED_gizmo_library.hh"
41#include "ED_mesh.hh"
42#include "ED_screen.hh"
43#include "ED_view3d.hh"
44
45using blender::Array;
46using blender::float3;
47using blender::Span;
48using blender::Vector;
49
50/* -------------------------------------------------------------------- */
53
70{
71 if (G.moving == false) {
73 if (!(rv3d && (rv3d->rflag & RV3D_NAVIGATING))) {
74 return true;
75 }
76 }
78 return false;
79}
80
82
83/* -------------------------------------------------------------------- */
86
96
98{
100 return;
101 }
102
103 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
104 if (gz_ele->base_index != -1) {
105 Object *ob = gz_ele->bases[gz_ele->base_index]->object;
106 EDBM_preselect_elem_draw(gz_ele->psel, ob->object_to_world().ptr());
107 }
108}
109
110static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
111{
113 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
114
115 /* Hack: Switch action mode based on key input */
116 const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0;
117 const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0;
119 if (is_ctrl_pressed && !is_shift_pressed) {
121 }
122 if (!is_ctrl_pressed && is_shift_pressed) {
124 }
125
126 struct Best {
127 Object *ob;
128 BMElem *ele;
129 float dist;
130 int base_index;
131 } best{};
132 best.dist = ED_view3d_select_dist_px();
133
134 {
135 const Scene *scene = CTX_data_scene(C);
136 ViewLayer *view_layer = CTX_data_view_layer(C);
137 View3D *v3d = CTX_wm_view3d(C);
138 BKE_view_layer_synced_ensure(scene, view_layer);
139 if (gz_ele->bases.is_empty() ||
140 (gz_ele->bases[0] != BKE_view_layer_active_base_get(view_layer)))
141 {
142 gz_ele->bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, v3d);
143 }
144 }
145
147 copy_v2_v2_int(vc.mval, mval);
148
149 {
150 /* TODO: support faces. */
151 int base_index_vert = -1;
152 int base_index_edge = -1;
153 int base_index_face = -1;
154 BMVert *eve_test;
155 BMEdge *eed_test;
156 BMFace *efa_test;
157
159 gz_ele->bases,
160 false,
161 true,
162 &base_index_vert,
163 &base_index_edge,
164 &base_index_face,
165 &eve_test,
166 &eed_test,
167 &efa_test))
168 {
170 /* Delete action */
171 if (efa_test) {
172 best.ele = (BMElem *)efa_test;
173 best.base_index = base_index_face;
174 }
175 }
176
177 else {
178 /* Transform and create action */
179 if (eed_test) {
180 best.ele = (BMElem *)eed_test;
181 best.base_index = base_index_edge;
182 }
183 }
184
185 /* All actions use same vertex pre-selection. */
186 /* Re-topology should always prioritize edge pre-selection.
187 * Only pre-select a vertex when the cursor is really close to it. */
188 if (eve_test) {
189 BMVert *vert = eve_test;
190 float vert_p_co[2], vert_co[3];
191 const float mval_f[2] = {float(vc.mval[0]), float(vc.mval[1])};
193 vert_co, gz_ele->bases[base_index_vert]->object->object_to_world().ptr(), vert->co);
194 ED_view3d_project_v2(vc.region, vert_co, vert_p_co);
195 float len = len_v2v2(vert_p_co, mval_f);
196 if (len < 35) {
197 best.ele = (BMElem *)eve_test;
198 best.base_index = base_index_vert;
199 }
200 if (!BM_vert_is_boundary(vert) &&
202 {
203 best.ele = (BMElem *)eve_test;
204 best.base_index = base_index_vert;
205 }
206 }
207
208 /* Check above should never fail, if it does it's an internal error. */
209 BLI_assert(best.base_index != -1);
210
211 Base *base = gz_ele->bases[best.base_index];
212 best.ob = base->object;
213 }
214 }
215
216 BMesh *bm = nullptr;
217
218 gz_ele->base_index = -1;
219 gz_ele->vert_index = -1;
220 gz_ele->edge_index = -1;
221 gz_ele->face_index = -1;
222
223 if (best.ele) {
224 gz_ele->base_index = best.base_index;
225 bm = BKE_editmesh_from_object(gz_ele->bases[gz_ele->base_index]->object)->bm;
226 BM_mesh_elem_index_ensure(bm, best.ele->head.htype);
227
228 if (best.ele->head.htype == BM_VERT) {
229 gz_ele->vert_index = BM_elem_index_get(best.ele);
230 }
231 else if (best.ele->head.htype == BM_EDGE) {
232 gz_ele->edge_index = BM_elem_index_get(best.ele);
233 }
234 else if (best.ele->head.htype == BM_FACE) {
235 gz_ele->face_index = BM_elem_index_get(best.ele);
236 }
237 }
238
239 if (best.ele) {
240 Span<float3> vert_positions;
241 {
242 Object *ob = gz_ele->bases[gz_ele->base_index]->object;
244 const Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
245 const Mesh *mesh_eval = BKE_object_get_editmesh_eval_cage(ob_eval);
246 if (BKE_mesh_wrapper_vert_len(mesh_eval) == bm->totvert) {
247 vert_positions = BKE_mesh_wrapper_vert_coords(mesh_eval);
248 }
249 }
250 EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, vert_positions);
251 EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval);
252 }
253 else {
256 }
257
258 RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
259 RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
260 RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
261 RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
262
263 if (best.ele) {
264 ARegion *region = CTX_wm_region(C);
266 }
267
268 return best.ele ? 0 : -1;
269}
270
272{
273 /* Needed so it's possible to "highlight" the gizmo without having the
274 * tweak operator attempting to handle it's input. */
276
277 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
278 if (gz_ele->psel == nullptr) {
280 }
281 gz_ele->base_index = -1;
282 new (&gz_ele->bases) Vector<Base *>();
283}
284
286{
287 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
289 gz_ele->psel = nullptr;
290 gz_ele->bases.~Vector();
291}
292
294 wmGizmo * /*gz*/,
295 const wmEvent * /*event*/)
296{
298}
299
301{
302 /* identifiers */
303 gzt->idname = "GIZMO_GT_mesh_preselect_elem_3d";
304
305 /* API callbacks. */
311
312 gzt->struct_size = sizeof(MeshElemGizmo3D);
313
314 RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
315 RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX);
316 RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
317 RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX);
318}
319
321
322/* -------------------------------------------------------------------- */
325
333
335{
337 return;
338 }
339
341 if (gz_ring->base_index != -1) {
342 Object *ob = gz_ring->bases[gz_ring->base_index]->object;
343 EDBM_preselect_edgering_draw(gz_ring->psel, ob->object_to_world().ptr());
344 }
345}
346
347static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
348{
350 struct Best {
351 Object *ob;
352 BMEdge *eed;
353 float dist;
354 int base_index;
355 } best{};
356 best.dist = ED_view3d_select_dist_px();
357
358 struct Prev {
359 int base_index;
360 int edge_index;
361 } prev{};
362 prev.base_index = gz_ring->base_index;
363 prev.edge_index = gz_ring->edge_index;
364
365 {
366 const Scene *scene = CTX_data_scene(C);
367 ViewLayer *view_layer = CTX_data_view_layer(C);
368 View3D *v3d = CTX_wm_view3d(C);
369 BKE_view_layer_synced_ensure(scene, view_layer);
370 if (gz_ring->bases.is_empty() ||
371 (gz_ring->bases[0] != BKE_view_layer_active_base_get(view_layer)))
372 {
373 gz_ring->bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, v3d);
374 }
375 }
376
378 copy_v2_v2_int(vc.mval, mval);
379
380 uint base_index;
382 &vc, &best.dist, nullptr, false, false, nullptr, gz_ring->bases, &base_index);
383
384 if (eed_test) {
385 best.ob = gz_ring->bases[base_index]->object;
386 best.eed = eed_test;
387 best.base_index = base_index;
388 }
389
390 BMesh *bm = nullptr;
391 if (best.eed) {
392 gz_ring->base_index = best.base_index;
393 bm = BKE_editmesh_from_object(gz_ring->bases[gz_ring->base_index]->object)->bm;
395 gz_ring->edge_index = BM_elem_index_get(best.eed);
396 }
397 else {
398 gz_ring->base_index = -1;
399 gz_ring->edge_index = -1;
400 }
401
402 if ((prev.base_index == gz_ring->base_index) && (prev.edge_index == gz_ring->edge_index)) {
403 /* pass (only recalculate on change) */
404 }
405 else {
406 if (best.eed) {
407 Object *ob = gz_ring->bases[gz_ring->base_index]->object;
408 Scene *scene_eval = DEG_get_evaluated(vc.depsgraph, vc.scene);
409 Object *ob_eval = DEG_get_evaluated(vc.depsgraph, ob);
410 BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
411 /* Re-allocate coords each update isn't ideal, however we can't be sure
412 * the mesh hasn't been edited since last update. */
413 Array<float3> storage;
415 vc.depsgraph, em_eval, scene_eval, ob_eval, storage);
416 EDBM_preselect_edgering_update_from_edge(gz_ring->psel, bm, best.eed, 1, vert_positions);
417 }
418 else {
420 }
421
422 RNA_int_set(gz->ptr, "object_index", gz_ring->base_index);
423 RNA_int_set(gz->ptr, "edge_index", gz_ring->edge_index);
424
425 ARegion *region = CTX_wm_region(C);
427 }
428
429 return best.eed ? 0 : -1;
430}
431
433{
434 /* Needed so it's possible to "highlight" the gizmo without having the
435 * tweak operator attempting to handle it's input. */
437
439 if (gz_ring->psel == nullptr) {
441 }
442 gz_ring->base_index = -1;
443 new (&gz_ring->bases) Vector<Base *>();
444}
445
447{
450 gz_ring->psel = nullptr;
451 gz_ring->bases.~Vector();
452}
453
455 wmGizmo * /*gz*/,
456 const wmEvent * /*event*/)
457{
459}
460
462{
463 /* identifiers */
464 gzt->idname = "GIZMO_GT_mesh_preselect_edgering_3d";
465
466 /* API callbacks. */
472
473 gzt->struct_size = sizeof(MeshEdgeRingGizmo3D);
474
475 RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
476 RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
477}
478
480
481/* -------------------------------------------------------------------- */
484
490
492
493/* -------------------------------------------------------------------- */
499
501 const wmGizmo *gz,
502 Base **r_base,
503 BMElem **r_ele)
504{
505 const Scene *scene = CTX_data_scene(C);
506 ViewLayer *view_layer = CTX_data_view_layer(C);
507
508 const int object_index = RNA_int_get(gz->ptr, "object_index");
509
510 /* weak, allocate an array just to access the index. */
511 Base *base = nullptr;
512 Object *obedit = nullptr;
513 if (object_index != -1) {
515 scene, view_layer, CTX_wm_view3d(C));
516 if (object_index < bases.size()) {
517 base = bases[object_index];
518 obedit = base->object;
519 }
520 }
521
522 *r_base = base;
523 *r_ele = nullptr;
524
525 if (obedit) {
527 BMesh *bm = em->bm;
528 PropertyRNA *prop;
529
530 /* Ring select only defines edge, check properties exist first. */
531 prop = RNA_struct_find_property(gz->ptr, "vert_index");
532 const int vert_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
533 prop = RNA_struct_find_property(gz->ptr, "edge_index");
534 const int edge_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
535 prop = RNA_struct_find_property(gz->ptr, "face_index");
536 const int face_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
537
538 if (vert_index != -1) {
539 *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index);
540 }
541 else if (edge_index != -1) {
542 *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index);
543 }
544 else if (face_index != -1) {
545 *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index);
546 }
547 }
548}
549
551{
552 if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_elem_3d")) {
553 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
554 gz_ele->base_index = -1;
555 gz_ele->vert_index = -1;
556 gz_ele->edge_index = -1;
557 gz_ele->face_index = -1;
558 }
559 else if (STREQ(gz->type->idname, "GIZMO_GT_mesh_preselect_edgering_3d")) {
561 gz_ele->base_index = -1;
562 gz_ele->edge_index = -1;
563 }
564 else {
566 }
567
568 const char *prop_ids[] = {"object_index", "vert_index", "edge_index", "face_index"};
569 for (int i = 0; i < ARRAY_SIZE(prop_ids); i++) {
570 PropertyRNA *prop = RNA_struct_find_property(gz->ptr, prop_ids[i]);
571 if (prop == nullptr) {
572 continue;
573 }
574 RNA_property_int_set(gz->ptr, prop, -1);
575 }
576}
577
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:61
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:93
#define BLI_assert(a)
Definition BLI_assert.h:46
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)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ 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:414
@ PRESELECT_ACTION_DELETE
Definition ED_mesh.hh:415
@ PRESELECT_ACTION_TRANSFORM
Definition ED_mesh.hh:413
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:654
float ED_view3d_select_dist_px()
void ED_view3d_project_v2(const ARegion *region, const float world[3], float r_region_co[2])
#define C
Definition RandGen.cpp:29
@ WM_GIZMO_HIDDEN_KEYMAP
@ KM_CTRL
Definition WM_types.hh:279
@ KM_SHIFT
Definition WM_types.hh:278
#define BM_elem_index_get(ele)
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)
BPy_StructRNA * depsgraph
int64_t size() const
nullptr float
#define G(x, y, z)
VecBase< float, 3 > float3
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]
struct Object * object
EditMesh_PreSelEdgeRing * psel
EditMesh_PreSelElem * psel
ARegion * region
Definition ED_view3d.hh:77
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
Depsgraph * depsgraph
Definition ED_view3d.hh:72
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
i
Definition text_draw.cc:230
uint len
static wmOperatorStatus gizmo_preselect_elem_invoke(bContext *, wmGizmo *, const wmEvent *)
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()
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_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
static void gizmo_preselect_elem_free(wmGizmo *gz)
static wmOperatorStatus gizmo_preselect_edgering_invoke(bContext *, wmGizmo *, const wmEvent *)
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 *))