style cleanup: comments
[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 //#define NDOF_FLY_DEBUG
30 //#define NDOF_FLY_DRAW_TOOMUCH  /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */
31 #include "DNA_anim_types.h"
32 #include "DNA_scene_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_camera_types.h"
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_utildefines.h"
41
42 #include "BKE_context.h"
43 #include "BKE_object.h"
44 #include "BKE_report.h"
45
46 #include "BKE_depsgraph.h" /* for fly mode updating */
47
48 #include "BIF_gl.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "ED_keyframing.h"
54 #include "ED_screen.h"
55 #include "ED_space_api.h"
56
57 #include "PIL_time.h" /* smoothview */
58
59 #include "view3d_intern.h"  /* own include */
60 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
61 enum {
62         FLY_MODAL_CANCEL = 1,
63         FLY_MODAL_CONFIRM,
64         FLY_MODAL_ACCELERATE,
65         FLY_MODAL_DECELERATE,
66         FLY_MODAL_PAN_ENABLE,
67         FLY_MODAL_PAN_DISABLE,
68         FLY_MODAL_DIR_FORWARD,
69         FLY_MODAL_DIR_BACKWARD,
70         FLY_MODAL_DIR_LEFT,
71         FLY_MODAL_DIR_RIGHT,
72         FLY_MODAL_DIR_UP,
73         FLY_MODAL_DIR_DOWN,
74         FLY_MODAL_AXIS_LOCK_X,
75         FLY_MODAL_AXIS_LOCK_Z,
76         FLY_MODAL_PRECISION_ENABLE,
77         FLY_MODAL_PRECISION_DISABLE,
78         FLY_MODAL_FREELOOK_ENABLE,
79         FLY_MODAL_FREELOOK_DISABLE
80
81 };
82
83 /* called in transform_ops.c, on each regeneration of keymaps  */
84 void fly_modal_keymap(wmKeyConfig *keyconf)
85 {
86         static EnumPropertyItem modal_items[] = {
87                 {FLY_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
88                 {FLY_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
89                 {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
90                 {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
91
92                 {FLY_MODAL_PAN_ENABLE, "PAN_ENABLE", 0, "Pan Enable", ""},
93                 {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
94
95                 {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
96                 {FLY_MODAL_DIR_BACKWARD, "BACKWARD", 0, "Fly Backward", ""},
97                 {FLY_MODAL_DIR_LEFT, "LEFT", 0, "Fly Left", ""},
98                 {FLY_MODAL_DIR_RIGHT, "RIGHT", 0, "Fly Right", ""},
99                 {FLY_MODAL_DIR_UP, "UP", 0, "Fly Up", ""},
100                 {FLY_MODAL_DIR_DOWN, "DOWN", 0, "Fly Down", ""},
101
102                 {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
103                 {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
104
105                 {FLY_MODAL_PRECISION_ENABLE, "PRECISION_ENABLE", 0, "Precision Enable", ""},
106                 {FLY_MODAL_PRECISION_DISABLE, "PRECISION_DISABLE", 0, "Precision Disable", ""},
107
108                 {FLY_MODAL_FREELOOK_ENABLE, "FREELOOK_ENABLE", 0, "Rotation Enable", ""},
109                 {FLY_MODAL_FREELOOK_DISABLE, "FREELOOK_DISABLE", 0, "Rotation Disable", ""},
110
111                 {0, NULL, 0, NULL, NULL}};
112
113         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Fly Modal");
114
115         /* this function is called for each spacetype, only needs to add map once */
116         if (keymap && keymap->modal_items)
117                 return;
118
119         keymap = WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
120
121         /* items for modal map */
122         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
123         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
124
125         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
126         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
127         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
128         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
129
130         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_ACCELERATE);
131         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, KM_ANY, 0, FLY_MODAL_DECELERATE);
132         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_ACCELERATE);
133         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_DECELERATE);
134
135         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
136         /* XXX - Bug in the event system, middle mouse release doesnt work */
137         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PAN_DISABLE);
138
139         /* WASD */
140         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
141         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
142         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
143         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
144         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
145         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
146
147         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
148         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
149
150         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
151         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
152
153         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_FREELOOK_ENABLE);
154         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_FREELOOK_DISABLE);
155
156         /* assign map to operators */
157         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
158 }
159
160 typedef struct FlyInfo {
161         /* context stuff */
162         RegionView3D *rv3d;
163         View3D *v3d;
164         ARegion *ar;
165         Scene *scene;
166
167         wmTimer *timer; /* needed for redraws */
168
169         short state;
170         short redraw;
171         unsigned char use_precision;
172         /* if the user presses shift they can look about
173          * without moving the direction there looking */
174         unsigned char use_freelook;
175
176         int mval[2]; /* latest 2D mouse values */
177         wmNDOFMotionData *ndof;  /* latest 3D mouse values */
178
179         /* fly state state */
180         float speed; /* the speed the view is moving per redraw */
181         short axis; /* Axis index to move along by default Z to move along the view */
182         short pan_view; /* when true, pan the view instead of rotating */
183
184         /* relative view axis locking - xlock, zlock
185          * 0) disabled
186          * 1) enabled but not checking because mouse hasn't moved outside the margin since locking was checked an not needed
187          *    when the mouse moves, locking is set to 2 so checks are done.
188          * 2) mouse moved and checking needed, if no view altering is done its changed back to 1 */
189         short xlock, zlock;
190         float xlock_momentum, zlock_momentum; /* nicer dynamics */
191         float grid; /* world scale 1.0 default */
192
193         /* root most parent */
194         Object *root_parent;
195
196         /* backup values */
197         float dist_backup; /* backup the views distance since we use a zero dist for fly mode */
198         float ofs_backup[3]; /* backup the views offset in case the user cancels flying in non camera mode */
199
200         /* backup the views quat in case the user cancels flying in non camera mode.
201          * (quat for view, eul for camera) */
202         float rot_backup[4];
203         short persp_backup; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */
204
205         /* are we flying an ortho camera in perspective view,
206          * which was originall in ortho view?
207          * could probably figure it out but better be explicit */
208         short is_ortho_cam;
209         void *obtfm; /* backup the objects transform */
210
211         /* compare between last state */
212         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
213         double time_lastdraw; /* time between draws */
214
215         void *draw_handle_pixel;
216
217         /* use for some lag */
218         float dvec_prev[3]; /* old for some lag */
219
220 } FlyInfo;
221
222 static void drawFlyPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
223 {
224         FlyInfo *fly = arg;
225
226         /* draws 4 edge brackets that frame the safe area where the
227          * mouse can move during fly mode without spinning the view */
228         float x1, x2, y1, y2;
229
230         x1 = 0.45f * (float)fly->ar->winx;
231         y1 = 0.45f * (float)fly->ar->winy;
232         x2 = 0.55f * (float)fly->ar->winx;
233         y2 = 0.55f * (float)fly->ar->winy;
234         cpack(0);
235
236         glBegin(GL_LINES);
237         /* bottom left */
238         glVertex2f(x1, y1);
239         glVertex2f(x1, y1 + 5);
240
241         glVertex2f(x1, y1);
242         glVertex2f(x1 + 5, y1);
243
244         /* top right */
245         glVertex2f(x2, y2);
246         glVertex2f(x2, y2 - 5);
247
248         glVertex2f(x2, y2);
249         glVertex2f(x2 - 5, y2);
250
251         /* top left */
252         glVertex2f(x1, y2);
253         glVertex2f(x1, y2 - 5);
254
255         glVertex2f(x1, y2);
256         glVertex2f(x1 + 5, y2);
257
258         /* bottom right */
259         glVertex2f(x2, y1);
260         glVertex2f(x2, y1 + 5);
261
262         glVertex2f(x2, y1);
263         glVertex2f(x2 - 5, y1);
264         glEnd();
265 }
266
267 /* FlyInfo->state */
268 #define FLY_RUNNING     0
269 #define FLY_CANCEL      1
270 #define FLY_CONFIRM     2
271
272 static int initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event)
273 {
274         wmWindow *win = CTX_wm_window(C);
275         float upvec[3]; /* tmp */
276         float mat[3][3];
277
278         fly->rv3d = CTX_wm_region_view3d(C);
279         fly->v3d = CTX_wm_view3d(C);
280         fly->ar = CTX_wm_region(C);
281         fly->scene = CTX_data_scene(C);
282
283 #ifdef NDOF_FLY_DEBUG
284         puts("\n-- fly begin --");
285 #endif
286
287         /* sanity check: for rare but possible case (if lib-linking the camera fails) */
288         if ((fly->rv3d->persp == RV3D_CAMOB) && (fly->v3d->camera == NULL)) {
289                 fly->rv3d->persp = RV3D_PERSP;
290         }
291
292         if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->id.lib) {
293                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
294                 return FALSE;
295         }
296
297         if (fly->v3d->ob_centre) {
298                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
299                 return FALSE;
300         }
301
302         if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->constraints.first) {
303                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
304                 return FALSE;
305         }
306
307         fly->state = FLY_RUNNING;
308         fly->speed = 0.0f;
309         fly->axis = 2;
310         fly->pan_view = FALSE;
311         fly->xlock = FALSE;
312         fly->zlock = FALSE;
313         fly->xlock_momentum = 0.0f;
314         fly->zlock_momentum = 0.0f;
315         fly->grid = 1.0f;
316         fly->use_precision = FALSE;
317         fly->use_freelook = FALSE;
318
319 #ifdef NDOF_FLY_DRAW_TOOMUCH
320         fly->redraw = 1;
321 #endif
322         zero_v3(fly->dvec_prev);
323
324         fly->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f);
325
326         copy_v2_v2_int(fly->mval, event->mval);
327         fly->ndof = NULL;
328
329         fly->time_lastdraw = fly->time_lastwheel = PIL_check_seconds_timer();
330
331         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
332
333         fly->rv3d->rflag |= RV3D_NAVIGATING; /* so we draw the corner margins */
334
335         /* detect weather to start with Z locking */
336         upvec[0] = 1.0f;
337         upvec[1] = 0.0f;
338         upvec[2] = 0.0f;
339         copy_m3_m4(mat, fly->rv3d->viewinv);
340         mul_m3_v3(mat, upvec);
341         if (fabsf(upvec[2]) < 0.1f) {
342                 fly->zlock = 1;
343         }
344         upvec[0] = 0;
345         upvec[1] = 0;
346         upvec[2] = 0;
347
348         fly->persp_backup = fly->rv3d->persp;
349         fly->dist_backup = fly->rv3d->dist;
350
351         /* check for flying ortho camera - which we cant support well
352          * we _could_ also check for an ortho camera but this is easier */
353         if ((fly->rv3d->persp == RV3D_CAMOB) &&
354             (fly->rv3d->is_persp == FALSE))
355         {
356                 ((Camera *)fly->v3d->camera->data)->type = CAM_PERSP;
357                 fly->is_ortho_cam = TRUE;
358         }
359
360         if (fly->rv3d->persp == RV3D_CAMOB) {
361                 Object *ob_back;
362                 if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (fly->root_parent = fly->v3d->camera->parent)) {
363                         while (fly->root_parent->parent)
364                                 fly->root_parent = fly->root_parent->parent;
365                         ob_back = fly->root_parent;
366                 }
367                 else {
368                         ob_back = fly->v3d->camera;
369                 }
370
371                 /* store the original camera loc and rot */
372                 /* TODO. axis angle etc */
373
374                 fly->obtfm = BKE_object_tfm_backup(ob_back);
375
376                 BKE_object_where_is_calc(fly->scene, fly->v3d->camera);
377                 negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
378
379                 fly->rv3d->dist = 0.0;
380         }
381         else {
382                 /* perspective or ortho */
383                 if (fly->rv3d->persp == RV3D_ORTHO)
384                         fly->rv3d->persp = RV3D_PERSP;  /* if ortho projection, make perspective */
385
386                 copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
387                 copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
388
389                 /* the dist defines a vector that is infront of the offset
390                  * to rotate the view about.
391                  * this is no good for fly mode because we
392                  * want to rotate about the viewers center.
393                  * but to correct the dist removal we must
394                  * alter offset so the view doesn't jump. */
395
396                 fly->rv3d->dist = 0.0f;
397
398                 upvec[2] = fly->dist_backup; /* x and y are 0 */
399                 mul_m3_v3(mat, upvec);
400                 sub_v3_v3(fly->rv3d->ofs, upvec);
401                 /* Done with correcting for the dist */
402         }
403
404         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
405         WM_cursor_warp(win, fly->ar->winrct.xmin + fly->ar->winx / 2, fly->ar->winrct.ymin + fly->ar->winy / 2);
406
407         return 1;
408 }
409
410 static int flyEnd(bContext *C, FlyInfo *fly)
411 {
412         RegionView3D *rv3d = fly->rv3d;
413         View3D *v3d = fly->v3d;
414
415         float upvec[3];
416
417         if (fly->state == FLY_RUNNING)
418                 return OPERATOR_RUNNING_MODAL;
419
420 #ifdef NDOF_FLY_DEBUG
421         puts("\n-- fly end --");
422 #endif
423
424         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
425
426         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
427
428         rv3d->dist = fly->dist_backup;
429
430         if (fly->state == FLY_CANCEL) {
431                 /* Revert to original view? */
432                 if (fly->persp_backup == RV3D_CAMOB) { /* a camera view */
433                         Object *ob_back;
434                         ob_back = (fly->root_parent) ? fly->root_parent : fly->v3d->camera;
435
436                         /* store the original camera loc and rot */
437                         BKE_object_tfm_restore(ob_back, fly->obtfm);
438
439                         DAG_id_tag_update(&ob_back->id, OB_RECALC_OB);
440                 }
441                 else {
442                         /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
443                         copy_qt_qt(rv3d->viewquat, fly->rot_backup);
444                         copy_v3_v3(rv3d->ofs, fly->ofs_backup);
445                         rv3d->persp = fly->persp_backup;
446                 }
447         }
448         else if (fly->persp_backup == RV3D_CAMOB) { /* camera */
449                 DAG_id_tag_update(fly->root_parent ? &fly->root_parent->id : &v3d->camera->id, OB_RECALC_OB);
450         }
451         else { /* not camera */
452
453                 /* Apply the fly mode view */
454                 /* restore the dist */
455                 float mat[3][3];
456                 upvec[0] = upvec[1] = 0;
457                 upvec[2] = fly->dist_backup; /* x and y are 0 */
458                 copy_m3_m4(mat, rv3d->viewinv);
459                 mul_m3_v3(mat, upvec);
460                 add_v3_v3(rv3d->ofs, upvec);
461                 /* Done with correcting for the dist */
462         }
463
464         if (fly->is_ortho_cam) {
465                 ((Camera *)fly->v3d->camera->data)->type = CAM_ORTHO;
466         }
467
468         rv3d->rflag &= ~RV3D_NAVIGATING;
469 //XXX2.5        BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
470
471         if (fly->obtfm)
472                 MEM_freeN(fly->obtfm);
473         if (fly->ndof)
474                 MEM_freeN(fly->ndof);
475
476         if (fly->state == FLY_CONFIRM) {
477                 MEM_freeN(fly);
478                 return OPERATOR_FINISHED;
479         }
480
481         MEM_freeN(fly);
482         return OPERATOR_CANCELLED;
483 }
484
485 static void flyEvent(FlyInfo *fly, wmEvent *event)
486 {
487         if (event->type == TIMER && event->customdata == fly->timer) {
488                 fly->redraw = 1;
489         }
490         else if (event->type == MOUSEMOVE) {
491                 copy_v2_v2_int(fly->mval, event->mval);
492         }
493         else if (event->type == NDOF_MOTION) {
494                 /* do these automagically get delivered? yes. */
495                 // puts("ndof motion detected in fly mode!");
496                 // static const char *tag_name = "3D mouse position";
497
498                 wmNDOFMotionData *incoming_ndof = (wmNDOFMotionData *)event->customdata;
499                 switch (incoming_ndof->progress) {
500                         case P_STARTING:
501                                 /* start keeping track of 3D mouse position */
502 #ifdef NDOF_FLY_DEBUG
503                                 puts("start keeping track of 3D mouse position");
504 #endif
505                         /* fall through... */
506                         case P_IN_PROGRESS:
507                                 /* update 3D mouse position */
508 #ifdef NDOF_FLY_DEBUG
509                                 putchar('.'); fflush(stdout);
510 #endif
511                                 if (fly->ndof == NULL) {
512                                         // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
513                                         fly->ndof = MEM_dupallocN(incoming_ndof);
514                                         // fly->ndof = malloc(sizeof(wmNDOFMotionData));
515                                 }
516                                 else {
517                                         memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
518                                 }
519                                 break;
520                         case P_FINISHING:
521                                 /* stop keeping track of 3D mouse position */
522 #ifdef NDOF_FLY_DEBUG
523                                 puts("stop keeping track of 3D mouse position");
524 #endif
525                                 if (fly->ndof) {
526                                         MEM_freeN(fly->ndof);
527                                         // free(fly->ndof);
528                                         fly->ndof = NULL;
529                                 }
530                                 /* update the time else the view will jump when 2D mouse/timer resume */
531                                 fly->time_lastdraw = PIL_check_seconds_timer();
532                                 break;
533                         default:
534                                 break; /* should always be one of the above 3 */
535                 }
536         }
537         /* handle modal keymap first */
538         else if (event->type == EVT_MODAL_MAP) {
539                 switch (event->val) {
540                         case FLY_MODAL_CANCEL:
541                                 fly->state = FLY_CANCEL;
542                                 break;
543                         case FLY_MODAL_CONFIRM:
544                                 fly->state = FLY_CONFIRM;
545                                 break;
546
547                         case FLY_MODAL_ACCELERATE:
548                         {
549                                 double time_currwheel;
550                                 float time_wheel;
551
552                                 time_currwheel = PIL_check_seconds_timer();
553                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
554                                 fly->time_lastwheel = time_currwheel;
555                                 /* Mouse wheel delays range from (0.5 == slow) to (0.01 == fast) */
556                                 time_wheel = 1.0f + (10.0f - (20.0f * minf(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
557
558                                 if (fly->speed < 0.0f) {
559                                         fly->speed = 0.0f;
560                                 }
561                                 else {
562                                         fly->speed += fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
563                                 }
564                                 break;
565                         }
566                         case FLY_MODAL_DECELERATE:
567                         {
568                                 double time_currwheel;
569                                 float time_wheel;
570
571                                 time_currwheel = PIL_check_seconds_timer();
572                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
573                                 fly->time_lastwheel = time_currwheel;
574                                 time_wheel = 1.0f + (10.0f - (20.0f * minf(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
575
576                                 if (fly->speed > 0.0f) {
577                                         fly->speed = 0;
578                                 }
579                                 else {
580                                         fly->speed -= fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
581                                 }
582                                 break;
583                         }
584                         case FLY_MODAL_PAN_ENABLE:
585                                 fly->pan_view = TRUE;
586                                 break;
587                         case FLY_MODAL_PAN_DISABLE:
588 //XXX2.5                                WM_cursor_warp(CTX_wm_window(C), cent_orig[0], cent_orig[1]);
589                                 fly->pan_view = FALSE;
590                                 break;
591
592                         /* implement WASD keys,
593                          * comments only for 'forward '*/
594                         case FLY_MODAL_DIR_FORWARD:
595                                 if (fly->axis == 2 && fly->speed < 0.0f) { /* reverse direction stops, tap again to continue */
596                                         fly->axis = -1;
597                                 }
598                                 else {
599                                         /* flip speed rather than stopping, game like motion,
600                                          * else increase like mousewheel if were already moving in that direction */
601                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
602                                         else if (fly->axis == 2) fly->speed += fly->grid;
603                                         fly->axis = 2;
604                                 }
605                                 break;
606                         case FLY_MODAL_DIR_BACKWARD:
607                                 if (fly->axis == 2 && fly->speed > 0.0f) {
608                                         fly->axis = -1;
609                                 }
610                                 else {
611                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
612                                         else if (fly->axis == 2) fly->speed -= fly->grid;
613
614                                         fly->axis = 2;
615                                 }
616                                 break;
617                         case FLY_MODAL_DIR_LEFT:
618                                 if (fly->axis == 0 && fly->speed < 0.0f) {
619                                         fly->axis = -1;
620                                 }
621                                 else {
622                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
623                                         else if (fly->axis == 0) fly->speed += fly->grid;
624
625                                         fly->axis = 0;
626                                 }
627                                 break;
628                         case FLY_MODAL_DIR_RIGHT:
629                                 if (fly->axis == 0 && fly->speed > 0.0f) {
630                                         fly->axis = -1;
631                                 }
632                                 else {
633                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
634                                         else if (fly->axis == 0) fly->speed -= fly->grid;
635
636                                         fly->axis = 0;
637                                 }
638                                 break;
639                         case FLY_MODAL_DIR_DOWN:
640                                 if (fly->axis == 1 && fly->speed < 0.0f) {
641                                         fly->axis = -1;
642                                 }
643                                 else {
644                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
645                                         else if (fly->axis == 1) fly->speed += fly->grid;
646                                         fly->axis = 1;
647                                 }
648                                 break;
649                         case FLY_MODAL_DIR_UP:
650                                 if (fly->axis == 1 && fly->speed > 0.0f) {
651                                         fly->axis = -1;
652                                 }
653                                 else {
654                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
655                                         else if (fly->axis == 1) fly->speed -= fly->grid;
656                                         fly->axis = 1;
657                                 }
658                                 break;
659
660                         case FLY_MODAL_AXIS_LOCK_X:
661                                 if (fly->xlock)
662                                         fly->xlock = 0;
663                                 else {
664                                         fly->xlock = 2;
665                                         fly->xlock_momentum = 0.0;
666                                 }
667                                 break;
668                         case FLY_MODAL_AXIS_LOCK_Z:
669                                 if (fly->zlock)
670                                         fly->zlock = 0;
671                                 else {
672                                         fly->zlock = 2;
673                                         fly->zlock_momentum = 0.0;
674                                 }
675                                 break;
676
677                         case FLY_MODAL_PRECISION_ENABLE:
678                                 fly->use_precision = TRUE;
679                                 break;
680                         case FLY_MODAL_PRECISION_DISABLE:
681                                 fly->use_precision = FALSE;
682                                 break;
683
684                         case FLY_MODAL_FREELOOK_ENABLE:
685                                 fly->use_freelook = TRUE;
686                                 break;
687                         case FLY_MODAL_FREELOOK_DISABLE:
688                                 fly->use_freelook = FALSE;
689                                 break;
690                 }
691         }
692 }
693
694 static void move_camera(bContext *C, RegionView3D *rv3d, FlyInfo *fly, int orientationChanged, int positionChanged)
695 {
696         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
697
698         View3D *v3d = fly->v3d;
699         Scene *scene = fly->scene;
700         ID *id_key;
701
702         /* transform the parent or the camera? */
703         if (fly->root_parent) {
704                 Object *ob_update;
705
706                 float view_mat[4][4];
707                 float prev_view_mat[4][4];
708                 float prev_view_imat[4][4];
709                 float diff_mat[4][4];
710                 float parent_mat[4][4];
711
712                 ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
713                 invert_m4_m4(prev_view_imat, prev_view_mat);
714                 ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
715                 mult_m4_m4m4(diff_mat, view_mat, prev_view_imat);
716                 mult_m4_m4m4(parent_mat, diff_mat, fly->root_parent->obmat);
717                 BKE_object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
718
719                 // BKE_object_where_is_calc(scene, fly->root_parent);
720
721                 ob_update = v3d->camera->parent;
722                 while (ob_update) {
723                         DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
724                         ob_update = ob_update->parent;
725                 }
726
727                 id_key = &fly->root_parent->id;
728         }
729         else {
730                 float view_mat[4][4];
731                 ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
732                 BKE_object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
733                 id_key = &v3d->camera->id;
734         }
735
736         /* record the motion */
737         if (autokeyframe_cfra_can_key(scene, id_key)) {
738                 ListBase dsources = {NULL, NULL};
739
740                 /* add datasource override for the camera object */
741                 ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL);
742
743                 /* insert keyframes 
744                  *      1) on the first frame
745                  *      2) on each subsequent frame
746                  *              TODO: need to check in future that frame changed before doing this 
747                  */
748                 if (orientationChanged) {
749                         KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
750                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
751                 }
752                 if (positionChanged) {
753                         KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
754                         ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
755                 }
756
757                 /* free temp data */
758                 BLI_freelistN(&dsources);
759         }
760 }
761
762 static int flyApply(bContext *C, FlyInfo *fly)
763 {
764 #define FLY_ROTATE_FAC 2.5f /* more is faster */
765 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
766 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
767
768         /* fly mode - Shift+F
769          * a fly loop where the user can move move the view as if they are flying
770          */
771         RegionView3D *rv3d = fly->rv3d;
772         ARegion *ar = fly->ar;
773
774         float mat[3][3]; /* 3x3 copy of the view matrix so we can move along the view axis */
775         float dvec[3] = {0, 0, 0}; /* this is the direction thast added to the view offset per redraw */
776
777         /* Camera Uprighting variables */
778         float upvec[3] = {0, 0, 0}; /* stores the view's up vector */
779
780         float moffset[2]; /* mouse offset from the views center */
781         float tmp_quat[4]; /* used for rotating the view */
782
783 //      int cent_orig[2], /* view center */
784 //XXX- can avoid using //   cent[2], /* view center modified */
785         int xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
786
787 #ifdef NDOF_FLY_DEBUG
788         {
789                 static unsigned int iteration = 1;
790                 printf("fly timer %d\n", iteration++);
791         }
792 #endif
793
794         xmargin = ar->winx / 20.0f;
795         ymargin = ar->winy / 20.0f;
796
797         // UNUSED
798         // cent_orig[0]= ar->winrct.xmin + ar->winx/2;
799         // cent_orig[1]= ar->winrct.ymin + ar->winy/2;
800
801         {
802
803                 /* mouse offset from the center */
804                 moffset[0] = fly->mval[0] - ar->winx / 2;
805                 moffset[1] = fly->mval[1] - ar->winy / 2;
806
807                 /* enforce a view margin */
808                 if      (moffset[0] >  xmargin) moffset[0] -= xmargin;
809                 else if (moffset[0] < -xmargin) moffset[0] += xmargin;
810                 else                            moffset[0] =  0;
811
812                 if      (moffset[1] >  ymargin) moffset[1] -= ymargin;
813                 else if (moffset[1] < -ymargin) moffset[1] += ymargin;
814                 else                            moffset[1] =  0;
815
816
817                 /* scale the mouse movement by this value - scales mouse movement to the view size
818                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
819                  *
820                  * the mouse moves isn't linear */
821
822                 if (moffset[0]) {
823                         moffset[0] /= ar->winx - (xmargin * 2);
824                         moffset[0] *= fabsf(moffset[0]);
825                 }
826
827                 if (moffset[1]) {
828                         moffset[1] /= ar->winy - (ymargin * 2);
829                         moffset[1] *= fabsf(moffset[1]);
830                 }
831
832                 /* Should we redraw? */
833                 if ((fly->speed != 0.0f) ||
834                     moffset[0] || moffset[1] ||
835                     fly->zlock || fly->xlock ||
836                     dvec[0] || dvec[1] || dvec[2])
837                 {
838                         float dvec_tmp[3];
839
840                         /* time how fast it takes for us to redraw,
841                          * this is so simple scenes don't fly too fast */
842                         double time_current;
843                         float time_redraw;
844                         float time_redraw_clamped;
845 #ifdef NDOF_FLY_DRAW_TOOMUCH
846                         fly->redraw = 1;
847 #endif
848                         time_current = PIL_check_seconds_timer();
849                         time_redraw = (float)(time_current - fly->time_lastdraw);
850                         time_redraw_clamped = minf(0.05f, time_redraw); /* clamp redraw time to avoid jitter in roll correction */
851                         fly->time_lastdraw = time_current;
852
853                         /* Scale the time to use shift to scale the speed down- just like
854                          * shift slows many other areas of blender down */
855                         if (fly->use_precision)
856                                 fly->speed = fly->speed * (1.0f - time_redraw_clamped);
857
858                         copy_m3_m4(mat, rv3d->viewinv);
859
860                         if (fly->pan_view == TRUE) {
861                                 /* pan only */
862                                 dvec_tmp[0] = -moffset[0];
863                                 dvec_tmp[1] = -moffset[1];
864                                 dvec_tmp[2] = 0;
865
866                                 if (fly->use_precision) {
867                                         dvec_tmp[0] *= 0.1f;
868                                         dvec_tmp[1] *= 0.1f;
869                                 }
870
871                                 mul_m3_v3(mat, dvec_tmp);
872                                 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
873                         }
874                         else {
875                                 float roll; /* similar to the angle between the camera's up and the Z-up,
876                                              * but its very rough so just roll */
877
878                                 /* rotate about the X axis- look up/down */
879                                 if (moffset[1]) {
880                                         upvec[0] = 1;
881                                         upvec[1] = 0;
882                                         upvec[2] = 0;
883                                         mul_m3_v3(mat, upvec);
884                                         /* Rotate about the relative up vec */
885                                         axis_angle_to_quat(tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC);
886                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
887
888                                         if (fly->xlock)
889                                                 fly->xlock = 2;  /* check for rotation */
890                                         if (fly->zlock)
891                                                 fly->zlock = 2;
892                                         fly->xlock_momentum = 0.0f;
893                                 }
894
895                                 /* rotate about the Y axis- look left/right */
896                                 if (moffset[0]) {
897
898                                         /* if we're upside down invert the moffset */
899                                         upvec[0] = 0.0f;
900                                         upvec[1] = 1.0f;
901                                         upvec[2] = 0.0f;
902                                         mul_m3_v3(mat, upvec);
903
904                                         if (upvec[2] < 0.0f)
905                                                 moffset[0] = -moffset[0];
906
907                                         /* make the lock vectors */
908                                         if (fly->zlock) {
909                                                 upvec[0] = 0.0f;
910                                                 upvec[1] = 0.0f;
911                                                 upvec[2] = 1.0f;
912                                         }
913                                         else {
914                                                 upvec[0] = 0.0f;
915                                                 upvec[1] = 1.0f;
916                                                 upvec[2] = 0.0f;
917                                                 mul_m3_v3(mat, upvec);
918                                         }
919
920                                         /* Rotate about the relative up vec */
921                                         axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC);
922                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
923
924                                         if (fly->xlock)
925                                                 fly->xlock = 2;  /* check for rotation */
926                                         if (fly->zlock)
927                                                 fly->zlock = 2;
928                                 }
929
930                                 if (fly->zlock == 2) {
931                                         upvec[0] = 1.0f;
932                                         upvec[1] = 0.0f;
933                                         upvec[2] = 0.0f;
934                                         mul_m3_v3(mat, upvec);
935
936                                         /* make sure we have some z rolling */
937                                         if (fabsf(upvec[2]) > 0.00001f) {
938                                                 roll = upvec[2] * 5.0f;
939                                                 upvec[0] = 0.0f; /* rotate the view about this axis */
940                                                 upvec[1] = 0.0f;
941                                                 upvec[2] = 1.0f;
942
943                                                 mul_m3_v3(mat, upvec);
944                                                 /* Rotate about the relative up vec */
945                                                 axis_angle_to_quat(tmp_quat, upvec,
946                                                                    roll * time_redraw_clamped * fly->zlock_momentum * FLY_ZUP_CORRECT_FAC);
947                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
948
949                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
950                                         }
951                                         else {
952                                                 fly->zlock = 1; /* don't check until the view rotates again */
953                                                 fly->zlock_momentum = 0.0f;
954                                         }
955                                 }
956
957                                 if (fly->xlock == 2 && moffset[1] == 0) { /* only apply xcorrect when mouse isn't applying x rot */
958                                         upvec[0] = 0;
959                                         upvec[1] = 0;
960                                         upvec[2] = 1;
961                                         mul_m3_v3(mat, upvec);
962                                         /* make sure we have some z rolling */
963                                         if (fabsf(upvec[2]) > 0.00001f) {
964                                                 roll = upvec[2] * -5.0f;
965
966                                                 upvec[0] = 1.0f; /* rotate the view about this axis */
967                                                 upvec[1] = 0.0f;
968                                                 upvec[2] = 0.0f;
969
970                                                 mul_m3_v3(mat, upvec);
971
972                                                 /* Rotate about the relative up vec */
973                                                 axis_angle_to_quat(tmp_quat, upvec, roll * time_redraw_clamped * fly->xlock_momentum * 0.1f);
974                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
975
976                                                 fly->xlock_momentum += 0.05f;
977                                         }
978                                         else {
979                                                 fly->xlock = 1; /* see above */
980                                                 fly->xlock_momentum = 0.0f;
981                                         }
982                                 }
983
984                                 if (fly->axis == -1) {
985                                         /* pause */
986                                         zero_v3(dvec_tmp);
987                                 }
988                                 else if (!fly->use_freelook) {
989                                         /* Normal operation */
990                                         /* define dvec, view direction vector */
991                                         zero_v3(dvec_tmp);
992                                         /* move along the current axis */
993                                         dvec_tmp[fly->axis] = 1.0f;
994
995                                         mul_m3_v3(mat, dvec_tmp);
996                                 }
997                                 else {
998                                         normalize_v3_v3(dvec_tmp, fly->dvec_prev);
999                                         if (fly->speed < 0.0f) {
1000                                                 negate_v3(dvec_tmp);
1001                                         }
1002                                 }
1003
1004                                 mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
1005                         }
1006
1007                         /* impose a directional lag */
1008                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f / (1.0f + (time_redraw * 5.0f))));
1009
1010                         if (rv3d->persp == RV3D_CAMOB) {
1011                                 Object *lock_ob = fly->root_parent ? fly->root_parent : fly->v3d->camera;
1012                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
1013                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
1014                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
1015                         }
1016
1017                         add_v3_v3(rv3d->ofs, dvec);
1018
1019                         if (rv3d->persp == RV3D_CAMOB)
1020                                 move_camera(C, rv3d, fly, (fly->xlock || fly->zlock || moffset[0] || moffset[1]), fly->speed);
1021
1022                 }
1023                 else {
1024                         /* we're not redrawing but we need to update the time else the view will jump */
1025                         fly->time_lastdraw = PIL_check_seconds_timer();
1026                 }
1027                 /* end drawing */
1028                 copy_v3_v3(fly->dvec_prev, dvec);
1029         }
1030
1031         return OPERATOR_FINISHED;
1032 }
1033
1034 static int flyApply_ndof(bContext *C, FlyInfo *fly)
1035 {
1036         /* shorthand for oft-used variables */
1037         wmNDOFMotionData *ndof = fly->ndof;
1038         const float dt = ndof->dt;
1039         RegionView3D *rv3d = fly->rv3d;
1040         const int flag = U.ndof_flag;
1041
1042 #if 0
1043         int shouldRotate = (flag & NDOF_SHOULD_ROTATE) && (fly->pan_view == FALSE),
1044             shouldTranslate = (flag & (NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM));
1045 #endif
1046
1047         int shouldRotate = (fly->pan_view == FALSE);
1048         int shouldTranslate = TRUE;
1049
1050         float view_inv[4];
1051         invert_qt_qt(view_inv, rv3d->viewquat);
1052
1053         rv3d->rot_angle = 0.0f; /* disable onscreen rotation doo-dad */
1054
1055         if (shouldTranslate) {
1056                 const float forward_sensitivity  = 1.0f;
1057                 const float vertical_sensitivity = 0.4f;
1058                 const float lateral_sensitivity  = 0.6f;
1059
1060                 float speed = 10.0f; /* blender units per second */
1061                 /* ^^ this is ok for default cube scene, but should scale with.. something */
1062
1063                 float trans[3] = {lateral_sensitivity  * ndof->tvec[0],
1064                                   vertical_sensitivity * ndof->tvec[1],
1065                                   forward_sensitivity  * ndof->tvec[2]};
1066
1067                 if (fly->use_precision)
1068                         speed *= 0.2f;
1069
1070                 mul_v3_fl(trans, speed * dt);
1071
1072                 /* transform motion from view to world coordinates */
1073                 mul_qt_v3(view_inv, trans);
1074
1075                 if (flag & NDOF_FLY_HELICOPTER) {
1076                         /* replace world z component with device y (yes it makes sense) */
1077                         trans[2] = speed * dt * vertical_sensitivity * ndof->tvec[1];
1078                 }
1079
1080                 if (rv3d->persp == RV3D_CAMOB) {
1081                         /* respect camera position locks */
1082                         Object *lock_ob = fly->root_parent ? fly->root_parent : fly->v3d->camera;
1083                         if (lock_ob->protectflag & OB_LOCK_LOCX) trans[0] = 0.0f;
1084                         if (lock_ob->protectflag & OB_LOCK_LOCY) trans[1] = 0.0f;
1085                         if (lock_ob->protectflag & OB_LOCK_LOCZ) trans[2] = 0.0f;
1086                 }
1087
1088                 if (!is_zero_v3(trans)) {
1089                         /* move center of view opposite of hand motion (this is camera mode, not object mode) */
1090                         sub_v3_v3(rv3d->ofs, trans);
1091                         shouldTranslate = TRUE;
1092                 }
1093                 else {
1094                         shouldTranslate = FALSE;
1095                 }
1096         }
1097
1098         if (shouldRotate) {
1099                 const float turn_sensitivity = 1.0f;
1100
1101                 float rotation[4];
1102                 float axis[3];
1103                 float angle = turn_sensitivity * ndof_to_axis_angle(ndof, axis);
1104
1105                 if (fabsf(angle) > 0.0001f) {
1106                         shouldRotate = TRUE;
1107
1108                         if (fly->use_precision)
1109                                 angle *= 0.2f;
1110
1111                         /* transform rotation axis from view to world coordinates */
1112                         mul_qt_v3(view_inv, axis);
1113
1114                         /* apply rotation to view */
1115                         axis_angle_to_quat(rotation, axis, angle);
1116                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
1117
1118                         if (flag & NDOF_LOCK_HORIZON) {
1119                                 /* force an upright viewpoint
1120                                  * TODO: make this less... sudden */
1121                                 float view_horizon[3] = {1.0f, 0.0f, 0.0f}; /* view +x */
1122                                 float view_direction[3] = {0.0f, 0.0f, -1.0f}; /* view -z (into screen) */
1123
1124                                 /* find new inverse since viewquat has changed */
1125                                 invert_qt_qt(view_inv, rv3d->viewquat);
1126                                 /* could apply reverse rotation to existing view_inv to save a few cycles */
1127
1128                                 /* transform view vectors to world coordinates */
1129                                 mul_qt_v3(view_inv, view_horizon);
1130                                 mul_qt_v3(view_inv, view_direction);
1131
1132                                 /* find difference between view & world horizons
1133                                  * true horizon lives in world xy plane, so look only at difference in z */
1134                                 angle = -asinf(view_horizon[2]);
1135
1136 #ifdef NDOF_FLY_DEBUG
1137                                 printf("lock horizon: adjusting %.1f degrees\n\n", RAD2DEG(angle));
1138 #endif
1139
1140                                 /* rotate view so view horizon = world horizon */
1141                                 axis_angle_to_quat(rotation, view_direction, angle);
1142                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
1143                         }
1144
1145                         rv3d->view = RV3D_VIEW_USER;
1146                 }
1147                 else {
1148                         shouldRotate = FALSE;
1149                 }
1150         }
1151
1152         if (shouldTranslate || shouldRotate) {
1153                 fly->redraw = TRUE;
1154
1155                 if (rv3d->persp == RV3D_CAMOB) {
1156                         move_camera(C, rv3d, fly, shouldRotate, shouldTranslate);
1157                 }
1158         }
1159
1160         return OPERATOR_FINISHED;
1161 }
1162
1163 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
1164 {
1165         RegionView3D *rv3d = CTX_wm_region_view3d(C);
1166         FlyInfo *fly;
1167
1168         if (rv3d->viewlock)
1169                 return OPERATOR_CANCELLED;
1170
1171         fly = MEM_callocN(sizeof(FlyInfo), "FlyOperation");
1172
1173         op->customdata = fly;
1174
1175         if (initFlyInfo(C, fly, op, event) == FALSE) {
1176                 MEM_freeN(op->customdata);
1177                 return OPERATOR_CANCELLED;
1178         }
1179
1180         flyEvent(fly, event);
1181
1182         WM_event_add_modal_handler(C, op);
1183
1184         return OPERATOR_RUNNING_MODAL;
1185 }
1186
1187 static int fly_cancel(bContext *C, wmOperator *op)
1188 {
1189         FlyInfo *fly = op->customdata;
1190
1191         fly->state = FLY_CANCEL;
1192         flyEnd(C, fly);
1193         op->customdata = NULL;
1194
1195         return OPERATOR_CANCELLED;
1196 }
1197
1198 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
1199 {
1200         int exit_code;
1201         short do_draw = FALSE;
1202         FlyInfo *fly = op->customdata;
1203         RegionView3D *rv3d = fly->rv3d;
1204         Object *fly_object = fly->root_parent ? fly->root_parent : fly->v3d->camera;
1205
1206         fly->redraw = 0;
1207
1208         flyEvent(fly, event);
1209
1210         if (fly->ndof) { /* 3D mouse overrules [2D mouse + timer] */
1211                 if (event->type == NDOF_MOTION) {
1212                         flyApply_ndof(C, fly);
1213                 }
1214         }
1215         else if (event->type == TIMER && event->customdata == fly->timer) {
1216                 flyApply(C, fly);
1217         }
1218
1219         do_draw |= fly->redraw;
1220
1221         exit_code = flyEnd(C, fly);
1222
1223         if (exit_code != OPERATOR_RUNNING_MODAL)
1224                 do_draw = TRUE;
1225
1226         if (do_draw) {
1227                 if (rv3d->persp == RV3D_CAMOB) {
1228                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, fly_object);
1229                 }
1230
1231                 // puts("redraw!"); // too frequent, commented with NDOF_FLY_DRAW_TOOMUCH for now
1232                 ED_region_tag_redraw(CTX_wm_region(C));
1233         }
1234
1235         return exit_code;
1236 }
1237
1238 void VIEW3D_OT_fly(wmOperatorType *ot)
1239 {
1240         /* identifiers */
1241         ot->name = "Fly Navigation";
1242         ot->description = "Interactively fly around the scene";
1243         ot->idname = "VIEW3D_OT_fly";
1244
1245         /* api callbacks */
1246         ot->invoke = fly_invoke;
1247         ot->cancel = fly_cancel;
1248         ot->modal = fly_modal;
1249         ot->poll = ED_operator_view3d_active;
1250
1251         /* flags */
1252         ot->flag = OPTYPE_BLOCKING;
1253 }