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