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