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