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