bugfix [#24376] Fly mode disturbs the rotation or scale of the camera object
[blender.git] / source / blender / editors / space_view3d / view3d_fly.c
1 /**
2  * $Id: view3d_view.c 32630 2010-10-21 09:02:21Z campbellbarton $
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 /* defines VIEW3D_OT_fly modal operator */
26
27 #include "DNA_anim_types.h"
28 #include "DNA_scene_types.h"
29 #include "DNA_object_types.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_math.h"
34 #include "BLI_blenlib.h"
35
36 #include "BKE_context.h"
37 #include "BKE_object.h"
38 #include "BKE_report.h"
39
40 #include "BKE_depsgraph.h" /* for fly mode updating */
41
42 #include "BIF_gl.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46
47 #include "ED_keyframing.h"
48 #include "ED_screen.h"
49 #include "ED_space_api.h"
50
51 #include "PIL_time.h" /* smoothview */
52
53 #include "view3d_intern.h"      // own include
54
55 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
56 #define FLY_MODAL_CANCEL                        1
57 #define FLY_MODAL_CONFIRM                       2
58 #define FLY_MODAL_ACCELERATE            3
59 #define FLY_MODAL_DECELERATE            4
60 #define FLY_MODAL_PAN_ENABLE            5
61 #define FLY_MODAL_PAN_DISABLE           6
62 #define FLY_MODAL_DIR_FORWARD           7
63 #define FLY_MODAL_DIR_BACKWARD          8
64 #define FLY_MODAL_DIR_LEFT                      9
65 #define FLY_MODAL_DIR_RIGHT                     10
66 #define FLY_MODAL_DIR_UP                        11
67 #define FLY_MODAL_DIR_DOWN                      12
68 #define FLY_MODAL_AXIS_LOCK_X           13
69 #define FLY_MODAL_AXIS_LOCK_Z           14
70 #define FLY_MODAL_PRECISION_ENABLE      15
71 #define FLY_MODAL_PRECISION_DISABLE     16
72
73 /* called in transform_ops.c, on each regeneration of keymaps  */
74 void fly_modal_keymap(wmKeyConfig *keyconf)
75 {
76         static EnumPropertyItem modal_items[] = {
77         {FLY_MODAL_CANCEL,      "CANCEL", 0, "Cancel", ""},
78         {FLY_MODAL_CONFIRM,     "CONFIRM", 0, "Confirm", ""},
79         {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
80         {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
81
82         {FLY_MODAL_PAN_ENABLE,  "PAN_ENABLE", 0, "Pan Enable", ""},
83         {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
84
85         {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
86         {FLY_MODAL_DIR_BACKWARD,"BACKWARD", 0, "Fly Backward", ""},
87         {FLY_MODAL_DIR_LEFT,    "LEFT", 0, "Fly Left", ""},
88         {FLY_MODAL_DIR_RIGHT,   "RIGHT", 0, "Fly Right", ""},
89         {FLY_MODAL_DIR_UP,              "UP", 0, "Fly Up", ""},
90         {FLY_MODAL_DIR_DOWN,    "DOWN", 0, "Fly Down", ""},
91
92         {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
93         {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
94
95         {FLY_MODAL_PRECISION_ENABLE,    "PRECISION_ENABLE", 0, "Precision Enable", ""},
96         {FLY_MODAL_PRECISION_DISABLE,   "PRECISION_DISABLE", 0, "Precision Disable", ""},
97
98         {0, NULL, 0, NULL, NULL}};
99
100         wmKeyMap *keymap= WM_modalkeymap_get(keyconf, "View3D Fly Modal");
101
102         /* this function is called for each spacetype, only needs to add map once */
103         if(keymap) return;
104
105         keymap= WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
106
107         /* items for modal map */
108         WM_modalkeymap_add_item(keymap, ESCKEY,    KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
109         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
110
111         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
112         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
113         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
114         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
115
116         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
117         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
118         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, FLY_MODAL_ACCELERATE);
119         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, FLY_MODAL_DECELERATE);
120
121         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
122         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 */
123
124         /* WASD */
125         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
126         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
127         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
128         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
129         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
130         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
131
132         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
133         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
134
135         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
136         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
137
138         /* assign map to operators */
139         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
140
141 }
142
143 typedef struct FlyInfo {
144         /* context stuff */
145         RegionView3D *rv3d;
146         View3D *v3d;
147         ARegion *ar;
148         Scene *scene;
149
150         wmTimer *timer; /* needed for redraws */
151
152         short state;
153         short use_precision;
154         short redraw;
155         short mval[2];
156
157         /* fly state state */
158         float speed; /* the speed the view is moving per redraw */
159         short axis; /* Axis index to move allong by default Z to move allong the view */
160         short pan_view; /* when true, pan the view instead of rotating */
161
162         /* relative view axis locking - xlock, zlock
163         0; disabled
164         1; enabled but not checking because mouse hasnt moved outside the margin since locking was checked an not needed
165            when the mouse moves, locking is set to 2 so checks are done.
166         2; mouse moved and checking needed, if no view altering is donem its changed back to 1 */
167         short xlock, zlock;
168         float xlock_momentum, zlock_momentum; /* nicer dynamics */
169         float grid; /* world scale 1.0 default */
170
171         /* root most parent */
172         Object *root_parent;
173
174         /* backup values */
175         float dist_backup; /* backup the views distance since we use a zero dist for fly mode */
176         float ofs_backup[3]; /* backup the views offset incase the user cancels flying in non camera mode */
177         float rot_backup[4]; /* backup the views quat incase the user cancels flying in non camera mode. (quat for view, eul for camera) */
178         short persp_backup; /* remember if were ortho or not, only used for restoring the view if it was a ortho view */
179
180         void *obtfm; /* backup the objects transform */
181
182         /* compare between last state */
183         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
184         double time_lastdraw; /* time between draws */
185
186         void *draw_handle_pixel;
187
188         /* use for some lag */
189         float dvec_prev[3]; /* old for some lag */
190
191 } FlyInfo;
192
193 static void drawFlyPixel(const struct bContext *UNUSED(C), struct ARegion *UNUSED(ar), void *arg)
194 {
195         FlyInfo *fly = arg;
196
197         /* draws 4 edge brackets that frame the safe area where the
198         mouse can move during fly mode without spinning the view */
199         float x1, x2, y1, y2;
200         
201         x1= 0.45*(float)fly->ar->winx;
202         y1= 0.45*(float)fly->ar->winy;
203         x2= 0.55*(float)fly->ar->winx;
204         y2= 0.55*(float)fly->ar->winy;
205         cpack(0);
206         
207         
208         glBegin(GL_LINES);
209         /* bottom left */
210         glVertex2f(x1,y1); 
211         glVertex2f(x1,y1+5);
212         
213         glVertex2f(x1,y1); 
214         glVertex2f(x1+5,y1);
215         
216         /* top right */
217         glVertex2f(x2,y2); 
218         glVertex2f(x2,y2-5);
219         
220         glVertex2f(x2,y2); 
221         glVertex2f(x2-5,y2);
222         
223         /* top left */
224         glVertex2f(x1,y2); 
225         glVertex2f(x1,y2-5);
226         
227         glVertex2f(x1,y2); 
228         glVertex2f(x1+5,y2);
229         
230         /* bottom right */
231         glVertex2f(x2,y1); 
232         glVertex2f(x2,y1+5);
233         
234         glVertex2f(x2,y1); 
235         glVertex2f(x2-5,y1);
236         glEnd();
237 }
238
239 /* FlyInfo->state */
240 #define FLY_RUNNING             0
241 #define FLY_CANCEL              1
242 #define FLY_CONFIRM             2
243
244 static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event)
245 {
246         float upvec[3]; // tmp
247         float mat[3][3];
248
249         fly->rv3d= CTX_wm_region_view3d(C);
250         fly->v3d = CTX_wm_view3d(C);
251         fly->ar = CTX_wm_region(C);
252         fly->scene= CTX_data_scene(C);
253
254         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
255                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
256                 return FALSE;
257         }
258
259         if(fly->v3d->ob_centre) {
260                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view is locked to an object");
261                 return FALSE;
262         }
263
264         if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->constraints.first) {
265                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
266                 return FALSE;
267         }
268
269         fly->state= FLY_RUNNING;
270         fly->speed= 0.0f;
271         fly->axis= 2;
272         fly->pan_view= FALSE;
273         fly->xlock= FALSE;
274         fly->zlock= FALSE;
275         fly->xlock_momentum=0.0f;
276         fly->zlock_momentum=0.0f;
277         fly->grid= 1.0f;
278         fly->use_precision= 0;
279
280         fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
281
282         fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
283
284         fly->mval[0] = event->x - fly->ar->winrct.xmin;
285         fly->mval[1] = event->y - fly->ar->winrct.ymin;
286
287
288         fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
289
290         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
291
292         fly->rv3d->rflag |= RV3D_NAVIGATING; /* so we draw the corner margins */
293
294         /* detect weather to start with Z locking */
295         upvec[0]=1.0f; upvec[1]=0.0f; upvec[2]=0.0f;
296         copy_m3_m4(mat, fly->rv3d->viewinv);
297         mul_m3_v3(mat, upvec);
298         if (fabs(upvec[2]) < 0.1)
299                 fly->zlock = 1;
300         upvec[0]=0; upvec[1]=0; upvec[2]=0;
301
302         fly->persp_backup= fly->rv3d->persp;
303         fly->dist_backup= fly->rv3d->dist;
304         if (fly->rv3d->persp==RV3D_CAMOB) {
305                 Object *ob_back;
306                 if((fly->root_parent=fly->v3d->camera->parent)) {
307                         while(fly->root_parent->parent)
308                                 fly->root_parent= fly->root_parent->parent;
309                         ob_back= fly->root_parent;
310                 }
311                 else {
312                         ob_back= fly->v3d->camera;
313                 }
314
315                 /* store the original camera loc and rot */
316                 /* TODO. axis angle etc */
317
318                 fly->obtfm= object_tfm_backup(ob_back);
319
320                 where_is_object(fly->scene, fly->v3d->camera);
321                 negate_v3_v3(fly->rv3d->ofs, fly->v3d->camera->obmat[3]);
322
323                 fly->rv3d->dist=0.0;
324         } else {
325                 /* perspective or ortho */
326                 if (fly->rv3d->persp==RV3D_ORTHO)
327                         fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
328                 copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
329                 copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
330                 fly->rv3d->dist= 0.0f;
331
332                 upvec[2]= fly->dist_backup; /*x and y are 0*/
333                 mul_m3_v3(mat, upvec);
334                 sub_v3_v3(fly->rv3d->ofs, upvec);
335                 /*Done with correcting for the dist*/
336         }
337
338         
339         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
340         WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
341         
342         return 1;
343 }
344
345 static int flyEnd(bContext *C, FlyInfo *fly)
346 {
347         RegionView3D *rv3d= fly->rv3d;
348         View3D *v3d = fly->v3d;
349
350         float upvec[3];
351
352         if(fly->state == FLY_RUNNING)
353                 return OPERATOR_RUNNING_MODAL;
354
355         WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
356
357         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
358
359         rv3d->dist= fly->dist_backup;
360
361         if (fly->state == FLY_CANCEL) {
362         /* Revert to original view? */
363                 if (fly->persp_backup==RV3D_CAMOB) { /* a camera view */
364                         Object *ob_back;
365                         if(fly->root_parent)ob_back= fly->root_parent;
366                         else                            ob_back= fly->v3d->camera;
367
368                         /* store the original camera loc and rot */
369                         object_tfm_restore(ob_back, fly->obtfm);
370
371                         DAG_id_flush_update(&ob_back->id, OB_RECALC_OB);
372                 } else {
373                         /* Non Camera we need to reset the view back to the original location bacause the user canceled*/
374                         copy_qt_qt(rv3d->viewquat, fly->rot_backup);
375                         copy_v3_v3(rv3d->ofs, fly->ofs_backup);
376                         rv3d->persp= fly->persp_backup;
377                 }
378         }
379         else if (fly->persp_backup==RV3D_CAMOB) {       /* camera */
380                 DAG_id_flush_update(fly->root_parent ? &fly->root_parent->id : &v3d->camera->id, OB_RECALC_OB);
381         }
382         else { /* not camera */
383                 /* Apply the fly mode view */
384                 /*restore the dist*/
385                 float mat[3][3];
386                 upvec[0]= upvec[1]= 0;
387                 upvec[2]= fly->dist_backup; /*x and y are 0*/
388                 copy_m3_m4(mat, rv3d->viewinv);
389                 mul_m3_v3(mat, upvec);
390                 add_v3_v3(rv3d->ofs, upvec);
391                 /*Done with correcting for the dist */
392         }
393
394         rv3d->rflag &= ~RV3D_NAVIGATING;
395 //XXX2.5        BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */
396
397         if(fly->obtfm)
398                 MEM_freeN(fly->obtfm);
399
400         if(fly->state == FLY_CONFIRM) {
401                 MEM_freeN(fly);
402                 return OPERATOR_FINISHED;
403         }
404
405         MEM_freeN(fly);
406         return OPERATOR_CANCELLED;
407 }
408
409 static void flyEvent(FlyInfo *fly, wmEvent *event)
410 {
411         if (event->type == TIMER && event->customdata == fly->timer) {
412                 fly->redraw = 1;
413         }
414         else if (event->type == MOUSEMOVE) {
415                 fly->mval[0] = event->x - fly->ar->winrct.xmin;
416                 fly->mval[1] = event->y - fly->ar->winrct.ymin;
417         } /* handle modal keymap first */
418         else if (event->type == EVT_MODAL_MAP) {
419                 switch (event->val) {
420                         case FLY_MODAL_CANCEL:
421                                 fly->state = FLY_CANCEL;
422                                 break;
423                         case FLY_MODAL_CONFIRM:
424                                 fly->state = FLY_CONFIRM;
425                                 break;
426
427                         case FLY_MODAL_ACCELERATE:
428                         {
429                                 double time_currwheel;
430                                 float time_wheel;
431
432                                 time_currwheel= PIL_check_seconds_timer();
433                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
434                                 fly->time_lastwheel = time_currwheel;
435                                 /*printf("Wheel %f\n", time_wheel);*/
436                                 /*Mouse wheel delays range from 0.5==slow to 0.01==fast*/
437                                 time_wheel = 1+ (10 - (20*MIN2(time_wheel, 0.5))); /* 0-0.5 -> 0-5.0 */
438
439                                 if (fly->speed<0.0f) fly->speed= 0.0f;
440                                 else {
441                                         if (event->shift)
442                                                 fly->speed+= fly->grid*time_wheel*0.1;
443                                         else
444                                                 fly->speed+= fly->grid*time_wheel;
445                                 }
446                                 break;
447                         }
448                         case FLY_MODAL_DECELERATE:
449                         {
450                                 double time_currwheel;
451                                 float time_wheel;
452
453                                 time_currwheel= PIL_check_seconds_timer();
454                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
455                                 fly->time_lastwheel = time_currwheel;
456                                 time_wheel = 1+ (10 - (20*MIN2(time_wheel, 0.5))); /* 0-0.5 -> 0-5.0 */
457
458                                 if (fly->speed>0) fly->speed=0;
459                                 else {
460                                         if (event->shift)
461                                                 fly->speed-= fly->grid*time_wheel*0.1;
462                                         else
463                                                 fly->speed-= fly->grid*time_wheel;
464                                 }
465                                 break;
466                         }
467                         case FLY_MODAL_PAN_ENABLE:
468                                 fly->pan_view= TRUE;
469                                 break;
470                         case FLY_MODAL_PAN_DISABLE:
471 //XXX2.5                warp_pointer(cent_orig[0], cent_orig[1]);
472                                 fly->pan_view= FALSE;
473                                 break;
474
475                                 /* impliment WASD keys */
476                         case FLY_MODAL_DIR_FORWARD:
477                                 if (fly->speed < 0.0f) fly->speed= -fly->speed; /* flip speed rather then stopping, game like motion */
478                                 else if (fly->axis==2) fly->speed += fly->grid; /* increse like mousewheel if were already moving in that difection*/
479                                 fly->axis= 2;
480                                 break;
481                         case FLY_MODAL_DIR_BACKWARD:
482                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
483                                 else if (fly->axis==2) fly->speed -= fly->grid;
484                                 fly->axis= 2;
485                                 break;
486                         case FLY_MODAL_DIR_LEFT:
487                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
488                                 else if (fly->axis==0) fly->speed += fly->grid;
489                                 fly->axis= 0;
490                                 break;
491                         case FLY_MODAL_DIR_RIGHT:
492                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
493                                 else if (fly->axis==0) fly->speed -= fly->grid;
494                                 fly->axis= 0;
495                                 break;
496                         case FLY_MODAL_DIR_DOWN:
497                                 if (fly->speed < 0.0f) fly->speed= -fly->speed;
498                                 else if (fly->axis==1) fly->speed += fly->grid;
499                                 fly->axis= 1;
500                                 break;
501                         case FLY_MODAL_DIR_UP:
502                                 if (fly->speed > 0.0f) fly->speed= -fly->speed;
503                                 else if (fly->axis==1) fly->speed -= fly->grid;
504                                 fly->axis= 1;
505                                 break;
506
507                         case FLY_MODAL_AXIS_LOCK_X:
508                                 if (fly->xlock) fly->xlock=0;
509                                 else {
510                                         fly->xlock = 2;
511                                         fly->xlock_momentum = 0.0;
512                                 }
513                                 break;
514                         case FLY_MODAL_AXIS_LOCK_Z:
515                                 if (fly->zlock) fly->zlock=0;
516                                 else {
517                                         fly->zlock = 2;
518                                         fly->zlock_momentum = 0.0;
519                                 }
520                                 break;
521
522                         case FLY_MODAL_PRECISION_ENABLE:
523                                 fly->use_precision= TRUE;
524                                 break;
525                         case FLY_MODAL_PRECISION_DISABLE:
526                                 fly->use_precision= FALSE;
527                                 break;
528
529                 }
530         }
531 }
532
533 static int flyApply(bContext *C, FlyInfo *fly)
534 {
535
536 #define FLY_ROTATE_FAC 2.5f /* more is faster */
537 #define FLY_ZUP_CORRECT_FAC 0.1f /* ammount to correct per step */
538 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
539
540         /*
541         fly mode - Shift+F
542         a fly loop where the user can move move the view as if they are flying
543         */
544         RegionView3D *rv3d= fly->rv3d;
545         View3D *v3d = fly->v3d;
546         ARegion *ar = fly->ar;
547         Scene *scene= fly->scene;
548
549         float prev_view_mat[4][4];
550
551         float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
552         dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
553
554         /* Camera Uprighting variables */
555         upvec[3]={0,0,0}, /* stores the view's up vector */
556
557         moffset[2], /* mouse offset from the views center */
558         tmp_quat[4]; /* used for rotating the view */
559
560         int cent_orig[2], /* view center */
561 //XXX- can avoid using //       cent[2], /* view center modified */
562         xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
563         unsigned char
564         apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
565
566         if(fly->root_parent)
567                 view3d_persp_mat4(rv3d, prev_view_mat);
568
569         /* the dist defines a vector that is infront of the offset
570         to rotate the view about.
571         this is no good for fly mode because we
572         want to rotate about the viewers center.
573         but to correct the dist removal we must
574         alter offset so the view doesn't jump. */
575
576         xmargin= ar->winx/20.0f;
577         ymargin= ar->winy/20.0f;
578
579         cent_orig[0]= ar->winrct.xmin + ar->winx/2;
580         cent_orig[1]= ar->winrct.ymin + ar->winy/2;
581
582         {
583
584                 /* mouse offset from the center */
585                 moffset[0]= fly->mval[0]- ar->winx/2;
586                 moffset[1]= fly->mval[1]- ar->winy/2;
587
588                 /* enforce a view margin */
589                 if (moffset[0]>xmargin)                 moffset[0]-=xmargin;
590                 else if (moffset[0] < -xmargin) moffset[0]+=xmargin;
591                 else                                                    moffset[0]=0;
592
593                 if (moffset[1]>ymargin)                 moffset[1]-=ymargin;
594                 else if (moffset[1] < -ymargin) moffset[1]+=ymargin;
595                 else                                                    moffset[1]=0;
596
597
598                 /* scale the mouse movement by this value - scales mouse movement to the view size
599                  * moffset[0]/(ar->winx-xmargin*2) - window size minus margin (same for y)
600                  *
601                  * the mouse moves isnt linear */
602
603                 if(moffset[0]) {
604                         moffset[0] /= ar->winx - (xmargin*2);
605                         moffset[0] *= fabs(moffset[0]);
606                 }
607
608                 if(moffset[1]) {
609                         moffset[1] /= ar->winy - (ymargin*2);
610                         moffset[1] *= fabs(moffset[1]);
611                 }
612
613                 /* Should we redraw? */
614                 if(fly->speed != 0.0f || moffset[0] || moffset[1] || fly->zlock || fly->xlock || dvec[0] || dvec[1] || dvec[2] ) {
615                         float dvec_tmp[3];
616                         double time_current, time_redraw; /*time how fast it takes for us to redraw, this is so simple scenes dont fly too fast */
617                         float time_redraw_clamped;
618
619                         time_current= PIL_check_seconds_timer();
620                         time_redraw= (float)(time_current - fly->time_lastdraw);
621                         time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
622                         fly->time_lastdraw= time_current;
623                         /*fprintf(stderr, "%f\n", time_redraw);*/ /* 0.002 is a small redraw 0.02 is larger */
624
625                         /* Scale the time to use shift to scale the speed down- just like
626                         shift slows many other areas of blender down */
627                         if (fly->use_precision)
628                                 fly->speed= fly->speed * (1.0f-time_redraw_clamped);
629
630                         copy_m3_m4(mat, rv3d->viewinv);
631
632                         if (fly->pan_view==TRUE) {
633                                 /* pan only */
634                                 dvec_tmp[0]= -moffset[0];
635                                 dvec_tmp[1]= -moffset[1];
636                                 dvec_tmp[2]= 0;
637
638                                 if (fly->use_precision) {
639                                         dvec_tmp[0] *= 0.1;
640                                         dvec_tmp[1] *= 0.1;
641                                 }
642
643                                 mul_m3_v3(mat, dvec_tmp);
644                                 mul_v3_fl(dvec_tmp, time_redraw*200.0 * fly->grid);
645
646                         } else {
647                                 float roll; /* similar to the angle between the camera's up and the Z-up, but its very rough so just roll*/
648
649                                 /* rotate about the X axis- look up/down */
650                                 if (moffset[1]) {
651                                         upvec[0]=1;
652                                         upvec[1]=0;
653                                         upvec[2]=0;
654                                         mul_m3_v3(mat, upvec);
655                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); /* Rotate about the relative up vec */
656                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
657
658                                         if (fly->xlock) fly->xlock = 2; /*check for rotation*/
659                                         if (fly->zlock) fly->zlock = 2;
660                                         fly->xlock_momentum= 0.0f;
661                                 }
662
663                                 /* rotate about the Y axis- look left/right */
664                                 if (moffset[0]) {
665
666                                         /* if we're upside down invert the moffset */
667                                         upvec[0]=0;
668                                         upvec[1]=1;
669                                         upvec[2]=0;
670                                         mul_m3_v3(mat, upvec);
671
672                                         if(upvec[2] < 0.0f)
673                                                 moffset[0]= -moffset[0];
674
675                                         /* make the lock vectors */
676                                         if (fly->zlock) {
677                                                 upvec[0]=0;
678                                                 upvec[1]=0;
679                                                 upvec[2]=1;
680                                         } else {
681                                                 upvec[0]=0;
682                                                 upvec[1]=1;
683                                                 upvec[2]=0;
684                                                 mul_m3_v3(mat, upvec);
685                                         }
686
687                                         axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
688                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
689
690                                         if (fly->xlock) fly->xlock = 2;/*check for rotation*/
691                                         if (fly->zlock) fly->zlock = 2;
692                                 }
693
694                                 if (fly->zlock==2) {
695                                         upvec[0]=1;
696                                         upvec[1]=0;
697                                         upvec[2]=0;
698                                         mul_m3_v3(mat, upvec);
699
700                                         /*make sure we have some z rolling*/
701                                         if (fabs(upvec[2]) > 0.00001f) {
702                                                 roll= upvec[2]*5;
703                                                 upvec[0]=0; /*rotate the view about this axis*/
704                                                 upvec[1]=0;
705                                                 upvec[2]=1;
706
707                                                 mul_m3_v3(mat, upvec);
708                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->zlock_momentum * FLY_ZUP_CORRECT_FAC); /* Rotate about the relative up vec */
709                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
710
711                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
712                                         } else {
713                                                 fly->zlock=1; /* dont check until the view rotates again */
714                                                 fly->zlock_momentum= 0.0f;
715                                         }
716                                 }
717
718                                 if (fly->xlock==2 && moffset[1]==0) { /*only apply xcorrect when mouse isnt applying x rot*/
719                                         upvec[0]=0;
720                                         upvec[1]=0;
721                                         upvec[2]=1;
722                                         mul_m3_v3(mat, upvec);
723                                         /*make sure we have some z rolling*/
724                                         if (fabs(upvec[2]) > 0.00001) {
725                                                 roll= upvec[2] * -5;
726
727                                                 upvec[0]= 1.0f; /*rotate the view about this axis*/
728                                                 upvec[1]= 0.0f;
729                                                 upvec[2]= 0.0f;
730
731                                                 mul_m3_v3(mat, upvec);
732
733                                                 axis_angle_to_quat( tmp_quat, upvec, roll*time_redraw_clamped*fly->xlock_momentum*0.1f); /* Rotate about the relative up vec */
734                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
735
736                                                 fly->xlock_momentum += 0.05f;
737                                         } else {
738                                                 fly->xlock=1; /* see above */
739                                                 fly->xlock_momentum= 0.0f;
740                                         }
741                                 }
742
743
744                                 if (apply_rotation) {
745                                         /* Normal operation */
746                                         /* define dvec, view direction vector */
747                                         dvec_tmp[0]= dvec_tmp[1]= dvec_tmp[2]= 0.0f;
748                                         /* move along the current axis */
749                                         dvec_tmp[fly->axis]= 1.0f;
750
751                                         mul_m3_v3(mat, dvec_tmp);
752
753                                         mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
754                                 }
755                         }
756
757                         /* impose a directional lag */
758                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f/(1.0f+(time_redraw*5.0f))));
759
760                         if (rv3d->persp==RV3D_CAMOB) {
761                                 Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
762                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
763                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
764                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
765                         }
766
767                         add_v3_v3(rv3d->ofs, dvec);
768
769                         /* todo, dynamic keys */
770 #if 0
771                         if (fly->zlock && fly->xlock)
772                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
773                         else if (fly->zlock)
774                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z on,   Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
775                         else if (fly->xlock)
776                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X  on/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
777                         else
778                                 ED_area_headerprint(fly->ar, "FlyKeys  Speed:(+/- | Wheel),  Upright Axis:X off/Z off,  Slow:Shift,  Direction:WASDRF,  Ok:LMB,  Pan:MMB,  Cancel:RMB");
779 #endif
780
781                         /* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
782                         if (rv3d->persp==RV3D_CAMOB) {
783                                 ID *id_key;
784                                 /* transform the parent or the camera? */
785                                 if(fly->root_parent) {
786                                         Object *ob_update;
787                     
788                                         float view_mat[4][4];
789                                         float prev_view_imat[4][4];
790                                         float diff_mat[4][4];
791                                         float parent_mat[4][4];
792
793                                         invert_m4_m4(prev_view_imat, prev_view_mat);
794                                         view3d_persp_mat4(rv3d, view_mat);
795                                         mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
796                                         mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
797                                         object_apply_mat4(fly->root_parent, parent_mat, TRUE);
798
799                                         // where_is_object(scene, fly->root_parent);
800
801                                         ob_update= v3d->camera->parent;
802                                         while(ob_update) {
803                                                 DAG_id_flush_update(&ob_update->id, OB_RECALC_OB);
804                                                 ob_update= ob_update->parent;
805                                         }
806
807                                         copy_m4_m4(prev_view_mat, view_mat);
808
809                                         id_key= &fly->root_parent->id;
810
811                                 }
812                                 else {
813                                         float view_mat[4][4];
814                                         view3d_persp_mat4(rv3d, view_mat);
815                                         object_apply_mat4(v3d->camera, view_mat, TRUE);
816                                         id_key= &v3d->camera->id;
817                                 }
818
819                                 /* record the motion */
820                                 if (autokeyframe_cfra_can_key(scene, id_key)) {
821                                         ListBase dsources = {NULL, NULL};
822                                         
823                                         /* add datasource override for the camera object */
824                                         ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); 
825                                         
826                                         /* insert keyframes 
827                                          *      1) on the first frame
828                                          *      2) on each subsequent frame
829                                          *              TODO: need to check in future that frame changed before doing this 
830                                          */
831                                         if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
832                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
833                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
834                                         }
835                                         if (fly->speed) {
836                                                 KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
837                                                 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
838                                         }
839                                         
840                                         /* free temp data */
841                                         BLI_freelistN(&dsources);
842                                 }
843                         }
844                 } else
845                         /*were not redrawing but we need to update the time else the view will jump */
846                         fly->time_lastdraw= PIL_check_seconds_timer();
847                 /* end drawing */
848                 copy_v3_v3(fly->dvec_prev, dvec);
849         }
850
851 /* moved to flyEnd() */
852
853         return OPERATOR_FINISHED;
854 }
855
856
857
858 static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
859 {
860         RegionView3D *rv3d= CTX_wm_region_view3d(C);
861         FlyInfo *fly;
862
863         if(rv3d->viewlock)
864                 return OPERATOR_CANCELLED;
865
866         fly= MEM_callocN(sizeof(FlyInfo), "FlyOperation");
867
868         op->customdata= fly;
869
870         if(initFlyInfo(C, fly, op, event)==FALSE) {
871                 MEM_freeN(op->customdata);
872                 return OPERATOR_CANCELLED;
873         }
874
875         flyEvent(fly, event);
876
877         WM_event_add_modal_handler(C, op);
878
879         return OPERATOR_RUNNING_MODAL;
880 }
881
882 static int fly_cancel(bContext *C, wmOperator *op)
883 {
884         FlyInfo *fly = op->customdata;
885
886         fly->state = FLY_CANCEL;
887         flyEnd(C, fly);
888         op->customdata= NULL;
889
890         return OPERATOR_CANCELLED;
891 }
892
893 static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
894 {
895         int exit_code;
896         short do_draw= FALSE;
897         FlyInfo *fly = op->customdata;
898
899         fly->redraw= 0;
900
901         flyEvent(fly, event);
902
903         if(event->type==TIMER && event->customdata == fly->timer)
904                 flyApply(C, fly);
905
906         do_draw |= fly->redraw;
907
908         exit_code = flyEnd(C, fly);
909
910         if(exit_code!=OPERATOR_RUNNING_MODAL)
911                 do_draw= TRUE;
912         
913         if(do_draw) {
914                 if(fly->rv3d->persp==RV3D_CAMOB) {
915                         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly->root_parent ? fly->root_parent : fly->v3d->camera);
916                 }
917
918                 ED_region_tag_redraw(CTX_wm_region(C));
919         }
920
921         return exit_code;
922 }
923
924 void VIEW3D_OT_fly(wmOperatorType *ot)
925 {
926         /* identifiers */
927         ot->name= "Fly Navigation";
928         ot->description= "Interactively fly around the scene";
929         ot->idname= "VIEW3D_OT_fly";
930
931         /* api callbacks */
932         ot->invoke= fly_invoke;
933         ot->cancel= fly_cancel;
934         ot->modal= fly_modal;
935         ot->poll= ED_operator_view3d_active;
936
937         /* flags */
938         ot->flag= OPTYPE_BLOCKING;
939 }