translated ndof menu from C to Python, enabled helicopter fly mode
[blender-staging.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                                 break;
481                         default:
482                                 ; // should always be one of the above 3
483                         }
484                 }
485         /* handle modal keymap first */
486         else if (event->type == EVT_MODAL_MAP) {
487                 switch (event->val) {
488                         case FLY_MODAL_CANCEL:
489                                 fly->state = FLY_CANCEL;
490                                 break;
491                         case FLY_MODAL_CONFIRM:
492                                 fly->state = FLY_CONFIRM;
493                                 break;
494
495                         case FLY_MODAL_ACCELERATE:
496                         {
497                                 double time_currwheel;
498                                 float time_wheel;
499
500                                 time_currwheel= PIL_check_seconds_timer();
501                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
502                                 fly->time_lastwheel = time_currwheel;
503                                 /*printf("Wheel %f\n", time_wheel);*/
504                                 /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
505                                 time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
506
507                                 if (fly->speed<0.0f) fly->speed= 0.0f;
508                                 else {
509                                         if (event->shift)
510                                                 fly->speed += fly->grid*time_wheel * 0.1f;
511                                         else
512                                                 fly->speed += fly->grid*time_wheel;
513                                 }
514                                 break;
515                         }
516                         case FLY_MODAL_DECELERATE:
517                         {
518                                 double time_currwheel;
519                                 float time_wheel;
520
521                                 time_currwheel= PIL_check_seconds_timer();
522                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
523                                 fly->time_lastwheel = time_currwheel;
524                                 time_wheel = 1.0f + (10.0f - (20.0f * MIN2(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
525
526                                 if (fly->speed>0) fly->speed=0;
527                                 else {
528                                         if (event->shift)
529                                                 fly->speed-= fly->grid*time_wheel * 0.1f;
530                                         else
531                                                 fly->speed-= fly->grid*time_wheel;
532                                 }
533                                 break;
534                         }
535                         case FLY_MODAL_PAN_ENABLE:
536                                 fly->pan_view= TRUE;
537                                 break;
538                         case FLY_MODAL_PAN_DISABLE:
539 //XXX2.5                warp_pointer(cent_orig[0], cent_orig[1]);
540                                 fly->pan_view= FALSE;
541                                 break;
542
543                                 /* impliment WASD keys */
544                         case FLY_MODAL_DIR_FORWARD:
545                                 if (fly->speed < 0.0f) fly->speed= -fly->speed; /* flip speed rather than stopping, game like motion */
546                                 else if (fly->axis==2) fly->speed += fly->grid; /* increse like mousewheel if were already moving in that difection*/
547                                 fly->axis= 2;
548                                 break;
549                         case FLY_MODAL_DIR_BACKWARD:
550                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
551                                 else if (fly->axis==2) fly->speed -= fly->grid;
552                                 fly->axis= 2;
553                                 break;
554                         case FLY_MODAL_DIR_LEFT:
555                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
556                                 else if (fly->axis==0) fly->speed += fly->grid;
557                                 fly->axis= 0;
558                                 break;
559                         case FLY_MODAL_DIR_RIGHT:
560                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
561                                 else if (fly->axis==0) fly->speed -= fly->grid;
562                                 fly->axis= 0;
563                                 break;
564                         case FLY_MODAL_DIR_DOWN:
565                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
566                                 else if (fly->axis==1) fly->speed += fly->grid;
567                                 fly->axis= 1;
568                                 break;
569                         case FLY_MODAL_DIR_UP:
570                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
571                                 else if (fly->axis==1) fly->speed -= fly->grid;
572                                 fly->axis= 1;
573                                 break;
574
575                         case FLY_MODAL_AXIS_LOCK_X:
576                                 if (fly->xlock) fly->xlock=0;
577                                 else {
578                                         fly->xlock = 2;
579                                         fly->xlock_momentum = 0.0;
580                                 }
581                                 break;
582                         case FLY_MODAL_AXIS_LOCK_Z:
583                                 if (fly->zlock) fly->zlock=0;
584                                 else {
585                                         fly->zlock = 2;
586                                         fly->zlock_momentum = 0.0;
587                                 }
588                                 break;
589
590                         case FLY_MODAL_PRECISION_ENABLE:
591                                 fly->use_precision= TRUE;
592                                 break;
593                         case FLY_MODAL_PRECISION_DISABLE:
594                                 fly->use_precision= FALSE;
595                                 break;
596                 }
597         }
598 }
599
600 static int flyApply(bContext *C, FlyInfo *fly)
601 {
602
603 #define FLY_ROTATE_FAC 2.5f /* more is faster */
604 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
605 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
606
607         /*
608         fly mode - Shift+F
609         a fly loop where the user can move move the view as if they are flying
610         */
611         RegionView3D *rv3d= fly->rv3d;
612         View3D *v3d = fly->v3d;
613         ARegion *ar = fly->ar;
614         Scene *scene= fly->scene;
615
616         float prev_view_mat[4][4];
617
618         float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
619         dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
620
621         /* Camera Uprighting variables */
622         upvec[3]={0,0,0}, /* stores the view's up vector */
623
624         moffset[2], /* mouse offset from the views center */
625         tmp_quat[4]; /* used for rotating the view */
626
627         int
628 //      cent_orig[2], /* view center */
629 //XXX- can avoid using //       cent[2], /* view center modified */
630         xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
631         unsigned char
632         apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
633
634         #ifdef NDOF_FLY_DEBUG
635         static unsigned int iteration = 1;
636         printf("fly timer %d\n", iteration++);
637         #endif
638
639         if(fly->root_parent)
640                 ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
641
642         xmargin= ar->winx/20.0f;
643         ymargin= ar->winy/20.0f;
644
645         // UNUSED
646         // cent_orig[0]= ar->winrct.xmin + ar->winx/2;
647         // cent_orig[1]= ar->winrct.ymin + ar->winy/2;
648
649         {
650
651                 /* mouse offset from the center */
652                 moffset[0]= fly->mval[0]- ar->winx/2;
653                 moffset[1]= fly->mval[1]- ar->winy/2;
654
655                 /* enforce a view margin */
656                 if (moffset[0]>xmargin)                 moffset[0]-=xmargin;
657                 else if (moffset[0] < -xmargin) moffset[0]+=xmargin;
658                 else                                                    moffset[0]=0;
659
660                 if (moffset[1]>ymargin)                 moffset[1]-=ymargin;
661                 else if (moffset[1] < -ymargin) moffset[1]+=ymargin;
662                 else                                                    moffset[1]=0;
663
664
665                 /* scale the mouse movement by this value - scales mouse movement to the view size
666                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
667                  *
668                  * the mouse moves isnt linear */
669
670                 if(moffset[0]) {
671                         moffset[0] /= ar->winx - (xmargin*2);
672                         moffset[0] *= fabsf(moffset[0]);
673                 }
674
675                 if(moffset[1]) {
676                         moffset[1] /= ar->winy - (ymargin*2);
677                         moffset[1] *= fabsf(moffset[1]);
678                 }
679
680                 /* Should we redraw? */
681                 if(fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
682                         float dvec_tmp[3];
683                         double time_current; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
684                         float time_redraw;
685                         float time_redraw_clamped;
686
687                         fly->redraw= 1;
688
689                         time_current= PIL_check_seconds_timer();
690                         time_redraw= (float)(time_current - fly->time_lastdraw);
691                         time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
692                         fly->time_lastdraw= time_current;
693                         /*fprintf(stderr, "%f\n", time_redraw);*/ /* 0.002 is a small redraw 0.02 is larger */
694
695                         /* Scale the time to use shift to scale the speed down- just like
696                         shift slows many other areas of blender down */
697                         if (fly->use_precision)
698                                 fly->speed= fly->speed * (1.0f-time_redraw_clamped);
699
700                         copy_m3_m4(mat, rv3d->viewinv);
701
702                         if (fly->pan_view==TRUE) {
703                                 /* pan only */
704                                 dvec_tmp[0]= -moffset[0];
705                                 dvec_tmp[1]= -moffset[1];
706                                 dvec_tmp[2]= 0;
707
708                                 if (fly->use_precision) {
709                                         dvec_tmp[0] *= 0.1f;
710                                         dvec_tmp[1] *= 0.1f;
711                                 }
712
713                                 mul_m3_v3(mat, dvec_tmp);
714                                 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
715
716                         } else {
717                                 float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
718
719                                 /* rotate about the X axis- look up/down */
720                                 if (moffset[1]) {
721                                         upvec[0]=1;
722                                         upvec[1]=0;
723                                         upvec[2]=0;
724                                         mul_m3_v3(mat, upvec);
725                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); /* Rotate about the relative up vec */
726                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
727
728                                         if (fly->xlock) fly->xlock = 2; /*check for rotation*/
729                                         if (fly->zlock) fly->zlock = 2;
730                                         fly->xlock_momentum= 0.0f;
731                                 }
732
733                                 /* rotate about the Y axis- look left/right */
734                                 if (moffset[0]) {
735
736                                         /* if we're upside down invert the moffset */
737                                         upvec[0]=0;
738                                         upvec[1]=1;
739                                         upvec[2]=0;
740                                         mul_m3_v3(mat, upvec);
741
742                                         if(upvec[2] < 0.0f)
743                                                 moffset[0]= -moffset[0];
744
745                                         /* make the lock vectors */
746                                         if (fly->zlock) {
747                                                 upvec[0]=0;
748                                                 upvec[1]=0;
749                                                 upvec[2]=1;
750                                         } else {
751                                                 upvec[0]=0;
752                                                 upvec[1]=1;
753                                                 upvec[2]=0;
754                                                 mul_m3_v3(mat, upvec);
755                                         }
756
757                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
758                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
759
760                                         if (fly->xlock) fly->xlock = 2;/*check for rotation*/
761                                         if (fly->zlock) fly->zlock = 2;
762                                 }
763
764                                 if (fly->zlock==2) {
765                                         upvec[0]=1;
766                                         upvec[1]=0;
767                                         upvec[2]=0;
768                                         mul_m3_v3(mat, upvec);
769
770                                         /*make sure we have some z rolling*/
771                                         if (fabsf(upvec[2]) > 0.00001f) {
772                                                 roll= upvec[2]*5;
773                                                 upvec[0]=0; /*rotate the view about this axis*/
774                                                 upvec[1]=0;
775                                                 upvec[2]=1;
776
777                                                 mul_m3_v3(mat, upvec);
778                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
779                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
780
781                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
782                                         } else {
783                                                 fly->zlock=1; /* dont check until the view rotates again */
784                                                 fly->zlock_momentum= 0.0f;
785                                         }
786                                 }
787
788                                 if (fly->xlock==2 && moffset[1]==0) { /*only apply xcorrect when mouse isnt applying x rot*/
789                                         upvec[0]=0;
790                                         upvec[1]=0;
791                                         upvec[2]=1;
792                                         mul_m3_v3(mat, upvec);
793                                         /*make sure we have some z rolling*/
794                                         if (fabs(upvec[2]) > 0.00001) {
795                                                 roll= upvec[2] * -5;
796
797                                                 upvec[0]= 1.0f; /*rotate the view about this axis*/
798                                                 upvec[1]= 0.0f;
799                                                 upvec[2]= 0.0f;
800
801                                                 mul_m3_v3(mat, upvec);
802
803                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->xlock_momentum*0.1f); /* Rotate about the relative up vec */
804                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
805
806                                                 fly->xlock_momentum += 0.05f;
807                                         } else {
808                                                 fly->xlock=1; /* see above */
809                                                 fly->xlock_momentum= 0.0f;
810                                         }
811                                 }
812
813
814                                 if (apply_rotation) {
815                                         /* Normal operation */
816                                         /* define dvec, view direction vector */
817                                         dvec_tmp[0]= dvec_tmp[1]= dvec_tmp[2]= 0.0f;
818                                         /* move along the current axis */
819                                         dvec_tmp[fly->axis]= 1.0f;
820
821                                         mul_m3_v3(mat, dvec_tmp);
822
823                                         mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
824                                 }
825                         }
826
827                         /* impose a directional lag */
828                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f/(1.0f+(time_redraw*5.0f))));
829
830                         if (rv3d->persp==RV3D_CAMOB) {
831                                 Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
832                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
833                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
834                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
835                         }
836
837                         add_v3_v3(rv3d->ofs, dvec);
838
839                         /* todo, dynamic keys */
840 #if 0
841                         if (fly->zlock && fly->xlock)
842                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
843                         else if (fly->zlock)
844                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
845                         else if (fly->xlock)
846                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
847                         else
848                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
849 #endif
850
851                         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
852                         if (rv3d->persp==RV3D_CAMOB) {
853                                 ID *id_key;
854                                 /* transform the parent or the camera? */
855                                 if(fly->root_parent) {
856                                         Object *ob_update;
857
858                                         float view_mat[4][4];
859                                         float prev_view_imat[4][4];
860                                         float diff_mat[4][4];
861                                         float parent_mat[4][4];
862
863                                         invert_m4_m4(prev_view_imat, prev_view_mat);
864                                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
865                                         mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
866                                         mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
867                                         object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
868
869                                         // where_is_object(scene, fly->root_parent);
870
871                                         ob_update= v3d->camera->parent;
872                                         while(ob_update) {
873                                                 DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
874                                                 ob_update= ob_update->parent;
875                                         }
876
877                                         copy_m4_m4(prev_view_mat, view_mat);
878
879                                         id_key= &fly->root_parent->id;
880
881                                 }
882                                 else {
883                                         float view_mat[4][4];
884                                         ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
885                                         object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
886                                         id_key= &v3d->camera->id;
887                                 }
888
889                                 /* record the motion */
890                                 if (autokeyframe_cfra_can_key(scene, id_key)) {
891                                         ListBase dsources = {NULL, NULL};
892                                         
893                                         /* add datasource override for the camera object */
894                                         ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
895                                         
896                                         /* insert keyframes 
897                                          *      1) on the first frame
898                                          *      2) on each subsequent frame
899                                          *              TODO: need to check in future that frame changed before doing this 
900                                          */
901                                         if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
902                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
903                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
904                                         }
905                                         if (fly->speed) {
906                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
907                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
908                                         }
909                                         
910                                         /* free temp data */
911                                         BLI_freelistN(&dsources);
912                                 }
913                         }
914                 } else
915                         /*were not redrawing but we need to update the time else the view will jump */
916                         fly->time_lastdraw= PIL_check_seconds_timer();
917                 /* end drawing */
918                 copy_v3_v3(fly->dvec_prev, dvec);
919         }
920
921         return OPERATOR_FINISHED;
922 }
923
924 static int flyApply_ndof(bContext *C, FlyInfo *fly)
925 {
926         // shorthand for oft-used variables
927         wmNDOFMotionData* ndof = fly->ndof;
928         const float dt = ndof->dt;
929         RegionView3D* rv3d = fly->rv3d;
930
931         const int shouldRotate = 1, shouldTranslate = 1;
932
933         float view_inv[4];
934         invert_qt_qt(view_inv, rv3d->viewquat);
935
936         if (shouldRotate)
937                 {
938                 const float turn_sensitivity = 1.f;
939
940                 float rotation[4];
941                 float axis[3];
942                 float angle = turn_sensitivity * ndof_to_angle_axis(ndof, axis);
943
944                 // transform rotation axis from view to world coordinates
945                 mul_qt_v3(view_inv, axis);
946
947                 // apply rotation to view
948                 axis_angle_to_quat(rotation, axis, angle);
949                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);          
950
951                 rv3d->view = RV3D_VIEW_USER;
952
953                 fly->redraw = 1;
954                 }
955
956         if (shouldTranslate)
957                 {
958                 const float forward_sensitivity = 1.f;
959                 const float vertical_sensitivity = 0.4f;
960                 const float lateral_sensitivity = 0.6f;
961
962                 float speed = 10.f; // blender units per second
963                 // ^^ this is ok for default cube scene, but should scale with.. something
964
965                 float trans[3] = {
966                         lateral_sensitivity * ndof->tx,
967                         vertical_sensitivity * ndof->ty,
968                         forward_sensitivity * ndof->tz
969                         };
970
971                 mul_v3_fl(trans, speed * dt);
972
973                 // transform motion from view to world coordinates
974                 mul_qt_v3(view_inv, trans);
975
976                 // int fly_mode = TRUE;
977                 int fly_mode = U.ndof_flag & NDOF_FLY_HELICOPTER;
978                 // could also use RNA to get a simple boolean value
979
980                 if (fly_mode)
981                         {
982                         trans[2] = speed * dt * vertical_sensitivity * ndof->ty;
983                         }
984
985                 // move center of view opposite of hand motion (this is camera mode, not object mode)
986                 sub_v3_v3(rv3d->ofs, trans);
987
988                 fly->redraw = 1;
989                 }
990
991         return OPERATOR_FINISHED;
992 }
993
994
995 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
996 {
997         RegionView3D *rv3d= CTX_wm_region_view3d(C);
998         FlyInfo *fly;
999
1000         if(rv3d->viewlock)
1001                 return OPERATOR_CANCELLED;
1002
1003         fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
1004
1005         op->customdata= fly;
1006
1007         if(initFlyInfo(C, fly, op, event)==FALSE) {
1008                 MEM_freeN(op->customdata);
1009                 return OPERATOR_CANCELLED;
1010         }
1011
1012         flyEvent(fly, event);
1013
1014         WM_event_add_modal_handler(C, op);
1015
1016         return OPERATOR_RUNNING_MODAL;
1017 }
1018
1019 static int fly_cancel(bContext *C, wmOperator *op)
1020 {
1021         FlyInfo *fly = op->customdata;
1022
1023         fly->state = FLY_CANCEL;
1024         flyEnd(C, fly);
1025         op->customdata= NULL;
1026
1027         return OPERATOR_CANCELLED;
1028 }
1029
1030 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
1031 {
1032         int exit_code;
1033         short do_draw= FALSE;
1034         FlyInfo *fly= op->customdata;
1035         RegionView3D *rv3d= fly->rv3d;
1036         Object *fly_object= fly->root_parent ? fly->root_parent : fly->v3d->camera;
1037
1038         fly->redraw= 0;
1039
1040         flyEvent(fly, event);
1041
1042         if (fly->ndof) // 3D mouse overrules [2D mouse + timer]
1043                 {
1044                 if (event->type==NDOF_MOTION)
1045                         flyApply_ndof(C, fly);
1046                 }
1047         else if (event->type==TIMER && event->customdata == fly->timer)
1048                 flyApply(C, fly);
1049
1050         do_draw |= fly->redraw;
1051
1052         exit_code = flyEnd(C, fly);
1053
1054         if(exit_code!=OPERATOR_RUNNING_MODAL)
1055                 do_draw= TRUE;
1056
1057         if(do_draw) {
1058                 if(rv3d->persp==RV3D_CAMOB) {
1059                         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object);
1060                 }
1061
1062                 ED_region_tag_redraw(CTX_wm_region(C));
1063         }
1064
1065         return exit_code;
1066 }
1067
1068 void VIEW3D_OT_fly(wmOperatorType *ot)
1069 {
1070         /* identifiers */
1071         ot->name= "Fly Navigation";
1072         ot->description= "Interactively fly around the scene";
1073         ot->idname= "VIEW3D_OT_fly";
1074
1075         /* api callbacks */
1076         ot->invoke= fly_invoke;
1077         ot->cancel= fly_cancel;
1078         ot->modal= fly_modal;
1079         ot->poll= ED_operator_view3d_active;
1080
1081         /* flags */
1082         ot->flag= OPTYPE_BLOCKING;
1083 }