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
17 /** \file
18 * \ingroup wm
19 *
20 * \name Preselection Gizmo
21 *
22 * Use for tools to hover over data before activation.
23 *
24 * \note This is a slight misuse of gizmo's, since clicking performs no action.
25 */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_math.h"
30
31 #include "DNA_mesh_types.h"
32
33 #include "BKE_context.h"
34 #include "BKE_editmesh.h"
35 #include "BKE_layer.h"
36
37 #include "DEG_depsgraph.h"
38 #include "DEG_depsgraph_query.h"
39
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "bmesh.h"
47
48 #include "ED_gizmo_library.h"
49 #include "ED_mesh.h"
50 #include "ED_screen.h"
51 #include "ED_view3d.h"
52
53 /* -------------------------------------------------------------------- */
54 /** \name Mesh Element (Vert/Edge/Face) Pre-Select Gizmo API
55 *
56 * \{ */
57
58 typedef struct MeshElemGizmo3D {
59 wmGizmo gizmo;
60 Base **bases;
61 uint bases_len;
62 int base_index;
63 int vert_index;
64 int edge_index;
65 int face_index;
66 struct EditMesh_PreSelElem *psel;
67 } MeshElemGizmo3D;
68
gizmo_preselect_elem_draw(const bContext * UNUSED (C),wmGizmo * gz)69 static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz)
70 {
71 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
72 if (gz_ele->base_index != -1) {
73 Object *ob = gz_ele->bases[gz_ele->base_index]->object;
74 EDBM_preselect_elem_draw(gz_ele->psel, ob->obmat);
75 }
76 }
77
gizmo_preselect_elem_test_select(bContext * C,wmGizmo * gz,const int mval[2])78 static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
79 {
80 wmEvent *event = CTX_wm_window(C)->eventstate;
81 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
82
83 /* Hack: Switch action mode based on key input */
84 const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL;
85 const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT;
86 EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM);
87 if (is_ctrl_pressed && !is_shift_pressed) {
88 EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE);
89 }
90 if (!is_ctrl_pressed && is_shift_pressed) {
91 EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE);
92 }
93
94 struct {
95 Object *ob;
96 BMElem *ele;
97 float dist;
98 int base_index;
99 } best = {
100 .dist = ED_view3d_select_dist_px(),
101 };
102
103 {
104 ViewLayer *view_layer = CTX_data_view_layer(C);
105 View3D *v3d = CTX_wm_view3d(C);
106 if (((gz_ele->bases)) == NULL || (gz_ele->bases[0] != view_layer->basact)) {
107 MEM_SAFE_FREE(gz_ele->bases);
108 gz_ele->bases = BKE_view_layer_array_from_bases_in_edit_mode(
109 view_layer, v3d, &gz_ele->bases_len);
110 }
111 }
112
113 ViewContext vc;
114 em_setup_viewcontext(C, &vc);
115 copy_v2_v2_int(vc.mval, mval);
116
117 {
118 /* TODO: support faces. */
119 int base_index_vert = -1;
120 int base_index_edge = -1;
121 int base_index_face = -1;
122 BMVert *eve_test;
123 BMEdge *eed_test;
124 BMFace *efa_test;
125
126 if (EDBM_unified_findnearest_from_raycast(&vc,
127 gz_ele->bases,
128 gz_ele->bases_len,
129 false,
130 true,
131 &base_index_vert,
132 &base_index_edge,
133 &base_index_face,
134 &eve_test,
135 &eed_test,
136 &efa_test)) {
137 if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) {
138 /* Delete action */
139 if (efa_test) {
140 best.ele = (BMElem *)efa_test;
141 best.base_index = base_index_face;
142 }
143 }
144
145 else {
146 /* Transform and create action */
147 if (eed_test) {
148 best.ele = (BMElem *)eed_test;
149 best.base_index = base_index_edge;
150 }
151 }
152
153 /* All actions use same vertex pre-selection. */
154 /* Re-topology should always prioritize edge pre-selection.
155 * Only pre-select a vertex when the cursor is really close to it. */
156 if (eve_test) {
157 BMVert *vert = (BMVert *)eve_test;
158 float vert_p_co[3], vert_co[3];
159 const float mval_f[2] = {UNPACK2(vc.mval)};
160 mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co);
161 ED_view3d_project(vc.region, vert_co, vert_p_co);
162 float len = len_v2v2(vert_p_co, mval_f);
163 if (len < 35) {
164 best.ele = (BMElem *)eve_test;
165 best.base_index = base_index_vert;
166 }
167 if (!BM_vert_is_boundary(vert) &&
168 EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) {
169 best.ele = (BMElem *)eve_test;
170 best.base_index = base_index_vert;
171 }
172 }
173
174 /* Check above should never fail, if it does it's an internal error. */
175 BLI_assert(best.base_index != -1);
176
177 Base *base = gz_ele->bases[best.base_index];
178 best.ob = base->object;
179 }
180 }
181
182 BMesh *bm = NULL;
183
184 gz_ele->base_index = -1;
185 gz_ele->vert_index = -1;
186 gz_ele->edge_index = -1;
187 gz_ele->face_index = -1;
188
189 if (best.ele) {
190 gz_ele->base_index = best.base_index;
191 bm = BKE_editmesh_from_object(gz_ele->bases[gz_ele->base_index]->object)->bm;
192 BM_mesh_elem_index_ensure(bm, best.ele->head.htype);
193
194 if (best.ele->head.htype == BM_VERT) {
195 gz_ele->vert_index = BM_elem_index_get(best.ele);
196 }
197 else if (best.ele->head.htype == BM_EDGE) {
198 gz_ele->edge_index = BM_elem_index_get(best.ele);
199 }
200 else if (best.ele->head.htype == BM_FACE) {
201 gz_ele->face_index = BM_elem_index_get(best.ele);
202 }
203 }
204
205 if (best.ele) {
206 const float(*coords)[3] = NULL;
207 {
208 Object *ob = gz_ele->bases[gz_ele->base_index]->object;
209 Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
210 Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
211 if (me_eval->runtime.edit_data) {
212 coords = me_eval->runtime.edit_data->vertexCos;
213 }
214 }
215 EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords);
216 EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval);
217 }
218 else {
219 EDBM_preselect_elem_clear(gz_ele->psel);
220 EDBM_preselect_preview_clear(gz_ele->psel);
221 }
222
223 RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
224 RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
225 RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
226 RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
227
228 if (best.ele) {
229 ARegion *region = CTX_wm_region(C);
230 ED_region_tag_redraw_editor_overlays(region);
231 }
232
233 // return best.eed ? 0 : -1;
234 return -1;
235 }
236
gizmo_preselect_elem_setup(wmGizmo * gz)237 static void gizmo_preselect_elem_setup(wmGizmo *gz)
238 {
239 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
240 if (gz_ele->psel == NULL) {
241 gz_ele->psel = EDBM_preselect_elem_create();
242 }
243 gz_ele->base_index = -1;
244 }
245
gizmo_preselect_elem_free(wmGizmo * gz)246 static void gizmo_preselect_elem_free(wmGizmo *gz)
247 {
248 MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
249 EDBM_preselect_elem_destroy(gz_ele->psel);
250 gz_ele->psel = NULL;
251 MEM_SAFE_FREE(gz_ele->bases);
252 }
253
gizmo_preselect_elem_invoke(bContext * UNUSED (C),wmGizmo * UNUSED (gz),const wmEvent * UNUSED (event))254 static int gizmo_preselect_elem_invoke(bContext *UNUSED(C),
255 wmGizmo *UNUSED(gz),
256 const wmEvent *UNUSED(event))
257 {
258 return OPERATOR_PASS_THROUGH;
259 }
260
GIZMO_GT_mesh_preselect_elem_3d(wmGizmoType * gzt)261 static void GIZMO_GT_mesh_preselect_elem_3d(wmGizmoType *gzt)
262 {
263 /* identifiers */
264 gzt->idname = "GIZMO_GT_mesh_preselect_elem_3d";
265
266 /* api callbacks */
267 gzt->invoke = gizmo_preselect_elem_invoke;
268 gzt->draw = gizmo_preselect_elem_draw;
269 gzt->test_select = gizmo_preselect_elem_test_select;
270 gzt->setup = gizmo_preselect_elem_setup;
271 gzt->free = gizmo_preselect_elem_free;
272
273 gzt->struct_size = sizeof(MeshElemGizmo3D);
274
275 RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
276 RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX);
277 RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
278 RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX);
279 }
280
281 /** \} */
282
283 /* -------------------------------------------------------------------- */
284 /** \name Mesh Edge-Ring Pre-Select Gizmo API
285 *
286 * \{ */
287
288 typedef struct MeshEdgeRingGizmo3D {
289 wmGizmo gizmo;
290 Base **bases;
291 uint bases_len;
292 int base_index;
293 int edge_index;
294 struct EditMesh_PreSelEdgeRing *psel;
295 } MeshEdgeRingGizmo3D;
296
gizmo_preselect_edgering_draw(const bContext * UNUSED (C),wmGizmo * gz)297 static void gizmo_preselect_edgering_draw(const bContext *UNUSED(C), wmGizmo *gz)
298 {
299 MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
300 if (gz_ring->base_index != -1) {
301 Object *ob = gz_ring->bases[gz_ring->base_index]->object;
302 EDBM_preselect_edgering_draw(gz_ring->psel, ob->obmat);
303 }
304 }
305
gizmo_preselect_edgering_test_select(bContext * C,wmGizmo * gz,const int mval[2])306 static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
307 {
308 MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
309 struct {
310 Object *ob;
311 BMEdge *eed;
312 float dist;
313 int base_index;
314 } best = {
315 .dist = ED_view3d_select_dist_px(),
316 };
317
318 struct {
319 int base_index;
320 int edge_index;
321 } prev = {
322 .base_index = gz_ring->base_index,
323 .edge_index = gz_ring->edge_index,
324 };
325
326 {
327 ViewLayer *view_layer = CTX_data_view_layer(C);
328 View3D *v3d = CTX_wm_view3d(C);
329 if (((gz_ring->bases)) == NULL || (gz_ring->bases[0] != view_layer->basact)) {
330 MEM_SAFE_FREE(gz_ring->bases);
331 gz_ring->bases = BKE_view_layer_array_from_bases_in_edit_mode(
332 view_layer, v3d, &gz_ring->bases_len);
333 }
334 }
335
336 ViewContext vc;
337 em_setup_viewcontext(C, &vc);
338 copy_v2_v2_int(vc.mval, mval);
339
340 uint base_index;
341 BMEdge *eed_test = EDBM_edge_find_nearest_ex(
342 &vc, &best.dist, NULL, false, false, NULL, gz_ring->bases, gz_ring->bases_len, &base_index);
343
344 if (eed_test) {
345 best.ob = gz_ring->bases[base_index]->object;
346 best.eed = eed_test;
347 best.base_index = base_index;
348 }
349
350 BMesh *bm = NULL;
351 if (best.eed) {
352 gz_ring->base_index = best.base_index;
353 bm = BKE_editmesh_from_object(gz_ring->bases[gz_ring->base_index]->object)->bm;
354 BM_mesh_elem_index_ensure(bm, BM_EDGE);
355 gz_ring->edge_index = BM_elem_index_get(best.eed);
356 }
357 else {
358 gz_ring->base_index = -1;
359 gz_ring->edge_index = -1;
360 }
361
362 if ((prev.base_index == gz_ring->base_index) && (prev.edge_index == gz_ring->edge_index)) {
363 /* pass (only recalculate on change) */
364 }
365 else {
366 if (best.eed) {
367 Object *ob = gz_ring->bases[gz_ring->base_index]->object;
368 Scene *scene_eval = (Scene *)DEG_get_evaluated_id(vc.depsgraph, &vc.scene->id);
369 Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, ob);
370 BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
371 /* Re-allocate coords each update isn't ideal, however we can't be sure
372 * the mesh hasn't been edited since last update. */
373 bool is_alloc = false;
374 const float(*coords)[3] = BKE_editmesh_vert_coords_when_deformed(
375 vc.depsgraph, em_eval, scene_eval, ob_eval, NULL, &is_alloc);
376 EDBM_preselect_edgering_update_from_edge(gz_ring->psel, bm, best.eed, 1, coords);
377 if (is_alloc) {
378 MEM_freeN((void *)coords);
379 }
380 }
381 else {
382 EDBM_preselect_edgering_clear(gz_ring->psel);
383 }
384
385 RNA_int_set(gz->ptr, "object_index", gz_ring->base_index);
386 RNA_int_set(gz->ptr, "edge_index", gz_ring->edge_index);
387
388 ARegion *region = CTX_wm_region(C);
389 ED_region_tag_redraw_editor_overlays(region);
390 }
391
392 // return best.eed ? 0 : -1;
393 return -1;
394 }
395
gizmo_preselect_edgering_setup(wmGizmo * gz)396 static void gizmo_preselect_edgering_setup(wmGizmo *gz)
397 {
398 MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
399 if (gz_ring->psel == NULL) {
400 gz_ring->psel = EDBM_preselect_edgering_create();
401 }
402 gz_ring->base_index = -1;
403 }
404
gizmo_preselect_edgering_free(wmGizmo * gz)405 static void gizmo_preselect_edgering_free(wmGizmo *gz)
406 {
407 MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
408 EDBM_preselect_edgering_destroy(gz_ring->psel);
409 gz_ring->psel = NULL;
410 MEM_SAFE_FREE(gz_ring->bases);
411 }
412
gizmo_preselect_edgering_invoke(bContext * UNUSED (C),wmGizmo * UNUSED (gz),const wmEvent * UNUSED (event))413 static int gizmo_preselect_edgering_invoke(bContext *UNUSED(C),
414 wmGizmo *UNUSED(gz),
415 const wmEvent *UNUSED(event))
416 {
417 return OPERATOR_PASS_THROUGH;
418 }
419
GIZMO_GT_mesh_preselect_edgering_3d(wmGizmoType * gzt)420 static void GIZMO_GT_mesh_preselect_edgering_3d(wmGizmoType *gzt)
421 {
422 /* identifiers */
423 gzt->idname = "GIZMO_GT_mesh_preselect_edgering_3d";
424
425 /* api callbacks */
426 gzt->invoke = gizmo_preselect_edgering_invoke;
427 gzt->draw = gizmo_preselect_edgering_draw;
428 gzt->test_select = gizmo_preselect_edgering_test_select;
429 gzt->setup = gizmo_preselect_edgering_setup;
430 gzt->free = gizmo_preselect_edgering_free;
431
432 gzt->struct_size = sizeof(MeshEdgeRingGizmo3D);
433
434 RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
435 RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
436 }
437
438 /** \} */
439
440 /* -------------------------------------------------------------------- */
441 /** \name Gizmo API
442 *
443 * \{ */
444
ED_gizmotypes_preselect_3d(void)445 void ED_gizmotypes_preselect_3d(void)
446 {
447 WM_gizmotype_append(GIZMO_GT_mesh_preselect_elem_3d);
448 WM_gizmotype_append(GIZMO_GT_mesh_preselect_edgering_3d);
449 }
450
451 /** \} */
452
453 /* -------------------------------------------------------------------- */
454 /** \name Gizmo Accessors
455 *
456 * This avoids each user of the gizmo needing to write their own look-ups to access
457 * the information from this gizmo.
458 * \{ */
459
ED_view3d_gizmo_mesh_preselect_get_active(bContext * C,wmGizmo * gz,Base ** r_base,BMElem ** r_ele)460 void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
461 wmGizmo *gz,
462 Base **r_base,
463 BMElem **r_ele)
464 {
465 ViewLayer *view_layer = CTX_data_view_layer(C);
466
467 const int object_index = RNA_int_get(gz->ptr, "object_index");
468
469 /* weak, allocate an array just to access the index. */
470 Base *base = NULL;
471 Object *obedit = NULL;
472 {
473 uint bases_len;
474 Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
475 view_layer, CTX_wm_view3d(C), &bases_len);
476 if (object_index < bases_len) {
477 base = bases[object_index];
478 obedit = base->object;
479 }
480 MEM_freeN(bases);
481 }
482
483 *r_base = base;
484 *r_ele = NULL;
485
486 if (obedit) {
487 BMEditMesh *em = BKE_editmesh_from_object(obedit);
488 BMesh *bm = em->bm;
489 PropertyRNA *prop;
490
491 /* Ring select only defines edge, check properties exist first. */
492 prop = RNA_struct_find_property(gz->ptr, "vert_index");
493 const int vert_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
494 prop = RNA_struct_find_property(gz->ptr, "edge_index");
495 const int edge_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
496 prop = RNA_struct_find_property(gz->ptr, "face_index");
497 const int face_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
498
499 if (vert_index != -1) {
500 *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index);
501 }
502 else if (edge_index != -1) {
503 *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index);
504 }
505 else if (face_index != -1) {
506 *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index);
507 }
508 }
509 }
510 /** \} */
511