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