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