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