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