Fix T49624: Fly uses camera settings outside camera view
[blender.git] / source / blender / editors / space_view3d / view3d_fly.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/space_view3d/view3d_fly.c
24  *  \ingroup spview3d
25  */
26
27 /* defines VIEW3D_OT_fly modal operator */
28
29 #ifdef WITH_INPUT_NDOF
30 //#  define NDOF_FLY_DEBUG
31 //#  define NDOF_FLY_DRAW_TOOMUCH  /* is this needed for ndof? - commented so redraw doesn't thrash - campbell */
32 #endif /* WITH_INPUT_NDOF */
33
34 #include "DNA_object_types.h"
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40
41 #include "BKE_context.h"
42 #include "BKE_report.h"
43
44 #include "BLT_translation.h"
45
46 #include "BIF_gl.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "ED_screen.h"
52 #include "ED_space_api.h"
53
54 #include "PIL_time.h" /* smoothview */
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 #include "view3d_intern.h"  /* own include */
60
61 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
62 enum {
63         FLY_MODAL_CANCEL = 1,
64         FLY_MODAL_CONFIRM,
65         FLY_MODAL_ACCELERATE,
66         FLY_MODAL_DECELERATE,
67         FLY_MODAL_PAN_ENABLE,
68         FLY_MODAL_PAN_DISABLE,
69         FLY_MODAL_DIR_FORWARD,
70         FLY_MODAL_DIR_BACKWARD,
71         FLY_MODAL_DIR_LEFT,
72         FLY_MODAL_DIR_RIGHT,
73         FLY_MODAL_DIR_UP,
74         FLY_MODAL_DIR_DOWN,
75         FLY_MODAL_AXIS_LOCK_X,
76         FLY_MODAL_AXIS_LOCK_Z,
77         FLY_MODAL_PRECISION_ENABLE,
78         FLY_MODAL_PRECISION_DISABLE,
79         FLY_MODAL_FREELOOK_ENABLE,
80         FLY_MODAL_FREELOOK_DISABLE,
81         FLY_MODAL_SPEED,        /* mousepan typically */
82 };
83
84 /* relative view axis locking - xlock, zlock */
85 typedef enum eFlyPanState {
86         /* disabled */
87         FLY_AXISLOCK_STATE_OFF    = 0,
88
89         /* enabled but not checking because mouse hasn't moved outside the margin since locking was checked an not needed
90          * when the mouse moves, locking is set to 2 so checks are done. */
91         FLY_AXISLOCK_STATE_IDLE   = 1,
92
93         /* mouse moved and checking needed, if no view altering is done its changed back to #FLY_AXISLOCK_STATE_IDLE */
94         FLY_AXISLOCK_STATE_ACTIVE = 2
95 } eFlyPanState;
96
97 /* called in transform_ops.c, on each regeneration of keymaps  */
98 void fly_modal_keymap(wmKeyConfig *keyconf)
99 {
100         static const EnumPropertyItem modal_items[] = {
101                 {FLY_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
102                 {FLY_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
103
104                 {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Forward", ""},
105                 {FLY_MODAL_DIR_BACKWARD, "BACKWARD", 0, "Backward", ""},
106                 {FLY_MODAL_DIR_LEFT, "LEFT", 0, "Left", ""},
107                 {FLY_MODAL_DIR_RIGHT, "RIGHT", 0, "Right", ""},
108                 {FLY_MODAL_DIR_UP, "UP", 0, "Up", ""},
109                 {FLY_MODAL_DIR_DOWN, "DOWN", 0, "Down", ""},
110
111                 {FLY_MODAL_PAN_ENABLE, "PAN_ENABLE", 0, "Pan", ""},
112                 {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan (Off)", ""},
113
114                 {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
115                 {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
116
117                 {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
118                 {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
119
120                 {FLY_MODAL_PRECISION_ENABLE, "PRECISION_ENABLE", 0, "Precision", ""},
121                 {FLY_MODAL_PRECISION_DISABLE, "PRECISION_DISABLE", 0, "Precision (Off)", ""},
122
123                 {FLY_MODAL_FREELOOK_ENABLE, "FREELOOK_ENABLE", 0, "Rotation", ""},
124                 {FLY_MODAL_FREELOOK_DISABLE, "FREELOOK_DISABLE", 0, "Rotation (Off)", ""},
125
126                 {0, NULL, 0, NULL, NULL}};
127
128         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Fly Modal");
129
130         /* this function is called for each spacetype, only needs to add map once */
131         if (keymap && keymap->modal_items)
132                 return;
133
134         keymap = WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
135
136         /* items for modal map */
137         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
138         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
139
140         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
141         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
142         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
143         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
144
145         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_ACCELERATE);
146         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, KM_ANY, 0, FLY_MODAL_DECELERATE);
147         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_ACCELERATE);
148         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_DECELERATE);
149
150         WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, FLY_MODAL_SPEED);
151
152         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
153         /* XXX - Bug in the event system, middle mouse release doesn't work */
154         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PAN_DISABLE);
155
156         /* WASD */
157         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
158         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
159         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
160         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
161         WM_modalkeymap_add_item(keymap, EKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
162         WM_modalkeymap_add_item(keymap, QKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
163
164         /* for legacy reasons, leave R/F working */
165         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
166         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
167
168         WM_modalkeymap_add_item(keymap, UPARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
169         WM_modalkeymap_add_item(keymap, DOWNARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
170         WM_modalkeymap_add_item(keymap, LEFTARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
171         WM_modalkeymap_add_item(keymap, RIGHTARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
172
173         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
174         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
175
176         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
177         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
178
179         /* for legacy reasons, leave shift working */
180         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
181         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
182
183         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_FREELOOK_ENABLE);
184         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_FREELOOK_DISABLE);
185
186         /* assign map to operators */
187         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
188 }
189
190 typedef struct FlyInfo {
191         /* context stuff */
192         RegionView3D *rv3d;
193         View3D *v3d;
194         ARegion *ar;
195         Scene *scene;
196
197         wmTimer *timer; /* needed for redraws */
198
199         short state;
200         bool redraw;
201         bool use_precision;
202         /* if the user presses shift they can look about
203          * without moving the direction there looking */
204         bool use_freelook;
205
206         int mval[2]; /* latest 2D mouse values */
207         int center_mval[2]; /* center mouse values */
208         float width, height; /* camera viewport dimensions */
209
210 #ifdef WITH_INPUT_NDOF
211         wmNDOFMotionData *ndof;  /* latest 3D mouse values */
212 #endif
213
214         /* fly state state */
215         float speed; /* the speed the view is moving per redraw */
216         short axis; /* Axis index to move along by default Z to move along the view */
217         bool pan_view; /* when true, pan the view instead of rotating */
218
219         eFlyPanState xlock, zlock;
220         float xlock_momentum, zlock_momentum; /* nicer dynamics */
221         float grid; /* world scale 1.0 default */
222
223         /* compare between last state */
224         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
225         double time_lastdraw; /* time between draws */
226
227         void *draw_handle_pixel;
228
229         /* use for some lag */
230         float dvec_prev[3]; /* old for some lag */
231
232         struct View3DCameraControl *v3d_camera_control;
233
234 } FlyInfo;
235
236 static void drawFlyPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
237 {
238         FlyInfo *fly = arg;
239         rctf viewborder;
240         int xoff, yoff;
241         float x1, x2, y1, y2;
242
243         if (ED_view3d_cameracontrol_object_get(fly->v3d_camera_control)) {
244                 ED_view3d_calc_camera_border(fly->scene, fly->ar, fly->v3d, fly->rv3d, &viewborder, false);
245                 xoff = viewborder.xmin;
246                 yoff = viewborder.ymin;
247         }
248         else {
249                 xoff = 0;
250                 yoff = 0;
251         }
252
253         /* draws 4 edge brackets that frame the safe area where the
254          * mouse can move during fly mode without spinning the view */
255
256         x1 = xoff + 0.45f * fly->width;
257         y1 = yoff + 0.45f * fly->height;
258         x2 = xoff + 0.55f * fly->width;
259         y2 = yoff + 0.55f * fly->height;
260
261         UI_ThemeColor(TH_VIEW_OVERLAY);
262         glBegin(GL_LINES);
263         /* bottom left */
264         glVertex2f(x1, y1);
265         glVertex2f(x1, y1 + 5);
266
267         glVertex2f(x1, y1);
268         glVertex2f(x1 + 5, y1);
269
270         /* top right */
271         glVertex2f(x2, y2);
272         glVertex2f(x2, y2 - 5);
273
274         glVertex2f(x2, y2);
275         glVertex2f(x2 - 5, y2);
276
277         /* top left */
278         glVertex2f(x1, y2);
279         glVertex2f(x1, y2 - 5);
280
281         glVertex2f(x1, y2);
282         glVertex2f(x1 + 5, y2);
283
284         /* bottom right */
285         glVertex2f(x2, y1);
286         glVertex2f(x2, y1 + 5);
287
288         glVertex2f(x2, y1);
289         glVertex2f(x2 - 5, y1);
290         glEnd();
291 }
292
293 static void fly_update_header(bContext *C, wmOperator *op, FlyInfo *fly)
294 {
295         char header[UI_MAX_DRAW_STR];
296         char buf[UI_MAX_DRAW_STR];
297
298         char *p = buf;
299         int available_len = sizeof(buf);
300
301 #define WM_MODALKEY(_id) \
302         WM_modalkeymap_operator_items_to_string_buf(op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
303
304         BLI_snprintf(header, sizeof(header), IFACE_("%s: confirm, %s: cancel, "
305                                                     "%s: pan enable, "
306                                                     "%s|%s|%s|%s|%s|%s: direction, "
307                                                     "%s: slow, %s: free look, "
308                                                     "%s: Upright x axis (%s), "
309                                                     "%s: Upright z axis (%s), "
310                                                     "%s: increase  speed, %s: decrease speed"),
311                      WM_MODALKEY(FLY_MODAL_CONFIRM), WM_MODALKEY(FLY_MODAL_CANCEL),
312                      WM_MODALKEY(FLY_MODAL_PAN_ENABLE),
313                      WM_MODALKEY(FLY_MODAL_DIR_FORWARD), WM_MODALKEY(FLY_MODAL_DIR_LEFT),
314                      WM_MODALKEY(FLY_MODAL_DIR_BACKWARD), WM_MODALKEY(FLY_MODAL_DIR_RIGHT),
315                      WM_MODALKEY(FLY_MODAL_DIR_UP), WM_MODALKEY(FLY_MODAL_DIR_DOWN),
316                      WM_MODALKEY(FLY_MODAL_PRECISION_ENABLE), WM_MODALKEY(FLY_MODAL_FREELOOK_ENABLE),
317                      WM_MODALKEY(FLY_MODAL_AXIS_LOCK_X), WM_bool_as_string(fly->xlock != FLY_AXISLOCK_STATE_OFF),
318                      WM_MODALKEY(FLY_MODAL_AXIS_LOCK_Z), WM_bool_as_string(fly->zlock != FLY_AXISLOCK_STATE_OFF),
319                      WM_MODALKEY(FLY_MODAL_ACCELERATE), WM_MODALKEY(FLY_MODAL_DECELERATE));
320
321 #undef WM_MODALKEY
322
323         ED_area_headerprint(CTX_wm_area(C), header);
324 }
325
326 /* FlyInfo->state */
327 enum {
328         FLY_RUNNING     = 0,
329         FLY_CANCEL      = 1,
330         FLY_CONFIRM     = 2,
331 };
332
333 static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent *event)
334 {
335         wmWindow *win = CTX_wm_window(C);
336         rctf viewborder;
337
338         float upvec[3]; /* tmp */
339         float mat[3][3];
340
341         fly->rv3d = CTX_wm_region_view3d(C);
342         fly->v3d = CTX_wm_view3d(C);
343         fly->ar = CTX_wm_region(C);
344         fly->scene = CTX_data_scene(C);
345
346 #ifdef NDOF_FLY_DEBUG
347         puts("\n-- fly begin --");
348 #endif
349
350         /* sanity check: for rare but possible case (if lib-linking the camera fails) */
351         if ((fly->rv3d->persp == RV3D_CAMOB) && (fly->v3d->camera == NULL)) {
352                 fly->rv3d->persp = RV3D_PERSP;
353         }
354
355         if (fly->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED(fly->v3d->camera)) {
356                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
357                 return false;
358         }
359
360         if (ED_view3d_offset_lock_check(fly->v3d, fly->rv3d)) {
361                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view offset is locked");
362                 return false;
363         }
364
365         if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->constraints.first) {
366                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
367                 return false;
368         }
369
370         fly->state = FLY_RUNNING;
371         fly->speed = 0.0f;
372         fly->axis = 2;
373         fly->pan_view = false;
374         fly->xlock = FLY_AXISLOCK_STATE_OFF;
375         fly->zlock = FLY_AXISLOCK_STATE_OFF;
376         fly->xlock_momentum = 0.0f;
377         fly->zlock_momentum = 0.0f;
378         fly->grid = 1.0f;
379         fly->use_precision = false;
380         fly->use_freelook = false;
381
382 #ifdef NDOF_FLY_DRAW_TOOMUCH
383         fly->redraw = 1;
384 #endif
385         zero_v3(fly->dvec_prev);
386
387         fly->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f);
388
389         copy_v2_v2_int(fly->mval, event->mval);
390
391 #ifdef WITH_INPUT_NDOF
392         fly->ndof = NULL;
393 #endif
394
395         fly->time_lastdraw = fly->time_lastwheel = PIL_check_seconds_timer();
396
397         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
398
399         fly->rv3d->rflag |= RV3D_NAVIGATING;
400
401         /* detect whether to start with Z locking */
402         copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
403         copy_m3_m4(mat, fly->rv3d->viewinv);
404         mul_m3_v3(mat, upvec);
405         if (fabsf(upvec[2]) < 0.1f) {
406                 fly->zlock = FLY_AXISLOCK_STATE_IDLE;
407         }
408
409         fly->v3d_camera_control = ED_view3d_cameracontrol_acquire(
410                 fly->scene, fly->v3d, fly->rv3d,
411                 (U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0);
412
413         /* calculate center */
414         if (ED_view3d_cameracontrol_object_get(fly->v3d_camera_control)) {
415                 ED_view3d_calc_camera_border(fly->scene, fly->ar, fly->v3d, fly->rv3d, &viewborder, false);
416
417                 fly->width = BLI_rctf_size_x(&viewborder);
418                 fly->height = BLI_rctf_size_y(&viewborder);
419
420                 fly->center_mval[0] = viewborder.xmin + fly->width / 2;
421                 fly->center_mval[1] = viewborder.ymin + fly->height / 2;
422         }
423         else {
424                 fly->width = fly->ar->winx;
425                 fly->height = fly->ar->winy;
426
427                 fly->center_mval[0] = fly->width / 2;
428                 fly->center_mval[1] = fly->height / 2;
429         }
430
431         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
432         WM_cursor_warp(win, fly->ar->winrct.xmin + fly->center_mval[0], fly->ar->winrct.ymin + fly->center_mval[1]);
433
434         fly_update_header(C, op, fly);
435         return 1;
436 }
437
438 static int flyEnd(bContext *C, FlyInfo *fly)
439 {
440         wmWindow *win;
441         RegionView3D *rv3d;
442
443         if (fly->state == FLY_RUNNING)
444                 return OPERATOR_RUNNING_MODAL;
445
446 #ifdef NDOF_FLY_DEBUG
447         puts("\n-- fly end --");
448 #endif
449
450         win = CTX_wm_window(C);
451         rv3d = fly->rv3d;
452
453         WM_event_remove_timer(CTX_wm_manager(C), win, fly->timer);
454
455         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
456
457         ED_view3d_cameracontrol_release(fly->v3d_camera_control, fly->state == FLY_CANCEL);
458
459         rv3d->rflag &= ~RV3D_NAVIGATING;
460
461 #ifdef WITH_INPUT_NDOF
462         if (fly->ndof)
463                 MEM_freeN(fly->ndof);
464 #endif
465
466         if (fly->state == FLY_CONFIRM) {
467                 MEM_freeN(fly);
468                 return OPERATOR_FINISHED;
469         }
470
471         MEM_freeN(fly);
472         return OPERATOR_CANCELLED;
473 }
474
475 static void flyEvent(bContext *C, wmOperator *op, FlyInfo *fly, const wmEvent *event)
476 {
477         if (event->type == TIMER && event->customdata == fly->timer) {
478                 fly->redraw = 1;
479         }
480         else if (event->type == MOUSEMOVE) {
481                 copy_v2_v2_int(fly->mval, event->mval);
482         }
483 #ifdef WITH_INPUT_NDOF
484         else if (event->type == NDOF_MOTION) {
485                 /* do these automagically get delivered? yes. */
486                 // puts("ndof motion detected in fly mode!");
487                 // static const char *tag_name = "3D mouse position";
488
489                 const wmNDOFMotionData *incoming_ndof = event->customdata;
490                 switch (incoming_ndof->progress) {
491                         case P_STARTING:
492                                 /* start keeping track of 3D mouse position */
493 #  ifdef NDOF_FLY_DEBUG
494                                 puts("start keeping track of 3D mouse position");
495 #  endif
496                                 /* fall-through */
497                         case P_IN_PROGRESS:
498                                 /* update 3D mouse position */
499 #  ifdef NDOF_FLY_DEBUG
500                                 putchar('.'); fflush(stdout);
501 #  endif
502                                 if (fly->ndof == NULL) {
503                                         // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
504                                         fly->ndof = MEM_dupallocN(incoming_ndof);
505                                         // fly->ndof = malloc(sizeof(wmNDOFMotionData));
506                                 }
507                                 else {
508                                         memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
509                                 }
510                                 break;
511                         case P_FINISHING:
512                                 /* stop keeping track of 3D mouse position */
513 #  ifdef NDOF_FLY_DEBUG
514                                 puts("stop keeping track of 3D mouse position");
515 #  endif
516                                 if (fly->ndof) {
517                                         MEM_freeN(fly->ndof);
518                                         // free(fly->ndof);
519                                         fly->ndof = NULL;
520                                 }
521                                 /* update the time else the view will jump when 2D mouse/timer resume */
522                                 fly->time_lastdraw = PIL_check_seconds_timer();
523                                 break;
524                         default:
525                                 break; /* should always be one of the above 3 */
526                 }
527         }
528 #endif /* WITH_INPUT_NDOF */
529         /* handle modal keymap first */
530         else if (event->type == EVT_MODAL_MAP) {
531                 switch (event->val) {
532                         case FLY_MODAL_CANCEL:
533                                 fly->state = FLY_CANCEL;
534                                 break;
535                         case FLY_MODAL_CONFIRM:
536                                 fly->state = FLY_CONFIRM;
537                                 break;
538
539                         /* speed adjusting with mousepan (trackpad) */
540                         case FLY_MODAL_SPEED:
541                         {
542                                 float fac = 0.02f * (event->prevy - event->y);
543
544                                 /* allowing to brake immediate */
545                                 if (fac > 0.0f && fly->speed < 0.0f)
546                                         fly->speed = 0.0f;
547                                 else if (fac < 0.0f && fly->speed > 0.0f)
548                                         fly->speed = 0.0f;
549                                 else
550                                         fly->speed += fly->grid * fac;
551
552                                 break;
553                         }
554                         case FLY_MODAL_ACCELERATE:
555                         {
556                                 double time_currwheel;
557                                 float time_wheel;
558
559                                 /* not quite correct but avoids confusion WASD/arrow keys 'locking up' */
560                                 if (fly->axis == -1) {
561                                         fly->axis = 2;
562                                         fly->speed = fabsf(fly->speed);
563                                 }
564
565                                 time_currwheel = PIL_check_seconds_timer();
566                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
567                                 fly->time_lastwheel = time_currwheel;
568                                 /* Mouse wheel delays range from (0.5 == slow) to (0.01 == fast) */
569                                 time_wheel = 1.0f + (10.0f - (20.0f * min_ff(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
570
571                                 if (fly->speed < 0.0f) {
572                                         fly->speed = 0.0f;
573                                 }
574                                 else {
575                                         fly->speed += fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
576                                 }
577                                 break;
578                         }
579                         case FLY_MODAL_DECELERATE:
580                         {
581                                 double time_currwheel;
582                                 float time_wheel;
583
584                                 /* not quite correct but avoids confusion WASD/arrow keys 'locking up' */
585                                 if (fly->axis == -1) {
586                                         fly->axis = 2;
587                                         fly->speed = -fabsf(fly->speed);
588                                 }
589
590                                 time_currwheel = PIL_check_seconds_timer();
591                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
592                                 fly->time_lastwheel = time_currwheel;
593                                 time_wheel = 1.0f + (10.0f - (20.0f * min_ff(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
594
595                                 if (fly->speed > 0.0f) {
596                                         fly->speed = 0;
597                                 }
598                                 else {
599                                         fly->speed -= fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
600                                 }
601                                 break;
602                         }
603                         case FLY_MODAL_PAN_ENABLE:
604                                 fly->pan_view = true;
605                                 break;
606                         case FLY_MODAL_PAN_DISABLE:
607                                 fly->pan_view = false;
608                                 break;
609
610                         /* implement WASD keys,
611                          * comments only for 'forward '*/
612                         case FLY_MODAL_DIR_FORWARD:
613                                 if (fly->axis == 2 && fly->speed < 0.0f) { /* reverse direction stops, tap again to continue */
614                                         fly->axis = -1;
615                                 }
616                                 else {
617                                         /* flip speed rather than stopping, game like motion,
618                                          * else increase like mousewheel if were already moving in that direction */
619                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
620                                         else if (fly->axis == 2) fly->speed += fly->grid;
621                                         fly->axis = 2;
622                                 }
623                                 break;
624                         case FLY_MODAL_DIR_BACKWARD:
625                                 if (fly->axis == 2 && fly->speed > 0.0f) {
626                                         fly->axis = -1;
627                                 }
628                                 else {
629                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
630                                         else if (fly->axis == 2) fly->speed -= fly->grid;
631
632                                         fly->axis = 2;
633                                 }
634                                 break;
635                         case FLY_MODAL_DIR_LEFT:
636                                 if (fly->axis == 0 && fly->speed < 0.0f) {
637                                         fly->axis = -1;
638                                 }
639                                 else {
640                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
641                                         else if (fly->axis == 0) fly->speed += fly->grid;
642
643                                         fly->axis = 0;
644                                 }
645                                 break;
646                         case FLY_MODAL_DIR_RIGHT:
647                                 if (fly->axis == 0 && fly->speed > 0.0f) {
648                                         fly->axis = -1;
649                                 }
650                                 else {
651                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
652                                         else if (fly->axis == 0) fly->speed -= fly->grid;
653
654                                         fly->axis = 0;
655                                 }
656                                 break;
657                         case FLY_MODAL_DIR_DOWN:
658                                 if (fly->axis == 1 && fly->speed < 0.0f) {
659                                         fly->axis = -1;
660                                 }
661                                 else {
662                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
663                                         else if (fly->axis == 1) fly->speed += fly->grid;
664                                         fly->axis = 1;
665                                 }
666                                 break;
667                         case FLY_MODAL_DIR_UP:
668                                 if (fly->axis == 1 && fly->speed > 0.0f) {
669                                         fly->axis = -1;
670                                 }
671                                 else {
672                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
673                                         else if (fly->axis == 1) fly->speed -= fly->grid;
674                                         fly->axis = 1;
675                                 }
676                                 break;
677
678                         case FLY_MODAL_AXIS_LOCK_X:
679                                 if (fly->xlock != FLY_AXISLOCK_STATE_OFF)
680                                         fly->xlock = FLY_AXISLOCK_STATE_OFF;
681                                 else {
682                                         fly->xlock = FLY_AXISLOCK_STATE_ACTIVE;
683                                         fly->xlock_momentum = 0.0;
684                                 }
685                                 fly_update_header(C, op, fly);
686                                 break;
687                         case FLY_MODAL_AXIS_LOCK_Z:
688                                 if (fly->zlock != FLY_AXISLOCK_STATE_OFF)
689                                         fly->zlock = FLY_AXISLOCK_STATE_OFF;
690                                 else {
691                                         fly->zlock = FLY_AXISLOCK_STATE_ACTIVE;
692                                         fly->zlock_momentum = 0.0;
693                                 }
694                                 fly_update_header(C, op, fly);
695                                 break;
696
697                         case FLY_MODAL_PRECISION_ENABLE:
698                                 fly->use_precision = true;
699                                 break;
700                         case FLY_MODAL_PRECISION_DISABLE:
701                                 fly->use_precision = false;
702                                 break;
703
704                         case FLY_MODAL_FREELOOK_ENABLE:
705                                 fly->use_freelook = true;
706                                 break;
707                         case FLY_MODAL_FREELOOK_DISABLE:
708                                 fly->use_freelook = false;
709                                 break;
710                 }
711         }
712 }
713
714 static void flyMoveCamera(bContext *C, FlyInfo *fly,
715                           const bool do_rotate, const bool do_translate)
716 {
717         ED_view3d_cameracontrol_update(fly->v3d_camera_control, true, C, do_rotate, do_translate);
718 }
719
720 static int flyApply(bContext *C, FlyInfo *fly)
721 {
722 #define FLY_ROTATE_FAC 10.0f /* more is faster */
723 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
724 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
725 #define FLY_SMOOTH_FAC 20.0f  /* higher value less lag */
726
727         /* fly mode - Shift+F
728          * a fly loop where the user can move move the view as if they are flying
729          */
730         RegionView3D *rv3d = fly->rv3d;
731
732         float mat[3][3]; /* 3x3 copy of the view matrix so we can move along the view axis */
733         float dvec[3] = {0, 0, 0}; /* this is the direction that's added to the view offset per redraw */
734
735         /* Camera Uprighting variables */
736         float moffset[2]; /* mouse offset from the views center */
737         float tmp_quat[4]; /* used for rotating the view */
738
739         int xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
740
741 #ifdef NDOF_FLY_DEBUG
742         {
743                 static unsigned int iteration = 1;
744                 printf("fly timer %d\n", iteration++);
745         }
746 #endif
747
748         xmargin = fly->width / 20.0f;
749         ymargin = fly->height / 20.0f;
750
751         {
752
753                 /* mouse offset from the center */
754                 moffset[0] = fly->mval[0] - fly->center_mval[0];
755                 moffset[1] = fly->mval[1] - fly->center_mval[1];
756
757                 /* enforce a view margin */
758                 if      (moffset[0] >  xmargin) moffset[0] -= xmargin;
759                 else if (moffset[0] < -xmargin) moffset[0] += xmargin;
760                 else                            moffset[0] =  0;
761
762                 if      (moffset[1] >  ymargin) moffset[1] -= ymargin;
763                 else if (moffset[1] < -ymargin) moffset[1] += ymargin;
764                 else                            moffset[1] =  0;
765
766
767                 /* scale the mouse movement by this value - scales mouse movement to the view size
768                  * moffset[0] / (ar->winx-xmargin * 2) - window size minus margin (same for y)
769                  *
770                  * the mouse moves isn't linear */
771
772                 if (moffset[0]) {
773                         moffset[0] /= fly->width - (xmargin * 2);
774                         moffset[0] *= fabsf(moffset[0]);
775                 }
776
777                 if (moffset[1]) {
778                         moffset[1] /= fly->height - (ymargin * 2);
779                         moffset[1] *= fabsf(moffset[1]);
780                 }
781
782                 /* Should we redraw? */
783                 if ((fly->speed != 0.0f) ||
784                     moffset[0] || moffset[1] ||
785                     (fly->zlock != FLY_AXISLOCK_STATE_OFF) ||
786                     (fly->xlock != FLY_AXISLOCK_STATE_OFF) ||
787                     dvec[0] || dvec[1] || dvec[2])
788                 {
789                         float dvec_tmp[3];
790
791                         /* time how fast it takes for us to redraw,
792                          * this is so simple scenes don't fly too fast */
793                         double time_current;
794                         float time_redraw;
795                         float time_redraw_clamped;
796 #ifdef NDOF_FLY_DRAW_TOOMUCH
797                         fly->redraw = 1;
798 #endif
799                         time_current = PIL_check_seconds_timer();
800                         time_redraw = (float)(time_current - fly->time_lastdraw);
801                         time_redraw_clamped = min_ff(0.05f, time_redraw); /* clamp redraw time to avoid jitter in roll correction */
802                         fly->time_lastdraw = time_current;
803
804                         /* Scale the time to use shift to scale the speed down- just like
805                          * shift slows many other areas of blender down */
806                         if (fly->use_precision)
807                                 fly->speed = fly->speed * (1.0f - time_redraw_clamped);
808
809                         copy_m3_m4(mat, rv3d->viewinv);
810
811                         if (fly->pan_view == true) {
812                                 /* pan only */
813                                 copy_v3_fl3(dvec_tmp, -moffset[0], -moffset[1], 0.0f);
814
815                                 if (fly->use_precision) {
816                                         dvec_tmp[0] *= 0.1f;
817                                         dvec_tmp[1] *= 0.1f;
818                                 }
819
820                                 mul_m3_v3(mat, dvec_tmp);
821                                 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
822                         }
823                         else {
824                                 float roll; /* similar to the angle between the camera's up and the Z-up,
825                                              * but its very rough so just roll */
826
827                                 /* rotate about the X axis- look up/down */
828                                 if (moffset[1]) {
829                                         float upvec[3];
830                                         copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
831                                         mul_m3_v3(mat, upvec);
832                                         /* Rotate about the relative up vec */
833                                         axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -FLY_ROTATE_FAC);
834                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
835
836                                         if (fly->xlock != FLY_AXISLOCK_STATE_OFF)
837                                                 fly->xlock = FLY_AXISLOCK_STATE_ACTIVE;  /* check for rotation */
838                                         if (fly->zlock != FLY_AXISLOCK_STATE_OFF)
839                                                 fly->zlock = FLY_AXISLOCK_STATE_ACTIVE;
840                                         fly->xlock_momentum = 0.0f;
841                                 }
842
843                                 /* rotate about the Y axis- look left/right */
844                                 if (moffset[0]) {
845                                         float upvec[3];
846                                         /* if we're upside down invert the moffset */
847                                         copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
848                                         mul_m3_v3(mat, upvec);
849
850                                         if (upvec[2] < 0.0f)
851                                                 moffset[0] = -moffset[0];
852
853                                         /* make the lock vectors */
854                                         if (fly->zlock) {
855                                                 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
856                                         }
857                                         else {
858                                                 copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
859                                                 mul_m3_v3(mat, upvec);
860                                         }
861
862                                         /* Rotate about the relative up vec */
863                                         axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * FLY_ROTATE_FAC);
864                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
865
866                                         if (fly->xlock != FLY_AXISLOCK_STATE_OFF)
867                                                 fly->xlock = FLY_AXISLOCK_STATE_ACTIVE;  /* check for rotation */
868                                         if (fly->zlock != FLY_AXISLOCK_STATE_OFF)
869                                                 fly->zlock = FLY_AXISLOCK_STATE_ACTIVE;
870                                 }
871
872                                 if (fly->zlock == FLY_AXISLOCK_STATE_ACTIVE) {
873                                         float upvec[3];
874                                         copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
875                                         mul_m3_v3(mat, upvec);
876
877                                         /* make sure we have some z rolling */
878                                         if (fabsf(upvec[2]) > 0.00001f) {
879                                                 roll = upvec[2] * 5.0f;
880                                                 /* rotate the view about this axis */
881                                                 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
882                                                 mul_m3_v3(mat, upvec);
883                                                 /* Rotate about the relative up vec */
884                                                 axis_angle_to_quat(tmp_quat, upvec,
885                                                                    roll * time_redraw_clamped * fly->zlock_momentum * FLY_ZUP_CORRECT_FAC);
886                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
887
888                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
889                                         }
890                                         else {
891                                                 fly->zlock = FLY_AXISLOCK_STATE_IDLE; /* don't check until the view rotates again */
892                                                 fly->zlock_momentum = 0.0f;
893                                         }
894                                 }
895
896                                 /* only apply xcorrect when mouse isn't applying x rot */
897                                 if (fly->xlock == FLY_AXISLOCK_STATE_ACTIVE && moffset[1] == 0) {
898                                         float upvec[3];
899                                         copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
900                                         mul_m3_v3(mat, upvec);
901                                         /* make sure we have some z rolling */
902                                         if (fabsf(upvec[2]) > 0.00001f) {
903                                                 roll = upvec[2] * -5.0f;
904                                                 /* rotate the view about this axis */
905                                                 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
906                                                 mul_m3_v3(mat, upvec);
907
908                                                 /* Rotate about the relative up vec */
909                                                 axis_angle_to_quat(tmp_quat, upvec, roll * time_redraw_clamped * fly->xlock_momentum * 0.1f);
910                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
911
912                                                 fly->xlock_momentum += 0.05f;
913                                         }
914                                         else {
915                                                 fly->xlock = FLY_AXISLOCK_STATE_IDLE; /* see above */
916                                                 fly->xlock_momentum = 0.0f;
917                                         }
918                                 }
919
920                                 if (fly->axis == -1) {
921                                         /* pause */
922                                         zero_v3(dvec_tmp);
923                                 }
924                                 else if (!fly->use_freelook) {
925                                         /* Normal operation */
926                                         /* define dvec, view direction vector */
927                                         zero_v3(dvec_tmp);
928                                         /* move along the current axis */
929                                         dvec_tmp[fly->axis] = 1.0f;
930
931                                         mul_m3_v3(mat, dvec_tmp);
932                                 }
933                                 else {
934                                         normalize_v3_v3(dvec_tmp, fly->dvec_prev);
935                                         if (fly->speed < 0.0f) {
936                                                 negate_v3(dvec_tmp);
937                                         }
938                                 }
939
940                                 mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
941                         }
942
943                         /* impose a directional lag */
944                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f / (1.0f + (time_redraw * FLY_SMOOTH_FAC))));
945
946                         if (rv3d->persp == RV3D_CAMOB) {
947                                 Object *lock_ob = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
948                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
949                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
950                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
951                         }
952
953                         add_v3_v3(rv3d->ofs, dvec);
954
955                         if (rv3d->persp == RV3D_CAMOB) {
956                                 const bool do_rotate = ((fly->xlock != FLY_AXISLOCK_STATE_OFF) ||
957                                                         (fly->zlock != FLY_AXISLOCK_STATE_OFF) ||
958                                                         ((moffset[0] || moffset[1]) && !fly->pan_view));
959                                 const bool do_translate = (fly->speed != 0.0f || fly->pan_view);
960                                 flyMoveCamera(C, fly, do_rotate, do_translate);
961                         }
962
963                 }
964                 else {
965                         /* we're not redrawing but we need to update the time else the view will jump */
966                         fly->time_lastdraw = PIL_check_seconds_timer();
967                 }
968                 /* end drawing */
969                 copy_v3_v3(fly->dvec_prev, dvec);
970         }
971
972         return OPERATOR_FINISHED;
973 }
974
975 #ifdef WITH_INPUT_NDOF
976 static void flyApply_ndof(bContext *C, FlyInfo *fly)
977 {
978         Object *lock_ob = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
979         bool has_translate, has_rotate;
980
981         view3d_ndof_fly(fly->ndof,
982                         fly->v3d, fly->rv3d,
983                         fly->use_precision, lock_ob ? lock_ob->protectflag : 0,
984                         &has_translate, &has_rotate);
985
986         if (has_translate || has_rotate) {
987                 fly->redraw = true;
988
989                 if (fly->rv3d->persp == RV3D_CAMOB) {
990                         flyMoveCamera(C, fly, has_rotate, has_translate);
991                 }
992         }
993 }
994 #endif /* WITH_INPUT_NDOF */
995
996 static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
997 {
998         RegionView3D *rv3d = CTX_wm_region_view3d(C);
999         FlyInfo *fly;
1000
1001         if (rv3d->viewlock & RV3D_LOCKED)
1002                 return OPERATOR_CANCELLED;
1003
1004         fly = MEM_callocN(sizeof(FlyInfo), "FlyOperation");
1005
1006         op->customdata = fly;
1007
1008         if (initFlyInfo(C, fly, op, event) == false) {
1009                 MEM_freeN(op->customdata);
1010                 return OPERATOR_CANCELLED;
1011         }
1012
1013         flyEvent(C, op, fly, event);
1014
1015         WM_event_add_modal_handler(C, op);
1016
1017         return OPERATOR_RUNNING_MODAL;
1018 }
1019
1020 static void fly_cancel(bContext *C, wmOperator *op)
1021 {
1022         FlyInfo *fly = op->customdata;
1023
1024         fly->state = FLY_CANCEL;
1025         flyEnd(C, fly);
1026         op->customdata = NULL;
1027 }
1028
1029 static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
1030 {
1031         int exit_code;
1032         bool do_draw = false;
1033         FlyInfo *fly = op->customdata;
1034         RegionView3D *rv3d = fly->rv3d;
1035         Object *fly_object = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
1036
1037         fly->redraw = 0;
1038
1039         flyEvent(C, op, fly, event);
1040
1041 #ifdef WITH_INPUT_NDOF
1042         if (fly->ndof) { /* 3D mouse overrules [2D mouse + timer] */
1043                 if (event->type == NDOF_MOTION) {
1044                         flyApply_ndof(C, fly);
1045                 }
1046         }
1047         else
1048 #endif /* WITH_INPUT_NDOF */
1049         if (event->type == TIMER && event->customdata == fly->timer) {
1050                 flyApply(C, fly);
1051         }
1052
1053         do_draw |= fly->redraw;
1054
1055         exit_code = flyEnd(C, fly);
1056
1057         if (exit_code != OPERATOR_RUNNING_MODAL)
1058                 do_draw = true;
1059
1060         if (do_draw) {
1061                 if (rv3d->persp == RV3D_CAMOB) {
1062                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, fly_object);
1063                 }
1064
1065                 // puts("redraw!"); // too frequent, commented with NDOF_FLY_DRAW_TOOMUCH for now
1066                 ED_region_tag_redraw(CTX_wm_region(C));
1067         }
1068
1069         if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED))
1070                 ED_area_headerprint(CTX_wm_area(C), NULL);
1071
1072         return exit_code;
1073 }
1074
1075 void VIEW3D_OT_fly(wmOperatorType *ot)
1076 {
1077         /* identifiers */
1078         ot->name = "Fly Navigation";
1079         ot->description = "Interactively fly around the scene";
1080         ot->idname = "VIEW3D_OT_fly";
1081
1082         /* api callbacks */
1083         ot->invoke = fly_invoke;
1084         ot->cancel = fly_cancel;
1085         ot->modal = fly_modal;
1086         ot->poll = ED_operator_region_view3d_active;
1087
1088         /* flags */
1089         ot->flag = OPTYPE_BLOCKING;
1090 }